feat(migrations): implement Phinx database migration system (#1976)

* feat(migrations): implement Phinx database migration system and update installation process

- Introduced a modern database migration system using Phinx, replacing the legacy SQL import method.
- Updated `install.php` to run migrations instead of importing SQL dumps.
- Added migration configuration file `phinx.php` and initial migration files for schema and data seeding.
- Created a new admin panel for migration status management.
- Updated UPGRADE_GUIDE.md to include migration setup instructions and benefits.
- Ensured backward compatibility for existing installations while facilitating a smoother transition to the new system.

* update runProcess to return process exit code

* refactor(migrations): clean up whitespace and formatting in migration files

- Removed unnecessary whitespace and adjusted formatting for consistency across migration files.

* fix(migrations): enforce NOT NULL constraints on migration columns

- Updated various migration columns to enforce NOT NULL constraints, ensuring data integrity across the schema.
- Adjusted default values and nullability for multiple fields in the initial schema migration files.

* refactor(database): standardize table engines to InnoDB for reliability

- Updated UPGRADE_GUIDE.md to reflect the use of InnoDB for all tables, emphasizing data integrity and reliability.
- Modified migration files to change table engines from MyISAM to InnoDB for various tracker and temporary tables.
- Optimized session variable settings in cron jobs for InnoDB compatibility.
- Ensured consistency across the schema by enforcing InnoDB usage in all relevant areas.

* fix(migrations): correct MySQL integer field types to match original schema

- Fix bb_forums table: forum_status (INT→TINYINT), forum_tpl_id (INT→SMALLINT)
- Fix bb_users table: avatar_ext_id remains TINYINT as per original schema
- Fix bb_groups table: avatar_ext_id (SMALLINT→INT) to match original INT(15)
- Fix bb_topics table: topic_show_first_post, topic_allow_robots (TINYINT(1)→TINYINT UNSIGNED)
- Remove incorrect 'limit' => 11 from standard INT fields, use default Phinx behavior
- Fix search_size field to use proper INT type instead of maximum value hack
- Correct poll table field types: vote_id (TINYINT), user_id (MEDIUMINT), vote_result (MEDIUMINT UNSIGNED)
- Standardize all timestamp and ID fields to use appropriate MySQL integer types

Ensures migration creates database schema identical to install/sql/mysql.sql
while maintaining InnoDB engine for all tables instead of MyISAM.

* fix(cache): auto-create cache directories when using FileStorage

The UnifiedCacheSystem was constructing directory paths for Nette FileStorage
but not creating them, causing "Directory not found" errors when accessing
caches like 'bb_login_err'. FileStorage expects directories to already exist.

Changes:
- Add automatic directory creation using bb_mkdir() before FileStorage init
- Handle both regular cache directories and SQLite parent directories
- Apply to both _buildStorage() and _buildDatastoreStorage() methods
- Add proper error handling with RuntimeException for failed creation
- Maintain consistency with TorrentPier's directory creation patterns

This ensures cache directories are created automatically when first accessed,
eliminating the need for manual directory creation during deployment.

Fixes: Cache initialization failures with missing directories

* refactor(docs): update README for clarity and remove legacy SQL file

- Improved formatting and clarity in the README, ensuring consistent line breaks and spacing.
- Updated installation instructions to reflect the new migration process, emphasizing the use of `phinx` for database setup.
- Removed the legacy SQL dump file `mysql.sql` and the `legacy-changes.txt` file, streamlining the installation process and reducing confusion for new users.
- Enhanced the documentation to guide users through the setup process more effectively.

* docs: enhance CLAUDE.md with migration details and directory updates

- Updated the `/library/` section to clarify its purpose.
- Added a new `/migrations/` directory section detailing database migration files managed by Phinx.
- Included migration commands for running and checking migration status.
- Revised the initial schema and seed data references for clarity.
- Improved formatting for consistency throughout the document.

* refactor(cron): remove demo_mode.php cron job and related functionality

- Deleted the demo_mode.php cron job file, which was responsible for managing demo mode operations.
- Added a migration to remove the demo_mode.php entry from the bb_cron table, ensuring a clean database state.
- Updated the initial schema migration comment to reflect the creation of essential database schema for fresh installations.

* refactor(docs): Fixed some typos

* chore: update changelog generation starting from v2.4.6-alpha.4

* refactor: Changed some `php_sapi_name()` to `PHP_SAPI` constants

* refactor: Extract hardcoded migrations to class property

* refactor: Use `count()` to count $initialMigrations elements

* feat(migrations): enhance migration management UI with new language variables

- Added new language variables for migration status, instructions, and applied/pending migrations to improve user interface clarity.
- Updated admin migration template to utilize these new language variables for better localization and maintainability.
- Introduced a new file 'CLAUDE.md' to the cleanup process for documentation purposes.

---------

Co-authored-by: Roman Kelesidis <roman25052006.kelesh@gmail.com>
This commit is contained in:
Yury Pikhtarev 2025-06-20 13:23:33 +04:00 committed by GitHub
parent 0d4c869f30
commit fbde8cd421
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
26 changed files with 4084 additions and 2942 deletions

View file

@ -22,7 +22,7 @@ jobs:
id: git-cliff
with:
config: cliff.toml
args: v2.4.5-rc.2.. --verbose
args: v2.4.6-alpha.4.. --verbose
env:
OUTPUT: CHANGELOG.md
GITHUB_REPO: ${{ github.repository }}

144
CLAUDE.md Normal file
View file

@ -0,0 +1,144 @@
# CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
## Project Overview
TorrentPier is a BitTorrent tracker engine written in PHP, designed for hosting BitTorrent communities with forum functionality. The project is in active modernization, transitioning from legacy code to modern PHP practices while maintaining backward compatibility.
## Technology Stack & Architecture
- **PHP 8.1+** with modern features
- **MySQL/MariaDB/Percona** database
- **Nette Database** with backward-compatible wrapper
- **Composer** for dependency management
- **Custom BitTorrent tracker** implementation
## Key Directory Structure
- `/src/` - Modern PHP classes (PSR-4 autoloaded as `TorrentPier\`)
- `/library/` - Core application logic and legacy code
- `/admin/` - Administrative interface
- `/bt/` - BitTorrent tracker functionality (announce.php, scrape.php)
- `/styles/` - Templates, CSS, JS, images
- `/internal_data/` - Cache, logs, compiled templates
- `/install/` - Installation scripts and configuration examples
- `/migrations/` - Database migration files (Phinx)
## Entry Points & Key Files
- `index.php` - Main forum homepage
- `tracker.php` - Torrent search/browse interface
- `bt/announce.php` - BitTorrent announce endpoint
- `bt/scrape.php` - BitTorrent scrape endpoint
- `admin/index.php` - Administrative panel
- `cron.php` - Background task runner (CLI only)
- `install.php` - Installation script (CLI only)
## Development Commands
### Installation & Setup
```bash
# Automated installation (CLI)
php install.php
# Install dependencies
composer install
# Update dependencies
composer update
```
### Maintenance & Operations
```bash
# Run background maintenance tasks
php cron.php
```
### Code Quality
The project uses **StyleCI** with PSR-2 preset for code style enforcement. StyleCI configuration is in `.styleci.yml` targeting `src/` directory.
## Modern Architecture Components
### Database Layer (`/src/Database/`)
- **Nette Database** with full old SqlDb backward compatibility
- Singleton pattern accessible via `DB()` function
- Support for multiple database connections and debug functionality
- Migration path to ORM-style Explorer queries
### Cache System (`/src/Cache/`)
- **Unified caching** using Nette Caching internally
- 100% backward compatibility with existing `CACHE()` and $datastore calls
- Supports file, SQLite, memory, and Memcached storage
- Advanced features: memoization, cache dependencies
### Configuration Management
- Environment-based config with `.env` files
- Singleton `Config` class accessible via `config()` function
- Local overrides supported via `library/config.local.php`
## Configuration Files
- `.env` - Environment variables (copy from `.env.example`)
- `library/config.php` - Main application configuration
- `library/config.local.php` - Local configuration overrides
- `composer.json` - Dependencies and PSR-4 autoloading
## Development Workflow
### CI/CD Pipeline
- **GitHub Actions** for automated testing and deployment
- **StyleCI** for code style enforcement
- **Dependabot** for dependency updates
- **FTP deployment** to demo environment
### Installation Methods
1. **Automated**: `php install.php` (recommended)
2. **Composer**: `composer create-project torrentpier/torrentpier`
3. **Manual**: Git clone + `composer install` + database setup
## Database & Schema
- **Database migrations** managed via Phinx in `/migrations/` directory
- Initial schema: `20250619000001_initial_schema.php`
- Initial seed data: `20250619000002_seed_initial_data.php`
- UTF-8 (utf8mb4) character set required
- Multiple database alias support for different components
### Migration Commands
```bash
# Run all pending migrations
php vendor/bin/phinx migrate --configuration=phinx.php
# Check migration status
php vendor/bin/phinx status --configuration=phinx.php
# Mark migrations as applied (for existing installations)
php vendor/bin/phinx migrate --fake --configuration=phinx.php
```
## Legacy Compatibility Strategy
The codebase maintains 100% backward compatibility while introducing modern alternatives:
- **Database layer**: Existing old SqlDb calls work while new code can use Nette Database
- **Cache system**: All existing `CACHE()` and $datastore calls preserved while adding modern features
- **Configuration**: Legacy config access maintained alongside new singleton pattern
This approach allows gradual modernization without breaking existing functionality - critical for a mature application with existing deployments.
## Security & Performance
- **Environment-based secrets** management via `.env`
- **CDN/proxy support** (Cloudflare, Fastly)
- **Input sanitization** and CSRF protection
- **Advanced caching** with multiple storage backends
- **Rate limiting** and IP-based restrictions
## BitTorrent Tracker Features
- **BitTorrent v1 & v2** support
- **TorrServer integration** capability
- **Client ban system** for problematic torrent clients
- **Scrape support** for tracker statistics
When working with this codebase, prioritize understanding the legacy compatibility approach and modern architecture patterns. Always test both legacy and modern code paths when making changes to core systems.

1131
HISTORY.md

File diff suppressed because it is too large Load diff

View file

@ -19,15 +19,15 @@
## 🐂 About TorrentPier
TorrentPier — bull-powered BitTorrent Public/Private tracker engine, written in PHP. High speed, simple modifications, load-balanced
architecture. In addition, we have a very helpful
TorrentPier — bull-powered BitTorrent Public/Private tracker engine, written in PHP. High speed, simple modifications, load-balanced
architecture. In addition, we have a very helpful
[official support forum](https://torrentpier.com), where it's possible to get support and download modifications for the engine.
## 🌈 Current status
TorrentPier is currently in active development. The goal is to remove all legacy code and rewrite the existing code to
modern specifications. If you want delve deep into the code, check our [issues](https://github.com/torrentpier/torrentpier/issues)
and go from there. The documentation will be translated to english in the near future, currently russian is the main language of it.
TorrentPier is currently in active development. The goal is to remove all legacy code and rewrite the existing code to
modern specifications. If you want to delve deep into the code, check our [issues](https://github.com/torrentpier/torrentpier/issues)
and go from there. The documentation will be translated to English in the near future, currently Russian is the main language.
## ✨ Features
* Rich forum with browsing/moderation tools
@ -40,7 +40,7 @@ and go from there. The documentation will be translated to english in the near f
* Bonus points
* Polling system
* PM/DM system
* Multilingual support (Russian and English is currently fully supported, with others in the future)
* Multilingual support (Russian and English are currently fully supported, with others in the future)
* Atom/RSS feeds
* ... and so MUCH MORE!
@ -56,7 +56,7 @@ and go from there. The documentation will be translated to english in the near f
## 🔧 Requirements
* Apache / nginx ([example config](install/nginx.conf)) / caddy ([example config](install/Caddyfile))
* MySQL 5.5.3 or above / MariaDB 10.0 or above / Percona
* MySQL 5.5.3 or above (including MySQL 8.0+) / MariaDB 10.0 or above / Percona
* PHP: 8.1 / 8.2 / 8.3 / 8.4
* PHP Extensions: mbstring, gd, bcmath, intl, tidy (optional), xml, xmlwriter
* Crontab (Recommended)
@ -100,14 +100,20 @@ Check out our [autoinstall](https://github.com/torrentpier/autoinstall) reposito
```shell
composer install
```
5. Create a database and import the dump located at `install/sql/mysql.sql`
6. Edit database configuration settings in the environment (`.env.example`), after, rename to `.env`
5. Edit database configuration settings in the environment (`.env.example`), after, rename to `.env`
6. Create a database and run migrations to set up the schema
```shell
php vendor/bin/phinx migrate --configuration=phinx.php
```
7. Provide write permissions to the specified folders:
* `data/avatars`, `data/uploads`, `data/uploads/thumbs`
* `internal_data/atom`, `internal_data/cache`, `internal_data/log`, `internal_data/triggers`
* `sitemap`
8. Voila! ✨
> [!TIP]
> You can automate steps 4-7 by running `php install.php` instead, which will guide you through the setup process interactively.
> [!IMPORTANT]
> The specific settings depend on the server you are using, but in general we recommend chmod **0755** for folders, and chmod **0644** for the files in them.
@ -124,13 +130,13 @@ If you discover a security vulnerability within TorrentPier, please follow our [
## 📌 Our recommendations
* *It's recommended to run `cron.php`.* - For significant tracker speed increase it ay be required to replace the built-in cron.php in operating system daemon.
* *It's recommended to run `cron.php`.* - For significant tracker speed increase it may be required to replace the built-in cron.php with an operating system daemon.
* *Local configuration copy.* - You can override the settings using the local configuration file `library/config.local.php`.
## 💚 Contributing / Contributors
Please read our [contributing policy](CONTRIBUTING.md) and [code of conduct](CODE_OF_CONDUCT.md) for details, and the process for
submitting pull requests to us. But we are always ready to renew your pull-request for compliance with
Please read our [contributing policy](CONTRIBUTING.md) and [code of conduct](CODE_OF_CONDUCT.md) for details, and the process for
submitting pull requests to us. But we are always ready to review your pull-request for compliance with
these requirements. Just send it!
<a href="https://github.com/torrentpier/torrentpier/graphs/contributors">
@ -141,7 +147,7 @@ Made with [contrib.rocks](https://contrib.rocks).
## 💞 Sponsoring
Support this project by becoming a sponsor or a backer.
Support this project by becoming a sponsor or a backer.
[![OpenCollective sponsors](https://opencollective.com/torrentpier/sponsors/badge.svg)](https://opencollective.com/torrentpier)
[![OpenCollective backers](https://opencollective.com/torrentpier/backers/badge.svg)](https://opencollective.com/torrentpier)
@ -164,7 +170,7 @@ Support this project by becoming a sponsor or a backer.
## 📦 Versioning
We use [SemVer](http://semver.org/) for versioning. For the versions available, see the [tags on this repository](https://github.com/torrentpier/torrentpier/tags).
We use [SemVer](http://semver.org/) for versioning. For the versions available, see the [tags on this repository](https://github.com/torrentpier/torrentpier/tags).
## 📖 License

View file

@ -4,6 +4,7 @@ This guide helps you upgrade your TorrentPier installation to the latest version
## 📖 Table of Contents
- [Database Migration System](#database-migration-system)
- [Database Layer Migration](#database-layer-migration)
- [Unified Cache System Migration](#unified-cache-system-migration)
- [Configuration System Migration](#configuration-system-migration)
@ -14,6 +15,357 @@ This guide helps you upgrade your TorrentPier installation to the latest version
- [Breaking Changes](#breaking-changes)
- [Best Practices](#best-practices)
## 🗄️ Database Migration System
TorrentPier now includes a modern database migration system using **Phinx** (from CakePHP), replacing the legacy direct SQL import approach. This provides version-controlled database schema management with rollback capabilities.
### Key Benefits
- **Version Control**: Database schema changes are tracked in code
- **Environment Consistency**: Same database structure across development, staging, and production
- **Safe Rollbacks**: Ability to safely revert schema changes
- **Team Collaboration**: No more merge conflicts on database changes
- **Automated Deployments**: Database updates as part of deployment process
### Migration Architecture
#### Engine Strategy
- **InnoDB**: Used for all tables for maximum data integrity and reliability
- **ACID Compliance**: Full transaction support and crash recovery for all data
- **Row-Level Locking**: Better concurrency for high-traffic operations
#### Directory Structure
```
/migrations/
├── 20250619000001_initial_schema.php # Complete database schema
├── 20250619000002_seed_initial_data.php # Essential data seeding
└── future_migrations... # Your custom migrations
/phinx.php # Migration configuration
```
### For New Installations
New installations automatically use migrations instead of the legacy SQL dump:
```bash
# Fresh installation now uses migrations
php install.php
```
The installer will:
1. Set up environment configuration
2. Create the database
3. Run all migrations automatically
4. Seed initial data (admin user, configuration, etc.)
### For Existing Installations
Existing installations continue to work without changes. The migration system is designed for new installations and development workflows.
**Important**: Existing installations should **not** attempt to migrate to the new system without proper backup and testing procedures.
### Developer Workflow
#### Creating Migrations
```bash
# Create a new migration
php vendor/bin/phinx create AddNewFeatureTable
# Edit the generated migration file
# /migrations/YYYYMMDDHHMMSS_add_new_feature_table.php
```
#### Running Migrations
```bash
# Run all pending migrations
php vendor/bin/phinx migrate
# Check migration status
php vendor/bin/phinx status
# Rollback last migration
php vendor/bin/phinx rollback
```
#### Migration Template
```php
<?php
use Phinx\Migration\AbstractMigration;
class AddNewFeatureTable extends AbstractMigration
{
public function change()
{
// InnoDB for data integrity
$table = $this->table('bb_new_feature', [
'engine' => 'InnoDB',
'collation' => 'utf8mb4_unicode_ci'
]);
$table->addColumn('name', 'string', ['limit' => 100])
->addColumn('created_at', 'timestamp', ['default' => 'CURRENT_TIMESTAMP'])
->addIndex('name')
->create();
}
// Optional: explicit up/down methods for complex operations
public function up()
{
// Complex data migration logic
}
public function down()
{
// Rollback logic
}
}
```
#### Engine Guidelines
```php
// Use InnoDB for all tables for maximum reliability
$table = $this->table('bb_user_posts', [
'engine' => 'InnoDB',
'collation' => 'utf8mb4_unicode_ci'
]);
// All tracker tables also use InnoDB for data integrity
$table = $this->table('bb_bt_peer_stats', [
'engine' => 'InnoDB',
'collation' => 'utf8mb4_unicode_ci'
]);
// Buffer tables use InnoDB for consistency and reliability
public function up() {
$this->execute('DROP TABLE IF EXISTS buf_temp_data');
// Recreate with new structure using InnoDB
}
```
### Admin Panel Integration
The admin panel includes a read-only migration status page at `/admin/admin_migrations.php`:
- **Current migration version**
- **Applied migrations history**
- **Pending migrations list**
- **Database statistics**
- **Clear instructions for CLI operations**
**Important**: The admin panel is **read-only** for security. All migration operations must be performed via CLI.
### Complex Migration Handling
For complex data transformations, create external scripts:
```php
// migrations/YYYYMMDDHHMMSS_complex_data_migration.php
class ComplexDataMigration extends AbstractMigration
{
public function up()
{
$this->output->writeln('Running complex data migration...');
// Call external script for complex operations
$result = shell_exec('php ' . __DIR__ . '/../scripts/migrate_torrent_data.php');
$this->output->writeln($result);
if (strpos($result, 'ERROR') !== false) {
throw new Exception('Complex migration failed');
}
}
}
```
### Best Practices
#### Migration Development
```bash
# 1. Create migration
php vendor/bin/phinx create MyFeature
# 2. Edit migration file
# 3. Test locally
php vendor/bin/phinx migrate -e development
# 4. Test rollback
php vendor/bin/phinx rollback -e development
# 5. Commit to version control
git add migrations/
git commit -m "Add MyFeature migration"
```
#### Production Deployment
```bash
# Always backup database first
mysqldump tracker_db > backup_$(date +%Y%m%d_%H%M%S).sql
# Run migrations
php vendor/bin/phinx migrate -e production
# Verify application functionality
# Monitor error logs
```
#### Team Collaboration
- **Never modify existing migrations** that have been deployed
- **Always create new migrations** for schema changes
- **Test migrations on production-like data** before deployment
- **Coordinate with team** before major schema changes
### Configuration
The migration system uses your existing `.env` configuration:
```php
// phinx.php automatically reads from .env
'production' => [
'adapter' => 'mysql',
'host' => env('DB_HOST', 'localhost'),
'port' => (int) env('DB_PORT', 3306),
'name' => env('DB_DATABASE'),
'user' => env('DB_USERNAME'),
'pass' => env('DB_PASSWORD', ''),
'charset' => 'utf8mb4',
'collation' => 'utf8mb4_unicode_ci'
]
```
### Troubleshooting
#### Common Issues
```bash
# Migration table doesn't exist
php vendor/bin/phinx init # Re-run if needed
# Migration fails mid-way
php vendor/bin/phinx rollback # Rollback to previous state
# Check what would be applied
php vendor/bin/phinx status # See pending migrations
```
#### Migration Recovery
```bash
# If migration fails, check status first
php vendor/bin/phinx status
# Rollback to known good state
php vendor/bin/phinx rollback -t 20250619000002
# Fix the migration code and re-run
php vendor/bin/phinx migrate
```
### Legacy SQL Import Removal
The legacy `install/sql/mysql.sql` approach has been replaced by migrations:
- ✅ **New installations**: Use migrations automatically
- ✅ **Development workflow**: Create migrations for all schema changes
- ✅ **Version control**: All schema changes tracked in Git
- ❌ **Direct SQL imports**: No longer used for new installations
### Security Considerations
- **CLI-only execution**: Migrations run via command line only
- **Read-only admin interface**: Web interface shows status only
- **Backup requirements**: Always backup before production migrations
- **Access control**: Restrict migration command access to authorized personnel
### Migration Setup for Existing Installations
If you have an **existing TorrentPier installation** and want to adopt the migration system, you need to mark the initial migrations as already applied to avoid recreating your existing database schema.
#### Detection: Do You Need This?
You need migration setup if:
- ✅ You have an existing TorrentPier installation with data
- ✅ Your database already has tables like `bb_users`, `bb_forums`, etc.
- ✅ The admin migration panel shows "Migration System: ✗ Not Initialized"
#### Step-by-Step Setup Process
**1. Backup Your Database**
```bash
mysqldump -u username -p database_name > backup_$(date +%Y%m%d_%H%M%S).sql
```
**2. Initialize Migration Table**
```bash
# This creates the bb_migrations table without running any migrations
php vendor/bin/phinx init
```
**3. Mark Initial Migrations as Applied (Fake Run)**
```bash
# Mark the schema migration as applied without running it
php vendor/bin/phinx migrate --fake --target=20250619000001
# Mark the data seeding migration as applied without running it
php vendor/bin/phinx migrate --fake --target=20250619000002
```
**4. Verify Setup**
```bash
# Check migration status
php vendor/bin/phinx status
```
You should see both initial migrations marked as "up" (applied).
#### Alternative: Manual SQL Method
If you prefer manual control, you can directly insert migration records:
```sql
-- Create migration table (if phinx init didn't work)
CREATE TABLE IF NOT EXISTS bb_migrations (
version bigint(20) NOT NULL,
migration_name varchar(100) DEFAULT NULL,
start_time timestamp NULL DEFAULT NULL,
end_time timestamp NULL DEFAULT NULL,
breakpoint tinyint(1) NOT NULL DEFAULT '0',
PRIMARY KEY (version)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-- Mark initial migrations as applied
INSERT INTO bb_migrations (version, migration_name, start_time, end_time, breakpoint)
VALUES
('20250619000001', 'InitialSchema', NOW(), NOW(), 0),
('20250619000002', 'SeedInitialData', NOW(), NOW(), 0);
```
#### Post-Setup Workflow
After setup, your existing installation will work exactly like a fresh installation:
```bash
# Create new migrations
php vendor/bin/phinx create AddNewFeature
# Run new migrations
php vendor/bin/phinx migrate
# Check status
php vendor/bin/phinx status
```
#### Troubleshooting
**Migration table already exists:**
- Check if you've already set up migrations: `php vendor/bin/phinx status`
- If it shows errors, you may need to recreate: `DROP TABLE bb_migrations;` then restart
**"Nothing to migrate" message:**
- This is normal after fake runs - it means setup was successful
- New migrations will appear when you create them
**Admin panel shows "Needs Setup":**
- Follow the setup process above
- Refresh the admin panel after completion
## 🗄️ Database Layer Migration
TorrentPier has completely replaced its legacy database layer (SqlDb/Dbs) with a modern implementation using Nette Database while maintaining 100% backward compatibility.

View file

@ -13,7 +13,7 @@ if (!defined('BB_ROOT')) {
}
// Check CLI mode
if (php_sapi_name() !== 'cli') {
if (PHP_SAPI != 'cli') {
exit;
}
@ -34,12 +34,14 @@ $items = [
'_release.php',
'CHANGELOG.md',
'cliff.toml',
'CLAUDE.md',
'CODE_OF_CONDUCT.md',
'CONTRIBUTING.md',
'crowdin.yml',
'HISTORY.md',
'README.md',
'SECURITY.md'
'SECURITY.md',
'UPGRADE_GUIDE.md'
];
foreach ($items as $item) {

View file

@ -11,7 +11,7 @@ define('BB_ROOT', __DIR__ . DIRECTORY_SEPARATOR);
define('BB_PATH', BB_ROOT);
// Check CLI mode
if (php_sapi_name() !== 'cli') {
if (PHP_SAPI != 'cli') {
die('Please run <code style="background:#222;color:#00e01f;padding:2px 6px;border-radius:3px;">php ' . basename(__FILE__) . '</code> in CLI mode');
}
@ -114,7 +114,7 @@ if ($bytesWritten === 0) {
out("\n- Config file has been updated!", 'success');
// Update CHANGELOG.md
runProcess('npx git-cliff v2.4.5-rc.2.. --config cliff.toml --tag "' . $version . '" > CHANGELOG.md');
runProcess('npx git-cliff v2.4.6-alpha.4.. --config cliff.toml --tag "' . $version . '" > CHANGELOG.md');
// Git add & commit
runProcess('git add -A && git commit -m "release: ' . escapeshellarg($version) . (!empty($versionEmoji) ? (' ' . $versionEmoji) : '') . '"');

View file

@ -0,0 +1,79 @@
<?php
/**
* TorrentPier Bull-powered BitTorrent tracker engine
*
* @copyright Copyright (c) 2005-2025 TorrentPier (https://torrentpier.com)
* @link https://github.com/torrentpier/torrentpier for the canonical source repository
* @license https://github.com/torrentpier/torrentpier/blob/master/LICENSE MIT License
*/
if (!empty($setmodules)) {
if (IS_SUPER_ADMIN) {
$module['GENERAL']['MIGRATIONS_STATUS'] = basename(__FILE__);
}
return;
}
require __DIR__ . '/pagestart.php';
if (!IS_SUPER_ADMIN) {
bb_die(__('ONLY_FOR_SUPER_ADMIN'));
}
use TorrentPier\Database\MigrationStatus;
// Initialize migration status
$migrationStatus = new MigrationStatus();
$status = $migrationStatus->getMigrationStatus();
$schemaInfo = $migrationStatus->getSchemaInfo();
// Template variables
$template->assign_vars([
'PAGE_TITLE' => __('MIGRATIONS_STATUS'),
'CURRENT_TIME' => date('Y-m-d H:i:s'),
// Migration status individual fields
'MIGRATION_TABLE_EXISTS' => $status['table_exists'],
'MIGRATION_CURRENT_VERSION' => $status['current_version'],
'MIGRATION_APPLIED_COUNT' => count($status['applied_migrations']),
'MIGRATION_PENDING_COUNT' => count($status['pending_migrations']),
// Setup status fields
'SETUP_REQUIRES_SETUP' => $status['requires_setup'] ?? false,
'SETUP_TYPE' => $status['setup_status']['type'] ?? __('UNKNOWN'),
'SETUP_MESSAGE' => $status['setup_status']['message'] ?? '',
'SETUP_ACTION_REQUIRED' => $status['setup_status']['action_required'] ?? false,
'SETUP_INSTRUCTIONS' => $status['setup_status']['instructions'] ?? '',
// Schema info individual fields
'SCHEMA_DATABASE_NAME' => $schemaInfo['database_name'],
'SCHEMA_TABLE_COUNT' => $schemaInfo['table_count'],
'SCHEMA_SIZE_MB' => $schemaInfo['size_mb'],
]);
// Assign migration data for template
if (!empty($status['applied_migrations'])) {
foreach ($status['applied_migrations'] as $i => $migration) {
$template->assign_block_vars('applied_migrations', [
'VERSION' => $migration['version'],
'NAME' => $migration['migration_name'] ?? __('UNKNOWN'),
'START_TIME' => $migration['start_time'] ?? __('UNKNOWN'),
'END_TIME' => $migration['end_time'] ?? __('UNKNOWN'),
'ROW_CLASS' => ($i % 2) ? 'row1' : 'row2'
]);
}
}
if (!empty($status['pending_migrations'])) {
foreach ($status['pending_migrations'] as $i => $migration) {
$template->assign_block_vars('pending_migrations', [
'VERSION' => $migration['version'],
'NAME' => $migration['name'],
'FILENAME' => $migration['filename'],
'ROW_CLASS' => ($i % 2) ? 'row1' : 'row2'
]);
}
}
// Output template using standard admin pattern
print_page('admin_migrations.tpl', 'admin');

View file

@ -68,6 +68,7 @@
"nette/caching": "^3.3",
"nette/database": "^3.2",
"php-curl-class/php-curl-class": "^12.0.0",
"robmorgan/phinx": "^0.16.9",
"samdark/sitemap": "2.4.1",
"symfony/event-dispatcher": "^6.4",
"symfony/filesystem": "^6.4",

799
composer.lock generated
View file

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "61d990417a4943d9986cef7fc3c0f382",
"content-hash": "1c6ed0e1507a53ce5784c929c38e84cf",
"packages": [
{
"name": "arokettu/bencode",
@ -544,6 +544,330 @@
},
"time": "2025-03-06T12:03:07+00:00"
},
{
"name": "cakephp/chronos",
"version": "3.1.0",
"source": {
"type": "git",
"url": "https://github.com/cakephp/chronos.git",
"reference": "786d69e1ee4b735765cbdb5521b9603e9b98d650"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/cakephp/chronos/zipball/786d69e1ee4b735765cbdb5521b9603e9b98d650",
"reference": "786d69e1ee4b735765cbdb5521b9603e9b98d650",
"shasum": ""
},
"require": {
"php": ">=8.1",
"psr/clock": "^1.0"
},
"provide": {
"psr/clock-implementation": "1.0"
},
"require-dev": {
"cakephp/cakephp-codesniffer": "^5.0",
"phpunit/phpunit": "^10.1.0 || ^11.1.3"
},
"type": "library",
"autoload": {
"psr-4": {
"Cake\\Chronos\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Brian Nesbitt",
"email": "brian@nesbot.com",
"homepage": "http://nesbot.com"
},
{
"name": "The CakePHP Team",
"homepage": "https://cakephp.org"
}
],
"description": "A simple API extension for DateTime.",
"homepage": "https://cakephp.org",
"keywords": [
"date",
"datetime",
"time"
],
"support": {
"issues": "https://github.com/cakephp/chronos/issues",
"source": "https://github.com/cakephp/chronos"
},
"time": "2024-07-18T03:18:04+00:00"
},
{
"name": "cakephp/core",
"version": "5.2.4",
"source": {
"type": "git",
"url": "https://github.com/cakephp/core.git",
"reference": "a0a92ee7fbb7b7555dbf4ea7ff3fd4e779693da6"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/cakephp/core/zipball/a0a92ee7fbb7b7555dbf4ea7ff3fd4e779693da6",
"reference": "a0a92ee7fbb7b7555dbf4ea7ff3fd4e779693da6",
"shasum": ""
},
"require": {
"cakephp/utility": "5.2.*@dev",
"league/container": "^4.2",
"php": ">=8.1",
"psr/container": "^1.1 || ^2.0"
},
"provide": {
"psr/container-implementation": "^2.0"
},
"suggest": {
"cakephp/cache": "To use Configure::store() and restore().",
"cakephp/event": "To use PluginApplicationInterface or plugin applications.",
"league/container": "To use Container and ServiceProvider classes"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-5.x": "5.2.x-dev"
}
},
"autoload": {
"files": [
"functions.php"
],
"psr-4": {
"Cake\\Core\\": "."
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "CakePHP Community",
"homepage": "https://github.com/cakephp/core/graphs/contributors"
}
],
"description": "CakePHP Framework Core classes",
"homepage": "https://cakephp.org",
"keywords": [
"cakephp",
"core",
"framework"
],
"support": {
"forum": "https://stackoverflow.com/tags/cakephp",
"irc": "irc://irc.freenode.org/cakephp",
"issues": "https://github.com/cakephp/cakephp/issues",
"source": "https://github.com/cakephp/core"
},
"time": "2025-04-19T12:34:03+00:00"
},
{
"name": "cakephp/database",
"version": "5.2.4",
"source": {
"type": "git",
"url": "https://github.com/cakephp/database.git",
"reference": "8c4eaecf6612274b445172b680dc47a2dad681a9"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/cakephp/database/zipball/8c4eaecf6612274b445172b680dc47a2dad681a9",
"reference": "8c4eaecf6612274b445172b680dc47a2dad681a9",
"shasum": ""
},
"require": {
"cakephp/chronos": "^3.1",
"cakephp/core": "5.2.*@dev",
"cakephp/datasource": "5.2.*@dev",
"php": ">=8.1",
"psr/log": "^3.0"
},
"require-dev": {
"cakephp/i18n": "5.2.*@dev",
"cakephp/log": "5.2.*@dev"
},
"suggest": {
"cakephp/i18n": "If you are using locale-aware datetime formats.",
"cakephp/log": "If you want to use query logging without providing a logger yourself."
},
"type": "library",
"extra": {
"branch-alias": {
"dev-5.x": "5.2.x-dev"
}
},
"autoload": {
"psr-4": {
"Cake\\Database\\": "."
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "CakePHP Community",
"homepage": "https://github.com/cakephp/database/graphs/contributors"
}
],
"description": "Flexible and powerful Database abstraction library with a familiar PDO-like API",
"homepage": "https://cakephp.org",
"keywords": [
"abstraction",
"cakephp",
"database",
"database abstraction",
"pdo"
],
"support": {
"forum": "https://stackoverflow.com/tags/cakephp",
"irc": "irc://irc.freenode.org/cakephp",
"issues": "https://github.com/cakephp/cakephp/issues",
"source": "https://github.com/cakephp/database"
},
"time": "2025-05-09T15:08:51+00:00"
},
{
"name": "cakephp/datasource",
"version": "5.2.4",
"source": {
"type": "git",
"url": "https://github.com/cakephp/datasource.git",
"reference": "f7dc4292bec0ec746db3200a5b18bb371d50dab3"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/cakephp/datasource/zipball/f7dc4292bec0ec746db3200a5b18bb371d50dab3",
"reference": "f7dc4292bec0ec746db3200a5b18bb371d50dab3",
"shasum": ""
},
"require": {
"cakephp/core": "5.2.*@dev",
"php": ">=8.1",
"psr/simple-cache": "^2.0 || ^3.0"
},
"require-dev": {
"cakephp/cache": "5.2.*@dev",
"cakephp/collection": "5.2.*@dev",
"cakephp/utility": "5.2.*@dev"
},
"suggest": {
"cakephp/cache": "If you decide to use Query caching.",
"cakephp/collection": "If you decide to use ResultSetInterface.",
"cakephp/utility": "If you decide to use EntityTrait."
},
"type": "library",
"extra": {
"branch-alias": {
"dev-5.x": "5.2.x-dev"
}
},
"autoload": {
"psr-4": {
"Cake\\Datasource\\": "."
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "CakePHP Community",
"homepage": "https://github.com/cakephp/datasource/graphs/contributors"
}
],
"description": "Provides connection managing and traits for Entities and Queries that can be reused for different datastores",
"homepage": "https://cakephp.org",
"keywords": [
"cakephp",
"connection management",
"datasource",
"entity",
"query"
],
"support": {
"forum": "https://stackoverflow.com/tags/cakephp",
"irc": "irc://irc.freenode.org/cakephp",
"issues": "https://github.com/cakephp/cakephp/issues",
"source": "https://github.com/cakephp/datasource"
},
"time": "2025-04-26T23:00:26+00:00"
},
{
"name": "cakephp/utility",
"version": "5.2.4",
"source": {
"type": "git",
"url": "https://github.com/cakephp/utility.git",
"reference": "76dcd5c20e46aaf5bfdf9ad51e9f5313abffe104"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/cakephp/utility/zipball/76dcd5c20e46aaf5bfdf9ad51e9f5313abffe104",
"reference": "76dcd5c20e46aaf5bfdf9ad51e9f5313abffe104",
"shasum": ""
},
"require": {
"cakephp/core": "5.2.*@dev",
"php": ">=8.1"
},
"suggest": {
"ext-intl": "To use Text::transliterate() or Text::slug()",
"lib-ICU": "To use Text::transliterate() or Text::slug()"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-5.x": "5.2.x-dev"
}
},
"autoload": {
"files": [
"bootstrap.php"
],
"psr-4": {
"Cake\\Utility\\": "."
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "CakePHP Community",
"homepage": "https://github.com/cakephp/utility/graphs/contributors"
}
],
"description": "CakePHP Utility classes such as Inflector, String, Hash, and Security",
"homepage": "https://cakephp.org",
"keywords": [
"cakephp",
"hash",
"inflector",
"security",
"string",
"utility"
],
"support": {
"forum": "https://stackoverflow.com/tags/cakephp",
"irc": "irc://irc.freenode.org/cakephp",
"issues": "https://github.com/cakephp/cakephp/issues",
"source": "https://github.com/cakephp/utility"
},
"time": "2025-04-19T12:34:03+00:00"
},
{
"name": "claviska/simpleimage",
"version": "4.2.1",
@ -1618,6 +1942,88 @@
},
"time": "2022-09-24T15:57:16+00:00"
},
{
"name": "league/container",
"version": "4.2.5",
"source": {
"type": "git",
"url": "https://github.com/thephpleague/container.git",
"reference": "d3cebb0ff4685ff61c749e54b27db49319e2ec00"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/thephpleague/container/zipball/d3cebb0ff4685ff61c749e54b27db49319e2ec00",
"reference": "d3cebb0ff4685ff61c749e54b27db49319e2ec00",
"shasum": ""
},
"require": {
"php": "^7.2 || ^8.0",
"psr/container": "^1.1 || ^2.0"
},
"provide": {
"psr/container-implementation": "^1.0"
},
"replace": {
"orno/di": "~2.0"
},
"require-dev": {
"nette/php-generator": "^3.4",
"nikic/php-parser": "^4.10",
"phpstan/phpstan": "^0.12.47",
"phpunit/phpunit": "^8.5.17",
"roave/security-advisories": "dev-latest",
"scrutinizer/ocular": "^1.8",
"squizlabs/php_codesniffer": "^3.6"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-1.x": "1.x-dev",
"dev-2.x": "2.x-dev",
"dev-3.x": "3.x-dev",
"dev-4.x": "4.x-dev",
"dev-master": "4.x-dev"
}
},
"autoload": {
"psr-4": {
"League\\Container\\": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Phil Bennett",
"email": "mail@philbennett.co.uk",
"role": "Developer"
}
],
"description": "A fast and intuitive dependency injection container.",
"homepage": "https://github.com/thephpleague/container",
"keywords": [
"container",
"dependency",
"di",
"injection",
"league",
"provider",
"service"
],
"support": {
"issues": "https://github.com/thephpleague/container/issues",
"source": "https://github.com/thephpleague/container/tree/4.2.5"
},
"funding": [
{
"url": "https://github.com/philipobenito",
"type": "github"
}
],
"time": "2025-05-20T12:55:37+00:00"
},
{
"name": "league/flysystem",
"version": "3.29.1",
@ -2559,6 +2965,54 @@
},
"time": "2021-02-03T23:26:27+00:00"
},
{
"name": "psr/clock",
"version": "1.0.0",
"source": {
"type": "git",
"url": "https://github.com/php-fig/clock.git",
"reference": "e41a24703d4560fd0acb709162f73b8adfc3aa0d"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/php-fig/clock/zipball/e41a24703d4560fd0acb709162f73b8adfc3aa0d",
"reference": "e41a24703d4560fd0acb709162f73b8adfc3aa0d",
"shasum": ""
},
"require": {
"php": "^7.0 || ^8.0"
},
"type": "library",
"autoload": {
"psr-4": {
"Psr\\Clock\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "PHP-FIG",
"homepage": "https://www.php-fig.org/"
}
],
"description": "Common interface for reading the clock.",
"homepage": "https://github.com/php-fig/clock",
"keywords": [
"clock",
"now",
"psr",
"psr-20",
"time"
],
"support": {
"issues": "https://github.com/php-fig/clock/issues",
"source": "https://github.com/php-fig/clock/tree/1.0.0"
},
"time": "2022-11-25T14:36:26+00:00"
},
{
"name": "psr/container",
"version": "2.0.2",
@ -2967,6 +3421,93 @@
},
"time": "2019-03-08T08:55:37+00:00"
},
{
"name": "robmorgan/phinx",
"version": "0.16.9",
"source": {
"type": "git",
"url": "https://github.com/cakephp/phinx.git",
"reference": "524ebdeb0e1838a845d752a3418726b38cd1e654"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/cakephp/phinx/zipball/524ebdeb0e1838a845d752a3418726b38cd1e654",
"reference": "524ebdeb0e1838a845d752a3418726b38cd1e654",
"shasum": ""
},
"require": {
"cakephp/database": "^5.0.2",
"composer-runtime-api": "^2.0",
"php-64bit": ">=8.1",
"psr/container": "^1.1|^2.0",
"symfony/config": "^4.0|^5.0|^6.0|^7.0",
"symfony/console": "^6.0|^7.0"
},
"require-dev": {
"cakephp/cakephp-codesniffer": "^5.0",
"cakephp/i18n": "^5.0",
"ext-json": "*",
"ext-pdo": "*",
"phpunit/phpunit": "^9.5.19",
"symfony/yaml": "^4.0|^5.0|^6.0|^7.0"
},
"suggest": {
"ext-json": "Install if using JSON configuration format",
"ext-pdo": "PDO extension is needed",
"symfony/yaml": "Install if using YAML configuration format"
},
"bin": [
"bin/phinx"
],
"type": "library",
"autoload": {
"psr-4": {
"Phinx\\": "src/Phinx/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Rob Morgan",
"email": "robbym@gmail.com",
"homepage": "https://robmorgan.id.au",
"role": "Lead Developer"
},
{
"name": "Woody Gilk",
"email": "woody.gilk@gmail.com",
"homepage": "https://shadowhand.me",
"role": "Developer"
},
{
"name": "Richard Quadling",
"email": "rquadling@gmail.com",
"role": "Developer"
},
{
"name": "CakePHP Community",
"homepage": "https://github.com/cakephp/phinx/graphs/contributors",
"role": "Developer"
}
],
"description": "Phinx makes it ridiculously easy to manage the database migrations for your PHP app.",
"homepage": "https://phinx.org",
"keywords": [
"database",
"database migrations",
"db",
"migrations",
"phinx"
],
"support": {
"issues": "https://github.com/cakephp/phinx/issues",
"source": "https://github.com/cakephp/phinx/tree/0.16.9"
},
"time": "2025-05-25T16:07:44+00:00"
},
{
"name": "samdark/sitemap",
"version": "2.4.1",
@ -3026,6 +3567,175 @@
],
"time": "2023-11-01T08:41:34+00:00"
},
{
"name": "symfony/config",
"version": "v7.0.8",
"source": {
"type": "git",
"url": "https://github.com/symfony/config.git",
"reference": "f8a8fb0c2d0a188a00a2dd5af8a4eb070641ec60"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/config/zipball/f8a8fb0c2d0a188a00a2dd5af8a4eb070641ec60",
"reference": "f8a8fb0c2d0a188a00a2dd5af8a4eb070641ec60",
"shasum": ""
},
"require": {
"php": ">=8.2",
"symfony/deprecation-contracts": "^2.5|^3",
"symfony/filesystem": "^6.4|^7.0",
"symfony/polyfill-ctype": "~1.8"
},
"conflict": {
"symfony/finder": "<6.4",
"symfony/service-contracts": "<2.5"
},
"require-dev": {
"symfony/event-dispatcher": "^6.4|^7.0",
"symfony/finder": "^6.4|^7.0",
"symfony/messenger": "^6.4|^7.0",
"symfony/service-contracts": "^2.5|^3",
"symfony/yaml": "^6.4|^7.0"
},
"type": "library",
"autoload": {
"psr-4": {
"Symfony\\Component\\Config\\": ""
},
"exclude-from-classmap": [
"/Tests/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Helps you find, load, combine, autofill and validate configuration values of any kind",
"homepage": "https://symfony.com",
"support": {
"source": "https://github.com/symfony/config/tree/v7.0.8"
},
"funding": [
{
"url": "https://symfony.com/sponsor",
"type": "custom"
},
{
"url": "https://github.com/fabpot",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
"time": "2024-05-31T14:55:39+00:00"
},
{
"name": "symfony/console",
"version": "v7.3.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/console.git",
"reference": "66c1440edf6f339fd82ed6c7caa76cb006211b44"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/console/zipball/66c1440edf6f339fd82ed6c7caa76cb006211b44",
"reference": "66c1440edf6f339fd82ed6c7caa76cb006211b44",
"shasum": ""
},
"require": {
"php": ">=8.2",
"symfony/deprecation-contracts": "^2.5|^3",
"symfony/polyfill-mbstring": "~1.0",
"symfony/service-contracts": "^2.5|^3",
"symfony/string": "^7.2"
},
"conflict": {
"symfony/dependency-injection": "<6.4",
"symfony/dotenv": "<6.4",
"symfony/event-dispatcher": "<6.4",
"symfony/lock": "<6.4",
"symfony/process": "<6.4"
},
"provide": {
"psr/log-implementation": "1.0|2.0|3.0"
},
"require-dev": {
"psr/log": "^1|^2|^3",
"symfony/config": "^6.4|^7.0",
"symfony/dependency-injection": "^6.4|^7.0",
"symfony/event-dispatcher": "^6.4|^7.0",
"symfony/http-foundation": "^6.4|^7.0",
"symfony/http-kernel": "^6.4|^7.0",
"symfony/lock": "^6.4|^7.0",
"symfony/messenger": "^6.4|^7.0",
"symfony/process": "^6.4|^7.0",
"symfony/stopwatch": "^6.4|^7.0",
"symfony/var-dumper": "^6.4|^7.0"
},
"type": "library",
"autoload": {
"psr-4": {
"Symfony\\Component\\Console\\": ""
},
"exclude-from-classmap": [
"/Tests/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Eases the creation of beautiful and testable command line interfaces",
"homepage": "https://symfony.com",
"keywords": [
"cli",
"command-line",
"console",
"terminal"
],
"support": {
"source": "https://github.com/symfony/console/tree/v7.3.0"
},
"funding": [
{
"url": "https://symfony.com/sponsor",
"type": "custom"
},
{
"url": "https://github.com/fabpot",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
"time": "2025-05-24T10:34:04+00:00"
},
{
"name": "symfony/deprecation-contracts",
"version": "v3.6.0",
@ -3744,6 +4454,93 @@
],
"time": "2025-04-25T09:37:31+00:00"
},
{
"name": "symfony/string",
"version": "v7.3.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/string.git",
"reference": "f3570b8c61ca887a9e2938e85cb6458515d2b125"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/string/zipball/f3570b8c61ca887a9e2938e85cb6458515d2b125",
"reference": "f3570b8c61ca887a9e2938e85cb6458515d2b125",
"shasum": ""
},
"require": {
"php": ">=8.2",
"symfony/polyfill-ctype": "~1.8",
"symfony/polyfill-intl-grapheme": "~1.0",
"symfony/polyfill-intl-normalizer": "~1.0",
"symfony/polyfill-mbstring": "~1.0"
},
"conflict": {
"symfony/translation-contracts": "<2.5"
},
"require-dev": {
"symfony/emoji": "^7.1",
"symfony/error-handler": "^6.4|^7.0",
"symfony/http-client": "^6.4|^7.0",
"symfony/intl": "^6.4|^7.0",
"symfony/translation-contracts": "^2.5|^3.0",
"symfony/var-exporter": "^6.4|^7.0"
},
"type": "library",
"autoload": {
"files": [
"Resources/functions.php"
],
"psr-4": {
"Symfony\\Component\\String\\": ""
},
"exclude-from-classmap": [
"/Tests/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Nicolas Grekas",
"email": "p@tchwork.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Provides an object-oriented API to strings and deals with bytes, UTF-8 code points and grapheme clusters in a unified way",
"homepage": "https://symfony.com",
"keywords": [
"grapheme",
"i18n",
"string",
"unicode",
"utf-8",
"utf8"
],
"support": {
"source": "https://github.com/symfony/string/tree/v7.3.0"
},
"funding": [
{
"url": "https://symfony.com/sponsor",
"type": "custom"
},
{
"url": "https://github.com/fabpot",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
"time": "2025-04-20T20:19:01+00:00"
},
{
"name": "vlucas/phpdotenv",
"version": "v5.6.2",

View file

@ -11,7 +11,7 @@ define('BB_ROOT', __DIR__ . DIRECTORY_SEPARATOR);
define('BB_PATH', BB_ROOT);
// Check CLI mode
if (php_sapi_name() !== 'cli') {
if (PHP_SAPI != 'cli') {
die('Please run <code style="background:#222;color:#00e01f;padding:2px 6px;border-radius:3px;">php ' . basename(__FILE__) . '</code> in CLI mode');
}
@ -24,7 +24,7 @@ require INC_DIR . '/functions_cli.php';
/**
* System requirements
*/
define('CHECK_REQUIREMENTS', [
const CHECK_REQUIREMENTS = [
'php_min_version' => '8.1.0',
'ext_list' => [
'json',
@ -39,7 +39,7 @@ define('CHECK_REQUIREMENTS', [
'zip',
'gd'
],
]);
];
// Welcoming message
out("--- TorrentPier Installer ---\n", 'info');
@ -265,35 +265,26 @@ if (!empty($DB_HOST) && !empty($DB_DATABASE) && !empty($DB_USERNAME)) {
}
$conn->select_db($DB_DATABASE);
// Checking SQL dump
$dumpPath = BB_ROOT . 'install/sql/mysql.sql';
if (is_file($dumpPath) && is_readable($dumpPath)) {
out('- SQL dump file found and readable!', 'success');
} else {
out('- SQL dump file not found / not readable', 'error');
// Close database connection - migrations will handle their own connections
$conn->close();
// Run database migrations
out('- Setting up database using migrations...', 'info');
// Check if phinx.php exists
if (!is_file(BB_ROOT . 'phinx.php')) {
out('- Migration configuration (phinx.php) not found', 'error');
exit;
}
// Inserting SQL dump
out('- Start importing SQL dump...', 'info');
$tempLine = '';
foreach (file($dumpPath) as $line) {
if (str_starts_with($line, '--') || $line == '') {
continue;
}
$tempLine .= $line;
if (str_ends_with(trim($line), ';')) {
if (!$conn->query($tempLine)) {
out("- Error performing query: $tempLine", 'error');
exit;
}
$tempLine = '';
}
// Run migrations
$migrationResult = runProcess('php vendor/bin/phinx migrate --configuration=' . BB_ROOT . 'phinx.php');
if ($migrationResult !== 0) {
out('- Database migration failed', 'error');
exit;
}
$conn->close();
out("- Importing SQL dump completed!\n", 'success');
out("- Database setup completed!\n", 'success');
// Autofill host in robots.txt
$robots_txt_file = BB_ROOT . 'robots.txt';

File diff suppressed because it is too large Load diff

View file

@ -1,157 +0,0 @@
// Changes from v2.2.0 to 2.4.5
// 2.2.0
UPDATE `bb_config` SET `config_value` = 'http://whatismyipaddress.com/ip/' WHERE `config_name` = 'whois_info';
DELETE FROM `bb_smilies` WHERE `code` = ':ad:';
INSERT INTO `bb_smilies` (`code`, `smile_url`, `emoticon`) VALUES (':сd:', 'сd.gif', 'сd');
DROP TABLE IF EXISTS `bb_ads`;
DELETE FROM `bb_config` WHERE `config_name` = 'active_ads';
ALTER TABLE `bb_log` DROP COLUMN `log_username`;
DELETE FROM `bb_config` WHERE `config_name` = 'new_tpls';
UPDATE `bb_posts` SET `poster_ip` = '0';
ALTER TABLE `bb_posts` CHANGE `poster_ip` `poster_ip` varchar(42) NOT NULL DEFAULT '0';
UPDATE `bb_bt_tracker` SET `ip` = '0';
ALTER TABLE `bb_bt_tracker` CHANGE `ip` `ip` varchar(42) NOT NULL DEFAULT '0';
UPDATE `bb_users` SET `user_last_ip` = '0';
ALTER TABLE `bb_users` CHANGE `user_last_ip` `user_last_ip` varchar(42) NOT NULL DEFAULT '0';
UPDATE `bb_users` SET `user_reg_ip` = '0';
ALTER TABLE `bb_users` CHANGE `user_reg_ip` `user_reg_ip` varchar(42) NOT NULL DEFAULT '0';
UPDATE `bb_log` SET `log_user_ip` = '0';
ALTER TABLE `bb_log` CHANGE `log_user_ip` `log_user_ip` varchar(42) NOT NULL DEFAULT '0';
UPDATE `bb_poll_users` SET `vote_ip` = '0';
ALTER TABLE `bb_poll_users` CHANGE `vote_ip` `vote_ip` varchar(42) NOT NULL DEFAULT '0';
UPDATE `bb_privmsgs` SET `privmsgs_ip` = '0';
ALTER TABLE `bb_privmsgs` CHANGE `privmsgs_ip` `privmsgs_ip` varchar(42) NOT NULL DEFAULT '0';
UPDATE `bb_sessions` SET `session_ip` = '0';
ALTER TABLE `bb_sessions` CHANGE `session_ip` `session_ip` varchar(42) NOT NULL DEFAULT '0';
UPDATE `bb_banlist` SET `ban_ip` = '0';
ALTER TABLE `bb_banlist` CHANGE `ban_ip` `ban_ip` varchar(42) NOT NULL DEFAULT '0';
// 2.2.2
ALTER TABLE `bb_ranks` DROP `rank_min`;
ALTER TABLE `bb_ranks` DROP `rank_special`;
// 2.3.0
ALTER TABLE `bb_cron` CHANGE `last_run` `last_run` DATETIME NOT NULL DEFAULT '1900-01-01 00:00:00';
ALTER TABLE `bb_cron` CHANGE `next_run` `next_run` DATETIME NOT NULL DEFAULT '1900-01-01 00:00:00';
ALTER TABLE `bb_users` CHANGE `user_birthday` `user_birthday` DATE NOT NULL DEFAULT '1900-01-01';
ALTER TABLE `bb_posts` CHANGE `mc_comment` `mc_comment` TEXT NOT NULL DEFAULT '';
// 2.3.0.2
ALTER TABLE `bb_users` CHANGE `user_sig` `user_sig` TEXT NOT NULL DEFAULT '';
ALTER TABLE `bb_groups` CHANGE `group_signature` `group_signature` TEXT NOT NULL DEFAULT '';
ALTER TABLE `bb_groups` CHANGE `group_description` `group_description` TEXT NOT NULL DEFAULT '';
UPDATE `bb_smilies` SET `code` = ':cd:', `smile_url` = 'cd.gif', `emoticon` = 'cd' WHERE `code` = ':сd:' AND `smile_url` = 'сd.gif' AND `emoticon` = 'сd';
// 2.3.1
ALTER TABLE `bb_search_results` CHANGE `search_id` `search_id` VARCHAR(255) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL DEFAULT '';
ALTER TABLE `bb_users` CHANGE `autologin_id` `autologin_id` VARCHAR(255) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL DEFAULT '';
DELETE FROM `bb_config` WHERE `config_name` = 'cron_enabled';
// 2.4.0-alpha1
ALTER TABLE `bb_search_results` CHANGE `session_id` `session_id` CHAR(255) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL DEFAULT '';
ALTER TABLE `bb_sessions` CHANGE `session_id` `session_id` CHAR(255) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL DEFAULT '';
ALTER TABLE `bb_users` CHANGE `username` `username` VARCHAR(255) NOT NULL DEFAULT '';
ALTER TABLE `bb_users` CHANGE `user_password` `user_password` VARCHAR(255) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL DEFAULT '';
ALTER TABLE `bb_users` CHANGE `user_actkey` `user_actkey` VARCHAR(255) NOT NULL DEFAULT '';
ALTER TABLE `bb_users` CHANGE `user_newpasswd` `user_newpasswd` VARCHAR(255) NOT NULL DEFAULT '';
// 2.4.0-alpha3
INSERT INTO bb_config VALUES ('show_board_start_index', '1');
// 2.4.0-beta2
INSERT INTO `bb_cron` (`cron_active`, `cron_title`, `cron_script`, `schedule`, `run_day`, `run_time`, `run_order`,
`last_run`, `next_run`, `run_interval`, `log_enabled`, `log_file`, `log_sql_queries`,
`disable_board`, `run_counter`) VALUES ('1', 'PM cleanup', 'clean_pm.php', 'daily', '', '05:00:00', '70', '', '', '', '1', '', '0', '1', '0');
ALTER TABLE `bb_posts_text` CHANGE `post_text` `post_text` MEDIUMTEXT NOT NULL;
ALTER TABLE `bb_privmsgs_text` CHANGE `privmsgs_text` `privmsgs_text` MEDIUMTEXT NOT NULL;
ALTER TABLE `bb_bt_torrents` ADD COLUMN `info_hash_v2` VARBINARY(32) NOT NULL DEFAULT '';
ALTER TABLE `bb_bt_tracker_snap` ADD COLUMN `completed` INT(10) NOT NULL DEFAULT '0';
ALTER TABLE `bb_bt_tracker` CHANGE `complete` `complete` TINYINT(1) NOT NULL DEFAULT '0';
// 2.4.0-beta3
INSERT INTO `bb_extensions` (`group_id`, `extension`, `comment`) VALUES ('1', 'webp', '');
INSERT INTO `bb_extensions` (`group_id`, `extension`, `comment`) VALUES ('2', '7z', '');
INSERT INTO `bb_extensions` (`group_id`, `extension`, `comment`) VALUES ('1', 'bmp', '');
ALTER TABLE `bb_bt_tracker` CHANGE `speed_up` `speed_up` INT(11) UNSIGNED NOT NULL DEFAULT '0';
ALTER TABLE `bb_bt_tracker` CHANGE `speed_down` `speed_down` INT(11) UNSIGNED NOT NULL DEFAULT '0';
ALTER TABLE `bb_bt_tracker_snap` CHANGE `speed_up` `speed_up` INT(11) UNSIGNED NOT NULL DEFAULT '0';
ALTER TABLE `bb_bt_tracker_snap` CHANGE `speed_down` `speed_down` INT(11) UNSIGNED NOT NULL DEFAULT '0';
ALTER TABLE `bb_bt_torrents` ADD COLUMN `last_seeder_id` MEDIUMINT(8) NOT NULL DEFAULT '0';
ALTER TABLE `buf_last_seeder` ADD COLUMN `user_id` MEDIUMINT(8) NOT NULL DEFAULT '0';
ALTER TABLE `bb_bt_tracker` CHANGE `ip` `ip` VARCHAR(42) DEFAULT NULL;
ALTER TABLE `bb_bt_tracker` CHANGE `ipv6` `ipv6` VARCHAR(42) DEFAULT NULL;
ALTER TABLE `bb_bt_users` CHANGE `auth_key` `auth_key` CHAR(20) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL DEFAULT '';
// 2.4.0-beta4
DELETE FROM `bb_extensions` WHERE `extension` = 'tif';
INSERT INTO `bb_extensions` (`group_id`, `extension`, `comment`) VALUES ('4', 'tif', '');
INSERT INTO `bb_extensions` (`group_id`, `extension`, `comment`) VALUES ('4', 'tiff', '');
DELETE FROM `bb_extensions` WHERE `extension` = 'tga';
INSERT INTO `bb_extensions` (`group_id`, `extension`, `comment`) VALUES ('4', 'tga', '');
// 2.4.0-rc1
ALTER TABLE `bb_bt_tracker` DROP COLUMN `client`;
DROP TABLE IF EXISTS `bb_thx`;
CREATE TABLE IF NOT EXISTS `bb_thx`
(
`topic_id` MEDIUMINT(8) UNSIGNED NOT NULL DEFAULT '0',
`user_id` MEDIUMINT(8) NOT NULL DEFAULT '0',
`time` INT(11) NOT NULL DEFAULT '0',
PRIMARY KEY (`topic_id`, `user_id`)
)
ENGINE = MyISAM
DEFAULT CHARSET = utf8mb4
COLLATE = utf8mb4_unicode_ci;
// 2.4.0
UPDATE `bb_attachments_config` SET `config_value` = 'data/uploads' WHERE `config_name` = 'upload_dir';
UPDATE `bb_attachments_config` SET `config_value` = '12000' WHERE `config_name` = 'img_min_thumb_filesize';
DELETE FROM `bb_attachments_config` WHERE config_name = 'attach_version';
DELETE FROM `bb_attachments_config` WHERE config_name = 'img_min_thumb_filesize';
DELETE FROM `bb_attachments_config` WHERE config_name = 'img_imagick';
DELETE FROM `bb_attachments_config` WHERE config_name = 'use_gd2';
DELETE FROM `bb_attachments_config` WHERE config_name = 'wma_autoplay';
DELETE FROM `bb_attachments_config` WHERE config_name = 'flash_autoplay';
DELETE FROM `bb_extensions` WHERE extension = 'tif';
DELETE FROM `bb_extensions` WHERE extension = 'tiff';
DELETE FROM `bb_extensions` WHERE extension = 'tga';
DROP TABLE IF EXISTS `bb_banlist`;
CREATE TABLE IF NOT EXISTS `bb_banlist`
(
`ban_id` MEDIUMINT(8) UNSIGNED NOT NULL AUTO_INCREMENT,
`ban_userid` MEDIUMINT(8) NOT NULL DEFAULT '0',
`ban_reason` VARCHAR(255) NOT NULL DEFAULT '',
PRIMARY KEY (`ban_id`, `ban_userid`)
)
ENGINE = MyISAM
DEFAULT CHARSET = utf8mb4
COLLATE = utf8mb4_unicode_ci;
// 2.4.1
UPDATE `bb_config` SET `config_value` = '' WHERE `config_name` = 'bt_announce_url';
// 2.4.2
INSERT INTO `bb_cron` (`cron_active`, `cron_title`, `cron_script`, `schedule`, `run_day`, `run_time`, `run_order`,
`last_run`, `next_run`, `run_interval`, `log_enabled`, `log_file`, `log_sql_queries`,
`disable_board`, `run_counter`) VALUES ('1', 'Demo mode', 'demo_mode.php', 'daily', '', '05:00:00', '255', '', '', '', '1', 'demo_mode_cron', '1', '1', '0');
// 2.4.3
UPDATE `bb_config` SET `config_value` = 'https://localhost/bt/announce.php' WHERE `config_name` = 'bt_announce_url';
// 2.4.4
ALTER TABLE `bb_poll_users` CHANGE `user_id` `user_id` MEDIUMINT(8) NOT NULL;
ALTER TABLE `bb_bt_users` ADD COLUMN `ratio_nulled` TINYINT(1) NOT NULL DEFAULT '0';
DELETE FROM `bb_cron` WHERE `cron_script` = 'cache_gc.php';
UPDATE `bb_cron` SET `run_interval` = '00:10:00' WHERE `cron_script` = 'tr_seed_bonus.php';
// 2.4.5-rc.1
INSERT INTO `bb_extensions` (`group_id`, `extension`, `comment`) VALUES ('1', 'avif', ''), ('3', 'm3u', '');
ALTER TABLE `bb_topics` ADD COLUMN `topic_allow_robots` TINYINT(1) UNSIGNED NOT NULL DEFAULT '0';
// 2.4.5-rc.2
INSERT INTO `bb_config` VALUES ('magnet_links_for_guests', '0');
INSERT INTO `bb_config` VALUES ('tp_instance_hash', '');
// 2.4.5-rc.5
DELETE FROM `bb_config` WHERE `config_name` = 'tp_instance_hash';

View file

@ -83,6 +83,9 @@ define('CRON_RUNNING', TRIGGERS_DIR . '/cron_running');
define('GZIP_OUTPUT_ALLOWED', extension_loaded('zlib') && !ini_get('zlib.output_compression'));
define('UA_GZIP_SUPPORTED', isset($_SERVER['HTTP_ACCEPT_ENCODING']) && str_contains($_SERVER['HTTP_ACCEPT_ENCODING'], 'gzip'));
// Migrations table
define('BB_MIGRATIONS', 'bb_migrations');
// Tracker shared constants
define('BB_BT_TORRENTS', 'bb_bt_torrents');
define('BB_BT_TRACKER', 'bb_bt_tracker');

View file

@ -13,11 +13,10 @@ if (!defined('BB_ROOT')) {
define('IN_CRON', true);
// Set SESSION vars
// Set SESSION vars (optimized for InnoDB)
DB()->query("
SET SESSION
myisam_sort_buffer_size = 16*1024*1024
, bulk_insert_buffer_size = 8*1024*1024
bulk_insert_buffer_size = 8*1024*1024
, join_buffer_size = 4*1024*1024
, read_buffer_size = 4*1024*1024
, read_rnd_buffer_size = 8*1024*1024
@ -29,8 +28,7 @@ DB()->query("
// Restore vars at shutdown
DB()->add_shutdown_query("
SET SESSION
myisam_sort_buffer_size = DEFAULT
, bulk_insert_buffer_size = DEFAULT
bulk_insert_buffer_size = DEFAULT
, join_buffer_size = DEFAULT
, read_buffer_size = DEFAULT
, read_rnd_buffer_size = DEFAULT

View file

@ -28,7 +28,7 @@ DB()->query("
CREATE TEMPORARY TABLE $tmp_attach_tbl (
physical_filename VARCHAR(255) NOT NULL default '',
KEY physical_filename (physical_filename(20))
) ENGINE = MyISAM DEFAULT CHARSET = utf8
) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4
");
DB()->add_shutdown_query("DROP TEMPORARY TABLE IF EXISTS $tmp_attach_tbl");

View file

@ -1,44 +0,0 @@
<?php
/**
* TorrentPier Bull-powered BitTorrent tracker engine
*
* @copyright Copyright (c) 2005-2025 TorrentPier (https://torrentpier.com)
* @link https://github.com/torrentpier/torrentpier for the canonical source repository
* @license https://github.com/torrentpier/torrentpier/blob/master/LICENSE MIT License
*/
if (!defined('BB_ROOT')) {
die(basename(__FILE__));
}
set_time_limit(600);
global $cron_runtime_log;
$dump_path = BB_ROOT . 'install/sql/mysql.sql';
if (!IN_DEMO_MODE || !is_file($dump_path) || !is_readable($dump_path)) {
return;
}
// Clean cache & datastore
$datastore->clean();
foreach (config()->get('cache.engines') as $cache_name => $cache_val) {
CACHE($cache_name)->rm();
}
// Drop tables & Insert sql dump
$temp_line = '';
foreach (file($dump_path) as $line) {
if (str_starts_with($line, '--') || $line == '') {
continue;
}
$temp_line .= $line;
if (str_ends_with(trim($line), ';')) {
if (!DB()->query($temp_line)) {
$cron_runtime_log[] = date('Y-m-d H:i:s') . " -- Error performing query: " . $temp_line . " | " . DB()->sql_error()['message'];
}
$temp_line = '';
}
}

View file

@ -86,9 +86,9 @@ function out(string $str, string $type = ''): void
*
* @param string $cmd
* @param string|null $input
* @return void
* @return int
*/
function runProcess(string $cmd, ?string $input = null): void
function runProcess(string $cmd, ?string $input = null): int
{
$descriptorSpec = [
0 => ['pipe', 'r'],
@ -100,7 +100,7 @@ function runProcess(string $cmd, ?string $input = null): void
if (!is_resource($process)) {
out('- Could not start subprocess', 'error');
return;
return -1;
}
// Write input if provided
@ -124,7 +124,7 @@ function runProcess(string $cmd, ?string $input = null): void
fclose($pipes[1]);
fclose($pipes[2]);
proc_close($process);
return proc_close($process);
}
/**

View file

@ -1946,6 +1946,31 @@ $lang['TRACKER_CONFIG'] = 'Tracker settings';
$lang['RELEASE_TEMPLATES'] = 'Release Templates';
$lang['ACTIONS_LOG'] = 'Report on action';
// Migrations
$lang['MIGRATIONS_STATUS'] = 'Database Migration Status';
$lang['MIGRATIONS_DATABASE_NAME'] = 'Database Name';
$lang['MIGRATIONS_DATABASE_TOTAL'] = 'Total Tables';
$lang['MIGRATIONS_DATABASE_SIZE'] = 'Database Size';
$lang['MIGRATIONS_DATABASE_INFO'] = 'Database Information';
$lang['MIGRATIONS_SYSTEM'] = 'Migration System';
$lang['MIGRATIONS_NEEDS_SETUP'] = 'Needs Setup';
$lang['MIGRATIONS_ACTIVE'] = 'Active';
$lang['MIGRATIONS_NOT_INITIALIZED'] = 'Not Initialized';
$lang['MIGRATIONS_UP_TO_DATE'] = 'All up to date';
$lang['MIGRATIONS_PENDING_COUNT'] = 'pending';
$lang['MIGRATIONS_APPLIED'] = 'Applied Migrations';
$lang['MIGRATIONS_PENDING'] = 'Pending Migrations';
$lang['MIGRATIONS_VERSION'] = 'Version';
$lang['MIGRATIONS_NAME'] = 'Migration Name';
$lang['MIGRATIONS_APPLIED_AT'] = 'Applied At';
$lang['MIGRATIONS_COMPLETED_AT'] = 'Completed At';
$lang['MIGRATIONS_CURRENT_VERSION'] = 'Current Version';
$lang['MIGRATIONS_NOT_APPLIED'] = 'No migrations applied';
$lang['MIGRATIONS_INSTRUCTIONS'] = 'Instructions';
$lang['MIGRATIONS_SETUP_STATUS'] = 'Setup Status';
$lang['MIGRATIONS_SETUP_GUIDE'] = 'See setup guide below';
$lang['MIGRATIONS_ACTION_REQUIRED'] = 'Action Required';
// Index
$lang['MAIN_INDEX'] = 'Forum Index';
$lang['FORUM_STATS'] = 'Forum Statistics';

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,968 @@
<?php
/**
* TorrentPier Initial Data Seeding Migration
* Seeds essential data for fresh installations
*/
use Phinx\Migration\AbstractMigration;
class SeedInitialData extends AbstractMigration
{
public function up()
{
// Seed all essential data
$this->seedCategories();
$this->seedForums();
$this->seedUsers();
$this->seedBtUsers();
$this->seedConfiguration();
$this->seedCronJobs();
$this->seedExtensions();
$this->seedSmilies();
$this->seedRanks();
$this->seedQuotaLimits();
$this->seedDisallowedUsernames();
$this->seedAttachmentConfig();
$this->seedTopicsAndPosts();
$this->seedTopicWatch();
}
private function seedCategories()
{
$this->table('bb_categories')->insert([
[
'cat_id' => 1,
'cat_title' => 'Your first category',
'cat_order' => 10
]
])->saveData();
}
private function seedForums()
{
$this->table('bb_forums')->insert([
[
'forum_id' => 1,
'cat_id' => 1,
'forum_name' => 'Your first forum',
'forum_desc' => 'Description of the forum.',
'forum_status' => 0,
'forum_order' => 10,
'forum_posts' => 1,
'forum_topics' => 1,
'forum_last_post_id' => 1,
'forum_tpl_id' => 0,
'prune_days' => 0,
'auth_view' => 0,
'auth_read' => 0,
'auth_post' => 1,
'auth_reply' => 1,
'auth_edit' => 1,
'auth_delete' => 1,
'auth_sticky' => 3,
'auth_announce' => 3,
'auth_vote' => 1,
'auth_pollcreate' => 1,
'auth_attachments' => 1,
'auth_download' => 1,
'allow_reg_tracker' => 0,
'allow_porno_topic' => 0,
'self_moderated' => 0,
'forum_parent' => 0,
'show_on_index' => 1,
'forum_display_sort' => 0,
'forum_display_order' => 0
]
])->saveData();
}
private function seedUsers()
{
$this->table('bb_users')->insert([
[
'user_id' => -1,
'user_active' => 0,
'username' => 'Guest',
'user_password' => '$2y$10$sfZSmqPio8mxxFQLRRXaFuVMkFKZARRz/RzqddfYByN3M53.CEe.O',
'user_session_time' => 0,
'user_lastvisit' => 0,
'user_last_ip' => '0',
'user_regdate' => time(),
'user_reg_ip' => '0',
'user_level' => 0,
'user_posts' => 0,
'user_timezone' => 0.00,
'user_lang' => 'en',
'user_new_privmsg' => 0,
'user_unread_privmsg' => 0,
'user_last_privmsg' => 0,
'user_opt' => 0,
'user_rank' => 0,
'avatar_ext_id' => 0,
'user_gender' => 0,
'user_birthday' => '1900-01-01',
'user_email' => '',
'user_skype' => '',
'user_twitter' => '',
'user_icq' => '',
'user_website' => '',
'user_from' => '',
'user_sig' => '',
'user_occ' => '',
'user_interests' => '',
'user_actkey' => '',
'user_newpasswd' => '',
'autologin_id' => '',
'user_newest_pm_id' => 0,
'user_points' => 0.00,
'tpl_name' => 'default'
],
[
'user_id' => -746,
'user_active' => 0,
'username' => 'bot',
'user_password' => '$2y$10$sfZSmqPio8mxxFQLRRXaFuVMkFKZARRz/RzqddfYByN3M53.CEe.O',
'user_session_time' => 0,
'user_lastvisit' => 0,
'user_last_ip' => '0',
'user_regdate' => time(),
'user_reg_ip' => '0',
'user_level' => 0,
'user_posts' => 0,
'user_timezone' => 0.00,
'user_lang' => 'en',
'user_new_privmsg' => 0,
'user_unread_privmsg' => 0,
'user_last_privmsg' => 0,
'user_opt' => 144,
'user_rank' => 0,
'avatar_ext_id' => 0,
'user_gender' => 0,
'user_birthday' => '1900-01-01',
'user_email' => 'bot@torrentpier.com',
'user_skype' => '',
'user_twitter' => '',
'user_icq' => '',
'user_website' => '',
'user_from' => '',
'user_sig' => '',
'user_occ' => '',
'user_interests' => '',
'user_actkey' => '',
'user_newpasswd' => '',
'autologin_id' => '',
'user_newest_pm_id' => 0,
'user_points' => 0.00,
'tpl_name' => 'default'
],
[
'user_id' => 2,
'user_active' => 1,
'username' => 'admin',
'user_password' => '$2y$10$QeekUGqdfMO0yp7AT7la8OhgbiNBoJ627BO38MdS1h5kY7oX6UUKu',
'user_session_time' => 0,
'user_lastvisit' => 0,
'user_last_ip' => '0',
'user_regdate' => time(),
'user_reg_ip' => '0',
'user_level' => 1,
'user_posts' => 1,
'user_timezone' => 0.00,
'user_lang' => 'en',
'user_new_privmsg' => 0,
'user_unread_privmsg' => 0,
'user_last_privmsg' => 0,
'user_opt' => 304,
'user_rank' => 1,
'avatar_ext_id' => 0,
'user_gender' => 0,
'user_birthday' => '1900-01-01',
'user_email' => 'admin@torrentpier.com',
'user_skype' => '',
'user_twitter' => '',
'user_icq' => '',
'user_website' => '',
'user_from' => '',
'user_sig' => '',
'user_occ' => '',
'user_interests' => '',
'user_actkey' => '',
'user_newpasswd' => '',
'autologin_id' => '',
'user_newest_pm_id' => 0,
'user_points' => 0.00,
'tpl_name' => 'default'
]
])->saveData();
}
private function seedBtUsers()
{
$this->table('bb_bt_users')->insert([
[
'user_id' => -1,
'auth_key' => substr(md5(rand()), 0, 20)
],
[
'user_id' => -746,
'auth_key' => substr(md5(rand()), 0, 20)
],
[
'user_id' => 2,
'auth_key' => substr(md5(rand()), 0, 20)
]
])->saveData();
}
private function seedConfiguration()
{
$currentTime = time();
$configs = [
['config_name' => 'allow_autologin', 'config_value' => '1'],
['config_name' => 'allow_bbcode', 'config_value' => '1'],
['config_name' => 'allow_namechange', 'config_value' => '0'],
['config_name' => 'allow_sig', 'config_value' => '1'],
['config_name' => 'allow_smilies', 'config_value' => '1'],
['config_name' => 'board_disable', 'config_value' => '0'],
['config_name' => 'board_startdate', 'config_value' => (string)$currentTime],
['config_name' => 'board_timezone', 'config_value' => '0'],
['config_name' => 'bonus_upload', 'config_value' => ''],
['config_name' => 'bonus_upload_price', 'config_value' => ''],
['config_name' => 'birthday_enabled', 'config_value' => '1'],
['config_name' => 'birthday_max_age', 'config_value' => '99'],
['config_name' => 'birthday_min_age', 'config_value' => '10'],
['config_name' => 'birthday_check_day', 'config_value' => '7'],
['config_name' => 'bt_add_auth_key', 'config_value' => '1'],
['config_name' => 'bt_allow_spmode_change', 'config_value' => '1'],
['config_name' => 'bt_announce_url', 'config_value' => 'https://localhost/bt/announce.php'],
['config_name' => 'bt_disable_dht', 'config_value' => '0'],
['config_name' => 'bt_check_announce_url', 'config_value' => '0'],
['config_name' => 'bt_del_addit_ann_urls', 'config_value' => '1'],
['config_name' => 'bt_dl_list_only_1st_page', 'config_value' => '1'],
['config_name' => 'bt_dl_list_only_count', 'config_value' => '1'],
['config_name' => 'bt_newtopic_auto_reg', 'config_value' => '1'],
['config_name' => 'bt_replace_ann_url', 'config_value' => '1'],
['config_name' => 'bt_search_bool_mode', 'config_value' => '1'],
['config_name' => 'bt_set_dltype_on_tor_reg', 'config_value' => '1'],
['config_name' => 'bt_show_dl_but_cancel', 'config_value' => '1'],
['config_name' => 'bt_show_dl_but_compl', 'config_value' => '1'],
['config_name' => 'bt_show_dl_but_down', 'config_value' => '0'],
['config_name' => 'bt_show_dl_but_will', 'config_value' => '1'],
['config_name' => 'bt_show_dl_list', 'config_value' => '0'],
['config_name' => 'bt_show_dl_list_buttons', 'config_value' => '1'],
['config_name' => 'bt_show_dl_stat_on_index', 'config_value' => '1'],
['config_name' => 'bt_show_ip_only_moder', 'config_value' => '1'],
['config_name' => 'bt_show_peers', 'config_value' => '1'],
['config_name' => 'bt_show_peers_mode', 'config_value' => '1'],
['config_name' => 'bt_show_port_only_moder', 'config_value' => '1'],
['config_name' => 'bt_tor_browse_only_reg', 'config_value' => '0'],
['config_name' => 'bt_unset_dltype_on_tor_unreg', 'config_value' => '1'],
['config_name' => 'cron_last_check', 'config_value' => '0'],
['config_name' => 'default_dateformat', 'config_value' => 'Y-m-d H:i'],
['config_name' => 'default_lang', 'config_value' => 'en'],
['config_name' => 'flood_interval', 'config_value' => '15'],
['config_name' => 'hot_threshold', 'config_value' => '300'],
['config_name' => 'login_reset_time', 'config_value' => '30'],
['config_name' => 'max_autologin_time', 'config_value' => '10'],
['config_name' => 'max_login_attempts', 'config_value' => '5'],
['config_name' => 'max_poll_options', 'config_value' => '6'],
['config_name' => 'max_sig_chars', 'config_value' => '255'],
['config_name' => 'posts_per_page', 'config_value' => '15'],
['config_name' => 'prune_enable', 'config_value' => '1'],
['config_name' => 'record_online_date', 'config_value' => (string)$currentTime],
['config_name' => 'record_online_users', 'config_value' => '0'],
['config_name' => 'seed_bonus_enabled', 'config_value' => '1'],
['config_name' => 'seed_bonus_release', 'config_value' => ''],
['config_name' => 'seed_bonus_points', 'config_value' => ''],
['config_name' => 'seed_bonus_tor_size', 'config_value' => '0'],
['config_name' => 'seed_bonus_user_regdate', 'config_value' => '0'],
['config_name' => 'site_desc', 'config_value' => 'Bull-powered BitTorrent tracker engine'],
['config_name' => 'sitemap_time', 'config_value' => ''],
['config_name' => 'sitename', 'config_value' => 'TorrentPier'],
['config_name' => 'smilies_path', 'config_value' => 'styles/images/smiles'],
['config_name' => 'static_sitemap', 'config_value' => ''],
['config_name' => 'topics_per_page', 'config_value' => '50'],
['config_name' => 'xs_use_cache', 'config_value' => '1'],
['config_name' => 'cron_check_interval', 'config_value' => '180'],
['config_name' => 'magnet_links_enabled', 'config_value' => '1'],
['config_name' => 'magnet_links_for_guests', 'config_value' => '0'],
['config_name' => 'gender', 'config_value' => '1'],
['config_name' => 'callseed', 'config_value' => '0'],
['config_name' => 'tor_stats', 'config_value' => '1'],
['config_name' => 'show_latest_news', 'config_value' => '1'],
['config_name' => 'max_news_title', 'config_value' => '50'],
['config_name' => 'latest_news_count', 'config_value' => '5'],
['config_name' => 'latest_news_forum_id', 'config_value' => '1'],
['config_name' => 'show_network_news', 'config_value' => '1'],
['config_name' => 'max_net_title', 'config_value' => '50'],
['config_name' => 'network_news_count', 'config_value' => '5'],
['config_name' => 'network_news_forum_id', 'config_value' => '2'],
['config_name' => 'whois_info', 'config_value' => 'https://whatismyipaddress.com/ip/'],
['config_name' => 'show_mod_index', 'config_value' => '0'],
['config_name' => 'premod', 'config_value' => '0'],
['config_name' => 'tor_comment', 'config_value' => '1'],
['config_name' => 'terms', 'config_value' => ''],
['config_name' => 'show_board_start_index', 'config_value' => '1']
];
$this->table('bb_config')->insert($configs)->saveData();
}
private function seedCronJobs()
{
$cronJobs = [
[
'cron_active' => 1,
'cron_title' => 'Attach maintenance',
'cron_script' => 'attach_maintenance.php',
'schedule' => 'daily',
'run_day' => null,
'run_time' => '05:00:00',
'run_order' => 40,
'last_run' => '1900-01-01 00:00:00',
'next_run' => '1900-01-01 00:00:00',
'run_interval' => null,
'log_enabled' => 0,
'log_file' => '',
'log_sql_queries' => 0,
'disable_board' => 1,
'run_counter' => 0
],
[
'cron_active' => 1,
'cron_title' => 'Board maintenance',
'cron_script' => 'board_maintenance.php',
'schedule' => 'daily',
'run_day' => null,
'run_time' => '05:00:00',
'run_order' => 40,
'last_run' => '1900-01-01 00:00:00',
'next_run' => '1900-01-01 00:00:00',
'run_interval' => null,
'log_enabled' => 0,
'log_file' => '',
'log_sql_queries' => 0,
'disable_board' => 1,
'run_counter' => 0
],
[
'cron_active' => 1,
'cron_title' => 'Prune forums',
'cron_script' => 'prune_forums.php',
'schedule' => 'daily',
'run_day' => null,
'run_time' => '05:00:00',
'run_order' => 50,
'last_run' => '1900-01-01 00:00:00',
'next_run' => '1900-01-01 00:00:00',
'run_interval' => null,
'log_enabled' => 0,
'log_file' => '',
'log_sql_queries' => 0,
'disable_board' => 1,
'run_counter' => 0
],
[
'cron_active' => 1,
'cron_title' => 'Prune topic moved stubs',
'cron_script' => 'prune_topic_moved.php',
'schedule' => 'daily',
'run_day' => null,
'run_time' => '05:00:00',
'run_order' => 60,
'last_run' => '1900-01-01 00:00:00',
'next_run' => '1900-01-01 00:00:00',
'run_interval' => null,
'log_enabled' => 0,
'log_file' => '',
'log_sql_queries' => 0,
'disable_board' => 1,
'run_counter' => 0
],
[
'cron_active' => 1,
'cron_title' => 'Logs cleanup',
'cron_script' => 'clean_log.php',
'schedule' => 'daily',
'run_day' => null,
'run_time' => '05:00:00',
'run_order' => 70,
'last_run' => '1900-01-01 00:00:00',
'next_run' => '1900-01-01 00:00:00',
'run_interval' => null,
'log_enabled' => 0,
'log_file' => '',
'log_sql_queries' => 0,
'disable_board' => 1,
'run_counter' => 0
],
[
'cron_active' => 1,
'cron_title' => 'PM cleanup',
'cron_script' => 'clean_pm.php',
'schedule' => 'daily',
'run_day' => null,
'run_time' => '05:00:00',
'run_order' => 70,
'last_run' => '1900-01-01 00:00:00',
'next_run' => '1900-01-01 00:00:00',
'run_interval' => null,
'log_enabled' => 0,
'log_file' => '',
'log_sql_queries' => 0,
'disable_board' => 1,
'run_counter' => 0
],
[
'cron_active' => 1,
'cron_title' => 'Tracker maintenance',
'cron_script' => 'tr_maintenance.php',
'schedule' => 'daily',
'run_day' => null,
'run_time' => '05:00:00',
'run_order' => 90,
'last_run' => '1900-01-01 00:00:00',
'next_run' => '1900-01-01 00:00:00',
'run_interval' => null,
'log_enabled' => 0,
'log_file' => '',
'log_sql_queries' => 0,
'disable_board' => 1,
'run_counter' => 0
],
[
'cron_active' => 1,
'cron_title' => 'Clean dlstat',
'cron_script' => 'clean_dlstat.php',
'schedule' => 'daily',
'run_day' => null,
'run_time' => '05:00:00',
'run_order' => 100,
'last_run' => '1900-01-01 00:00:00',
'next_run' => '1900-01-01 00:00:00',
'run_interval' => null,
'log_enabled' => 0,
'log_file' => '',
'log_sql_queries' => 0,
'disable_board' => 1,
'run_counter' => 0
],
[
'cron_active' => 1,
'cron_title' => 'Prune inactive users',
'cron_script' => 'prune_inactive_users.php',
'schedule' => 'daily',
'run_day' => null,
'run_time' => '05:00:00',
'run_order' => 110,
'last_run' => '1900-01-01 00:00:00',
'next_run' => '1900-01-01 00:00:00',
'run_interval' => null,
'log_enabled' => 0,
'log_file' => '',
'log_sql_queries' => 0,
'disable_board' => 1,
'run_counter' => 0
],
[
'cron_active' => 1,
'cron_title' => 'Sessions cleanup',
'cron_script' => 'sessions_cleanup.php',
'schedule' => 'interval',
'run_day' => null,
'run_time' => '04:00:00',
'run_order' => 255,
'last_run' => '1900-01-01 00:00:00',
'next_run' => '1900-01-01 00:00:00',
'run_interval' => '00:03:00',
'log_enabled' => 0,
'log_file' => '',
'log_sql_queries' => 0,
'disable_board' => 0,
'run_counter' => 0
],
[
'cron_active' => 1,
'cron_title' => 'DS update cat_forums',
'cron_script' => 'ds_update_cat_forums.php',
'schedule' => 'interval',
'run_day' => null,
'run_time' => '04:00:00',
'run_order' => 255,
'last_run' => '1900-01-01 00:00:00',
'next_run' => '1900-01-01 00:00:00',
'run_interval' => '00:05:00',
'log_enabled' => 0,
'log_file' => '',
'log_sql_queries' => 0,
'disable_board' => 0,
'run_counter' => 0
],
[
'cron_active' => 1,
'cron_title' => 'DS update stats',
'cron_script' => 'ds_update_stats.php',
'schedule' => 'interval',
'run_day' => null,
'run_time' => '04:00:00',
'run_order' => 255,
'last_run' => '1900-01-01 00:00:00',
'next_run' => '1900-01-01 00:00:00',
'run_interval' => '00:10:00',
'log_enabled' => 0,
'log_file' => '',
'log_sql_queries' => 0,
'disable_board' => 0,
'run_counter' => 0
],
[
'cron_active' => 1,
'cron_title' => 'Flash topic view',
'cron_script' => 'flash_topic_view.php',
'schedule' => 'interval',
'run_day' => null,
'run_time' => '04:00:00',
'run_order' => 255,
'last_run' => '1900-01-01 00:00:00',
'next_run' => '1900-01-01 00:00:00',
'run_interval' => '00:10:00',
'log_enabled' => 0,
'log_file' => '',
'log_sql_queries' => 0,
'disable_board' => 0,
'run_counter' => 0
],
[
'cron_active' => 1,
'cron_title' => 'Clean search results',
'cron_script' => 'clean_search_results.php',
'schedule' => 'interval',
'run_day' => null,
'run_time' => '04:00:00',
'run_order' => 255,
'last_run' => '1900-01-01 00:00:00',
'next_run' => '1900-01-01 00:00:00',
'run_interval' => '00:10:00',
'log_enabled' => 0,
'log_file' => '',
'log_sql_queries' => 0,
'disable_board' => 0,
'run_counter' => 0
],
[
'cron_active' => 1,
'cron_title' => 'Tracker cleanup and dlstat',
'cron_script' => 'tr_cleanup_and_dlstat.php',
'schedule' => 'interval',
'run_day' => null,
'run_time' => '04:00:00',
'run_order' => 20,
'last_run' => '1900-01-01 00:00:00',
'next_run' => '1900-01-01 00:00:00',
'run_interval' => '00:15:00',
'log_enabled' => 0,
'log_file' => '',
'log_sql_queries' => 0,
'disable_board' => 0,
'run_counter' => 0
],
[
'cron_active' => 1,
'cron_title' => 'Accrual seedbonus',
'cron_script' => 'tr_seed_bonus.php',
'schedule' => 'interval',
'run_day' => null,
'run_time' => '04:00:00',
'run_order' => 25,
'last_run' => '1900-01-01 00:00:00',
'next_run' => '1900-01-01 00:00:00',
'run_interval' => '00:10:00',
'log_enabled' => 0,
'log_file' => '',
'log_sql_queries' => 0,
'disable_board' => 0,
'run_counter' => 0
],
[
'cron_active' => 1,
'cron_title' => 'Make tracker snapshot',
'cron_script' => 'tr_make_snapshot.php',
'schedule' => 'interval',
'run_day' => null,
'run_time' => '04:00:00',
'run_order' => 10,
'last_run' => '1900-01-01 00:00:00',
'next_run' => '1900-01-01 00:00:00',
'run_interval' => '00:10:00',
'log_enabled' => 0,
'log_file' => '',
'log_sql_queries' => 0,
'disable_board' => 0,
'run_counter' => 0
],
[
'cron_active' => 1,
'cron_title' => 'Seeder last seen',
'cron_script' => 'tr_update_seeder_last_seen.php',
'schedule' => 'interval',
'run_day' => null,
'run_time' => '04:00:00',
'run_order' => 255,
'last_run' => '1900-01-01 00:00:00',
'next_run' => '1900-01-01 00:00:00',
'run_interval' => '01:00:00',
'log_enabled' => 0,
'log_file' => '',
'log_sql_queries' => 0,
'disable_board' => 0,
'run_counter' => 0
],
[
'cron_active' => 1,
'cron_title' => 'Tracker dl-complete count',
'cron_script' => 'tr_complete_count.php',
'schedule' => 'interval',
'run_day' => null,
'run_time' => '04:00:00',
'run_order' => 255,
'last_run' => '1900-01-01 00:00:00',
'next_run' => '1900-01-01 00:00:00',
'run_interval' => '06:00:00',
'log_enabled' => 0,
'log_file' => '',
'log_sql_queries' => 0,
'disable_board' => 0,
'run_counter' => 0
],
[
'cron_active' => 1,
'cron_title' => 'Sitemap update',
'cron_script' => 'sitemap.php',
'schedule' => 'daily',
'run_day' => null,
'run_time' => '06:00:00',
'run_order' => 30,
'last_run' => '1900-01-01 00:00:00',
'next_run' => '1900-01-01 00:00:00',
'run_interval' => null,
'log_enabled' => 0,
'log_file' => '',
'log_sql_queries' => 0,
'disable_board' => 0,
'run_counter' => 0
],
[
'cron_active' => 1,
'cron_title' => 'Update forums atom',
'cron_script' => 'update_forums_atom.php',
'schedule' => 'interval',
'run_day' => null,
'run_time' => '04:00:00',
'run_order' => 255,
'last_run' => '1900-01-01 00:00:00',
'next_run' => '1900-01-01 00:00:00',
'run_interval' => '00:15:00',
'log_enabled' => 0,
'log_file' => '',
'log_sql_queries' => 0,
'disable_board' => 0,
'run_counter' => 0
],
[
'cron_active' => 1,
'cron_title' => 'Demo mode',
'cron_script' => 'demo_mode.php',
'schedule' => 'daily',
'run_day' => null,
'run_time' => '05:00:00',
'run_order' => 255,
'last_run' => '1900-01-01 00:00:00',
'next_run' => '1900-01-01 00:00:00',
'run_interval' => null,
'log_enabled' => 1,
'log_file' => 'demo_mode_cron',
'log_sql_queries' => 1,
'disable_board' => 1,
'run_counter' => 0
]
];
$this->table('bb_cron')->insert($cronJobs)->saveData();
}
private function seedExtensions()
{
// Extension groups
$groups = [
['group_name' => 'Images', 'cat_id' => 1, 'allow_group' => 1, 'download_mode' => 1, 'upload_icon' => '', 'max_filesize' => 262144, 'forum_permissions' => ''],
['group_name' => 'Archives', 'cat_id' => 0, 'allow_group' => 1, 'download_mode' => 1, 'upload_icon' => '', 'max_filesize' => 262144, 'forum_permissions' => ''],
['group_name' => 'Plain text', 'cat_id' => 0, 'allow_group' => 1, 'download_mode' => 1, 'upload_icon' => '', 'max_filesize' => 262144, 'forum_permissions' => ''],
['group_name' => 'Documents', 'cat_id' => 0, 'allow_group' => 1, 'download_mode' => 1, 'upload_icon' => '', 'max_filesize' => 262144, 'forum_permissions' => ''],
['group_name' => 'Real media', 'cat_id' => 0, 'allow_group' => 0, 'download_mode' => 2, 'upload_icon' => '', 'max_filesize' => 262144, 'forum_permissions' => ''],
['group_name' => 'Torrent', 'cat_id' => 0, 'allow_group' => 1, 'download_mode' => 1, 'upload_icon' => '', 'max_filesize' => 262144, 'forum_permissions' => '']
];
$this->table('bb_extension_groups')->insert($groups)->saveData();
// Extensions
$extensions = [
['group_id' => 1, 'extension' => 'gif', 'comment' => ''],
['group_id' => 1, 'extension' => 'png', 'comment' => ''],
['group_id' => 1, 'extension' => 'jpeg', 'comment' => ''],
['group_id' => 1, 'extension' => 'jpg', 'comment' => ''],
['group_id' => 1, 'extension' => 'webp', 'comment' => ''],
['group_id' => 1, 'extension' => 'avif', 'comment' => ''],
['group_id' => 1, 'extension' => 'bmp', 'comment' => ''],
['group_id' => 2, 'extension' => 'gtar', 'comment' => ''],
['group_id' => 2, 'extension' => 'gz', 'comment' => ''],
['group_id' => 2, 'extension' => 'tar', 'comment' => ''],
['group_id' => 2, 'extension' => 'zip', 'comment' => ''],
['group_id' => 2, 'extension' => 'rar', 'comment' => ''],
['group_id' => 2, 'extension' => 'ace', 'comment' => ''],
['group_id' => 2, 'extension' => '7z', 'comment' => ''],
['group_id' => 3, 'extension' => 'txt', 'comment' => ''],
['group_id' => 3, 'extension' => 'c', 'comment' => ''],
['group_id' => 3, 'extension' => 'h', 'comment' => ''],
['group_id' => 3, 'extension' => 'cpp', 'comment' => ''],
['group_id' => 3, 'extension' => 'hpp', 'comment' => ''],
['group_id' => 3, 'extension' => 'diz', 'comment' => ''],
['group_id' => 3, 'extension' => 'm3u', 'comment' => ''],
['group_id' => 4, 'extension' => 'xls', 'comment' => ''],
['group_id' => 4, 'extension' => 'doc', 'comment' => ''],
['group_id' => 4, 'extension' => 'dot', 'comment' => ''],
['group_id' => 4, 'extension' => 'pdf', 'comment' => ''],
['group_id' => 4, 'extension' => 'ai', 'comment' => ''],
['group_id' => 4, 'extension' => 'ps', 'comment' => ''],
['group_id' => 4, 'extension' => 'ppt', 'comment' => ''],
['group_id' => 5, 'extension' => 'rm', 'comment' => ''],
['group_id' => 6, 'extension' => 'torrent', 'comment' => '']
];
$this->table('bb_extensions')->insert($extensions)->saveData();
}
private function seedSmilies()
{
$smilies = [
['code' => ':aa:', 'smile_url' => 'aa.gif', 'emoticon' => 'aa'],
['code' => ':ab:', 'smile_url' => 'ab.gif', 'emoticon' => 'ab'],
['code' => ':ac:', 'smile_url' => 'ac.gif', 'emoticon' => 'ac'],
['code' => ':ae:', 'smile_url' => 'ae.gif', 'emoticon' => 'ae'],
['code' => ':af:', 'smile_url' => 'af.gif', 'emoticon' => 'af'],
['code' => ':ag:', 'smile_url' => 'ag.gif', 'emoticon' => 'ag'],
['code' => ':ah:', 'smile_url' => 'ah.gif', 'emoticon' => 'ah'],
['code' => ':ai:', 'smile_url' => 'ai.gif', 'emoticon' => 'ai'],
['code' => ':aj:', 'smile_url' => 'aj.gif', 'emoticon' => 'aj'],
['code' => ':ak:', 'smile_url' => 'ak.gif', 'emoticon' => 'ak'],
['code' => ':al:', 'smile_url' => 'al.gif', 'emoticon' => 'al'],
['code' => ':am:', 'smile_url' => 'am.gif', 'emoticon' => 'am'],
['code' => ':an:', 'smile_url' => 'an.gif', 'emoticon' => 'an'],
['code' => ':ao:', 'smile_url' => 'ao.gif', 'emoticon' => 'ao'],
['code' => ':ap:', 'smile_url' => 'ap.gif', 'emoticon' => 'ap'],
['code' => ':aq:', 'smile_url' => 'aq.gif', 'emoticon' => 'aq'],
['code' => ':ar:', 'smile_url' => 'ar.gif', 'emoticon' => 'ar'],
['code' => ':as:', 'smile_url' => 'as.gif', 'emoticon' => 'as'],
['code' => ':at:', 'smile_url' => 'at.gif', 'emoticon' => 'at'],
['code' => ':au:', 'smile_url' => 'au.gif', 'emoticon' => 'au'],
['code' => ':av:', 'smile_url' => 'av.gif', 'emoticon' => 'av'],
['code' => ':aw:', 'smile_url' => 'aw.gif', 'emoticon' => 'aw'],
['code' => ':ax:', 'smile_url' => 'ax.gif', 'emoticon' => 'ax'],
['code' => ':ay:', 'smile_url' => 'ay.gif', 'emoticon' => 'ay'],
['code' => ':az:', 'smile_url' => 'az.gif', 'emoticon' => 'az'],
['code' => ':ba:', 'smile_url' => 'ba.gif', 'emoticon' => 'ba'],
['code' => ':bb:', 'smile_url' => 'bb.gif', 'emoticon' => 'bb'],
['code' => ':bc:', 'smile_url' => 'bc.gif', 'emoticon' => 'bc'],
['code' => ':bd:', 'smile_url' => 'bd.gif', 'emoticon' => 'bd'],
['code' => ':be:', 'smile_url' => 'be.gif', 'emoticon' => 'be'],
['code' => ':bf:', 'smile_url' => 'bf.gif', 'emoticon' => 'bf'],
['code' => ':bg:', 'smile_url' => 'bg.gif', 'emoticon' => 'bg'],
['code' => ':bh:', 'smile_url' => 'bh.gif', 'emoticon' => 'bh'],
['code' => ':bi:', 'smile_url' => 'bi.gif', 'emoticon' => 'bi'],
['code' => ':bj:', 'smile_url' => 'bj.gif', 'emoticon' => 'bj'],
['code' => ':bk:', 'smile_url' => 'bk.gif', 'emoticon' => 'bk'],
['code' => ':bl:', 'smile_url' => 'bl.gif', 'emoticon' => 'bl'],
['code' => ':bm:', 'smile_url' => 'bm.gif', 'emoticon' => 'bm'],
['code' => ':bn:', 'smile_url' => 'bn.gif', 'emoticon' => 'bn'],
['code' => ':bo:', 'smile_url' => 'bo.gif', 'emoticon' => 'bo'],
['code' => ':bp:', 'smile_url' => 'bp.gif', 'emoticon' => 'bp'],
['code' => ':bq:', 'smile_url' => 'bq.gif', 'emoticon' => 'bq'],
['code' => ':br:', 'smile_url' => 'br.gif', 'emoticon' => 'br'],
['code' => ':bs:', 'smile_url' => 'bs.gif', 'emoticon' => 'bs'],
['code' => ':bt:', 'smile_url' => 'bt.gif', 'emoticon' => 'bt'],
['code' => ':bu:', 'smile_url' => 'bu.gif', 'emoticon' => 'bu'],
['code' => ':bv:', 'smile_url' => 'bv.gif', 'emoticon' => 'bv'],
['code' => ':bw:', 'smile_url' => 'bw.gif', 'emoticon' => 'bw'],
['code' => ':bx:', 'smile_url' => 'bx.gif', 'emoticon' => 'bx'],
['code' => ':by:', 'smile_url' => 'by.gif', 'emoticon' => 'by'],
['code' => ':bz:', 'smile_url' => 'bz.gif', 'emoticon' => 'bz'],
['code' => ':ca:', 'smile_url' => 'ca.gif', 'emoticon' => 'ca'],
['code' => ':cb:', 'smile_url' => 'cb.gif', 'emoticon' => 'cb'],
['code' => ':cc:', 'smile_url' => 'cc.gif', 'emoticon' => 'cc'],
['code' => ':cd:', 'smile_url' => 'cd.gif', 'emoticon' => 'cd']
];
$this->table('bb_smilies')->insert($smilies)->saveData();
}
private function seedRanks()
{
$this->table('bb_ranks')->insert([
[
'rank_title' => 'Administrator',
'rank_image' => 'styles/images/ranks/admin.png',
'rank_style' => 'colorAdmin'
]
])->saveData();
}
private function seedQuotaLimits()
{
$quotas = [
['quota_desc' => 'Low', 'quota_limit' => 262144],
['quota_desc' => 'Medium', 'quota_limit' => 10485760],
['quota_desc' => 'High', 'quota_limit' => 15728640]
];
$this->table('bb_quota_limits')->insert($quotas)->saveData();
}
private function seedDisallowedUsernames()
{
$disallowed = [
['disallow_id' => 1, 'disallow_username' => 'torrentpier*'],
['disallow_id' => 2, 'disallow_username' => 'tracker*'],
['disallow_id' => 3, 'disallow_username' => 'forum*'],
['disallow_id' => 4, 'disallow_username' => 'torrent*'],
['disallow_id' => 5, 'disallow_username' => 'admin*']
];
$this->table('bb_disallow')->insert($disallowed)->saveData();
}
private function seedAttachmentConfig()
{
$attachConfig = [
['config_name' => 'upload_dir', 'config_value' => 'data/uploads'],
['config_name' => 'upload_img', 'config_value' => 'styles/images/icon_clip.gif'],
['config_name' => 'topic_icon', 'config_value' => 'styles/images/icon_clip.gif'],
['config_name' => 'display_order', 'config_value' => '0'],
['config_name' => 'max_filesize', 'config_value' => '262144'],
['config_name' => 'attachment_quota', 'config_value' => '52428800'],
['config_name' => 'max_filesize_pm', 'config_value' => '262144'],
['config_name' => 'max_attachments', 'config_value' => '1'],
['config_name' => 'max_attachments_pm', 'config_value' => '1'],
['config_name' => 'disable_mod', 'config_value' => '0'],
['config_name' => 'allow_pm_attach', 'config_value' => '1'],
['config_name' => 'default_upload_quota', 'config_value' => '0'],
['config_name' => 'default_pm_quota', 'config_value' => '0'],
['config_name' => 'img_display_inlined', 'config_value' => '1'],
['config_name' => 'img_max_width', 'config_value' => '2000'],
['config_name' => 'img_max_height', 'config_value' => '2000'],
['config_name' => 'img_link_width', 'config_value' => '600'],
['config_name' => 'img_link_height', 'config_value' => '400'],
['config_name' => 'img_create_thumbnail', 'config_value' => '1'],
['config_name' => 'img_min_thumb_filesize', 'config_value' => '12000']
];
$this->table('bb_attachments_config')->insert($attachConfig)->saveData();
}
private function seedTopicsAndPosts()
{
$currentTime = time();
// Create welcome topic
$this->table('bb_topics')->insert([
[
'topic_id' => 1,
'forum_id' => 1,
'topic_title' => 'Welcome to TorrentPier Cattle',
'topic_poster' => 2,
'topic_time' => $currentTime,
'topic_views' => 0,
'topic_replies' => 0,
'topic_status' => 0,
'topic_vote' => 0,
'topic_type' => 0,
'topic_first_post_id' => 1,
'topic_last_post_id' => 1,
'topic_moved_id' => 0,
'topic_attachment' => 0,
'topic_dl_type' => 0,
'topic_last_post_time' => $currentTime,
'topic_show_first_post' => 0,
'topic_allow_robots' => 1
]
])->saveData();
// Create welcome post
$this->table('bb_posts')->insert([
[
'post_id' => 1,
'topic_id' => 1,
'forum_id' => 1,
'poster_id' => 2,
'post_time' => $currentTime,
'poster_ip' => '0',
'poster_rg_id' => 0,
'attach_rg_sig' => 0,
'post_username' => '',
'post_edit_time' => 0,
'post_edit_count' => 0,
'post_attachment' => 0,
'user_post' => 1,
'mc_comment' => '',
'mc_type' => 0,
'mc_user_id' => 0
]
])->saveData();
// Create welcome post text
$welcomeText = "Thank you for installing the new — TorrentPier Cattle!\n\n" .
"What to do next? First of all configure your site in the administration panel (link in the bottom).\n\n" .
"Change main options: site description, number of messages per topic, time zone, language by default, seed-bonus options, birthdays etc... " .
"Create a couple of forums, delete or change this one. Change settings of categories to allow registration of torrents, change announcer url. " .
"If you will have questions or want additional modifications of the engine, [url=https://torrentpier.com/]visit our forum[/url] " .
"(you can use english, we will try to help in any case).\n\n" .
"If you want to help with the translations: [url=https://crowdin.com/project/torrentpier]Crowdin[/url].\n\n" .
"Our GitHub organization: [url=https://github.com/torrentpier]https://github.com/torrentpier[/url].\n" .
"Our SourceForge repository: [url=https://sourceforge.net/projects/torrentpier-engine]https://sourceforge.net/projects/torrentpier-engine[/url].\n" .
"Our demo website: [url=https://torrentpier.duckdns.org]https://torrentpier.duckdns.org[/url].\n\n" .
"We are sure that you will be able to create the best tracker available!\n" .
"Good luck! 😉";
$this->table('bb_posts_text')->insert([
[
'post_id' => 1,
'post_text' => $welcomeText
]
])->saveData();
}
private function seedTopicWatch()
{
$this->table('bb_topics_watch')->insert([
[
'topic_id' => 1,
'user_id' => 2,
'notify_status' => 1
]
])->saveData();
}
public function down()
{
// Clean all seeded data
$tables = [
'bb_topics_watch', 'bb_posts_text', 'bb_posts', 'bb_topics',
'bb_attachments_config', 'bb_disallow', 'bb_quota_limits',
'bb_ranks', 'bb_smilies', 'bb_extensions', 'bb_extension_groups',
'bb_cron', 'bb_config', 'bb_bt_users', 'bb_users', 'bb_forums', 'bb_categories'
];
foreach ($tables as $table) {
$this->execute("DELETE FROM {$table}");
}
}
}

View file

@ -0,0 +1,44 @@
<?php
declare(strict_types=1);
use Phinx\Migration\AbstractMigration;
final class RemoveDemoMode extends AbstractMigration
{
/**
* Migrate Up.
*/
public function up(): void
{
// Delete the demo_mode.php cron job from bb_cron table
$this->table('bb_cron')
->getAdapter()
->execute("DELETE FROM bb_cron WHERE cron_script = 'demo_mode.php'");
}
/**
* Migrate Down.
*/
public function down(): void
{
// Restore the demo_mode.php cron job to bb_cron table
$this->table('bb_cron')->insert([
'cron_active' => 1,
'cron_title' => 'Demo mode',
'cron_script' => 'demo_mode.php',
'schedule' => 'daily',
'run_day' => null,
'run_time' => '05:00:00',
'run_order' => 255,
'last_run' => '1900-01-01 00:00:00',
'next_run' => '1900-01-01 00:00:00',
'run_interval' => null,
'log_enabled' => 1,
'log_file' => 'demo_mode_cron',
'log_sql_queries' => 1,
'disable_board' => 1,
'run_counter' => 0
])->save();
}
}

74
phinx.php Normal file
View file

@ -0,0 +1,74 @@
<?php
/**
* Phinx configuration for TorrentPier
*/
if (PHP_SAPI != 'cli') {
die(basename(__FILE__));
}
// Only load what's needed for Phinx - don't bootstrap the entire application
const BB_ROOT = __DIR__ . DIRECTORY_SEPARATOR;
const BB_PATH = __DIR__;
require_once BB_ROOT . 'library/defines.php';
// Load environment variables
use Dotenv\Dotenv;
require_once __DIR__ . '/vendor/autoload.php';
if (file_exists(__DIR__ . '/.env')) {
$dotenv = Dotenv::createMutable(__DIR__);
$dotenv->load();
}
// Helper function for environment variables
function env(string $key, mixed $default = null): mixed
{
$value = $_ENV[$key] ?? getenv($key);
if ($value === false) {
return $default;
}
return $value;
}
return [
'paths' => [
'migrations' => __DIR__ . '/migrations'
],
'environments' => [
'default_migration_table' => BB_MIGRATIONS,
'default_environment' => env('APP_ENV', 'production'),
'production' => [
'adapter' => 'mysql',
'host' => env('DB_HOST', 'localhost'),
'port' => (int)env('DB_PORT', 3306),
'name' => env('DB_DATABASE'),
'user' => env('DB_USERNAME'),
'pass' => env('DB_PASSWORD', ''),
'charset' => 'utf8mb4',
'collation' => 'utf8mb4_unicode_ci',
'table_options' => [
'ENGINE' => 'InnoDB',
'DEFAULT CHARSET' => 'utf8mb4',
'COLLATE' => 'utf8mb4_unicode_ci'
]
],
'development' => [
'adapter' => 'mysql',
'host' => env('DB_HOST', 'localhost'),
'port' => (int)env('DB_PORT', 3306),
'name' => env('DB_DATABASE'),
'user' => env('DB_USERNAME'),
'pass' => env('DB_PASSWORD', ''),
'charset' => 'utf8mb4',
'collation' => 'utf8mb4_unicode_ci',
'table_options' => [
'ENGINE' => 'InnoDB',
'DEFAULT CHARSET' => 'utf8mb4',
'COLLATE' => 'utf8mb4_unicode_ci'
]
]
],
'version_order' => 'creation',
];

View file

@ -165,10 +165,23 @@ class UnifiedCacheSystem
case 'redis':
// Some deprecated cache types will fall back to file storage
$dir = rtrim($this->cfg['db_dir'] ?? sys_get_temp_dir() . '/cache/', '/') . '/' . $cache_name . '/';
// Create directory automatically using TorrentPier's bb_mkdir function
if (!is_dir($dir) && !bb_mkdir($dir)) {
throw new \RuntimeException("Failed to create cache directory: $dir");
}
return new FileStorage($dir);
case 'sqlite':
$dbFile = rtrim($this->cfg['db_dir'] ?? sys_get_temp_dir() . '/cache/', '/') . '/' . $cache_name . '.db';
// Create parent directory for SQLite file
$dbDir = dirname($dbFile);
if (!is_dir($dbDir) && !bb_mkdir($dbDir)) {
throw new \RuntimeException("Failed to create cache directory for SQLite: $dbDir");
}
return new SQLiteStorage($dbFile);
case 'memory':
@ -183,6 +196,12 @@ class UnifiedCacheSystem
default:
// Fallback to file storage
$dir = rtrim($this->cfg['db_dir'] ?? sys_get_temp_dir() . '/cache/', '/') . '/' . $cache_name . '/';
// Create directory automatically using TorrentPier's bb_mkdir function
if (!is_dir($dir) && !bb_mkdir($dir)) {
throw new \RuntimeException("Failed to create cache directory: $dir");
}
return new FileStorage($dir);
}
}
@ -218,10 +237,23 @@ class UnifiedCacheSystem
case 'redis':
// Some deprecated cache types will fall back to file storage
$dir = rtrim($this->cfg['db_dir'] ?? sys_get_temp_dir() . '/cache/', '/') . '/datastore/';
// Create directory automatically using TorrentPier's bb_mkdir function
if (!is_dir($dir) && !bb_mkdir($dir)) {
throw new \RuntimeException("Failed to create datastore directory: $dir");
}
return new FileStorage($dir);
case 'sqlite':
$dbFile = rtrim($this->cfg['db_dir'] ?? sys_get_temp_dir() . '/cache/', '/') . '/datastore.db';
// Create parent directory for SQLite file
$dbDir = dirname($dbFile);
if (!is_dir($dbDir) && !bb_mkdir($dbDir)) {
throw new \RuntimeException("Failed to create datastore directory for SQLite: $dbDir");
}
return new SQLiteStorage($dbFile);
case 'memory':
@ -236,6 +268,12 @@ class UnifiedCacheSystem
default:
// Fallback to file storage
$dir = rtrim($this->cfg['db_dir'] ?? sys_get_temp_dir() . '/cache/', '/') . '/datastore/';
// Create directory automatically using TorrentPier's bb_mkdir function
if (!is_dir($dir) && !bb_mkdir($dir)) {
throw new \RuntimeException("Failed to create datastore directory: $dir");
}
return new FileStorage($dir);
}
}

View file

@ -0,0 +1,305 @@
<?php
/**
* TorrentPier Bull-powered BitTorrent tracker engine
*
* @copyright Copyright (c) 2005-2025 TorrentPier (https://torrentpier.com)
* @link https://github.com/torrentpier/torrentpier for the canonical source repository
* @license https://github.com/torrentpier/torrentpier/blob/master/LICENSE MIT License
*/
namespace TorrentPier\Database;
/**
* Migration Status Manager
*
* Provides read-only access to database migration status information
* for the admin panel. Uses TorrentPier's database singleton and config system.
*/
class MigrationStatus
{
private string $migrationTable;
private string $migrationPath;
private array $initialMigrations = [
'20250619000001',
'20250619000002'
];
public function __construct()
{
$this->migrationTable = BB_MIGRATIONS;
$this->migrationPath = BB_ROOT . 'migrations';
}
/**
* Get complete migration status information
*
* @return array Migration status data including applied/pending migrations
*/
public function getMigrationStatus(): array
{
try {
// Check if migration table exists using Nette Database Explorer
$tableExists = $this->checkMigrationTableExists();
$setupStatus = $this->getSetupStatus();
if (!$tableExists) {
return [
'table_exists' => false,
'current_version' => null,
'applied_migrations' => [],
'pending_migrations' => $this->getAvailableMigrations(),
'setup_status' => $setupStatus,
'requires_setup' => $setupStatus['needs_setup']
];
}
// Get applied migrations using Nette Database Explorer
$appliedMigrations = DB()->query("
SELECT version, migration_name, start_time, end_time
FROM {$this->migrationTable}
ORDER BY version ASC
")->fetchAll();
// Convert Nette Result objects to arrays
$appliedMigrationsArray = [];
foreach ($appliedMigrations as $migration) {
$appliedMigrationsArray[] = [
'version' => $migration->version,
'migration_name' => $migration->migration_name,
'start_time' => $migration->start_time,
'end_time' => $migration->end_time
];
}
// Get current version (latest applied)
$currentVersion = null;
if (!empty($appliedMigrationsArray)) {
$currentVersion = end($appliedMigrationsArray)['version'];
}
// Get pending migrations
$availableMigrations = $this->getAvailableMigrations();
$appliedVersions = array_column($appliedMigrationsArray, 'version');
$pendingMigrations = array_filter($availableMigrations, function ($migration) use ($appliedVersions) {
return !in_array($migration['version'], $appliedVersions);
});
return [
'table_exists' => true,
'current_version' => $currentVersion,
'applied_migrations' => $appliedMigrationsArray,
'pending_migrations' => array_values($pendingMigrations),
'setup_status' => $setupStatus,
'requires_setup' => $setupStatus['needs_setup']
];
} catch (\Exception $e) {
bb_die('Error checking migration status: ' . $e->getMessage());
}
}
/**
* Determine setup status for existing installations
*
* @return array Setup status information
*/
private function getSetupStatus(): array
{
try {
// Check if core TorrentPier tables exist (indicates existing installation)
$coreTablesExist = $this->checkCoreTablesExist();
$migrationTableExists = $this->checkMigrationTableExists();
if (!$coreTablesExist) {
// Fresh installation
return [
'type' => 'fresh',
'needs_setup' => false,
'message' => 'Fresh installation - migrations will run normally',
'action_required' => false
];
}
if (!$migrationTableExists) {
// Existing installation without migration system
return [
'type' => 'existing_needs_setup',
'needs_setup' => true,
'message' => 'Existing installation detected - migration setup required',
'action_required' => true,
'instructions' => 'Mark initial migrations as applied using --fake flag'
];
}
// Check if initial migrations are marked as applied
$initialMigrationsApplied = $this->checkInitialMigrationsApplied();
if (!$initialMigrationsApplied) {
return [
'type' => 'existing_partial_setup',
'needs_setup' => true,
'message' => 'Migration table exists but initial migrations not marked as applied',
'action_required' => true,
'instructions' => 'Run: php vendor/bin/phinx migrate --fake --target=20250619000002'
];
}
// Fully set up
return [
'type' => 'fully_setup',
'needs_setup' => false,
'message' => 'Migration system fully configured',
'action_required' => false
];
} catch (\Exception $e) {
return [
'type' => 'error',
'needs_setup' => false,
'message' => 'Error detecting setup status: ' . $e->getMessage(),
'action_required' => false
];
}
}
/**
* Check if core TorrentPier tables exist
*
* @return bool True if core tables exist
*/
private function checkCoreTablesExist(): bool
{
try {
$coreTable = 'bb_users'; // Key table that should exist in any TorrentPier installation
$escapedTable = DB()->escape($coreTable);
$result = DB()->query("
SELECT COUNT(*) as table_count
FROM information_schema.tables
WHERE table_schema = DATABASE()
AND table_name = '{$escapedTable}'
")->fetch();
return $result && $result->table_count > 0;
} catch (\Exception $e) {
return false;
}
}
/**
* Check if initial migrations are marked as applied
*
* @return bool True if initial migrations are applied
*/
private function checkInitialMigrationsApplied(): bool
{
try {
$initialMigrationsCSV = implode(',', $this->initialMigrations);
$result = DB()->query("
SELECT COUNT(*) as migration_count
FROM {$this->migrationTable}
WHERE version IN ($initialMigrationsCSV)
")->fetch();
return $result && $result->migration_count >= count($this->initialMigrations);
} catch (\Exception $e) {
return false;
}
}
/**
* Check if migration table exists
*
* @return bool True if table exists, false otherwise
*/
private function checkMigrationTableExists(): bool
{
try {
// Using simple query without parameters to avoid issues
$escapedTable = DB()->escape($this->migrationTable);
$result = DB()->query("
SELECT COUNT(*) as table_count
FROM information_schema.tables
WHERE table_schema = DATABASE()
AND table_name = '{$escapedTable}'
")->fetch();
return $result && $result->table_count > 0;
} catch (\Exception $e) {
return false;
}
}
/**
* Get available migrations from filesystem
*
* @return array List of available migration files
*/
private function getAvailableMigrations(): array
{
$migrations = [];
if (is_dir($this->migrationPath)) {
$files = glob($this->migrationPath . '/*.php');
foreach ($files as $file) {
$filename = basename($file);
if (preg_match('/^(\d+)_(.+)\.php$/', $filename, $matches)) {
$migrations[] = [
'version' => $matches[1],
'name' => $matches[2],
'filename' => $filename,
'file_path' => $file
];
}
}
}
// Sort by version
usort($migrations, function ($a, $b) {
return strcmp($a['version'], $b['version']);
});
return $migrations;
}
/**
* Get database schema information
*
* @return array Database statistics and information
*/
public function getSchemaInfo(): array
{
try {
// Get database name using Nette Database Explorer
$dbInfo = DB()->query("SELECT DATABASE() as db_name")->fetch();
// Get table count using Nette Database Explorer
$tableInfo = DB()->query("
SELECT COUNT(*) as table_count
FROM information_schema.tables
WHERE table_schema = DATABASE()
")->fetch();
// Get database size using Nette Database Explorer
$sizeInfo = DB()->query("
SELECT
ROUND(SUM(data_length + index_length) / 1024 / 1024, 2) as size_mb
FROM information_schema.tables
WHERE table_schema = DATABASE()
")->fetch();
return [
'database_name' => $dbInfo->db_name ?? 'Unknown',
'table_count' => $tableInfo->table_count ?? 0,
'size_mb' => $sizeInfo->size_mb ?? 0
];
} catch (\Exception $e) {
return [
'database_name' => 'Unknown',
'table_count' => 0,
'size_mb' => 0,
'error' => $e->getMessage()
];
}
}
}

View file

@ -0,0 +1,183 @@
<h1>{L_MIGRATIONS_STATUS}</h1>
<table class="forumline" cellpadding="4" cellspacing="1" border="0" width="100%">
<tr>
<th class="thHead" colspan="2">{L_MIGRATIONS_DATABASE_INFO}</th>
</tr>
<tr>
<td class="row1" width="35%"><b>{L_MIGRATIONS_DATABASE_NAME}:</b></td>
<td class="row2">{SCHEMA_DATABASE_NAME}</td>
</tr>
<tr>
<td class="row1"><b>{L_MIGRATIONS_DATABASE_TOTAL}:</b></td>
<td class="row2">{SCHEMA_TABLE_COUNT}</td>
</tr>
<tr>
<td class="row1"><b>{L_MIGRATIONS_DATABASE_SIZE}:</b></td>
<td class="row2">{SCHEMA_SIZE_MB} MB</td>
</tr>
<tr>
<td class="row1"><b>{L_LAST_UPDATED}:</b></td>
<td class="row2">{CURRENT_TIME}</td>
</tr>
</table>
<br/>
<table class="forumline" cellpadding="4" cellspacing="1" border="0" width="100%">
<tr>
<th class="thHead" colspan="2">{L_MIGRATIONS_STATUS}</th>
</tr>
<tr>
<td class="row1" width="35%"><b>{L_MIGRATIONS_SYSTEM}:</b></td>
<td class="row2">
<!-- IF MIGRATION_TABLE_EXISTS -->
<!-- IF SETUP_REQUIRES_SETUP -->
<span style="color: orange; font-weight: bold;">⚠ {L_MIGRATIONS_NEEDS_SETUP}</span>
<!-- ELSE -->
<span style="color: green; font-weight: bold;">✓ {L_MIGRATIONS_ACTIVE}</span>
<!-- ENDIF -->
<!-- ELSE -->
<span style="color: red; font-weight: bold;">✗ {L_MIGRATIONS_NOT_INITIALIZED}</span>
<!-- ENDIF -->
</td>
</tr>
<!-- IF SETUP_ACTION_REQUIRED -->
<tr>
<td class="row1"><b>{L_MIGRATIONS_SETUP_STATUS}:</b></td>
<td class="row2">
<div
style="background: #fff3cd; padding: 8px; border: 1px solid #ffeaa7; border-radius: 4px; margin: 4px 0;">
<strong>{L_MIGRATIONS_ACTION_REQUIRED}:</strong> {SETUP_MESSAGE}<br>
<!-- IF SETUP_INSTRUCTIONS -->
<small><strong>{L_MIGRATIONS_INSTRUCTIONS}:</strong> {SETUP_INSTRUCTIONS}</small><br>
<!-- ENDIF -->
<small><a href="#migration-setup-guide">{L_MIGRATIONS_SETUP_GUIDE}</a></small>
</div>
</td>
</tr>
<!-- ENDIF -->
<tr>
<td class="row1"><b>Current Version:</b></td>
<td class="row2">
<!-- IF MIGRATION_CURRENT_VERSION -->
{MIGRATION_CURRENT_VERSION}
<!-- ELSE -->
<em>{L_MIGRATIONS_NOT_APPLIED}</em>
<!-- ENDIF -->
</td>
</tr>
<tr>
<td class="row1"><b>{L_MIGRATIONS_APPLIED}:</b></td>
<td class="row2">{MIGRATION_APPLIED_COUNT}</td>
</tr>
<tr>
<td class="row1"><b>{L_MIGRATIONS_PENDING}:</b></td>
<td class="row2">
<!-- IF MIGRATION_PENDING_COUNT > 0 -->
<span style="color: orange; font-weight: bold;">{MIGRATION_PENDING_COUNT} {L_MIGRATIONS_PENDING_COUNT}</span>
<!-- ELSE -->
<span style="color: green;">{L_MIGRATIONS_UP_TO_DATE}</span>
<!-- ENDIF -->
</td>
</tr>
</table>
<!-- IF MIGRATION_APPLIED_COUNT > 0 -->
<br/>
<table class="forumline" cellpadding="4" cellspacing="1" border="0" width="100%">
<tr>
<th class="thHead" colspan="4">{L_MIGRATIONS_APPLIED}</th>
</tr>
<tr>
<td class="catHead" width="15%"><b>{L_MIGRATIONS_VERSION}</b></td>
<td class="catHead" width="35%"><b>{L_MIGRATIONS_NAME}</b></td>
<td class="catHead" width="25%"><b>{L_MIGRATIONS_APPLIED_AT}</b></td>
<td class="catHead" width="25%"><b>{L_MIGRATIONS_COMPLETED_AT}</b></td>
</tr>
<!-- BEGIN applied_migrations -->
<tr>
<td class="{applied_migrations.ROW_CLASS}">{applied_migrations.VERSION}</td>
<td class="{applied_migrations.ROW_CLASS}">{applied_migrations.NAME}</td>
<td class="{applied_migrations.ROW_CLASS}">{applied_migrations.START_TIME}</td>
<td class="{applied_migrations.ROW_CLASS}">{applied_migrations.END_TIME}</td>
</tr>
<!-- END applied_migrations -->
</table>
<!-- ENDIF -->
<!-- IF MIGRATION_PENDING_COUNT > 0 -->
<br/>
<table class="forumline" cellpadding="4" cellspacing="1" border="0" width="100%">
<tr>
<th class="thHead" colspan="3">{L_MIGRATIONS_PENDING}</th>
</tr>
<tr>
<td class="catHead" width="15%"><b>{L_MIGRATIONS_VERSION}</b></td>
<td class="catHead" width="35%"><b>{L_MIGRATIONS_NAME}</b></td>
<td class="catHead" width="50%"><b>{L_MIGRATIONS_FILE}</b></td>
</tr>
<!-- BEGIN pending_migrations -->
<tr>
<td class="{pending_migrations.ROW_CLASS}">{pending_migrations.VERSION}</td>
<td class="{pending_migrations.ROW_CLASS}">{pending_migrations.NAME}</td>
<td class="{pending_migrations.ROW_CLASS}"><code>{pending_migrations.FILENAME}</code></td>
</tr>
<!-- END pending_migrations -->
</table>
<br/>
<div class="alert-warning"
style="padding: 10px; background-color: #fff3cd; border: 1px solid #ffeaa7; border-radius: 4px;">
<strong>⚠️ Pending Migrations Detected</strong><br/>
There are {MIGRATION_PENDING_COUNT} migration(s) that need to be applied.
Contact your system administrator to run:<br/>
<code style="background: #f8f9fa; padding: 2px 4px;">php vendor/bin/phinx migrate</code>
</div>
<!-- ENDIF -->
<br/>
<div class="info-box" style="padding: 15px; background-color: #e9ecef; border-radius: 4px;">
<h3>Migration Management</h3>
<p>This panel provides read-only information about the database migration status.
To manage migrations, use the command line interface:</p>
<ul>
<li><strong>Check status:</strong> <code>php vendor/bin/phinx status</code></li>
<li><strong>Run migrations:</strong> <code>php vendor/bin/phinx migrate</code></li>
<li><strong>Create new migration:</strong> <code>php vendor/bin/phinx create MigrationName</code></li>
<li><strong>Rollback last migration:</strong> <code>php vendor/bin/phinx rollback</code></li>
</ul>
<p><strong>⚠️ Important:</strong> Always backup your database before running migrations in production!</p>
</div>
<!-- IF SETUP_REQUIRES_SETUP -->
<br/>
<div id="migration-setup-guide" class="setup-guide"
style="padding: 15px; background-color: #f8d7da; border: 1px solid #f5c6cb; border-radius: 4px;">
<h3 style="color: #721c24;">🔧 Migration Setup Required</h3>
<p>Your installation has existing data but hasn't been set up for migrations yet. Follow these steps:</p>
<h4>Step 1: Backup Your Database</h4>
<pre style="background: #f8f9fa; padding: 10px; border-radius: 4px;">mysqldump -u username -p database_name > backup_$(date +%Y%m%d_%H%M%S).sql</pre>
<h4>Step 2: Initialize Migration System</h4>
<pre style="background: #f8f9fa; padding: 10px; border-radius: 4px;">php vendor/bin/phinx init</pre>
<h4>Step 3: Mark Initial Migrations as Applied</h4>
<pre style="background: #f8f9fa; padding: 10px; border-radius: 4px;">php vendor/bin/phinx migrate --fake --target=20250619000001
php vendor/bin/phinx migrate --fake --target=20250619000002</pre>
<h4>Step 4: Verify Setup</h4>
<pre style="background: #f8f9fa; padding: 10px; border-radius: 4px;">php vendor/bin/phinx status</pre>
<p><strong>What this does:</strong> The <code>--fake</code> flag marks migrations as applied without actually
running them,
since your database already has the schema. This allows future migrations to work normally.</p>
<p><strong>📖 Need help?</strong> See the complete guide in the
<a href="https://github.com/torrentpier/torrentpier/blob/master/UPGRADE_GUIDE.md#migration-setup-for-existing-installations"
target="_blank">UPGRADE_GUIDE.md</a></p>
</div>
<!-- ENDIF -->