Compare commits

..

No commits in common. "ec2784c94893d6a3ff4a6d7766041affffdd365a" and "83b644f0f5427427b326681d9baab1b6834e414b" have entirely different histories.

23 changed files with 96 additions and 600 deletions

View File

@ -1,60 +0,0 @@
# Example environment variables for the vichan imageboard Docker Compose setup.
# Copy this file to `.env` and customize values as needed.
# Do not commit `.env` with sensitive data to version control.
# General Settings
INSTANCE=0
# Web Service (Nginx) Settings
# SSL_CERT_PATH=/path/to/cert.pem
# SSL_KEY_PATH=/path/to/key.pem
# PHP Service Settings
## Database settings
VICHAN_MYSQL_HOST=db
VICHAN_MYSQL_USER=vichan
VICHAN_MYSQL_NAME=vichan
VICHAN_MYSQL_PASSWORD=vichan!
## Security
VICHAN_SECURE_LOGIN_ONLY=0
## Cookies
VICHAN_COOKIES_MOD=mod
## Flood Control
VICHAN_FLOOD_TIME=30
VICHAN_FLOOD_TIME_IP=120
VICHAN_FLOOD_TIME_SAME=3600
VICHAN_MAX_BODY=1800
VICHAN_REPLY_LIMIT=250
VICHAN_MAX_LINKS=20
## Images
VICHAN_IMAGES_MAX_FILESIZE=10485760
VICHAN_IMAGES_THUMB_WIDTH=250
VICHAN_IMAGES_THUMB_HEIGHT=250
VICHAN_IMAGES_MAX_WIDTH=10000
VICHAN_IMAGES_MAX_HEIGHT=10000
## Display
VICHAN_DISPLAY_THREADS_PER_PAGE=10
VICHAN_DISPLAY_MAX_PAGES=11
VICHAN_DISPLAY_THREADS_PREVIEW=5
## Directories
VICHAN_DIRECTORIES_ROOT=/
# Database Service (MariaDB) Settings
MYSQL_DATABASE=vichan
MYSQL_USER=vichan
MYSQL_PASSWORD=vichan!
MYSQL_ROOT_PASSWORD=vichan!!
# Cache Settings
VICHAN_CACHE_ENGINE=redis
VICHAN_CACHE_HOST=redis
VICHAN_CACHE_PORT=6379
VICHAN_CACHE_PASSWORD=redis!

1
.gitignore vendored
View File

@ -29,7 +29,6 @@
thumbs.db
Icon?
Thumbs.db
.env
*.patch
*.diff
*.rej

View File

@ -1,10 +1,8 @@
Vichan has reached its end of life. many of us have lost interest in the project, and the current direction of the internet has not been good for imageboards, whether its ID verification laws, big social media companies killing competition, or Google being hostile towards smaller sites. There are other imageboard engines out there but even if you use them, these are still issues you will need to think about if you start an imageboard or UGC website. You can still install or fork vichan, but do so at your own risk.
**In Memory of Fredrick Brennan (1994-2026).**
vichan - A lightweight and full featured PHP imageboard.
========================================================
**Please do not contact Fredrick Brennan in regards to vichan issues.**
As of 29 August 2022 it supports PHP8.1.
About
@ -84,9 +82,7 @@ To migrate from a Kusaba X board, use http://github.com/vichan-devel/Tinyboard-M
Demo
--------
Demo with the most updated version of [Vichan](https://demo.vichan.info).
This version of vichan has admin, janitor, and moderator access on it, with the usernames being admin, mod, and janny respectfully and the password being password. This test instance resets hourly.
Demo with the most updated version of [Vichan](https://vichan.27chan.org).
1. PHP 8.1
2. MySQL 5.7

View File

@ -1,22 +1,18 @@
services:
#nginx webserver + php 8.x
web:
build:
context: .
dockerfile: ./docker/nginx/Dockerfile
container_name: vichan_web
restart: unless-stopped
ports:
- "9090:80"
depends_on:
- db
volumes:
- ./local-instances/${INSTANCE:-0}/www:/var/www/html
- ./docker/nginx/vichan.conf:/etc/nginx/conf.d/default.conf
- ./docker/nginx/nginx.conf:/etc/nginx/nginx.conf
- ./docker/nginx/proxy.conf:/etc/nginx/conf.d/proxy.conf
# - ./docker/nginx/ssl:/etc/nginx/ssl # optional For SSL
ports:
- "9080:80"
# - "9081:443" # optional for SSL
depends_on:
- db
- php
links:
- php
@ -24,77 +20,21 @@ services:
build:
context: .
dockerfile: ./docker/php/Dockerfile
container_name: vichan_php
restart: unless-stopped
volumes:
- ./local-instances/${INSTANCE:-0}/www:/var/www
- ./docker/php/www.conf:/usr/local/etc/php-fpm.d/www.conf
- ./docker/php/jit.ini:/usr/local/etc/php/conf.d/jit.ini
- ./docker/php/php.ini:/usr/local/etc/php/php.ini
environment:
# Database settings
VICHAN_MYSQL_HOST: ${VICHAN_MYSQL_HOST}
VICHAN_MYSQL_USER: ${VICHAN_MYSQL_USER}
VICHAN_MYSQL_NAME: ${VICHAN_MYSQL_NAME}
VICHAN_MYSQL_PASSWORD: ${VICHAN_MYSQL_PASSWORD}
# Security
VICHAN_SECURE_LOGIN_ONLY: ${VICHAN_SECURE_LOGIN_ONLY}
# Cache settings
VICHAN_CACHE_ENGINE: ${VICHAN_CACHE_ENGINE}
VICHAN_CACHE_HOST: ${VICHAN_CACHE_HOST}
VICHAN_CACHE_PORT: ${VICHAN_CACHE_PORT}
VICHAN_CACHE_PASSWORD: ${VICHAN_CACHE_PASSWORD}
# Cookies
VICHAN_COOKIES_MOD: ${VICHAN_COOKIES_MOD}
# Flood Control
VICHAN_FLOOD_TIME: ${VICHAN_FLOOD_TIME}
VICHAN_FLOOD_TIME_IP: ${VICHAN_FLOOD_TIME_IP}
VICHAN_FLOOD_TIME_SAME: ${VICHAN_FLOOD_TIME_SAME}
VICHAN_MAX_BODY: ${VICHAN_MAX_BODY}
VICHAN_REPLY_LIMIT: ${VICHAN_REPLY_LIMIT}
VICHAN_MAX_LINKS: ${VICHAN_MAX_LINKS}
# Images
VICHAN_IMAGES_MAX_FILESIZE: ${VICHAN_IMAGES_MAX_FILESIZE}
VICHAN_IMAGES_THUMB_WIDTH: ${VICHAN_IMAGES_THUMB_WIDTH}
VICHAN_IMAGES_THUMB_HEIGHT: ${VICHAN_IMAGES_THUMB_HEIGHT}
VICHAN_IMAGES_MAX_WIDTH: ${VICHAN_IMAGES_MAX_WIDTH}
VICHAN_IMAGES_MAX_HEIGHT: ${VICHAN_IMAGES_MAX_HEIGHT}
# Display
VICHAN_DISPLAY_THREADS_PER_PAGE: ${VICHAN_DISPLAY_THREADS_PER_PAGE}
VICHAN_DISPLAY_MAX_PAGES: ${VICHAN_DISPLAY_MAX_PAGES}
VICHAN_DISPLAY_THREADS_PREVIEW: ${VICHAN_DISPLAY_THREADS_PREVIEW}
# Directories
VICHAN_DIRECTORIES_ROOT: ${VICHAN_DIRECTORIES_ROOT}
depends_on:
- db
#MySQL Service
db:
image: mysql:lts
# image: mariadb:lts # optional for more higher performance
container_name: vichan_db
image: mysql:8.0.35
container_name: db
restart: unless-stopped
volumes:
- ./local-instances/${INSTANCE:-0}/db:/var/lib/mysql
# ports:
# - "9082:3306" # optional for external access
tty: true
ports:
- "3306:3306"
environment:
MYSQL_DATABASE: ${MYSQL_DATABASE}
MYSQL_USER: ${MYSQL_USER}
MYSQL_PASSWORD: ${MYSQL_PASSWORD}
MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}
redis:
image: redis:latest
container_name: vichan_redis
restart: unless-stopped
MYSQL_DATABASE: vichan
MYSQL_ROOT_PASSWORD: password
volumes:
- ./local-instances/${INSTANCE:-0}/redis:/data
environment:
REDIS_PASSWORD: ${VICHAN_CACHE_PASSWORD}
command: redis-server --requirepass ${VICHAN_CACHE_PASSWORD}
healthcheck:
test: ["CMD", "redis-cli", "-a", "${VICHAN_CACHE_PASSWORD}", "ping"]
interval: 10s
timeout: 5s
retries: 5
start_period: 30s
- ./local-instances/${INSTANCE:-0}/mysql:/var/lib/mysql

View File

@ -1,160 +1,20 @@
# Vichan Docker Setup
The `php-fpm` process runs containerized.
The PHP application always uses `/var/www` as its work directory and home folder. If `/var/www` is bind mounted, you must adjust the path passed via FastCGI to `php-fpm`.
The php application always uses `/var/www` as it's work directory and home folder, and if `/var/www` is bind mounted it
is necessary to adjust the path passed via FastCGI to `php-fpm` by changing the root directory to `/var/www`.
This can achieved in nginx by setting the `fastcgi_param SCRIPT_FILENAME` to `/var/www/$fastcgi_script_name;`
To fix this:
1. **Adjust the root path**: Set `fastcgi_param SCRIPT_FILENAME` to `/var/www/$fastcgi_script_name;` in your nginx config.
The default docker compose settings are intended for development and testing purposes.
The folder structure expected by compose is as follows
The default Docker Compose settings are meant for development and testing.
Expected folder structure:
```
<vichan-project>
└── local-instances
└── 1
├── db
├── mysql
└── www
```
To run the app:
1. **Start all containers**: Run `docker compose up -d --build` at the root of vichan directory
2. **Rebuild just the PHP container**: Run `docker compose up -d --build php` (useful during development)
---
## PHP File Size Limit
To upload larger files, increase the default PHP file size limit (2MB). Since this setup uses Docker, follow these steps:
1. **Open the config file**: Edit `./docker/php/php.ini` in your project directory.
2. **Add or update these lines** to increase the file size limit to 10MB:
```ini
upload_max_filesize = 10M
post_max_size = 10M
```
3. **Restart your containers** to apply the changes:
```bash
docker compose restart
```
---
By default, PHP limits file uploads to **2MB** — so increasing this is often required when uploading images or documents.
---
## Using the `.env` File
Environment variables for the Docker Compose setup can be managed easily using a `.env` file.
### Steps to Use It:
1. **Copy the example**: Start by copying `.env.example` to `.env`
```bash
cp .env.example .env
```
2. **Edit `.env`**: Please make sure to change the default passwords for your setup.
### What It Controls:
- Instance folder reference (e.g. `local-instances/0`)
- Database credentials
- Redis connection details
- Secure login
- Optional SSL certificate paths for nginx
The vichan container is by itself much less rigid.
# Vichan Podman Setup (Docker alternative)
**Podman** offers a daemonless, rootless alternative for running Vichan in containers. Podman is API-compatible with Docker, allowing you to use the existing `compose.yml` file with minimal changes, while providing a more secure and lightweight environment.
This tutorial is for administrators who prefer to avoid Docker, addressing security concerns and ensuring a robust Vichan install.
---
## Why Podman?
- **Daemonless**: Unlike Docker, Podman doesnt require a central daemon, reducing the attack surface and eliminating the need for a privileged process.
- **Rootless**: Podman runs containers as a non-root user by default, improving security by limiting the impact of potential container escapes.
- **Lightweight**: Podman has a smaller footprint and is better suited for environments where simplicity and security are priorities.
- **Docker Compatibility**: Podman supports Docker Compose files via Podman Compose, making it easy to adapt the existing Vichan setup.
Learn more at: https://podman.io
---
## Prerequisites
**Install Podman**
The official installation tutorial can be found at: https://podman.io/docs/installation
**Configure the `.env` file** as described in the main setup guide (copy `.env.example` to `.env` and update passwords).
---
## Steps to Run Vichan with Podman
### 1. Prepare File Permissions
Since Podman runs rootless, ensure the `local-instances/1/db` and `local-instances/1/www` directories are writable by your user:
```bash
chmod -R u+rw local-instances/1
```
If using SELinux (e.g., on Fedora), you may need to set the correct context:
```bash
chcon -R -t container_file_t local-instances/1
```
### 2. Start Containers with Podman Compose
Run the following command from the root of the Vichan project directory to build and start all containers:
```bash
podman-compose up -d --build
```
This command mirrors `docker compose up -d --build` but uses Podmans container engine.
### 3. Rebuild Specific Containers
To rebuild only the PHP container (e.g., during development):
```bash
podman-compose up -d --build php
```
---
## Managing Podman Containers
**List running containers:**
```bash
podman ps
```
**Stop all containers:**
```bash
podman-compose down
```
**View logs for a specific service (e.g., PHP):**
```bash
podman logs vichan_php
```
## Troubleshooting
### unqualified-search-registries for Docker.io
1. Edit the config file with `nano /etc/containers/registries.conf`
2. Add the registry for `docker.io` with `unqualified-search-registries = ["docker.io"]`
3. Save and exit
4. Run `podman-compose up -d`
Use `docker compose up --build` to start the docker compose.
Use `docker compose up --build -d php` to rebuild just the vichan container while the compose is running. Useful for development.

View File

@ -1,4 +1,4 @@
FROM nginx:1.27.5-alpine-slim
FROM nginx:1.25.3-alpine
COPY . /code
RUN adduser --system www-data \

View File

@ -1,7 +1,7 @@
# Based on https://github.com/dead-guru/devichan/blob/master/php-fpm/Dockerfile
FROM composer:lts AS composer
FROM php:8.3-fpm-alpine
FROM php:8.1-fpm-alpine
RUN apk add --no-cache \
zlib \
@ -47,7 +47,7 @@ RUN apk add --no-cache \
&& pecl install -o -f igbinary \
&& pecl install redis \
&& pecl install imagick \
&& docker-php-ext-enable \
$$ docker-php-ext-enable \
igbinary \
redis \
imagick \

View File

@ -1,3 +0,0 @@
post_max_size=10M
upload_max_filesize=10M
max_file_uploads=5

View File

@ -1,65 +1,47 @@
<?php
namespace Vichan\Data\Driver;
// Prevent direct access to this file for security
defined('TINYBOARD') or exit;
// Handles caching using Redis, a fast in-memory data store
class RedisCacheDriver implements CacheDriver {
private string $prefix;
private \Redis $inner;
// Sets up the Redis connection
public function __construct(string $prefix, string $host, int $port, ?string $password, int $database) {
public function __construct(string $prefix, string $host, int $port, ?string $password, string $database) {
$this->inner = new \Redis();
$this->inner->connect($host, $port);
if ($password) {
$this->inner->auth($password);
}
if (!$this->inner->select($database)) {
throw new \RuntimeException('Unable to select Redis database ' . $database);
throw new \RuntimeException('Unable to connect to Redis!');
}
$this->prefix = $prefix;
$$this->prefix = $prefix;
}
// Retrieves a value from the cache by key
public function get(string $key): mixed {
$ret = $this->inner->get($this->prefix . $key);
if ($ret === false) {
// Return null if the key doesn't exist
return null;
}
return \json_decode($ret, true);
}
// Stores a value in the cache with an optional expiration time
public function set(string $key, mixed $value, mixed $expires = false): void {
// Convert the value to JSON for storage
$encodedValue = \json_encode($value);
if ($expires === false || !is_numeric($expires) || $expires <= 0) {
// Store the value without an expiration
$this->inner->set($this->prefix . $key, $encodedValue);
if ($expires === false) {
$this->inner->set($this->prefix . $key, \json_encode($value));
} else {
// Store the value with an expiration time (in seconds)
$ttl_seconds = (int)$expires;
$this->inner->setex($this->prefix . $key, $ttl_seconds, $encodedValue);
$expires = $expires * 1000; // Seconds to milliseconds.
$this->inner->setex($this->prefix . $key, $expires, \json_encode($value));
}
}
// Deletes a specific key from the cache
public function delete(string $key): void {
// Remove the key from Redis
$this->inner->del($this->prefix . $key);
}
// Clears all data in the current Redis database
public function flush(): void {
$this->inner->flushDB();
}

View File

@ -208,24 +208,4 @@ class Api {
return $apiCatalog;
}
public function translateBoard($board) {
$apiBoard = [];
foreach ($boards as $board) {
$apiBoard['uri'] = $board['uri'];
$apiBoard['title'] = $board['title'];
$apiBoard['subtitle'] = $board['subtitle'];
}
return $apiBoard;
}
public function translateBoards($boards) {
$apiBoards = [];
foreach ($boards as $board) {
$apiBoards[] = $this->translateBoard($board);
}
return $apiBoards;
}
}

View File

@ -12,32 +12,20 @@ defined('TINYBOARD') or exit;
class Cache {
private static function buildCache(): CacheDriver {
global $config;
// Determine if the cache engine is configured via environment variables.
$engine = \getenv('VICHAN_CACHE_ENGINE') ?: $config['cache']['enabled'];
switch ($engine) {
switch ($config['cache']['enabled']) {
case 'memcached':
return new MemcachedCacheDriver(
$config['cache']['prefix'],
$config['cache']['memcached']
);
case 'redis':
$host = $config['cache']['redis']["host"] ?? 'localhost';
$port = $config['cache']['redis']["port"] ?? 6379;
$password = $config['cache']['redis']["password"] ?? '';
$database = $config['cache']['redis']["database"] ?? 1;
$host = getenv('VICHAN_CACHE_HOST') ?: $host;
$port = getenv('VICHAN_CACHE_PORT') ?: $port;
$password = getenv('VICHAN_CACHE_PASSWORD') ?: $password;
$database = getenv('VICHAN_CACHE_DATABASE') ?: $database;
return new RedisCacheDriver(
$config['cache']['prefix'],
$host,
$port,
$password,
$database
$config['cache']['redis'][0],
$config['cache']['redis'][1],
$config['cache']['redis'][2],
$config['cache']['redis'][3]
);
case 'apcu':
return new ApcuCacheDriver;

View File

@ -146,7 +146,6 @@
*/
// Uses a PHP array. MUST NOT be used in multiprocess environments.
// This will be ignored if a environment variable ( VICHAN_CACHE_ENGINE ) is set.
$config['cache']['enabled'] = 'php';
// The recommended in-memory method of caching. Requires the extension. Due to how APCu works, this should be
// disabled when you run tools from the cli.
@ -162,23 +161,20 @@
// $config['cache']['enabled'] = 'none';
// Timeout for cached objects such as posts and HTML.
$config['cache']['redis'] = array(
'host' => 'localhost',
'port' => 6379,
'password' => '',
'database' => 1
);
// Cache timeout for cached objects
$config['cache']['timeout'] = 60 * 60 * 48; // 48 hours
// Optional prefix for multiple vichan instances
// Optional prefix if you're running multiple vichan instances on the same machine.
$config['cache']['prefix'] = '';
// Memcached servers (not used)
$config['cache']['memcached'] = [
['localhost', 11211]
];
// Memcached servers to use. Read more: http://www.php.net/manual/en/memcached.addservers.php
$config['cache']['memcached'] = array(
array('localhost', 11211)
);
// Redis server to use. Location, port, password, database id.
// Note that vichan may clear the database at times, so you may want to pick a database id just for
// vichan to use.
$config['cache']['redis'] = array('localhost', 6379, '', 1);
// EXPERIMENTAL: Should we cache configs? Warning: this changes board behaviour, i'd say, a lot.
// If you have any lambdas/includes present in your config, you should move them to instance-functions.php
@ -1275,7 +1271,6 @@
// Moderator errors
'toomanyunban' => _('You are only allowed to unban %s users at a time. You tried to unban %u users.'),
'invalid' => _('Invalid username and/or password.'),
'infotoolong' => _('Username and/or password contains too many characters.'),
'insecure' => _('Login on insecure connections is disabled.'),
'notamod' => _('You are not a mod…'),
'invalidafter' => _('Invalid username and/or password. Your user may have been deleted or changed.'),

View File

@ -432,18 +432,6 @@ function setupBoard($array) {
if (!file_exists($board['dir'] . $config['dir']['res']))
@mkdir($board['dir'] . $config['dir']['res'], 0777)
or error("Couldn't create " . $board['dir'] . $config['dir']['img'] . ". Check permissions.", true);
// json api
if ($config['api']['enabled'] && !$mod) {
$api = new Api(
$config['show_filename'],
$config['hide_email'],
$config['country_flags']
);
$json = json_encode($api->translateBoards(listBoards()));
$jsonFilename = 'boards.json';
file_write($jsonFilename, $json);
}
}
function openBoard($uri) {

View File

@ -250,7 +250,7 @@ class ImageConvert extends ImageBase {
if ($size = @getimagesize($src))
return $size;
}
$size = shell_exec_error(($this->gm ? 'gm ' : 'magick ') . 'identify -format "%w %h" ' . escapeshellarg($src . '[0]'));
$size = shell_exec_error(($this->gm ? 'gm ' : '') . 'identify -format "%w %h" ' . escapeshellarg($src . '[0]'));
if (preg_match('/^(\d+) (\d+)$/', $size, $m))
return array($m[1], $m[2]);
return false;
@ -276,13 +276,13 @@ class ImageConvert extends ImageBase {
if (!$this->temp) {
if ($config['strip_exif']) {
if($error = shell_exec_error(($this->gm ? 'gm convert ' : 'magick ') .
if($error = shell_exec_error(($this->gm ? 'gm ' : '') . 'convert ' .
escapeshellarg($this->src) . ' -auto-orient -strip ' . escapeshellarg($src))) {
$this->destroy();
error(_('Failed to redraw image!'), null, $error);
}
} else {
if($error = shell_exec_error(($this->gm ? 'gm convert ' : 'magick ') .
if($error = shell_exec_error(($this->gm ? 'gm ' : '') . 'convert ' .
escapeshellarg($this->src) . ' -auto-orient ' . escapeshellarg($src))) {
$this->destroy();
error(_('Failed to redraw image!'), null, $error);
@ -329,7 +329,7 @@ class ImageConvert extends ImageBase {
} else {
$convert_args = &$config['convert_args'];
if (($error = shell_exec_error(($this->gm ? 'gm convert ' : 'magick ') .
if (($error = shell_exec_error(($this->gm ? 'gm ' : '') . 'convert ' .
sprintf($convert_args,
$this->width,
$this->height,
@ -348,7 +348,7 @@ class ImageConvert extends ImageBase {
} else {
$convert_args = &$config['convert_args'];
if (($error = shell_exec_error(($this->gm ? 'gm convert ' : 'magick ') .
if (($error = shell_exec_error(($this->gm ? 'gm ' : '') . 'convert ' .
sprintf($convert_args,
$this->width,
$this->height,

View File

@ -103,13 +103,7 @@ function config_vars() {
continue; // This is just an alias.
if (!preg_match('/^array|\[\]|function/', $var['default']) && !preg_match('/^Example: /', trim(implode(' ', $var['comment'])))) {
$syntax_error = true;
try {
$temp = $var['default'];
$syntax_error = false;
}
catch (Exception $e) {
$temp = false;
}
$temp = eval('$syntax_error = false;return @' . $var['default'] . ';');
if ($syntax_error && $temp === false) {
error('Error parsing config.php (line ' . $line_no . ')!', null, $var);
} elseif (!isset($temp)) {

View File

@ -57,9 +57,6 @@ function mod_login(Context $ctx, $redirect = false) {
// Check if inputs are set and not empty
if (!isset($_POST['username'], $_POST['password']) || $_POST['username'] == '' || $_POST['password'] == '') {
$args['error'] = $config['error']['invalid'];
}
elseif (strlen($_POST['password']) > 128 || strlen($_POST['username']) > 128) {
$args['error'] = $config['error']['infotoolong'];
} elseif (!login($_POST['username'], $_POST['password'])) {
if ($config['syslog'])
_syslog(LOG_WARNING, 'Unauthorized login attempt!');

View File

@ -142,8 +142,7 @@ class Tinyboard extends Twig\Extension\AbstractExtension
new Twig\TwigFunction('ratio', 'twig_ratio_function'),
new Twig\TwigFunction('secure_link_confirm', 'twig_secure_link_confirm'),
new Twig\TwigFunction('secure_link', 'twig_secure_link'),
new Twig\TwigFunction('link_for', 'link_for'),
new Twig\TwigFunction('check_container', 'twig_check_container')
new Twig\TwigFunction('link_for', 'link_for')
);
}
@ -226,19 +225,3 @@ function twig_secure_link_confirm($text, $title, $confirm_message, $href) {
function twig_secure_link($href) {
return $href . '/' . make_secure_link_token($href);
}
/*
* ====================
* Container Detection
* ===================
*/
function twig_check_container() {
static $is_container = null;
if ($is_container === null) {
$is_docker = \is_file("/.dockerenv") || \is_file("/run/.containerenv");
$is_kubernetes = \is_file("/var/run/secrets/kubernetes.io/serviceaccount/namespace");
$is_container = $is_docker || $is_kubernetes;
}
return $is_container;
}

View File

@ -1,7 +1,7 @@
<?php
// Installation/upgrade file
define('VERSION', '5.2.2');
define('VERSION', '5.2.1');
require 'inc/bootstrap.php';
loadConfig();
@ -387,7 +387,7 @@ if (file_exists($config['has_installed'])) {
CHANGE `theme` `theme` VARCHAR( 40 ) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL ,
CHANGE `name` `name` VARCHAR( 40 ) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL ,
CHANGE `value` `value` TEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL ,
DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;") or error(db_error());
DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;") or eror(db_error());
case 'v0.9.6-dev-10':
query("ALTER TABLE `antispam`
CHANGE `board` `board` VARCHAR( 58 ) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL;") or error(db_error());
@ -680,7 +680,7 @@ function create_config_from_array(&$instance_config, &$array, $prefix = '') {
session_start();
if ($step == 0) {
// Agreement
// Agreeement
$page['body'] = '
<textarea style="width:700px;height:370px;margin:auto;display:block;background:white;color:black" disabled>' . htmlentities(file_get_contents('LICENSE.md')) . '</textarea>
<p style="text-align:center">
@ -914,64 +914,20 @@ if ($step == 0) {
'config' => $config,
));
} elseif ($step == 2) {
// Basic config
$page['title'] = 'Configuration';
$sg = new SaltGen();
// Initialize configuration with defaults and override with environment variables
$config['cookies'] = array(
'mod' => getenv('VICHAN_COOKIES_MOD') !== false ? getenv('VICHAN_COOKIES_MOD') : 'mod',
'salt' => $sg->generate(),
);
$config['flood_time'] = getenv('VICHAN_FLOOD_TIME') !== false ? (int)getenv('VICHAN_FLOOD_TIME') : 30;
$config['flood_time_ip'] = getenv('VICHAN_FLOOD_TIME_IP') !== false ? (int)getenv('VICHAN_FLOOD_TIME_IP') : 120;
$config['flood_time_same'] = getenv('VICHAN_FLOOD_TIME_SAME') !== false ? (int)getenv('VICHAN_FLOOD_TIME_SAME') : 3600;
$config['max_body'] = getenv('VICHAN_MAX_BODY') !== false ? (int)getenv('VICHAN_MAX_BODY') : 1800;
$config['reply_limit'] = getenv('VICHAN_REPLY_LIMIT') !== false ? (int)getenv('VICHAN_REPLY_LIMIT') : 250;
$config['max_links'] = getenv('VICHAN_MAX_LINKS') !== false ? (int)getenv('VICHAN_MAX_LINKS') : 20;
$config['max_filesize'] = getenv('VICHAN_IMAGES_MAX_FILESIZE') !== false ? (int)getenv('VICHAN_IMAGES_MAX_FILESIZE') : 10485760; // This is 10MB
$config['thumb_width'] = getenv('VICHAN_IMAGES_THUMB_WIDTH') !== false ? (int)getenv('VICHAN_IMAGES_THUMB_WIDTH') : 250;
$config['thumb_height'] = getenv('VICHAN_IMAGES_THUMB_HEIGHT') !== false ? (int)getenv('VICHAN_IMAGES_THUMB_HEIGHT') : 250;
$config['max_width'] = getenv('VICHAN_IMAGES_MAX_WIDTH') !== false ? (int)getenv('VICHAN_IMAGES_MAX_WIDTH') : 10000;
$config['max_height'] = getenv('VICHAN_IMAGES_MAX_HEIGHT') !== false ? (int)getenv('VICHAN_IMAGES_MAX_HEIGHT') : 10000;
$config['threads_per_page'] = getenv('VICHAN_DISPLAY_THREADS_PER_PAGE') !== false ? (int)getenv('VICHAN_DISPLAY_THREADS_PER_PAGE') : 10;
$config['max_pages'] = getenv('VICHAN_DISPLAY_MAX_PAGES') !== false ? (int)getenv('VICHAN_DISPLAY_MAX_PAGES') : 11;
$config['threads_preview'] = getenv('VICHAN_DISPLAY_THREADS_PREVIEW') !== false ? (int)getenv('VICHAN_DISPLAY_THREADS_PREVIEW') : 5;
$config['root'] = getenv('VICHAN_DIRECTORIES_ROOT') !== false ? getenv('VICHAN_DIRECTORIES_ROOT') : '/';
$config['cookies']['salt'] = $sg->generate();
$config['secure_trip_salt'] = $sg->generate();
$config['secure_password_salt'] = $sg->generate();
// Set database configuration from Docker environment variables, leave empty if not found
$config['db'] = array(
'type' => 'mysql', // Default, required for MySQL
'server' => getenv('VICHAN_MYSQL_HOST') !== false ? getenv('VICHAN_MYSQL_HOST') : '',
'database' => getenv('VICHAN_MYSQL_NAME') !== false ? getenv('VICHAN_MYSQL_NAME') : '',
'user' => getenv('VICHAN_MYSQL_USER') !== false ? getenv('VICHAN_MYSQL_USER') : '',
'password' => getenv('VICHAN_MYSQL_PASSWORD') !== false ? getenv('VICHAN_MYSQL_PASSWORD') : '',
);
// Append secure_login_only to $_SESSION['more'] if VICHAN_SECURE_LOGIN_ONLY is set
if (getenv('VICHAN_SECURE_LOGIN_ONLY') !== false) {
$secure_login_only = (int)getenv('VICHAN_SECURE_LOGIN_ONLY');
$_SESSION['more'] .= "\n\$config['cookies']['secure_login_only'] = $secure_login_only;";
}
// Configuration notice at the top
$page['body'] = '<div class="ban"><h2>Configuration Note</h2>' .
'<p style="text-align:center;">The following settings can still be configured later. For more customization options, <a href="https://github.com/vichan-devel/vichan/wiki/config" target="_blank" rel="noopener noreferrer">check the Vichan configuration wiki.</a></p></div>';
// Append the configuration form
$page['body'] .= Element('installer/config.html', array(
echo Element('page.html', array(
'body' => Element('installer/config.html', array(
'config' => $config,
'more' => $_SESSION['more'],
));
echo Element('page.html', array(
'body' => $page['body'],
)),
'title' => 'Configuration',
'config' => $config
));
@ -1017,6 +973,8 @@ if ($step == 0) {
echo Element('page.html', $page);
}
} elseif ($step == 4) {
// SQL installation
buildJavascript();
$sql = @file_get_contents('install.sql') or error("Couldn't load install.sql.");
@ -1046,22 +1004,10 @@ if ($step == 0) {
}
$page['title'] = 'Installation complete';
$page['body'] = '<p style="text-align:center">Thank you for using vichan. <a href="https://github.com/vichan-devel/vichan/issues/new/choose" target="_blank" rel="noopener noreferrer">Please report any bugs you discover.</a></p>' .
'<p style="text-align:center">If you are new to vichan, <a href="https://github.com/vichan-devel/vichan/wiki" target="_blank" rel="noopener noreferrer">please check out the documentation.</a></p>';
// Admin panel notice
$page['body'] .= '<div class="ban"><h2>Next Steps</h2>' .
'<p>You can now log in to the admin panel at <strong>/mod.php</strong> using the default credentials:</p>' .
'<p><strong>Username:</strong> admin</p>' .
'<p><strong>Password:</strong> password</p>' .
'<p><strong>Important:</strong> For security, please change the administrator password immediately after logging in.</p>' .
'<p style="text-align:center"><a href="/mod.php"><button>Go to Admin Panel</button></a></p></div>';
$page['body'] = '<p style="text-align:center">Thank you for using vichan. Please remember to report any bugs you discover. <a href="https://github.com/vichan-devel/vichan/wiki/Configuration-Basics">How do I edit the config files?</a></p>';
if (!empty($sql_errors)) {
$page['body'] .= '<div class="ban"><h2>SQL errors</h2><p>SQL errors were encountered when trying to install the database. This may be the result of using a database which is already occupied with a vichan installation; if so, you can probably ignore this.</p><p>The errors encountered were:</p><ul>' . $sql_errors . '</ul>' .
'<p style="text-align:center;color:#d00"><strong>Warning:</strong> Ignoring errors is not recommended and may cause installation issues.</p>' .
'<p style="text-align:center"><a href="?step=5"><button>Next</button></a></p></div>';
$page['body'] .= '<div class="ban"><h2>SQL errors</h2><p>SQL errors were encountered when trying to install the database. This may be the result of using a database which is already occupied with a vichan installation; if so, you can probably ignore this.</p><p>The errors encountered were:</p><ul>' . $sql_errors . '</ul><p><a href="?step=5">Ignore errors and complete installation.</a></p></div>';
} else {
$boards = listBoards();
foreach ($boards as &$_board) {
@ -1078,16 +1024,7 @@ if ($step == 0) {
echo Element('page.html', $page);
} elseif ($step == 5) {
$page['title'] = 'Installation complete';
$page['body'] = '<p style="text-align:center">Thank you for using vichan. Please report any bugs you discover.</p>' .
'<p style="text-align:center">If you are new to vichan, <a href="https://github.com/vichan-devel/vichan/wiki">please check out the documentation</a>.</p>';
// Admin panel notice
$page['body'] .= '<div class="ban"><h2>Next Steps</h2>' .
'<p>You can now log in to the admin panel at <strong>/mod.php</strong> using the default credentials:</p>' .
'<p><strong>Username:</strong> admin</p>' .
'<p><strong>Password:</strong> password</p>' .
'<p><strong>Important:</strong> For security, please change the administrator password immediately after logging in.</p>' .
'<p style="text-align:center"><a href="/mod.php"><button>Go to Admin Panel</button></a></p></div>';
$page['body'] = '<p style="text-align:center">Thank you for using vichan. Please remember to report any bugs you discover.</p>';
$boards = listBoards();
foreach ($boards as &$_board) {

View File

@ -1 +0,0 @@

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.6 KiB

After

Width:  |  Height:  |  Size: 3.6 KiB

View File

@ -1,8 +1,6 @@
<footer>
<p class="unimportant" style="margin-top:20px;text-align:center;">- Tinyboard +
<a href="https://github.com/vichan-devel/vichan">vichan</a> {{ config.version }}
{% if check_container() %}Running Containerized{% endif %}
-
<a href="https://github.com/vichan-devel/vichan">vichan</a> {{ config.version }} -
<br>Tinyboard Copyright &copy; 2010-2014 Tinyboard Development Group
<br><a href="https://github.com/vichan-devel/vichan">vichan</a> Copyright &copy; 2012-2025 vichan-devel</p>
{% for footer in config.footer %}<p class="unimportant" style="text-align:center;">{{ footer }}</p>{% endfor %}

View File

@ -45,13 +45,12 @@
{% endfor %}
</ul>
{% if errors %}
<p style="text-align:center;color:#d00"><strong>Warning:</strong> Ignoring these problems is not recommended and may cause installation issues.</p>
<p style="text-align:center"><a href="?step=2"><button>Proceed Anyway</button></a></p>
<p style="text-align:center"><a href="?step=2">Click here to ignore errors and attempt installing anyway (not recommended).</a></p>
{% else %}
<p style="text-align:center"><a href="?step=2"><button>Next</button></a></p>
<p style="text-align:center"><a href="?step=2">Click here to proceed with installation.</a></p>
{% endif %}
{% else %}
<p>There were no errors or warnings. Good!</p>
<p style="text-align:center"><a href="?step=2"><button>Next</button></a></p>
<p style="text-align:center"><a href="?step=2">Clik here to proceed with installation.</a></p>
{% endif %}
</div>

View File

@ -14,7 +14,10 @@
<input type="text" id="db_user" name="db[user]" value="{{ config.db.user }}">
<label for="db_pass">Password:</label>
<input type="password" id="db_pass" name="db[password]" value="{{ config.db.password }}">
<input type="password" id="db_pass" name="db[password]" value="">
</fieldset>
<p style="text-align:center" class="unimportant">The following is all later configurable. For more options, <a href="http://tinyboard.org/docs/?p=Config">edit your configuration files</a> after installing.</p>
<fieldset>
<legend>Cookies</legend>
<label for="cookies_mod">Moderator cookie:</label>
<input type="text" id="cookies_mod" name="cookies[mod]" value="{{ config.cookies.mod }}">
@ -48,38 +51,7 @@
<legend>Images</legend>
<label for="max_filesize">Maximum image filesize (bytes):</label>
<input type="text" id="max_filesize" name="max_filesize" value="{{ config.max_filesize }}">
<div class="expandable">
<legend>PHP File Size Limit</legend>
<div class="body">
<p>Configure the PHP file size limit in the <code>php.ini</code> file. If it doesn't exist, create one.</p>
<div class="expandable">
<legend>Using Docker</legend>
<div class="body">
<p>Edit or create <code>./vichan/docker/php/php.ini</code> in the project root.</p>
<p>To set the limit to 10MB, add:</p>
<pre>
upload_max_filesize = 10M
post_max_size = 10M
</pre>
<p>Then restart the containers.</p>
</div>
</div>
<div class="expandable">
<legend>Manual Configuration</legend>
<div class="body">
<p>Edit or create <code>/usr/local/etc/php/php.ini</code>.</p>
<p>To set the limit to 10MB, add:</p>
<pre>
upload_max_filesize = 10M
post_max_size = 10M
</pre>
<p>Then restart the PHP server.</p>
</div>
</div>
<hr>
<p>Default limit is 2MB if not set.</p>
</div>
</div>
<label for="thumb_width">Thumbnail width:</label>
<input type="text" id="thumb_width" name="thumb_width" value="{{ config.thumb_width }}">
@ -127,51 +99,3 @@
<input type="submit" value="Complete installation">
</p>
</form>
<script>
const expandables = document.querySelectorAll('.expandable');
expandables.forEach(expandable => {
const arrow = document.createElement('span');
arrow.innerHTML = ' &#9654;'; // Right arrow
arrow.style.cursor = 'pointer';
arrow.style.marginLeft = '5px';
expandable.querySelector('legend').appendChild(arrow);
expandable.querySelector('legend').style.cursor = 'pointer';
expandable.querySelector('legend').style.fontWeight = 'bold';
expandable.querySelector('legend').style.display = 'flex';
expandable.querySelector('legend').style.alignItems = 'center';
expandable.querySelector('legend').style.justifyContent = 'space-between';
const label = expandable.querySelector('legend');
const body = expandable.querySelector('.body');
label.addEventListener('click', () => {
if (body.style.display === 'none' || body.style.display === '') {
body.style.display = 'block';
arrow.innerHTML = ' &#9660;'; // Down arrow
} else {
body.style.display = 'none';
arrow.innerHTML = ' &#9654;'; // Up arrow
}
});
});
</script>
<style>
.expandable {
border: 1px solid #ccc;
padding: 10px;
margin: 10px 0;
}
.expandable legend {
cursor: pointer;
font-weight: bold;
}
.expandable fieldset {
border: none;
padding: 0;
margin: 0;
}
.expandable .body {
display: none; /* Ensure the body is hidden initially */
}
</style>