diff --git a/app/Console/Commands/AdditionalProcessingDiagnose.php b/app/Console/Commands/AdditionalProcessingDiagnose.php index 7293dbbd0..a1235192d 100644 --- a/app/Console/Commands/AdditionalProcessingDiagnose.php +++ b/app/Console/Commands/AdditionalProcessingDiagnose.php @@ -45,7 +45,7 @@ class AdditionalProcessingDiagnose extends Command ]); $this->table(['Preflight', 'Value'], [ ['Database driver', (string) $report['database_driver']], - ['Covering claim index', (string) ($report['indexes']['covering_index'] ?? 'missing')], + ['Claim queue index', (string) ($report['indexes']['claim_queue_index'] ?? 'missing')], ['Temp path', (string) $report['temp_path']['path']], ['Temp path writable', $report['temp_path']['writable'] ? 'yes' : 'no'], ]); diff --git a/app/Console/Commands/NntmuxResetPostProcessing.php b/app/Console/Commands/NntmuxResetPostProcessing.php index 501d76321..bd7860647 100644 --- a/app/Console/Commands/NntmuxResetPostProcessing.php +++ b/app/Console/Commands/NntmuxResetPostProcessing.php @@ -86,7 +86,6 @@ class NntmuxResetPostProcessing extends Command 'haspreview' => -1, 'jpgstatus' => 0, 'videostatus' => 0, - 'audiostatus' => 0, 'nfostatus' => -1, ] ); @@ -366,7 +365,6 @@ class NntmuxResetPostProcessing extends Command 'haspreview' => -1, 'jpgstatus' => 0, 'videostatus' => 0, - 'audiostatus' => 0, 'nfostatus' => -1, ]); Search::updateRelease((int) $releases->id); diff --git a/app/Console/Commands/ProcessAdditionalGuid.php b/app/Console/Commands/ProcessAdditionalGuid.php index 8f23fc14f..36169f847 100644 --- a/app/Console/Commands/ProcessAdditionalGuid.php +++ b/app/Console/Commands/ProcessAdditionalGuid.php @@ -75,7 +75,6 @@ class ProcessAdditionalGuid extends Command 'haspreview' => -1, 'jpgstatus' => 0, 'videostatus' => 0, - 'audiostatus' => 0, 'nfostatus' => -1, ]); Search::updateRelease((int) $release->id); diff --git a/app/Console/Commands/ReleasesOptimizePreflight.php b/app/Console/Commands/ReleasesOptimizePreflight.php new file mode 100644 index 000000000..55a2688d7 --- /dev/null +++ b/app/Console/Commands/ReleasesOptimizePreflight.php @@ -0,0 +1,287 @@ + */ + private const array REMOVED_INDEXES = [ + 'ix_releases_adddate_only', + 'ix_releases_videos_id', + 'ix_releases_movieinfo_id', + 'ix_releases_imdbid', + 'ix_releases_tv_search_covering', + 'ix_releases_passwordstatus', + 'ix_releases_haspreview_passwordstatus', + 'ix_releases_postdate_searchname', + 'ix_releases_predb_id_searchname', + 'ix_releases_size_cat', + 'ix_releases_add_pp_claim_queue', + 'ix_releases_nzb_creation_queue', + ]; + + /** @var list */ + private const array NEW_INDEXES = [ + 'ux_releases_guid', + 'ix_releases_predb_id', + 'ix_releases_size', + 'ix_releases_add_pp_claim_queue', + 'ix_releases_nzb_creation_group_queue', + 'ix_releases_nzb_creation_global_queue', + ]; + + /** @var array */ + private array $report = []; + + public function handle(): int + { + if (! Schema::hasTable('releases')) { + return $this->finish([ + 'ok' => false, + 'database_driver' => DB::getDriverName(), + 'blockers' => [['code' => 'missing-releases-table', 'count' => 1]], + ]); + } + + $identifierCounts = $this->identifierCounts(); + $blockers = []; + foreach ($identifierCounts as $code => $count) { + if ($count > 0) { + $blockers[] = ['code' => $code, 'count' => $count]; + } + } + + $indexes = array_values(array_filter(array_map( + static fn (array $index): ?string => isset($index['name']) ? (string) $index['name'] : null, + Schema::getIndexes('releases'), + ))); + sort($indexes); + + $this->report = [ + 'ok' => $blockers === [], + 'database_driver' => DB::getDriverName(), + 'releases' => [ + 'rows' => DB::table('releases')->count(), + ...$identifierCounts, + ], + 'storage' => $this->storage(), + 'migration_data' => [ + 'nzb_password_rows' => $this->countNonEmpty('releases', 'nzb_password'), + 'nzb_creation_failure_rows' => $this->retryStateCount(), + 'visible_comment_rows' => $this->visibleCommentCount(), + 'release_comment_counter_mismatches' => $this->commentCounterMismatchCount(), + ], + 'discarded_data' => $this->discardedDataCounts(), + 'indexes' => [ + 'present' => $indexes, + 'scheduled_for_removal' => array_values(array_intersect(self::REMOVED_INDEXES, $indexes)), + 'already_present_replacements' => array_values(array_intersect(self::NEW_INDEXES, $indexes)), + ], + 'blockers' => $blockers, + ]; + + return $this->finish($this->report); + } + + /** @return array */ + private function identifierCounts(): array + { + $duplicateIds = DB::query()->fromSub( + DB::table('releases')->select('id')->groupBy('id')->havingRaw('COUNT(*) > 1'), + 'duplicate_release_ids', + )->count(); + $duplicateGuids = DB::query()->fromSub( + DB::table('releases')->selectRaw('LOWER(guid) AS normalized_guid')->groupByRaw('LOWER(guid)')->havingRaw('COUNT(*) > 1'), + 'duplicate_release_guids', + )->count(); + + if (in_array(DB::getDriverName(), ['mariadb', 'mysql'], true)) { + $invalidGuids = DB::table('releases') + ->whereNull('guid') + ->orWhereRaw("guid NOT REGEXP '^[0-9A-Fa-f]{8}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{12}$'") + ->count(); + } else { + $invalidGuids = DB::table('releases')->pluck('guid')->filter( + static fn (mixed $guid): bool => ! is_string($guid) + || preg_match('/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/iD', $guid) !== 1, + )->count(); + } + + return [ + 'duplicate_ids' => $duplicateIds, + 'invalid_guids' => $invalidGuids, + 'case_insensitive_duplicate_guids' => $duplicateGuids, + 'leftguid_mismatches' => DB::table('releases') + ->whereRaw('LOWER(leftguid) <> LOWER(SUBSTR(guid, 1, 1))') + ->count(), + ]; + } + + /** @return array{data_bytes: int|null, index_bytes: int|null, total_bytes: int|null, required_free_bytes: int|null} */ + private function storage(): array + { + if (! in_array(DB::getDriverName(), ['mariadb', 'mysql'], true)) { + return ['data_bytes' => null, 'index_bytes' => null, 'total_bytes' => null, 'required_free_bytes' => null]; + } + + $tableName = DB::getTablePrefix().'releases'; + $row = DB::selectOne( + 'SELECT DATA_LENGTH AS data_bytes, INDEX_LENGTH AS index_bytes + FROM information_schema.TABLES WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = ?', + [$tableName], + ); + $dataBytes = isset($row->data_bytes) ? (int) $row->data_bytes : 0; + $indexBytes = isset($row->index_bytes) ? (int) $row->index_bytes : 0; + $totalBytes = $dataBytes + $indexBytes; + + return [ + 'data_bytes' => $dataBytes, + 'index_bytes' => $indexBytes, + 'total_bytes' => $totalBytes, + 'required_free_bytes' => $totalBytes * 2, + ]; + } + + /** @return array */ + private function discardedDataCounts(): array + { + $counts = []; + foreach ([ + 'updatetime' => ['not_null', null], + 'gid' => ['non_empty', null], + 'source' => ['not_null', null], + 'proc_sorter' => ['non_zero', null], + 'audiostatus' => ['non_zero', null], + ] as $column => [$condition]) { + $counts['releases.'.$column] = $this->countByCondition('releases', $column, $condition); + } + + foreach (['gid', 'cid', 'shareid', 'siteid'] as $column) { + $counts['release_comments.'.$column] = $this->countByCondition('release_comments', $column, 'non_empty'); + } + foreach (['issynced', 'shared', 'sourceid'] as $column) { + $counts['release_comments.'.$column] = $this->countByCondition( + 'release_comments', + $column, + $column === 'sourceid' ? 'not_null' : 'non_zero', + ); + } + + return $counts; + } + + private function retryStateCount(): int + { + if (! Schema::hasColumn('releases', 'nzb_creation_attempts')) { + return Schema::hasTable('release_nzb_creation_failures') + ? DB::table('release_nzb_creation_failures')->count() + : 0; + } + + return DB::table('releases')->where(function ($query): void { + $query->where('nzb_creation_attempts', '>', 0)->orWhereNotNull('nzb_creation_last_error'); + })->count(); + } + + private function visibleCommentCount(): int + { + return Schema::hasTable('release_comments') + ? DB::table('release_comments')->where('isvisible', 1)->count() + : 0; + } + + private function commentCounterMismatchCount(): int + { + if (! Schema::hasTable('release_comments') || ! Schema::hasColumn('releases', 'comments')) { + return 0; + } + + return DB::query()->fromSub( + DB::table('releases as r') + ->leftJoin('release_comments as rc', function ($join): void { + $join->on('rc.releases_id', '=', 'r.id')->where('rc.isvisible', 1); + }) + ->select('r.id', 'r.comments') + ->selectRaw('COUNT(rc.id) AS visible_comments') + ->groupBy('r.id', 'r.comments') + ->havingRaw('r.comments <> COUNT(rc.id)'), + 'comment_counter_mismatches', + )->count(); + } + + private function countNonEmpty(string $table, string $column): int + { + if (! Schema::hasTable($table) || ! Schema::hasColumn($table, $column)) { + return $table === 'releases' && Schema::hasTable('release_nzb_passwords') + ? DB::table('release_nzb_passwords')->count() + : 0; + } + + return DB::table($table)->whereNotNull($column)->where($column, '<>', '')->count(); + } + + private function countByCondition(string $table, string $column, string $condition): ?int + { + if (! Schema::hasTable($table) || ! Schema::hasColumn($table, $column)) { + return null; + } + + $query = DB::table($table); + + return match ($condition) { + 'non_empty' => $query->whereNotNull($column)->where($column, '<>', '')->count(), + 'non_zero' => $query->where($column, '<>', 0)->count(), + default => $query->whereNotNull($column)->count(), + }; + } + + /** @param array $report */ + private function finish(array $report): int + { + if ($this->option('json')) { + $this->line((string) json_encode($report, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_THROW_ON_ERROR)); + } else { + $this->renderHumanReport($report); + } + + return ($report['ok'] ?? false) ? self::SUCCESS : self::FAILURE; + } + + /** @param array $report */ + private function renderHumanReport(array $report): void + { + if (! isset($report['releases'])) { + $this->error('The releases table does not exist.'); + + return; + } + + $this->table(['Safety check', 'Count'], [ + ['Duplicate IDs', $report['releases']['duplicate_ids']], + ['Invalid GUIDs', $report['releases']['invalid_guids']], + ['Case-insensitive duplicate GUIDs', $report['releases']['case_insensitive_duplicate_guids']], + ['Mismatched leftguid values', $report['releases']['leftguid_mismatches']], + ]); + $this->table(['Migration data', 'Rows'], array_map( + static fn (string $key, int $value): array => [$key, $value], + array_keys($report['migration_data']), + array_values($report['migration_data']), + )); + + if ($report['ok']) { + $this->info('Releases optimization preflight passed.'); + } else { + $this->error('Releases optimization preflight failed. Resolve every blocker before migration.'); + } + } +} diff --git a/app/Http/Controllers/DetailsController.php b/app/Http/Controllers/DetailsController.php index 473c8d9ff..40e524232 100644 --- a/app/Http/Controllers/DetailsController.php +++ b/app/Http/Controllers/DetailsController.php @@ -56,7 +56,7 @@ class DetailsController extends BasePageController } if ($this->isPostBack($request)) { - ReleaseComment::addComment($data['id'], $data['gid'], $request->input('txtAddComment'), $this->userdata->id, $request->ip()); + ReleaseComment::addComment((int) $data['id'], (string) $request->input('txtAddComment'), (int) $this->userdata->id, $request->ip()); return redirect()->route('details', ['guid' => $guid])->with('success', 'Comment posted successfully!'); } diff --git a/app/Models/Release.php b/app/Models/Release.php index 52ac24998..c482a3192 100644 --- a/app/Models/Release.php +++ b/app/Models/Release.php @@ -132,6 +132,18 @@ class Release extends Model return $this->hasOne(ReleaseNfo::class, 'releases_id'); } + /** @return HasOne */ + public function nzbPassword(): HasOne + { + return $this->hasOne(ReleaseNzbPassword::class, 'releases_id'); + } + + /** @return HasOne */ + public function nzbCreationFailure(): HasOne + { + return $this->hasOne(ReleaseNzbCreationFailure::class, 'releases_id'); + } + /** * @return HasMany */ @@ -212,7 +224,6 @@ class Release extends Model 'isrenamed' => $parameters['isrenamed'], 'iscategorized' => 1, 'predb_id' => $parameters['predb_id'], - 'source' => $parameters['source'] ?? null, ] ); diff --git a/app/Models/ReleaseComment.php b/app/Models/ReleaseComment.php index edbfdbe25..24d61258a 100644 --- a/app/Models/ReleaseComment.php +++ b/app/Models/ReleaseComment.php @@ -8,6 +8,7 @@ use Carbon\Carbon; use Illuminate\Contracts\Pagination\LengthAwarePaginator; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsTo; +use Illuminate\Support\Facades\DB; /** * App\Models\ReleaseComment. @@ -16,34 +17,20 @@ use Illuminate\Database\Eloquent\Relations\BelongsTo; * @property int $releases_id FK to releases.id * @property string $text * @property bool $isvisible - * @property bool $issynced - * @property string|null $gid - * @property string|null $cid * @property string $text_hash * @property string $username * @property int $users_id * @property Carbon|null $created_at * @property Carbon|null $updated_at * @property string|null $host - * @property bool $shared - * @property string $shareid - * @property string $siteid - * @property int|null $sourceid * @property-read Release $release * @property-read User $user * - * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\ReleaseComment whereCid($value) * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\ReleaseComment whereCreatedAt($value) - * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\ReleaseComment whereGid($value) * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\ReleaseComment whereHost($value) * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\ReleaseComment whereId($value) - * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\ReleaseComment whereIssynced($value) * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\ReleaseComment whereIsvisible($value) * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\ReleaseComment whereReleasesId($value) - * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\ReleaseComment whereShared($value) - * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\ReleaseComment whereShareid($value) - * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\ReleaseComment whereSiteid($value) - * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\ReleaseComment whereSourceid($value) * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\ReleaseComment whereText($value) * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\ReleaseComment whereTextHash($value) * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\ReleaseComment whereUpdatedAt($value) @@ -108,13 +95,18 @@ class ReleaseComment extends Model /** * Delete single comment on the site. */ - public static function deleteComment(mixed $id): void + public static function deleteComment(int $id): void { - $res = self::getCommentById($id); - if ($res) { - self::query()->where('id', $id)->delete(); - self::updateReleaseCommentCount($res['gid']); - } + DB::transaction(function () use ($id): void { + $comment = self::query()->find($id, ['id', 'releases_id']); + if ($comment === null) { + return; + } + + $releaseId = (int) $comment->releases_id; + $comment->delete(); + self::updateReleaseCommentCount($releaseId); + }); } /** @@ -124,31 +116,31 @@ class ReleaseComment extends Model * * @throws \Exception */ - public static function addComment(mixed $id, mixed $gid, mixed $text, mixed $userid, mixed $host): int + public static function addComment(int $releaseId, string $text, int $userId, ?string $host): int { if (config('nntmux:settings.store_user_ip') === false) { $host = ''; } - $username = User::query()->where('id', $userid)->first(['username']); + $username = User::query()->where('id', $userId)->first(['username']); $username = ($username === null ? 'ANON' : $username['username']); - $comid = self::query() - ->insertGetId( + return DB::transaction(function () use ($releaseId, $text, $userId, $host, $username): int { + $commentId = self::query()->insertGetId( [ - 'releases_id' => $id, - 'gid' => $gid, + 'releases_id' => $releaseId, 'text' => $text, - 'users_id' => $userid, + 'users_id' => $userId, 'created_at' => now(), 'updated_at' => now(), 'host' => $host, 'username' => $username, ] ); - self::updateReleaseCommentCount($id); + self::updateReleaseCommentCount($releaseId); - return $comid; + return $commentId; + }); } /** @@ -167,10 +159,13 @@ class ReleaseComment extends Model /** * Update the denormalised count of comments for a release. */ - public static function updateReleaseCommentCount(mixed $gid): void + public static function updateReleaseCommentCount(int $releaseId): void { - $commentCount = self::query()->where('gid', '=', 'releases.gid')->where('isvisible', '=', 1)->count('id'); - Release::query()->where('gid', $gid)->update(['comments' => $commentCount]); + $commentCount = self::query() + ->where('releases_id', $releaseId) + ->where('isvisible', 1) + ->count('id'); + Release::query()->whereKey($releaseId)->update(['comments' => $commentCount]); } /** diff --git a/app/Models/ReleaseNzbCreationFailure.php b/app/Models/ReleaseNzbCreationFailure.php new file mode 100644 index 000000000..2c3d9dbac --- /dev/null +++ b/app/Models/ReleaseNzbCreationFailure.php @@ -0,0 +1,41 @@ + */ + protected $guarded = []; + + /** @return array */ + protected function casts(): array + { + return ['attempts' => 'integer']; + } + + /** @return BelongsTo */ + public function release(): BelongsTo + { + return $this->belongsTo(Release::class, 'releases_id'); + } +} diff --git a/app/Models/ReleaseNzbPassword.php b/app/Models/ReleaseNzbPassword.php new file mode 100644 index 000000000..4d0c9340e --- /dev/null +++ b/app/Models/ReleaseNzbPassword.php @@ -0,0 +1,33 @@ + */ + protected $guarded = []; + + /** @return BelongsTo */ + public function release(): BelongsTo + { + return $this->belongsTo(Release::class, 'releases_id'); + } +} diff --git a/app/Services/AdditionalProcessing/AdditionalProcessingDiagnostics.php b/app/Services/AdditionalProcessing/AdditionalProcessingDiagnostics.php index 63253c6de..fbae53786 100644 --- a/app/Services/AdditionalProcessing/AdditionalProcessingDiagnostics.php +++ b/app/Services/AdditionalProcessing/AdditionalProcessingDiagnostics.php @@ -23,8 +23,9 @@ final class AdditionalProcessingDiagnostics 'haspreview', 'nzbstatus', 'leftguid', - 'additional_pp_claimed_at', 'postdate', + 'id', + 'additional_pp_claimed_at', ]; /** @@ -150,12 +151,12 @@ final class AdditionalProcessingDiagnostics /** * @param list $warnings - * @return array{required_columns: list, present: list, covering_index: string|null, missing: bool} + * @return array{required_columns: list, present: list, claim_queue_index: string|null, missing: bool} */ private function indexes(array &$warnings): array { $present = []; - $coveringIndex = null; + $claimQueueIndex = null; if (Schema::hasTable('releases')) { foreach (Schema::getIndexes('releases') as $index) { @@ -169,13 +170,13 @@ final class AdditionalProcessingDiagnostics $present[] = $name; } if ($columns === self::REQUIRED_CLAIM_INDEX_COLUMNS) { - $coveringIndex = $name; + $claimQueueIndex = $name; } } } sort($present); - $missing = $coveringIndex === null; + $missing = $claimQueueIndex === null; if ($missing) { $warnings[] = $this->warning( 'missing-index', @@ -186,7 +187,7 @@ final class AdditionalProcessingDiagnostics return [ 'required_columns' => self::REQUIRED_CLAIM_INDEX_COLUMNS, 'present' => $present, - 'covering_index' => $coveringIndex, + 'claim_queue_index' => $claimQueueIndex, 'missing' => $missing, ]; } diff --git a/app/Services/AdditionalProcessing/MediaExtractionService.php b/app/Services/AdditionalProcessing/MediaExtractionService.php index c781eaa8a..014465474 100644 --- a/app/Services/AdditionalProcessing/MediaExtractionService.php +++ b/app/Services/AdditionalProcessing/MediaExtractionService.php @@ -390,7 +390,6 @@ class MediaExtractionService } @chmod($this->config->audioSavePath.$audioFileName, 0764); - Release::query()->where('id', $context->release->id)->update(['audiostatus' => 1]); $result['audioSample'] = true; $context->foundAudioSample = true; } diff --git a/app/Services/NameFixing/ReleaseUpdateService.php b/app/Services/NameFixing/ReleaseUpdateService.php index 8be61c420..1494d6216 100644 --- a/app/Services/NameFixing/ReleaseUpdateService.php +++ b/app/Services/NameFixing/ReleaseUpdateService.php @@ -352,7 +352,7 @@ class ReleaseUpdateService 'PAR2, ' => ['isrenamed' => 1, 'iscategorized' => 1, 'proc_par2' => 1], 'Filenames, ', 'file matched source: ' => ['isrenamed' => 1, 'iscategorized' => 1, 'proc_files' => 1], 'PreDB FT Exact, ' => ['isrenamed' => 1, 'iscategorized' => 1], - 'sorter, ' => ['isrenamed' => 1, 'iscategorized' => 1, 'proc_sorter' => 1], + 'sorter, ' => ['isrenamed' => 1, 'iscategorized' => 1], 'UID, ', 'Mediainfo, ' => ['isrenamed' => 1, 'iscategorized' => 1, 'proc_uid' => 1], 'PAR2 hash, ' => ['isrenamed' => 1, 'iscategorized' => 1, 'proc_hash16k' => 1], 'SRR, ' => ['isrenamed' => 1, 'iscategorized' => 1, 'proc_srr' => 1], diff --git a/app/Services/Nzb/NzbCreationCandidateQuery.php b/app/Services/Nzb/NzbCreationCandidateQuery.php index d0dcdba50..4ca88b95e 100644 --- a/app/Services/Nzb/NzbCreationCandidateQuery.php +++ b/app/Services/Nzb/NzbCreationCandidateQuery.php @@ -17,10 +17,6 @@ final class NzbCreationCandidateQuery public const string CLAIM_TOKEN_COLUMN = 'nzb_creation_claim_token'; - public const string ATTEMPTS_COLUMN = 'nzb_creation_attempts'; - - public const string LAST_ERROR_COLUMN = 'nzb_creation_last_error'; - /** * @return Builder */ @@ -83,12 +79,17 @@ final class NzbCreationCandidateQuery ]); } - return Release::query() + $releaseQuery = Release::query() ->whereIn('id', $ids) ->with('category.parent') ->select(self::selectableColumns($columns, $supportsClaims)) - ->orderByRaw(self::idOrderExpression($ids)) - ->get(); + ->orderByRaw(self::idOrderExpression($ids)); + + if (self::supportsFailureState()) { + $releaseQuery->with('nzbCreationFailure'); + } + + return $releaseQuery->get(); }, 3); } @@ -116,31 +117,12 @@ final class NzbCreationCandidateQuery } return Schema::hasColumn('releases', self::CLAIMED_AT_COLUMN) - && Schema::hasColumn('releases', self::CLAIM_TOKEN_COLUMN) - && Schema::hasColumn('releases', self::ATTEMPTS_COLUMN) - && Schema::hasColumn('releases', self::LAST_ERROR_COLUMN); + && Schema::hasColumn('releases', self::CLAIM_TOKEN_COLUMN); } - /** - * @return array - */ - public static function failureUpdateValues(string $reason, bool $incrementAttempts): array + public static function supportsFailureState(): bool { - if (! self::supportsClaims()) { - return []; - } - - $values = [ - self::CLAIMED_AT_COLUMN => null, - self::CLAIM_TOKEN_COLUMN => null, - self::LAST_ERROR_COLUMN => mb_substr($reason, 0, 1000), - ]; - - if ($incrementAttempts) { - $values[self::ATTEMPTS_COLUMN] = DB::raw(self::ATTEMPTS_COLUMN.' + 1'); - } - - return $values; + return Schema::hasTable('release_nzb_creation_failures'); } /** @@ -183,8 +165,6 @@ final class NzbCreationCandidateQuery static fn (string $column): bool => ! in_array($column, [ self::CLAIMED_AT_COLUMN, self::CLAIM_TOKEN_COLUMN, - self::ATTEMPTS_COLUMN, - self::LAST_ERROR_COLUMN, ], true), )); } diff --git a/app/Services/Nzb/NzbService.php b/app/Services/Nzb/NzbService.php index 7f95fc4b3..37db6661d 100644 --- a/app/Services/Nzb/NzbService.php +++ b/app/Services/Nzb/NzbService.php @@ -6,6 +6,7 @@ namespace App\Services\Nzb; use App\Models\Collection; use App\Models\Release; +use App\Models\ReleaseNzbCreationFailure; use App\Models\Settings; use App\Services\Binaries\BinariesConfig; use App\Services\CollectionCleanupService; @@ -262,8 +263,16 @@ class NzbService return NzbCreationResult::transient("Final NZB file is missing or unreadable: {$path}", $collectionIds, $path); } - // Mark release as having NZB. - $release->update($this->successfulReleaseUpdateValues()); + DB::transaction(function () use ($release): void { + $release->update($this->successfulReleaseUpdateValues()); + + if (NzbCreationCandidateQuery::supportsFailureState()) { + ReleaseNzbCreationFailure::query() + ->where('releases_id', $release->id) + ->delete(); + $release->unsetRelation('nzbCreationFailure'); + } + }); } catch (Throwable $e) { return NzbCreationResult::transient('Failed to write NZB file: '.$e->getMessage(), $collectionIds, $path); } finally { @@ -729,8 +738,6 @@ class NzbService if (NzbCreationCandidateQuery::supportsClaims()) { $values += [ - NzbCreationCandidateQuery::ATTEMPTS_COLUMN => 0, - NzbCreationCandidateQuery::LAST_ERROR_COLUMN => null, NzbCreationCandidateQuery::CLAIMED_AT_COLUMN => null, NzbCreationCandidateQuery::CLAIM_TOKEN_COLUMN => null, ]; diff --git a/app/Services/ReleaseProcessingService.php b/app/Services/ReleaseProcessingService.php index 743bbb514..470f57e48 100644 --- a/app/Services/ReleaseProcessingService.php +++ b/app/Services/ReleaseProcessingService.php @@ -9,6 +9,7 @@ use App\Models\Category; use App\Models\Collection; use App\Models\MusicInfo; use App\Models\Release; +use App\Models\ReleaseNzbCreationFailure; use App\Models\Settings; use App\Models\UsenetGroup; use App\Services\Binaries\BinariesConfig; @@ -690,7 +691,6 @@ final class ReleaseProcessingService 'groups_id', 'postdate', 'nzbstatus', - NzbCreationCandidateQuery::ATTEMPTS_COLUMN, NzbCreationCandidateQuery::CLAIM_TOKEN_COLUMN, ]; @@ -754,24 +754,34 @@ final class ReleaseProcessingService private function nextNzbCreationAttempt(Release $release): int { - return ((int) ($release->{NzbCreationCandidateQuery::ATTEMPTS_COLUMN} ?? 0)) + 1; + $failureState = $release->relationLoaded('nzbCreationFailure') + ? $release->getRelation('nzbCreationFailure') + : null; + + return ($failureState instanceof ReleaseNzbCreationFailure ? $failureState->attempts : 0) + 1; } private function recordNzbCreationRetry(Release $release, NzbCreationResult $result): void { - $values = NzbCreationCandidateQuery::failureUpdateValues($result->reason, true); - if ($values !== []) { - Release::query() - ->where('id', $release->id) - ->update($values); - } + $nextAttempt = $this->nextNzbCreationAttempt($release); + ReleaseNzbCreationFailure::query()->upsert( + [[ + 'releases_id' => (int) $release->id, + 'attempts' => $nextAttempt, + 'last_error' => mb_substr($result->reason, 0, 1000), + ]], + ['releases_id'], + ['attempts', 'last_error'], + ); + $failure = ReleaseNzbCreationFailure::query()->findOrFail((int) $release->id); + $release->setRelation('nzbCreationFailure', $failure); Log::channel('nzb_creation')->warning('NZB creation failed; release will be retried', [ 'release_id' => $release->id, 'guid' => $release->guid, 'failure_type' => $result->failureType, 'reason' => $result->reason, - 'next_attempt' => $this->nextNzbCreationAttempt($release), + 'next_attempt' => $nextAttempt, 'max_attempts' => self::NZB_CREATION_MAX_ATTEMPTS, ]); } diff --git a/database/migrations/2026_08_13_001652_normalize_and_optimize_releases_table.php b/database/migrations/2026_08_13_001652_normalize_and_optimize_releases_table.php new file mode 100644 index 000000000..da203693a --- /dev/null +++ b/database/migrations/2026_08_13_001652_normalize_and_optimize_releases_table.php @@ -0,0 +1,454 @@ + */ + private const array REMOVED_RELEASE_COLUMNS = [ + 'updatetime', + 'gid', + 'source', + 'nzb_password', + 'nzb_creation_attempts', + 'nzb_creation_last_error', + 'proc_sorter', + 'audiostatus', + ]; + + /** @var list */ + private const array REMOVED_COMMENT_COLUMNS = [ + 'gid', + 'cid', + 'issynced', + 'shared', + 'shareid', + 'siteid', + 'sourceid', + ]; + + /** @var list */ + private const array REMOVED_RELEASE_INDEXES = [ + 'ix_releases_guid', + 'ix_releases_adddate_only', + 'ix_releases_videos_id', + 'ix_releases_movieinfo_id', + 'ix_releases_imdbid', + 'ix_releases_tv_search_covering', + 'ix_releases_passwordstatus', + 'ix_releases_haspreview_passwordstatus', + 'ix_releases_postdate_searchname', + 'ix_releases_predb_id_searchname', + 'ix_releases_size_cat', + 'ix_releases_add_pp_claim_queue', + 'ix_releases_nzb_creation_queue', + ]; + + public function up(): void + { + if (! Schema::hasTable('releases')) { + return; + } + + $this->assertReleaseIdentifiersAreSafe(); + $this->createSparseTables(); + $this->backfillSparseTables(); + $this->recountVisibleComments(); + + if ($this->isMariaDbOrMySql()) { + $this->rebuildReleasesForMariaDb(); + } else { + $this->normalizeReleasesForPortableDatabase(); + } + + $this->addSparseForeignKeys(); + + if (Schema::hasTable('release_comments')) { + Schema::table('release_comments', function (Blueprint $table): void { + foreach (self::REMOVED_COMMENT_COLUMNS as $column) { + if (Schema::hasColumn('release_comments', $column)) { + $table->dropColumn($column); + } + } + }); + } + } + + public function down(): void + { + if (! Schema::hasTable('releases')) { + return; + } + + $this->dropSparseForeignKeys(); + + if (Schema::hasTable('release_comments')) { + Schema::table('release_comments', function (Blueprint $table): void { + $table->string('gid', 32)->nullable(); + $table->string('cid', 32)->nullable(); + $table->boolean('issynced')->default(false); + $table->boolean('shared')->default(false); + $table->string('shareid', 40)->default(''); + $table->string('siteid', 40)->default(''); + $table->unsignedBigInteger('sourceid')->nullable(); + }); + } + + if ($this->isMariaDbOrMySql()) { + $this->restoreReleasesForMariaDb(); + } else { + $this->restoreReleasesForPortableDatabase(); + } + + $this->restoreSparseDataToReleases(); + Schema::dropIfExists('release_nzb_creation_failures'); + Schema::dropIfExists('release_nzb_passwords'); + } + + private function createSparseTables(): void + { + if (! Schema::hasTable('release_nzb_passwords')) { + Schema::create('release_nzb_passwords', function (Blueprint $table): void { + $table->unsignedInteger('releases_id')->primary(); + $table->string('password', 255); + if (! $this->isMariaDbOrMySql()) { + $table->foreign('releases_id', 'FK_rnp_releases') + ->references('id')->on('releases') + ->cascadeOnUpdate()->cascadeOnDelete(); + } + }); + } + + if (! Schema::hasTable('release_nzb_creation_failures')) { + Schema::create('release_nzb_creation_failures', function (Blueprint $table): void { + $table->unsignedInteger('releases_id')->primary(); + $table->unsignedSmallInteger('attempts')->default(0); + $table->text('last_error')->nullable(); + $table->timestamps(); + if (! $this->isMariaDbOrMySql()) { + $table->foreign('releases_id', 'FK_rncf_releases') + ->references('id')->on('releases') + ->cascadeOnUpdate()->cascadeOnDelete(); + } + }); + } + } + + private function addSparseForeignKeys(): void + { + if (! $this->isMariaDbOrMySql()) { + return; + } + + DB::statement( + 'ALTER TABLE '.$this->table('release_nzb_passwords') + .' ADD CONSTRAINT `FK_rnp_releases` FOREIGN KEY (`releases_id`) REFERENCES ' + .$this->table('releases').' (`id`) ON DELETE CASCADE ON UPDATE CASCADE' + ); + DB::statement( + 'ALTER TABLE '.$this->table('release_nzb_creation_failures') + .' ADD CONSTRAINT `FK_rncf_releases` FOREIGN KEY (`releases_id`) REFERENCES ' + .$this->table('releases').' (`id`) ON DELETE CASCADE ON UPDATE CASCADE' + ); + } + + private function dropSparseForeignKeys(): void + { + if (! $this->isMariaDbOrMySql()) { + return; + } + + if (Schema::hasTable('release_nzb_passwords')) { + DB::statement('ALTER TABLE '.$this->table('release_nzb_passwords').' DROP FOREIGN KEY `FK_rnp_releases`'); + } + if (Schema::hasTable('release_nzb_creation_failures')) { + DB::statement('ALTER TABLE '.$this->table('release_nzb_creation_failures').' DROP FOREIGN KEY `FK_rncf_releases`'); + } + } + + private function backfillSparseTables(): void + { + if (Schema::hasColumn('releases', 'nzb_password')) { + DB::table('releases') + ->select(['id', 'nzb_password']) + ->whereNotNull('nzb_password') + ->where('nzb_password', '<>', '') + ->orderBy('id') + ->chunkById(1000, function ($releases): void { + $rows = []; + foreach ($releases as $release) { + $rows[] = [ + 'releases_id' => (int) $release->id, + 'password' => (string) $release->nzb_password, + ]; + } + DB::table('release_nzb_passwords')->upsert($rows, ['releases_id'], ['password']); + }); + } + + if (Schema::hasColumn('releases', 'nzb_creation_attempts')) { + DB::table('releases') + ->select(['id', 'nzb_creation_attempts', 'nzb_creation_last_error']) + ->where(function ($query): void { + $query->where('nzb_creation_attempts', '>', 0) + ->orWhereNotNull('nzb_creation_last_error'); + }) + ->orderBy('id') + ->chunkById(1000, function ($releases): void { + $now = now(); + $rows = []; + foreach ($releases as $release) { + $rows[] = [ + 'releases_id' => (int) $release->id, + 'attempts' => (int) $release->nzb_creation_attempts, + 'last_error' => $release->nzb_creation_last_error, + 'created_at' => $now, + 'updated_at' => $now, + ]; + } + DB::table('release_nzb_creation_failures')->upsert( + $rows, + ['releases_id'], + ['attempts', 'last_error', 'updated_at'], + ); + }); + } + } + + private function recountVisibleComments(): void + { + if (! Schema::hasTable('release_comments') || ! Schema::hasColumn('releases', 'comments')) { + return; + } + + DB::statement( + 'UPDATE '.$this->table('releases').' SET `comments` = (SELECT COUNT(*) FROM ' + .$this->table('release_comments').' WHERE ' + .$this->table('release_comments').'.`releases_id` = ' + .$this->table('releases').'.`id` AND ' + .$this->table('release_comments').'.`isvisible` = 1)' + ); + } + + private function rebuildReleasesForMariaDb(): void + { + $specifications = ['DROP PRIMARY KEY', 'ADD PRIMARY KEY (`id`)']; + foreach (self::REMOVED_RELEASE_INDEXES as $index) { + if ($this->indexExists($index)) { + $specifications[] = 'DROP INDEX `'.$index.'`'; + } + } + + $specifications[] = 'MODIFY `guid` CHAR(36) CHARACTER SET ascii COLLATE ascii_general_ci NOT NULL'; + $specifications[] = "MODIFY `leftguid` CHAR(1) CHARACTER SET ascii COLLATE ascii_general_ci NOT NULL COMMENT 'The first letter of the release guid'"; + foreach (self::REMOVED_RELEASE_COLUMNS as $column) { + if (Schema::hasColumn('releases', $column)) { + $specifications[] = 'DROP COLUMN `'.$column.'`'; + } + } + + $specifications = [ + ...$specifications, + 'ADD UNIQUE INDEX `ux_releases_guid` (`guid`)', + 'ADD INDEX `ix_releases_predb_id` (`predb_id`)', + 'ADD INDEX `ix_releases_size` (`size`)', + 'ADD INDEX `ix_releases_add_pp_claim_queue` (`passwordstatus`, `haspreview`, `nzbstatus`, `leftguid`, `postdate` DESC, `id`, `additional_pp_claimed_at`)', + 'ADD INDEX `ix_releases_nzb_creation_group_queue` (`nzbstatus`, `groups_id`, `postdate` DESC, `id`, `nzb_creation_claimed_at`)', + 'ADD INDEX `ix_releases_nzb_creation_global_queue` (`nzbstatus`, `postdate` DESC, `id`, `nzb_creation_claimed_at`)', + ]; + + DB::statement('ALTER TABLE '.$this->table('releases').' '.implode(', ', $specifications)); + } + + private function normalizeReleasesForPortableDatabase(): void + { + Schema::table('releases', function (Blueprint $table): void { + foreach (self::REMOVED_RELEASE_INDEXES as $index) { + if ($this->indexExists($index)) { + $table->dropIndex($index); + } + } + foreach (self::REMOVED_RELEASE_COLUMNS as $column) { + if (Schema::hasColumn('releases', $column)) { + $table->dropColumn($column); + } + } + $table->unique('guid', 'ux_releases_guid'); + $table->index('predb_id', 'ix_releases_predb_id'); + $table->index('size', 'ix_releases_size'); + $table->index( + ['passwordstatus', 'haspreview', 'nzbstatus', 'leftguid', 'postdate', 'id', 'additional_pp_claimed_at'], + 'ix_releases_add_pp_claim_queue', + ); + $table->index( + ['nzbstatus', 'groups_id', 'postdate', 'id', 'nzb_creation_claimed_at'], + 'ix_releases_nzb_creation_group_queue', + ); + $table->index( + ['nzbstatus', 'postdate', 'id', 'nzb_creation_claimed_at'], + 'ix_releases_nzb_creation_global_queue', + ); + }); + } + + private function restoreReleasesForMariaDb(): void + { + $specifications = ['DROP PRIMARY KEY', 'ADD PRIMARY KEY (`id`, `categories_id`)']; + foreach ([ + 'ux_releases_guid', + 'ix_releases_predb_id', + 'ix_releases_size', + 'ix_releases_add_pp_claim_queue', + 'ix_releases_nzb_creation_group_queue', + 'ix_releases_nzb_creation_global_queue', + ] as $index) { + if ($this->indexExists($index)) { + $specifications[] = 'DROP INDEX `'.$index.'`'; + } + } + + $specifications = [ + ...$specifications, + 'MODIFY `guid` VARCHAR(40) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL', + "MODIFY `leftguid` CHAR(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT 'The first letter of the release guid'", + 'ADD COLUMN `updatetime` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP AFTER `adddate`', + 'ADD COLUMN `gid` VARCHAR(32) NULL AFTER `updatetime`', + 'ADD COLUMN `source` SMALLINT UNSIGNED NULL', + 'ADD COLUMN `nzb_password` VARCHAR(255) NULL', + 'ADD COLUMN `nzb_creation_attempts` SMALLINT UNSIGNED NOT NULL DEFAULT 0 AFTER `nzb_creation_claim_token`', + 'ADD COLUMN `nzb_creation_last_error` TEXT NULL AFTER `nzb_creation_attempts`', + 'ADD COLUMN `proc_sorter` TINYINT(1) NOT NULL DEFAULT 0 AFTER `proc_pp`', + 'ADD COLUMN `audiostatus` TINYINT(1) NOT NULL DEFAULT 0 AFTER `videostatus`', + 'ADD INDEX `ix_releases_guid` (`guid`)', + 'ADD INDEX `ix_releases_adddate_only` (`adddate`)', + 'ADD INDEX `ix_releases_videos_id` (`videos_id`)', + 'ADD INDEX `ix_releases_movieinfo_id` (`movieinfo_id`)', + 'ADD INDEX `ix_releases_imdbid` (`imdbid`)', + 'ADD INDEX `ix_releases_tv_search_covering` (`passwordstatus`, `categories_id`, `postdate` DESC, `videos_id`, `tv_episodes_id`, `groups_id`)', + 'ADD INDEX `ix_releases_passwordstatus` (`passwordstatus`)', + 'ADD INDEX `ix_releases_haspreview_passwordstatus` (`haspreview`, `passwordstatus`)', + 'ADD INDEX `ix_releases_postdate_searchname` (`postdate`, `searchname`)', + 'ADD INDEX `ix_releases_predb_id_searchname` (`predb_id`, `searchname`)', + 'ADD INDEX `ix_releases_size_cat` (`size`, `categories_id`, `passwordstatus`)', + 'ADD INDEX `ix_releases_add_pp_claim_queue` (`passwordstatus`, `haspreview`, `nzbstatus`, `leftguid`, `additional_pp_claimed_at`, `postdate` DESC)', + 'ADD INDEX `ix_releases_nzb_creation_queue` (`nzbstatus`, `groups_id`, `leftguid`, `nzb_creation_claimed_at`, `postdate` DESC)', + ]; + + DB::statement('ALTER TABLE '.$this->table('releases').' '.implode(', ', $specifications)); + } + + private function restoreReleasesForPortableDatabase(): void + { + Schema::table('releases', function (Blueprint $table): void { + foreach ([ + 'ux_releases_guid', + 'ix_releases_predb_id', + 'ix_releases_size', + 'ix_releases_add_pp_claim_queue', + 'ix_releases_nzb_creation_group_queue', + 'ix_releases_nzb_creation_global_queue', + ] as $index) { + if ($this->indexExists($index)) { + $table->dropIndex($index); + } + } + + $table->timestamp('updatetime')->useCurrent(); + $table->string('gid', 32)->nullable(); + $table->unsignedSmallInteger('source')->nullable(); + $table->string('nzb_password')->nullable(); + $table->unsignedSmallInteger('nzb_creation_attempts')->default(0); + $table->text('nzb_creation_last_error')->nullable(); + $table->boolean('proc_sorter')->default(false); + $table->boolean('audiostatus')->default(false); + $table->index('guid', 'ix_releases_guid'); + }); + } + + private function restoreSparseDataToReleases(): void + { + if (Schema::hasTable('release_nzb_passwords')) { + DB::table('release_nzb_passwords')->orderBy('releases_id')->chunkById(1000, function ($rows): void { + foreach ($rows as $row) { + DB::table('releases')->where('id', $row->releases_id)->update(['nzb_password' => $row->password]); + } + }, 'releases_id'); + } + + if (Schema::hasTable('release_nzb_creation_failures')) { + DB::table('release_nzb_creation_failures')->orderBy('releases_id')->chunkById(1000, function ($rows): void { + foreach ($rows as $row) { + DB::table('releases')->where('id', $row->releases_id)->update([ + 'nzb_creation_attempts' => $row->attempts, + 'nzb_creation_last_error' => $row->last_error, + ]); + } + }, 'releases_id'); + } + } + + private function assertReleaseIdentifiersAreSafe(): void + { + $duplicateIds = DB::query()->fromSub( + DB::table('releases')->select('id')->groupBy('id')->havingRaw('COUNT(*) > 1'), + 'duplicate_release_ids', + )->count(); + + if ($this->isMariaDbOrMySql()) { + $invalidGuids = DB::table('releases') + ->whereNull('guid') + ->orWhereRaw("guid NOT REGEXP '^[0-9A-Fa-f]{8}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{12}$'") + ->count(); + } else { + $invalidGuids = DB::table('releases')->pluck('guid')->filter( + static fn (mixed $guid): bool => ! is_string($guid) + || preg_match('/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/iD', $guid) !== 1, + )->count(); + } + + $duplicateGuids = DB::query()->fromSub( + DB::table('releases')->selectRaw('LOWER(guid) AS normalized_guid')->groupByRaw('LOWER(guid)')->havingRaw('COUNT(*) > 1'), + 'duplicate_release_guids', + )->count(); + $leftGuidMismatches = DB::table('releases') + ->whereRaw('LOWER(leftguid) <> LOWER(SUBSTR(guid, 1, 1))') + ->count(); + + $blockers = array_filter([ + 'duplicate release IDs' => $duplicateIds, + 'invalid release GUIDs' => $invalidGuids, + 'case-insensitive duplicate release GUIDs' => $duplicateGuids, + 'mismatched release leftguid values' => $leftGuidMismatches, + ]); + + if ($blockers !== []) { + throw new RuntimeException('Releases optimization preflight failed: '.json_encode($blockers, JSON_THROW_ON_ERROR)); + } + } + + private function indexExists(string $name): bool + { + foreach (Schema::getIndexes('releases') as $index) { + if (($index['name'] ?? null) === $name) { + return true; + } + } + + return false; + } + + private function isMariaDbOrMySql(): bool + { + return in_array(DB::getDriverName(), ['mariadb', 'mysql'], true); + } + + private function table(string $name): string + { + return '`'.DB::getTablePrefix().$name.'`'; + } +}; diff --git a/database/schema/mariadb-schema.sql b/database/schema/mariadb-schema.sql index c32dcd4dc..041702d56 100644 --- a/database/schema/mariadb-schema.sql +++ b/database/schema/mariadb-schema.sql @@ -1,7 +1,13 @@ -SET FOREIGN_KEY_CHECKS = 0; - +/*M!999999\- enable the sandbox mode */ +/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */; +/*!40103 SET TIME_ZONE='+00:00' */; +/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */; +/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; +/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; +/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; DROP TABLE IF EXISTS `anidb_info`; - +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; CREATE TABLE `anidb_info` ( `anidbid` int(10) unsigned NOT NULL COMMENT 'ID of title from AniDB', `anilist_id` int(10) unsigned DEFAULT NULL COMMENT 'ID from AniList', @@ -20,7 +26,7 @@ CREATE TABLE `anidb_info` ( `related` varchar(1024) DEFAULT NULL, `similar` varchar(1024) DEFAULT NULL, `creators` varchar(1024) DEFAULT NULL, - `description` text DEFAULT NULL, + `description` mediumtext DEFAULT NULL, `rating` varchar(5) DEFAULT NULL, `picture` varchar(255) DEFAULT NULL, `categories` varchar(1024) DEFAULT NULL, @@ -30,20 +36,46 @@ CREATE TABLE `anidb_info` ( KEY `ix_anidb_info_anilist_id` (`anilist_id`), KEY `ix_anidb_info_mal_id` (`mal_id`), KEY `ix_anidb_info_country` (`country`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci; - +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; DROP TABLE IF EXISTS `anidb_titles`; - +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; CREATE TABLE `anidb_titles` ( `anidbid` int(10) unsigned NOT NULL COMMENT 'ID of title from AniDB', `type` varchar(25) NOT NULL COMMENT 'type of title.', `lang` varchar(25) NOT NULL, `title` varchar(255) NOT NULL, PRIMARY KEY (`anidbid`,`type`,`lang`,`title`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci; - +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; +DROP TABLE IF EXISTS `api_token_ip_addresses`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; +CREATE TABLE `api_token_ip_addresses` ( + `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, + `users_id` int(10) unsigned NOT NULL, + `api_token` varchar(64) NOT NULL, + `ip_address` varchar(45) NOT NULL, + `first_seen_at` timestamp NOT NULL DEFAULT current_timestamp(), + `last_seen_at` timestamp NOT NULL DEFAULT current_timestamp(), + `request_count` int(10) unsigned NOT NULL DEFAULT 1, + `is_rate_limited` tinyint(1) NOT NULL DEFAULT 0, + `rate_limited_until` timestamp NULL DEFAULT NULL, + `created_at` timestamp NULL DEFAULT NULL, + `updated_at` timestamp NULL DEFAULT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `api_token_ip_addresses_api_token_ip_address_unique` (`api_token`,`ip_address`), + KEY `api_token_ip_addresses_users_id_index` (`users_id`), + KEY `api_token_ip_addresses_is_rate_limited_index` (`is_rate_limited`), + KEY `api_token_ip_addresses_last_seen_at_index` (`last_seen_at`), + KEY `api_token_ip_addresses_api_token_index` (`api_token`), + CONSTRAINT `api_token_ip_addresses_users_id_foreign` FOREIGN KEY (`users_id`) REFERENCES `users` (`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC; +/*!40101 SET character_set_client = @saved_cs_client */; DROP TABLE IF EXISTS `audio_data`; - +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; CREATE TABLE `audio_data` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `releases_id` int(10) unsigned NOT NULL COMMENT 'FK to releases.id', @@ -60,10 +92,11 @@ CREATE TABLE `audio_data` ( PRIMARY KEY (`id`), UNIQUE KEY `ix_releaseaudio_releaseid_audioid` (`releases_id`,`audioid`), CONSTRAINT `FK_ad_releases` FOREIGN KEY (`releases_id`) REFERENCES `releases` (`id`) ON DELETE CASCADE ON UPDATE CASCADE -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci; - +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; DROP TABLE IF EXISTS `binaries`; - +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; CREATE TABLE `binaries` ( `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, `binaryhash` binary(16) NOT NULL, @@ -76,14 +109,15 @@ CREATE TABLE `binaries` ( `partsize` bigint(20) unsigned NOT NULL DEFAULT 0, PRIMARY KEY (`id`), UNIQUE KEY `ux_binaries_collection_hash` (`collections_id`,`binaryhash`), - KEY `ix_binaries_collection_filenumber` (`collections_id`,`filenumber`), KEY `ix_binaries_collection` (`collections_id`), KEY `ix_binaries_partcheck` (`partcheck`), + KEY `ix_binaries_collection_filenumber` (`collections_id`,`filenumber`), CONSTRAINT `FK_Collections` FOREIGN KEY (`collections_id`) REFERENCES `collections` (`id`) ON DELETE CASCADE ON UPDATE CASCADE -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci; - +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; DROP TABLE IF EXISTS `binaryblacklist`; - +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; CREATE TABLE `binaryblacklist` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `groupname` varchar(255) DEFAULT NULL, @@ -96,10 +130,11 @@ CREATE TABLE `binaryblacklist` ( PRIMARY KEY (`id`), KEY `ix_binaryblacklist_groupname` (`groupname`), KEY `ix_binaryblacklist_status` (`status`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci; - +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; DROP TABLE IF EXISTS `bookinfo`; - +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; CREATE TABLE `bookinfo` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `title` varchar(255) NOT NULL, @@ -120,19 +155,21 @@ CREATE TABLE `bookinfo` ( PRIMARY KEY (`id`), UNIQUE KEY `ix_bookinfo_asin` (`asin`), FULLTEXT KEY `ix_bookinfo_author_title_ft` (`author`,`title`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci; - +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; DROP TABLE IF EXISTS `cache`; - +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; CREATE TABLE `cache` ( `key` varchar(255) NOT NULL, - `value` mediumtext NOT NULL, + `value` longtext NOT NULL, `expiration` int(11) NOT NULL, UNIQUE KEY `cache_key_unique` (`key`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci ROW_FORMAT=DYNAMIC; - +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC; +/*!40101 SET character_set_client = @saved_cs_client */; DROP TABLE IF EXISTS `categories`; - +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; CREATE TABLE `categories` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `title` varchar(255) NOT NULL, @@ -146,10 +183,11 @@ CREATE TABLE `categories` ( KEY `ix_categories_parentid` (`root_categories_id`), KEY `ix_categories_status` (`status`), CONSTRAINT `fk_root_categories_id` FOREIGN KEY (`root_categories_id`) REFERENCES `root_categories` (`id`) ON DELETE CASCADE ON UPDATE CASCADE -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci; - +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; DROP TABLE IF EXISTS `category_regexes`; - +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; CREATE TABLE `category_regexes` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `group_regex` varchar(255) NOT NULL DEFAULT '' COMMENT 'This is a regex to match against usenet groups', @@ -163,10 +201,30 @@ CREATE TABLE `category_regexes` ( KEY `ix_category_regexes_status` (`status`), KEY `ix_category_regexes_ordinal` (`ordinal`), KEY `ix_category_regexes_categories_id` (`categories_id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci; - +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; +DROP TABLE IF EXISTS `cbp_optimization_checkpoints`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; +CREATE TABLE `cbp_optimization_checkpoints` ( + `step_name` varchar(64) CHARACTER SET ascii COLLATE ascii_bin NOT NULL, + `completed_at` datetime NOT NULL, + PRIMARY KEY (`step_name`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_uca1400_ai_ci; +/*!40101 SET character_set_client = @saved_cs_client */; +DROP TABLE IF EXISTS `collection_groups`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; +CREATE TABLE `collection_groups` ( + `collections_id` int(10) unsigned NOT NULL, + `group_name` varchar(255) NOT NULL, + PRIMARY KEY (`collections_id`,`group_name`), + CONSTRAINT `fk_collection_groups_collection` FOREIGN KEY (`collections_id`) REFERENCES `collections` (`id`) ON DELETE CASCADE ON UPDATE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC; +/*!40101 SET character_set_client = @saved_cs_client */; DROP TABLE IF EXISTS `collection_regexes`; - +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; CREATE TABLE `collection_regexes` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `group_regex` varchar(255) NOT NULL DEFAULT '' COMMENT 'This is a regex to match against usenet groups', @@ -178,10 +236,11 @@ CREATE TABLE `collection_regexes` ( KEY `ix_collection_regexes_group_regex` (`group_regex`), KEY `ix_collection_regexes_status` (`status`), KEY `ix_collection_regexes_ordinal` (`ordinal`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci; - +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; DROP TABLE IF EXISTS `collections`; - +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; CREATE TABLE `collections` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `subject` varchar(255) NOT NULL DEFAULT '', @@ -198,6 +257,11 @@ CREATE TABLE `collections` ( `filecheck` tinyint(1) NOT NULL DEFAULT 0, `filesize` bigint(20) unsigned NOT NULL DEFAULT 0, `releases_id` int(11) DEFAULT NULL, + `release_creation_claimed_at` timestamp NULL DEFAULT NULL, + `release_creation_claim_token` varchar(64) DEFAULT NULL, + `release_creation_failures` smallint(5) unsigned NOT NULL DEFAULT 0, + `release_creation_failed_at` timestamp NULL DEFAULT NULL, + `release_creation_error` varchar(512) DEFAULT NULL, `noise` char(32) NOT NULL DEFAULT '', PRIMARY KEY (`id`), UNIQUE KEY `ix_collection_collectionhash` (`collectionhash`), @@ -207,20 +271,16 @@ CREATE TABLE `collections` ( KEY `ix_collection_dateadded` (`dateadded`), KEY `ix_collection_filecheck` (`filecheck`), KEY `ix_collection_releaseid` (`releases_id`), + KEY `collections_groups_added_idx` (`groups_id`,`added`), + KEY `collections_dateadded_idx` (`dateadded`), + KEY `ix_collections_filecheck_filesize_groups` (`filecheck`,`filesize`,`groups_id`), + KEY `ix_collections_release_creation_claim_queue` (`filecheck`,`groups_id`,`release_creation_claimed_at`,`id`), KEY `ix_collections_group_filecheck_seen_id` (`groups_id`,`filecheck`,`last_seen_at`,`id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci; - -DROP TABLE IF EXISTS `collection_groups`; - -CREATE TABLE `collection_groups` ( - `collections_id` int(10) unsigned NOT NULL, - `group_name` varchar(255) NOT NULL, - PRIMARY KEY (`collections_id`,`group_name`), - CONSTRAINT `fk_collection_groups_collection` FOREIGN KEY (`collections_id`) REFERENCES `collections` (`id`) ON DELETE CASCADE ON UPDATE CASCADE -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci; - +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; DROP TABLE IF EXISTS `consoleinfo`; - +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; CREATE TABLE `consoleinfo` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `title` varchar(255) NOT NULL, @@ -238,52 +298,46 @@ CREATE TABLE `consoleinfo` ( `updated_at` timestamp NULL DEFAULT NULL, PRIMARY KEY (`id`), UNIQUE KEY `ix_consoleinfo_asin` (`asin`), + KEY `ix_consoleinfo_genres_id` (`genres_id`), FULLTEXT KEY `ix_consoleinfo_title_platform_ft` (`title`,`platform`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci; - +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; DROP TABLE IF EXISTS `content`; - +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; CREATE TABLE `content` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `title` varchar(255) NOT NULL, `url` varchar(2000) DEFAULT NULL, - `body` text DEFAULT NULL, + `body` mediumtext DEFAULT NULL, `metadescription` varchar(1000) NOT NULL, `metakeywords` varchar(1000) NOT NULL, `contenttype` int(11) NOT NULL, `status` int(11) NOT NULL, `ordinal` int(11) DEFAULT NULL, `role` int(11) NOT NULL DEFAULT 0, + `created_at` timestamp NULL DEFAULT NULL, + `updated_at` timestamp NULL DEFAULT NULL, PRIMARY KEY (`id`), - KEY `ix_status_contenttype_role` (`status`,`contenttype`,`role`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci; - + KEY `ix_status_contenttype_role` (`status`,`contenttype`,`role`), + KEY `ix_content_type_ordinal` (`contenttype`,`ordinal`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; DROP TABLE IF EXISTS `countries`; - +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; CREATE TABLE `countries` ( - `id` int(10) unsigned NOT NULL AUTO_INCREMENT, - `capital` varchar(255) DEFAULT NULL, - `citizenship` varchar(255) DEFAULT NULL, - `country_code` char(3) NOT NULL DEFAULT '', - `currency` varchar(255) DEFAULT NULL, - `currency_code` varchar(255) DEFAULT NULL, - `currency_sub_unit` varchar(255) DEFAULT NULL, - `currency_symbol` varchar(3) DEFAULT NULL, - `currency_decimals` int(11) DEFAULT NULL, + `iso_3166_2` char(2) NOT NULL, + `name` varchar(255) NOT NULL, `full_name` varchar(255) DEFAULT NULL, - `iso_3166_2` char(2) NOT NULL DEFAULT '', - `iso_3166_3` char(3) NOT NULL DEFAULT '', - `name` varchar(255) NOT NULL DEFAULT '', - `region_code` char(3) NOT NULL DEFAULT '', - `sub_region_code` char(3) NOT NULL DEFAULT '', - `eea` tinyint(1) NOT NULL DEFAULT 0, - `calling_code` varchar(3) DEFAULT NULL, - `flag` varchar(6) DEFAULT NULL, - PRIMARY KEY (`id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci ROW_FORMAT=DYNAMIC; - + PRIMARY KEY (`iso_3166_2`), + KEY `countries_name_index` (`name`), + KEY `countries_full_name_index` (`full_name`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC; +/*!40101 SET character_set_client = @saved_cs_client */; DROP TABLE IF EXISTS `dnzb_failures`; - +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; CREATE TABLE `dnzb_failures` ( `release_id` int(10) unsigned NOT NULL, `users_id` int(10) unsigned NOT NULL, @@ -293,21 +347,40 @@ CREATE TABLE `dnzb_failures` ( CONSTRAINT `FK_df_releases` FOREIGN KEY (`release_id`) REFERENCES `releases` (`id`) ON DELETE CASCADE ON UPDATE CASCADE, CONSTRAINT `FK_users_df` FOREIGN KEY (`users_id`) REFERENCES `users` (`id`) ON DELETE CASCADE ON UPDATE CASCADE ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci; - +/*!40101 SET character_set_client = @saved_cs_client */; +DROP TABLE IF EXISTS `download_stats`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; +CREATE TABLE `download_stats` ( + `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, + `created_at` timestamp NULL DEFAULT NULL, + `updated_at` timestamp NULL DEFAULT NULL, + `searchname` varchar(255) DEFAULT NULL, + `grabs` int(11) NOT NULL DEFAULT 0, + `guid` varchar(255) DEFAULT NULL, + `adddate` datetime DEFAULT NULL, + PRIMARY KEY (`id`), + KEY `ix_download_stats_created_at` (`created_at`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC; +/*!40101 SET character_set_client = @saved_cs_client */; DROP TABLE IF EXISTS `failed_jobs`; - +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; CREATE TABLE `failed_jobs` ( `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, - `connection` text NOT NULL, - `queue` text NOT NULL, + `connection` mediumtext NOT NULL, + `queue` mediumtext NOT NULL, `payload` longtext NOT NULL, `exception` longtext NOT NULL, `failed_at` timestamp NOT NULL DEFAULT current_timestamp(), - PRIMARY KEY (`id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci ROW_FORMAT=DYNAMIC; - + `uuid` varchar(255) NOT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `failed_jobs_uuid_unique` (`uuid`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC; +/*!40101 SET character_set_client = @saved_cs_client */; DROP TABLE IF EXISTS `firewall`; - +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; CREATE TABLE `firewall` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `ip_address` varchar(39) NOT NULL, @@ -316,10 +389,11 @@ CREATE TABLE `firewall` ( `updated_at` timestamp NULL DEFAULT NULL, PRIMARY KEY (`id`), UNIQUE KEY `firewall_ip_address_unique` (`ip_address`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci ROW_FORMAT=DYNAMIC; - +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC; +/*!40101 SET character_set_client = @saved_cs_client */; DROP TABLE IF EXISTS `forum_categories`; - +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; CREATE TABLE `forum_categories` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `title` varchar(255) NOT NULL, @@ -330,34 +404,41 @@ CREATE TABLE `forum_categories` ( `thread_count` int(11) NOT NULL DEFAULT 0, `post_count` int(11) NOT NULL DEFAULT 0, `is_private` tinyint(1) NOT NULL DEFAULT 0, + `thread_approval_enabled` tinyint(1) NOT NULL DEFAULT 0, + `post_approval_enabled` tinyint(1) NOT NULL DEFAULT 0, `created_at` timestamp NULL DEFAULT NULL, `updated_at` timestamp NULL DEFAULT NULL, `_lft` int(10) unsigned NOT NULL DEFAULT 0, `_rgt` int(10) unsigned NOT NULL DEFAULT 0, `parent_id` int(10) unsigned DEFAULT NULL, - `color` varchar(255) DEFAULT NULL, + `color_light_mode` varchar(255) DEFAULT NULL, + `color_dark_mode` varchar(255) DEFAULT NULL, + `depth` smallint(6) NOT NULL DEFAULT 0, PRIMARY KEY (`id`), KEY `forum_categories__lft__rgt_parent_id_index` (`_lft`,`_rgt`,`parent_id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci ROW_FORMAT=DYNAMIC; - +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC; +/*!40101 SET character_set_client = @saved_cs_client */; DROP TABLE IF EXISTS `forum_posts`; - +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; CREATE TABLE `forum_posts` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `thread_id` int(10) unsigned NOT NULL, `author_id` bigint(20) unsigned NOT NULL, - `content` text NOT NULL, + `content` mediumtext NOT NULL, `post_id` int(10) unsigned DEFAULT NULL, `sequence` int(10) unsigned NOT NULL DEFAULT 0, `created_at` timestamp NULL DEFAULT NULL, `updated_at` timestamp NULL DEFAULT NULL, + `approved_at` timestamp NULL DEFAULT NULL, `deleted_at` timestamp NULL DEFAULT NULL, PRIMARY KEY (`id`), KEY `forum_posts_thread_id_index` (`thread_id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci ROW_FORMAT=DYNAMIC; - +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC; +/*!40101 SET character_set_client = @saved_cs_client */; DROP TABLE IF EXISTS `forum_threads`; - +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; CREATE TABLE `forum_threads` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `category_id` int(10) unsigned NOT NULL, @@ -370,29 +451,34 @@ CREATE TABLE `forum_threads` ( `reply_count` int(11) NOT NULL DEFAULT 0, `created_at` timestamp NULL DEFAULT NULL, `updated_at` timestamp NULL DEFAULT NULL, + `approved_at` timestamp NULL DEFAULT NULL, `deleted_at` timestamp NULL DEFAULT NULL, PRIMARY KEY (`id`), - KEY `forum_threads_category_id_index` (`category_id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci ROW_FORMAT=DYNAMIC; - + KEY `forum_threads_category_id_index` (`category_id`), + KEY `ix_forum_threads_author_id` (`author_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC; +/*!40101 SET character_set_client = @saved_cs_client */; DROP TABLE IF EXISTS `forum_threads_read`; - +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; CREATE TABLE `forum_threads_read` ( `thread_id` int(10) unsigned NOT NULL, `user_id` bigint(20) unsigned NOT NULL, `created_at` timestamp NULL DEFAULT NULL, - `updated_at` timestamp NULL DEFAULT NULL + `updated_at` timestamp NULL DEFAULT NULL, + KEY `ix_forum_threads_read_user_thread` (`user_id`,`thread_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci ROW_FORMAT=DYNAMIC; - +/*!40101 SET character_set_client = @saved_cs_client */; DROP TABLE IF EXISTS `forumpost`; - +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; CREATE TABLE `forumpost` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `forumid` int(11) NOT NULL DEFAULT 1, `parentid` int(11) NOT NULL DEFAULT 0, `users_id` int(10) unsigned NOT NULL, `subject` varchar(255) NOT NULL, - `message` text NOT NULL, + `message` mediumtext NOT NULL, `locked` tinyint(1) NOT NULL DEFAULT 0, `sticky` tinyint(1) NOT NULL DEFAULT 0, `replies` int(10) unsigned NOT NULL DEFAULT 0, @@ -402,10 +488,11 @@ CREATE TABLE `forumpost` ( KEY `parentid` (`parentid`), KEY `userid` (`users_id`), CONSTRAINT `FK_users_fp` FOREIGN KEY (`users_id`) REFERENCES `users` (`id`) ON DELETE CASCADE ON UPDATE CASCADE -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci; - +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; DROP TABLE IF EXISTS `gamesinfo`; - +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; CREATE TABLE `gamesinfo` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `title` varchar(255) NOT NULL, @@ -424,34 +511,129 @@ CREATE TABLE `gamesinfo` ( `updated_at` timestamp NULL DEFAULT NULL, PRIMARY KEY (`id`), UNIQUE KEY `ix_gamesinfo_asin` (`asin`), + KEY `ix_gamesinfo_genres_id` (`genres_id`), FULLTEXT KEY `ix_title_ft` (`title`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci; - +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; +DROP TABLE IF EXISTS `gdpr_audit_logs`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; +CREATE TABLE `gdpr_audit_logs` ( + `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, + `gdpr_request_id` bigint(20) unsigned DEFAULT NULL, + `user_id` bigint(20) unsigned DEFAULT NULL, + `actor_id` bigint(20) unsigned DEFAULT NULL, + `event` varchar(64) NOT NULL, + `description` text NOT NULL, + `metadata` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL CHECK (json_valid(`metadata`)), + `created_at` timestamp NOT NULL DEFAULT current_timestamp(), + PRIMARY KEY (`id`), + KEY `ix_gdpr_audit_request_created` (`gdpr_request_id`,`created_at`), + KEY `ix_gdpr_audit_user_created` (`user_id`,`created_at`), + KEY `ix_gdpr_audit_event_created` (`event`,`created_at`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC; +/*!40101 SET character_set_client = @saved_cs_client */; +DROP TABLE IF EXISTS `gdpr_consents`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; +CREATE TABLE `gdpr_consents` ( + `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, + `user_id` bigint(20) unsigned DEFAULT NULL, + `consent_type` varchar(64) NOT NULL, + `status` varchar(32) NOT NULL DEFAULT 'granted', + `policy_version` varchar(64) DEFAULT NULL, + `consented_at` timestamp NULL DEFAULT NULL, + `withdrawn_at` timestamp NULL DEFAULT NULL, + `ip_address` varchar(45) DEFAULT NULL, + `user_agent_hash` varchar(64) DEFAULT NULL, + `metadata` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL CHECK (json_valid(`metadata`)), + `created_at` timestamp NULL DEFAULT NULL, + `updated_at` timestamp NULL DEFAULT NULL, + PRIMARY KEY (`id`), + KEY `ix_gdpr_consents_user_type` (`user_id`,`consent_type`), + KEY `ix_gdpr_consents_type_status` (`consent_type`,`status`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC; +/*!40101 SET character_set_client = @saved_cs_client */; +DROP TABLE IF EXISTS `gdpr_requests`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; +CREATE TABLE `gdpr_requests` ( + `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, + `user_id` bigint(20) unsigned DEFAULT NULL, + `requester_username` varchar(255) DEFAULT NULL, + `requester_email` varchar(255) DEFAULT NULL, + `type` varchar(32) NOT NULL, + `status` varchar(32) NOT NULL DEFAULT 'pending', + `request_payload` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL CHECK (json_valid(`request_payload`)), + `response_payload` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL CHECK (json_valid(`response_payload`)), + `notes` text DEFAULT NULL, + `admin_notes` text DEFAULT NULL, + `export_disk` varchar(255) DEFAULT NULL, + `export_path` varchar(255) DEFAULT NULL, + `export_expires_at` timestamp NULL DEFAULT NULL, + `processed_by` bigint(20) unsigned DEFAULT NULL, + `completed_at` timestamp NULL DEFAULT NULL, + `created_at` timestamp NULL DEFAULT NULL, + `updated_at` timestamp NULL DEFAULT NULL, + PRIMARY KEY (`id`), + KEY `ix_gdpr_requests_user_type_status` (`user_id`,`type`,`status`), + KEY `ix_gdpr_requests_status_created` (`status`,`created_at`), + KEY `ix_gdpr_requests_type_created` (`type`,`created_at`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC; +/*!40101 SET character_set_client = @saved_cs_client */; DROP TABLE IF EXISTS `genres`; - +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; CREATE TABLE `genres` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `title` varchar(255) NOT NULL, `type` int(11) DEFAULT NULL, `disabled` tinyint(1) NOT NULL DEFAULT 0, - PRIMARY KEY (`id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci; - + PRIMARY KEY (`id`), + KEY `ix_genres_type_disabled` (`type`,`disabled`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; +DROP TABLE IF EXISTS `grab_stats`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; +CREATE TABLE `grab_stats` ( + `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, + `created_at` timestamp NULL DEFAULT NULL, + `updated_at` timestamp NULL DEFAULT NULL, + `username` varchar(255) DEFAULT NULL, + `grabs` int(11) NOT NULL DEFAULT 0, + PRIMARY KEY (`id`), + KEY `ix_grab_stats_created_at` (`created_at`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC; +/*!40101 SET character_set_client = @saved_cs_client */; DROP TABLE IF EXISTS `invitations`; - +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; CREATE TABLE `invitations` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, - `guid` varchar(50) NOT NULL, - `users_id` int(10) unsigned NOT NULL, + `token` varchar(64) NOT NULL, + `email` varchar(255) NOT NULL, + `invited_by` bigint(20) unsigned NOT NULL, + `expires_at` timestamp NOT NULL, + `used_at` timestamp NULL DEFAULT NULL, + `used_by` bigint(20) unsigned DEFAULT NULL, + `is_active` tinyint(1) NOT NULL DEFAULT 1, + `metadata` longtext DEFAULT NULL CHECK (json_valid(`metadata`)), `created_at` timestamp NULL DEFAULT NULL, `updated_at` timestamp NULL DEFAULT NULL, PRIMARY KEY (`id`), - KEY `FK_users_inv` (`users_id`), - CONSTRAINT `FK_users_inv` FOREIGN KEY (`users_id`) REFERENCES `users` (`id`) ON DELETE CASCADE ON UPDATE CASCADE -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci; - + UNIQUE KEY `invitations_token_unique` (`token`), + KEY `invitations_invited_by_index` (`invited_by`), + KEY `invitations_used_by_index` (`used_by`), + KEY `invitations_token_is_active_index` (`token`,`is_active`), + KEY `invitations_email_is_active_index` (`email`,`is_active`), + KEY `invitations_expires_at_index` (`expires_at`), + KEY `ix_invitations_active_expires` (`is_active`,`expires_at`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; DROP TABLE IF EXISTS `jobs`; - +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; CREATE TABLE `jobs` ( `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, `queue` varchar(255) NOT NULL, @@ -462,29 +644,48 @@ CREATE TABLE `jobs` ( `created_at` int(10) unsigned NOT NULL, PRIMARY KEY (`id`), KEY `jobs_queue_index` (`queue`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci ROW_FORMAT=DYNAMIC; - +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC; +/*!40101 SET character_set_client = @saved_cs_client */; DROP TABLE IF EXISTS `logging`; - +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; CREATE TABLE `logging` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `time` datetime DEFAULT NULL, `username` varchar(50) DEFAULT NULL, `host` varchar(40) DEFAULT NULL, PRIMARY KEY (`id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci; - +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; +DROP TABLE IF EXISTS `media_infos`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; +CREATE TABLE `media_infos` ( + `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, + `releases_id` bigint(20) unsigned NOT NULL, + `movie_name` varchar(255) DEFAULT NULL, + `file_name` varchar(255) DEFAULT NULL, + `unique_id` varchar(255) DEFAULT NULL, + `created_at` timestamp NULL DEFAULT NULL, + `updated_at` timestamp NULL DEFAULT NULL, + PRIMARY KEY (`id`), + KEY `ix_media_infos_unique_id` (`unique_id`), + KEY `ix_media_infos_releases_id` (`releases_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC; +/*!40101 SET character_set_client = @saved_cs_client */; DROP TABLE IF EXISTS `migrations`; - +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; CREATE TABLE `migrations` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `migration` varchar(255) NOT NULL, `batch` int(11) NOT NULL, PRIMARY KEY (`id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci ROW_FORMAT=DYNAMIC; - +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC; +/*!40101 SET character_set_client = @saved_cs_client */; DROP TABLE IF EXISTS `missed_parts`; - +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; CREATE TABLE `missed_parts` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `numberid` bigint(20) unsigned NOT NULL, @@ -498,9 +699,10 @@ CREATE TABLE `missed_parts` ( KEY `ix_missed_parts_numberid_groupsid_attempts` (`numberid`,`groups_id`,`attempts`), KEY `ix_missed_parts_attempts` (`attempts`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci; - +/*!40101 SET character_set_client = @saved_cs_client */; DROP TABLE IF EXISTS `model_has_permissions`; - +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; CREATE TABLE `model_has_permissions` ( `permission_id` int(10) unsigned NOT NULL, `model_type` varchar(255) NOT NULL, @@ -508,10 +710,11 @@ CREATE TABLE `model_has_permissions` ( PRIMARY KEY (`permission_id`,`model_id`,`model_type`), KEY `model_has_permissions_model_type_model_id_index` (`model_type`,`model_id`), CONSTRAINT `model_has_permissions_permission_id_foreign` FOREIGN KEY (`permission_id`) REFERENCES `permissions` (`id`) ON DELETE CASCADE -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci; - +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; DROP TABLE IF EXISTS `model_has_roles`; - +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; CREATE TABLE `model_has_roles` ( `role_id` int(10) unsigned NOT NULL, `model_type` varchar(255) NOT NULL, @@ -519,10 +722,11 @@ CREATE TABLE `model_has_roles` ( PRIMARY KEY (`role_id`,`model_id`,`model_type`), KEY `model_has_roles_model_type_model_id_index` (`model_type`,`model_id`), CONSTRAINT `model_has_roles_role_id_foreign` FOREIGN KEY (`role_id`) REFERENCES `roles` (`id`) ON DELETE CASCADE -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci; - +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; DROP TABLE IF EXISTS `movieinfo`; - +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; CREATE TABLE `movieinfo` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `imdbid` varchar(100) NOT NULL, @@ -549,10 +753,11 @@ CREATE TABLE `movieinfo` ( KEY `ix_movieinfo_title` (`title`), KEY `ix_movieinfo_tmdbid` (`tmdbid`), KEY `ix_movieinfo_traktid` (`traktid`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci; - +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; DROP TABLE IF EXISTS `musicinfo`; - +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; CREATE TABLE `musicinfo` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `title` varchar(255) NOT NULL, @@ -571,23 +776,48 @@ CREATE TABLE `musicinfo` ( `updated_at` timestamp NULL DEFAULT NULL, PRIMARY KEY (`id`), UNIQUE KEY `ix_musicinfo_asin` (`asin`), + KEY `ix_musicinfo_genres_id` (`genres_id`), FULLTEXT KEY `ix_musicinfo_artist_title_ft` (`artist`,`title`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci; - +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; +DROP TABLE IF EXISTS `nzb_backup_runs`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; +CREATE TABLE `nzb_backup_runs` ( + `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, + `started_at` timestamp NOT NULL, + `completed_at` timestamp NULL DEFAULT NULL, + `status` enum('running','completed','failed','cancelled') NOT NULL DEFAULT 'running', + `files_found` int(10) unsigned NOT NULL DEFAULT 0 COMMENT 'Files checked by rclone', + `files_uploaded` int(10) unsigned NOT NULL DEFAULT 0 COMMENT 'Files transferred', + `files_skipped` int(10) unsigned NOT NULL DEFAULT 0, + `files_failed` int(10) unsigned NOT NULL DEFAULT 0 COMMENT 'Errors during transfer', + `bytes_uploaded` bigint(20) unsigned NOT NULL DEFAULT 0, + `error_message` text DEFAULT NULL, + `stats` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL COMMENT 'Rclone statistics' CHECK (json_valid(`stats`)), + `created_at` timestamp NULL DEFAULT NULL, + `updated_at` timestamp NULL DEFAULT NULL, + PRIMARY KEY (`id`), + KEY `nzb_backup_runs_started_at_index` (`started_at`), + KEY `nzb_backup_runs_status_index` (`status`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC; +/*!40101 SET character_set_client = @saved_cs_client */; DROP TABLE IF EXISTS `par_hashes`; - +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; CREATE TABLE `par_hashes` ( `releases_id` int(10) unsigned NOT NULL COMMENT 'FK to releases.id', `hash` varchar(32) NOT NULL COMMENT 'hash_16k block of par2', PRIMARY KEY (`releases_id`,`hash`), KEY `ix_par_hashes_hash_releases_id` (`hash`,`releases_id`), CONSTRAINT `FK_ph_releases` FOREIGN KEY (`releases_id`) REFERENCES `releases` (`id`) ON DELETE CASCADE ON UPDATE CASCADE -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci; - +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; DROP TABLE IF EXISTS `parts`; - +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; CREATE TABLE `parts` ( - `binaries_id` bigint(20) unsigned NOT NULL DEFAULT 0, + `binaries_id` bigint(20) unsigned NOT NULL, `messageid` varchar(255) CHARACTER SET ascii COLLATE ascii_bin NOT NULL DEFAULT '', `number` bigint(20) unsigned NOT NULL DEFAULT 0, `partnumber` int(10) unsigned NOT NULL DEFAULT 0, @@ -595,10 +825,38 @@ CREATE TABLE `parts` ( PRIMARY KEY (`binaries_id`,`partnumber`), KEY `ix_parts_number` (`number`), CONSTRAINT `FK_binaries` FOREIGN KEY (`binaries_id`) REFERENCES `binaries` (`id`) ON DELETE CASCADE ON UPDATE CASCADE -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci; - +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_uca1400_ai_ci; +/*!40101 SET character_set_client = @saved_cs_client */; +DROP TABLE IF EXISTS `passkeys`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; +CREATE TABLE `passkeys` ( + `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, + `authenticatable_id` int(10) unsigned NOT NULL, + `name` text NOT NULL, + `credential_id` text NOT NULL, + `data` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL CHECK (json_valid(`data`)), + `last_used_at` timestamp NULL DEFAULT NULL, + `created_at` timestamp NULL DEFAULT NULL, + `updated_at` timestamp NULL DEFAULT NULL, + PRIMARY KEY (`id`), + KEY `passkeys_authenticatable_fk` (`authenticatable_id`), + CONSTRAINT `passkeys_authenticatable_fk` FOREIGN KEY (`authenticatable_id`) REFERENCES `users` (`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC; +/*!40101 SET character_set_client = @saved_cs_client */; +DROP TABLE IF EXISTS `password_reset_tokens`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; +CREATE TABLE `password_reset_tokens` ( + `email` varchar(255) NOT NULL, + `token` varchar(255) NOT NULL, + `created_at` timestamp NULL DEFAULT NULL, + PRIMARY KEY (`email`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC; +/*!40101 SET character_set_client = @saved_cs_client */; DROP TABLE IF EXISTS `password_securities`; - +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; CREATE TABLE `password_securities` ( `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, `user_id` int(11) NOT NULL, @@ -606,11 +864,13 @@ CREATE TABLE `password_securities` ( `google2fa_secret` varchar(255) DEFAULT NULL, `created_at` timestamp NULL DEFAULT NULL, `updated_at` timestamp NULL DEFAULT NULL, - PRIMARY KEY (`id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci ROW_FORMAT=DYNAMIC; - + PRIMARY KEY (`id`), + KEY `ix_password_securities_user_id` (`user_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC; +/*!40101 SET character_set_client = @saved_cs_client */; DROP TABLE IF EXISTS `payments`; - +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; CREATE TABLE `payments` ( `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, `created_at` timestamp NULL DEFAULT NULL, @@ -621,27 +881,31 @@ CREATE TABLE `payments` ( `order_id` varchar(255) NOT NULL, `payment_id` varchar(255) NOT NULL, `payment_status` varchar(255) NOT NULL, + `invoice_status` varchar(255) DEFAULT 'Pending', `invoice_amount` varchar(255) NOT NULL, `payment_method` varchar(255) NOT NULL, `payment_value` varchar(255) NOT NULL, `webhook_id` varchar(255) NOT NULL, `invoice_id` varchar(255) NOT NULL, PRIMARY KEY (`id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci ROW_FORMAT=DYNAMIC; - +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC; +/*!40101 SET character_set_client = @saved_cs_client */; DROP TABLE IF EXISTS `paypal_payments`; - +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; CREATE TABLE `paypal_payments` ( `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, `users_id` int(11) NOT NULL, `transaction_id` varchar(255) NOT NULL, `created_at` timestamp NULL DEFAULT NULL, `updated_at` timestamp NULL DEFAULT NULL, - PRIMARY KEY (`id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci ROW_FORMAT=DYNAMIC; - + PRIMARY KEY (`id`), + KEY `ix_paypal_payments_users_id` (`users_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC; +/*!40101 SET character_set_client = @saved_cs_client */; DROP TABLE IF EXISTS `permissions`; - +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; CREATE TABLE `permissions` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `name` varchar(255) NOT NULL, @@ -649,17 +913,18 @@ CREATE TABLE `permissions` ( `created_at` timestamp NULL DEFAULT NULL, `updated_at` timestamp NULL DEFAULT NULL, PRIMARY KEY (`id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci; - +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; DROP TABLE IF EXISTS `personal_access_tokens`; - +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; CREATE TABLE `personal_access_tokens` ( `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, `tokenable_type` varchar(255) NOT NULL, `tokenable_id` bigint(20) unsigned NOT NULL, `name` varchar(255) NOT NULL, `token` varchar(64) NOT NULL, - `abilities` text DEFAULT NULL, + `abilities` mediumtext DEFAULT NULL, `last_used_at` timestamp NULL DEFAULT NULL, `expires_at` timestamp NULL DEFAULT NULL, `created_at` timestamp NULL DEFAULT NULL, @@ -667,10 +932,11 @@ CREATE TABLE `personal_access_tokens` ( PRIMARY KEY (`id`), UNIQUE KEY `personal_access_tokens_token_unique` (`token`), KEY `personal_access_tokens_tokenable_type_tokenable_id_index` (`tokenable_type`,`tokenable_id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci ROW_FORMAT=DYNAMIC; - +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC; +/*!40101 SET character_set_client = @saved_cs_client */; DROP TABLE IF EXISTS `poster_renames`; - +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; CREATE TABLE `poster_renames` ( `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, `created_at` timestamp NULL DEFAULT NULL, @@ -680,10 +946,11 @@ CREATE TABLE `poster_renames` ( `source` varchar(20) NOT NULL, PRIMARY KEY (`id`), UNIQUE KEY `poster_title` (`poster`,`title`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci ROW_FORMAT=DYNAMIC; - +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC; +/*!40101 SET character_set_client = @saved_cs_client */; DROP TABLE IF EXISTS `predb`; - +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; CREATE TABLE `predb` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT 'Primary key', `title` varchar(255) NOT NULL DEFAULT '', @@ -708,9 +975,11 @@ CREATE TABLE `predb` ( KEY `ix_predb_searched` (`searched`), KEY `ix_predb_searched_predate_id` (`searched`,`predate`,`id`), FULLTEXT KEY `ft_predb_filename` (`filename`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; DROP TABLE IF EXISTS `predb_crcs`; - +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; CREATE TABLE `predb_crcs` ( `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, `predb_id` int(10) unsigned NOT NULL COMMENT 'FK to predb.id', @@ -723,10 +992,11 @@ CREATE TABLE `predb_crcs` ( PRIMARY KEY (`id`), KEY `predb_crcs_crchash_filesize_filedate_index` (`crchash`,`filesize`,`filedate`), KEY `predb_crcs_osohash_index` (`osohash`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci ROW_FORMAT=DYNAMIC; - +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC; +/*!40101 SET character_set_client = @saved_cs_client */; DROP TABLE IF EXISTS `predb_imports`; - +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; CREATE TABLE `predb_imports` ( `title` varchar(255) NOT NULL DEFAULT '', `nfo` varchar(255) DEFAULT NULL, @@ -742,16 +1012,17 @@ CREATE TABLE `predb_imports` ( `filename` varchar(255) NOT NULL DEFAULT '', `searched` tinyint(1) NOT NULL DEFAULT 0, `groupname` varchar(255) DEFAULT NULL -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci; - +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; DROP TABLE IF EXISTS `pulse_aggregates`; - +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; CREATE TABLE `pulse_aggregates` ( `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, `bucket` int(10) unsigned NOT NULL, `period` mediumint(8) unsigned NOT NULL, `type` varchar(255) NOT NULL, - `key` text NOT NULL, + `key` mediumtext NOT NULL, `key_hash` binary(16) GENERATED ALWAYS AS (unhex(md5(`key`))) VIRTUAL, `aggregate` varchar(255) NOT NULL, `value` decimal(20,2) NOT NULL, @@ -761,15 +1032,16 @@ CREATE TABLE `pulse_aggregates` ( KEY `pulse_aggregates_period_bucket_index` (`period`,`bucket`), KEY `pulse_aggregates_type_index` (`type`), KEY `pulse_aggregates_period_type_aggregate_bucket_index` (`period`,`type`,`aggregate`,`bucket`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci ROW_FORMAT=DYNAMIC; - +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC; +/*!40101 SET character_set_client = @saved_cs_client */; DROP TABLE IF EXISTS `pulse_entries`; - +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; CREATE TABLE `pulse_entries` ( `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, `timestamp` int(10) unsigned NOT NULL, `type` varchar(255) NOT NULL, - `key` text NOT NULL, + `key` mediumtext NOT NULL, `key_hash` binary(16) GENERATED ALWAYS AS (unhex(md5(`key`))) VIRTUAL, `value` bigint(20) DEFAULT NULL, PRIMARY KEY (`id`), @@ -777,54 +1049,92 @@ CREATE TABLE `pulse_entries` ( KEY `pulse_entries_type_index` (`type`), KEY `pulse_entries_key_hash_index` (`key_hash`), KEY `pulse_entries_timestamp_type_key_hash_value_index` (`timestamp`,`type`,`key_hash`,`value`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci ROW_FORMAT=DYNAMIC; - +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC; +/*!40101 SET character_set_client = @saved_cs_client */; DROP TABLE IF EXISTS `pulse_values`; - +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; CREATE TABLE `pulse_values` ( `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, `timestamp` int(10) unsigned NOT NULL, `type` varchar(255) NOT NULL, - `key` text NOT NULL, + `key` mediumtext NOT NULL, `key_hash` binary(16) GENERATED ALWAYS AS (unhex(md5(`key`))) VIRTUAL, - `value` text NOT NULL, + `value` mediumtext NOT NULL, PRIMARY KEY (`id`), UNIQUE KEY `pulse_values_type_key_hash_unique` (`type`,`key_hash`), KEY `pulse_values_timestamp_index` (`timestamp`), KEY `pulse_values_type_index` (`type`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci ROW_FORMAT=DYNAMIC; - +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC; +/*!40101 SET character_set_client = @saved_cs_client */; +DROP TABLE IF EXISTS `registration_periods`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; +CREATE TABLE `registration_periods` ( + `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, + `name` varchar(255) NOT NULL, + `starts_at` datetime NOT NULL, + `ends_at` datetime NOT NULL, + `is_enabled` tinyint(1) NOT NULL DEFAULT 1, + `notes` text DEFAULT NULL, + `created_by` bigint(20) unsigned DEFAULT NULL, + `updated_by` bigint(20) unsigned DEFAULT NULL, + `created_at` timestamp NULL DEFAULT NULL, + `updated_at` timestamp NULL DEFAULT NULL, + PRIMARY KEY (`id`), + KEY `registration_periods_starts_at_index` (`starts_at`), + KEY `registration_periods_ends_at_index` (`ends_at`), + KEY `registration_periods_is_enabled_index` (`is_enabled`), + KEY `registration_periods_created_by_index` (`created_by`), + KEY `registration_periods_updated_by_index` (`updated_by`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC; +/*!40101 SET character_set_client = @saved_cs_client */; +DROP TABLE IF EXISTS `registration_status_history`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; +CREATE TABLE `registration_status_history` ( + `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, + `action` varchar(100) NOT NULL, + `old_status` tinyint(3) unsigned DEFAULT NULL, + `new_status` tinyint(3) unsigned DEFAULT NULL, + `registration_period_id` bigint(20) unsigned DEFAULT NULL, + `changed_by` bigint(20) unsigned DEFAULT NULL, + `description` varchar(255) NOT NULL, + `metadata` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL CHECK (json_valid(`metadata`)), + `created_at` timestamp NOT NULL DEFAULT current_timestamp(), + `updated_at` timestamp NULL DEFAULT NULL, + PRIMARY KEY (`id`), + KEY `registration_status_history_action_index` (`action`), + KEY `registration_status_history_registration_period_id_index` (`registration_period_id`), + KEY `registration_status_history_changed_by_index` (`changed_by`), + KEY `registration_status_history_created_at_index` (`created_at`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC; +/*!40101 SET character_set_client = @saved_cs_client */; DROP TABLE IF EXISTS `release_comments`; - +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; CREATE TABLE `release_comments` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `releases_id` int(10) unsigned NOT NULL COMMENT 'FK to releases.id', `text` varchar(2000) NOT NULL DEFAULT '', `isvisible` tinyint(1) NOT NULL DEFAULT 1, - `issynced` tinyint(1) NOT NULL DEFAULT 0, - `gid` varchar(32) DEFAULT NULL, - `cid` varchar(32) DEFAULT NULL, `username` varchar(255) NOT NULL DEFAULT '', `users_id` int(10) unsigned NOT NULL, `created_at` timestamp NULL DEFAULT NULL, `updated_at` timestamp NULL DEFAULT NULL, `host` varchar(15) DEFAULT NULL, - `shared` tinyint(1) NOT NULL DEFAULT 0, - `shareid` varchar(40) NOT NULL DEFAULT '', - `siteid` varchar(40) NOT NULL DEFAULT '', - `sourceid` bigint(20) unsigned DEFAULT NULL, - `nzb_guid` binary(16) NOT NULL DEFAULT '0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0', PRIMARY KEY (`id`), KEY `ix_releasecomment_releases_id` (`releases_id`), KEY `ix_releasecomment_userid` (`users_id`), CONSTRAINT `FK_rc_releases` FOREIGN KEY (`releases_id`) REFERENCES `releases` (`id`) ON DELETE CASCADE ON UPDATE CASCADE -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci; - +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; DROP TABLE IF EXISTS `release_files`; - +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; CREATE TABLE `release_files` ( `releases_id` int(10) unsigned NOT NULL COMMENT 'FK to releases.id', - `name` varchar(255) NOT NULL DEFAULT '', + `name` varchar(255) NOT NULL, `size` bigint(20) unsigned NOT NULL DEFAULT 0, `crc32` varchar(255) NOT NULL DEFAULT '', `created_at` timestamp NULL DEFAULT NULL, @@ -833,8 +1143,11 @@ CREATE TABLE `release_files` ( PRIMARY KEY (`releases_id`,`name`), KEY `ix_release_files_crc32_releases_id` (`crc32`,`releases_id`), CONSTRAINT `FK_rf_releases` FOREIGN KEY (`releases_id`) REFERENCES `releases` (`id`) ON DELETE CASCADE ON UPDATE CASCADE -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; DROP TABLE IF EXISTS `release_informs`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; CREATE TABLE `release_informs` ( `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, `created_at` timestamp NULL DEFAULT NULL, @@ -843,8 +1156,11 @@ CREATE TABLE `release_informs` ( `relPName` varchar(255) NOT NULL, `api_token` varchar(255) NOT NULL, PRIMARY KEY (`id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci ROW_FORMAT=DYNAMIC; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC; +/*!40101 SET character_set_client = @saved_cs_client */; DROP TABLE IF EXISTS `release_naming_regexes`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; CREATE TABLE `release_naming_regexes` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `group_regex` varchar(255) NOT NULL DEFAULT '' COMMENT 'This is a regex to match against usenet groups', @@ -856,22 +1172,99 @@ CREATE TABLE `release_naming_regexes` ( KEY `ix_release_naming_regexes_group_regex` (`group_regex`), KEY `ix_release_naming_regexes_status` (`status`), KEY `ix_release_naming_regexes_ordinal` (`ordinal`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; DROP TABLE IF EXISTS `release_nfos`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; CREATE TABLE `release_nfos` ( `releases_id` int(10) unsigned NOT NULL COMMENT 'FK to releases.id', `nfo` blob DEFAULT NULL, PRIMARY KEY (`releases_id`), CONSTRAINT `FK_rn_releases` FOREIGN KEY (`releases_id`) REFERENCES `releases` (`id`) ON DELETE CASCADE ON UPDATE CASCADE ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; +DROP TABLE IF EXISTS `release_nzb_creation_failures`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; +CREATE TABLE `release_nzb_creation_failures` ( + `releases_id` int(10) unsigned NOT NULL, + `attempts` smallint(5) unsigned NOT NULL DEFAULT 0, + `last_error` text DEFAULT NULL, + `created_at` timestamp NULL DEFAULT NULL, + `updated_at` timestamp NULL DEFAULT NULL, + PRIMARY KEY (`releases_id`), + CONSTRAINT `FK_rncf_releases` FOREIGN KEY (`releases_id`) REFERENCES `releases` (`id`) ON DELETE CASCADE ON UPDATE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC; +/*!40101 SET character_set_client = @saved_cs_client */; +DROP TABLE IF EXISTS `release_nzb_passwords`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; +CREATE TABLE `release_nzb_passwords` ( + `releases_id` int(10) unsigned NOT NULL, + `password` varchar(255) NOT NULL, + PRIMARY KEY (`releases_id`), + CONSTRAINT `FK_rnp_releases` FOREIGN KEY (`releases_id`) REFERENCES `releases` (`id`) ON DELETE CASCADE ON UPDATE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC; +/*!40101 SET character_set_client = @saved_cs_client */; DROP TABLE IF EXISTS `release_regexes`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; CREATE TABLE `release_regexes` ( `releases_id` int(10) unsigned NOT NULL DEFAULT 0, `collection_regex_id` int(11) NOT NULL DEFAULT 0, `naming_regex_id` int(11) NOT NULL DEFAULT 0, PRIMARY KEY (`releases_id`,`collection_regex_id`,`naming_regex_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; +DROP TABLE IF EXISTS `release_reports`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; +CREATE TABLE `release_reports` ( + `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, + `releases_id` int(10) unsigned NOT NULL, + `users_id` int(10) unsigned NOT NULL, + `reason` varchar(255) NOT NULL, + `description` text DEFAULT NULL, + `response` text DEFAULT NULL, + `status` enum('pending','reviewed','resolved','dismissed') NOT NULL DEFAULT 'pending', + `reviewed_by` int(10) unsigned DEFAULT NULL, + `reviewed_at` timestamp NULL DEFAULT NULL, + `responded_by` int(10) unsigned DEFAULT NULL, + `responded_at` timestamp NULL DEFAULT NULL, + `response_is_public` tinyint(1) NOT NULL DEFAULT 1, + `created_at` timestamp NULL DEFAULT NULL, + `updated_at` timestamp NULL DEFAULT NULL, + PRIMARY KEY (`id`), + KEY `release_reports_users_id_foreign` (`users_id`), + KEY `release_reports_reviewed_by_foreign` (`reviewed_by`), + KEY `release_reports_releases_id_status_index` (`releases_id`,`status`), + KEY `release_reports_status_created_at_index` (`status`,`created_at`), + KEY `release_reports_responded_by_foreign` (`responded_by`), + KEY `release_reports_response_lookup_idx` (`releases_id`,`response_is_public`,`responded_at`), + KEY `ix_release_reports_status_created_admin` (`status`,`created_at`), + CONSTRAINT `release_reports_releases_id_foreign` FOREIGN KEY (`releases_id`) REFERENCES `releases` (`id`) ON DELETE CASCADE, + CONSTRAINT `release_reports_responded_by_foreign` FOREIGN KEY (`responded_by`) REFERENCES `users` (`id`) ON DELETE SET NULL, + CONSTRAINT `release_reports_reviewed_by_foreign` FOREIGN KEY (`reviewed_by`) REFERENCES `users` (`id`) ON DELETE SET NULL, + CONSTRAINT `release_reports_users_id_foreign` FOREIGN KEY (`users_id`) REFERENCES `users` (`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC; +/*!40101 SET character_set_client = @saved_cs_client */; +DROP TABLE IF EXISTS `release_stats`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; +CREATE TABLE `release_stats` ( + `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, + `created_at` timestamp NULL DEFAULT NULL, + `updated_at` timestamp NULL DEFAULT NULL, + `category` varchar(255) NOT NULL, + `count` int(11) NOT NULL DEFAULT 0, + PRIMARY KEY (`id`), + KEY `release_stats_category_index` (`category`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC; +/*!40101 SET character_set_client = @saved_cs_client */; DROP TABLE IF EXISTS `release_subtitles`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; CREATE TABLE `release_subtitles` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `releases_id` int(10) unsigned NOT NULL COMMENT 'FK to releases.id', @@ -880,15 +1273,21 @@ CREATE TABLE `release_subtitles` ( PRIMARY KEY (`id`), UNIQUE KEY `ix_releasesubs_releases_id_subsid` (`releases_id`,`subsid`), CONSTRAINT `FK_rs_releases` FOREIGN KEY (`releases_id`) REFERENCES `releases` (`id`) ON DELETE CASCADE ON UPDATE CASCADE -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; DROP TABLE IF EXISTS `release_unique`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; CREATE TABLE `release_unique` ( `releases_id` int(10) unsigned NOT NULL COMMENT 'FK to releases.id.', `uniqueid` varchar(255) NOT NULL COMMENT 'Unique_ID from mediainfo.', PRIMARY KEY (`releases_id`,`uniqueid`), CONSTRAINT `FK_ru_releases` FOREIGN KEY (`releases_id`) REFERENCES `releases` (`id`) ON DELETE CASCADE ON UPDATE CASCADE -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; DROP TABLE IF EXISTS `releases`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; CREATE TABLE `releases` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `name` varchar(255) NOT NULL DEFAULT '', @@ -898,10 +1297,8 @@ CREATE TABLE `releases` ( `size` bigint(20) unsigned NOT NULL DEFAULT 0, `postdate` datetime DEFAULT NULL, `adddate` datetime DEFAULT NULL, - `updatetime` timestamp NOT NULL DEFAULT current_timestamp(), - `gid` varchar(32) DEFAULT NULL, - `guid` varchar(40) NOT NULL, - `leftguid` char(1) NOT NULL COMMENT 'The first letter of the release guid', + `guid` char(36) CHARACTER SET ascii COLLATE ascii_general_ci NOT NULL, + `leftguid` char(1) CHARACTER SET ascii COLLATE ascii_general_ci NOT NULL COMMENT 'The first letter of the release guid', `fromname` varchar(255) DEFAULT NULL, `completion` double NOT NULL DEFAULT 0, `categories_id` int(11) NOT NULL DEFAULT 10, @@ -923,13 +1320,12 @@ CREATE TABLE `releases` ( `nfostatus` tinyint(1) NOT NULL DEFAULT 0, `jpgstatus` tinyint(1) NOT NULL DEFAULT 0, `videostatus` tinyint(1) NOT NULL DEFAULT 0, - `audiostatus` tinyint(1) NOT NULL DEFAULT 0, - `reqidstatus` tinyint(1) NOT NULL DEFAULT 0, `nzbstatus` tinyint(1) NOT NULL DEFAULT 0, + `nzb_creation_claimed_at` timestamp NULL DEFAULT NULL, + `nzb_creation_claim_token` varchar(64) DEFAULT NULL, `iscategorized` tinyint(1) NOT NULL DEFAULT 0, `isrenamed` tinyint(1) NOT NULL DEFAULT 0, `proc_pp` tinyint(1) NOT NULL DEFAULT 0, - `proc_sorter` tinyint(1) NOT NULL DEFAULT 0, `proc_par2` tinyint(1) NOT NULL DEFAULT 0, `proc_nfo` tinyint(1) NOT NULL DEFAULT 0, `proc_files` tinyint(1) NOT NULL DEFAULT 0, @@ -937,37 +1333,51 @@ CREATE TABLE `releases` ( `proc_srr` tinyint(1) NOT NULL DEFAULT 0 COMMENT 'Has the release been srr\nprocessed', `proc_hash16k` tinyint(1) NOT NULL DEFAULT 0 COMMENT 'Has the release been hash16k\nprocessed', `proc_crc32` tinyint(1) NOT NULL DEFAULT 0 COMMENT 'Has the release been crc32 processed', - `nzb_guid` blob NOT NULL, - `source` smallint(5) unsigned DEFAULT NULL, - PRIMARY KEY (`id`,`categories_id`), + `pp_timeout_count` tinyint(4) NOT NULL DEFAULT 0 COMMENT 'Number of times this release timed out during additional post-processing', + `additional_pp_claimed_at` timestamp NULL DEFAULT NULL, + `additional_pp_claim_token` varchar(64) DEFAULT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `ux_releases_guid` (`guid`), KEY `ix_releases_groupsid` (`groups_id`,`passwordstatus`), - KEY `ix_releases_postdate_searchname` (`postdate`,`searchname`), KEY `ix_releases_leftguid` (`leftguid`,`predb_id`), KEY `ix_releases_musicinfo_id` (`musicinfo_id`,`passwordstatus`), - KEY `ix_releases_predb_id_searchname` (`predb_id`,`searchname`), - KEY `ix_releases_haspreview_passwordstatus` (`haspreview`,`passwordstatus`), KEY `ix_releases_nfostatus` (`nfostatus`,`size`), KEY `ix_releases_name` (`name`), - KEY `ix_releases_guid` (`guid`), - KEY `ix_releases_videos_id` (`videos_id`), KEY `ix_releases_tv_episodes_id` (`tv_episodes_id`), - KEY `ix_releases_imdbid` (`imdbid`), KEY `ix_releases_consoleinfo_id` (`consoleinfo_id`), KEY `ix_releases_gamesinfo_id` (`gamesinfo_id`), KEY `ix_releases_bookinfo_id` (`bookinfo_id`), KEY `ix_releases_anidbid` (`anidbid`), - KEY `ix_releases_movieinfo_id` (`movieinfo_id`), - KEY `ix_releases_passwordstatus` (`passwordstatus`), - KEY `ix_releases_nzb_guid` (`nzb_guid`(3072)) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci; + KEY `ix_releases_password_categories_postdate` (`passwordstatus`,`categories_id`,`postdate` DESC), + KEY `ix_releases_adddate` (`adddate`,`categories_id`), + KEY `ix_releases_grabs` (`grabs`,`categories_id`,`postdate`), + KEY `ix_releases_movieinfo_cat` (`movieinfo_id`,`categories_id`,`passwordstatus`,`postdate`), + KEY `ix_releases_videos_categories` (`videos_id`,`categories_id`), + KEY `ix_releases_imdbid_password_cat_postdate` (`imdbid`,`passwordstatus`,`categories_id`,`postdate` DESC), + KEY `ix_releases_searchname` (`searchname`), + KEY `ix_releases_categories_postdate_admin` (`categories_id`,`postdate`), + KEY `ix_releases_postdate_admin` (`postdate`), + KEY `ix_releases_adddate_id` (`adddate`,`id`), + KEY `ix_releases_predb_id` (`predb_id`), + KEY `ix_releases_size` (`size`), + KEY `ix_releases_add_pp_claim_queue` (`passwordstatus`,`haspreview`,`nzbstatus`,`leftguid`,`postdate` DESC,`id`,`additional_pp_claimed_at`), + KEY `ix_releases_nzb_creation_group_queue` (`nzbstatus`,`groups_id`,`postdate` DESC,`id`,`nzb_creation_claimed_at`), + KEY `ix_releases_nzb_creation_global_queue` (`nzbstatus`,`postdate` DESC,`id`,`nzb_creation_claimed_at`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; DROP TABLE IF EXISTS `releases_groups`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; CREATE TABLE `releases_groups` ( `releases_id` int(10) unsigned NOT NULL DEFAULT 0 COMMENT 'FK to releases.id', `groups_id` int(10) unsigned NOT NULL DEFAULT 0 COMMENT 'FK to groups.id', PRIMARY KEY (`releases_id`,`groups_id`), CONSTRAINT `FK_rg_releases` FOREIGN KEY (`releases_id`) REFERENCES `releases` (`id`) ON DELETE CASCADE ON UPDATE CASCADE ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; DROP TABLE IF EXISTS `role_expiration_emails`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; CREATE TABLE `role_expiration_emails` ( `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, `users_id` int(11) NOT NULL, @@ -979,7 +1389,10 @@ CREATE TABLE `role_expiration_emails` ( PRIMARY KEY (`id`), UNIQUE KEY `role_expiration_emails_users_id_unique` (`users_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci ROW_FORMAT=DYNAMIC; +/*!40101 SET character_set_client = @saved_cs_client */; DROP TABLE IF EXISTS `role_has_permissions`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; CREATE TABLE `role_has_permissions` ( `permission_id` int(10) unsigned NOT NULL, `role_id` int(10) unsigned NOT NULL, @@ -988,7 +1401,65 @@ CREATE TABLE `role_has_permissions` ( CONSTRAINT `role_has_permissions_permission_id_foreign` FOREIGN KEY (`permission_id`) REFERENCES `permissions` (`id`) ON DELETE CASCADE, CONSTRAINT `role_has_permissions_role_id_foreign` FOREIGN KEY (`role_id`) REFERENCES `roles` (`id`) ON DELETE CASCADE ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; +DROP TABLE IF EXISTS `role_promotion_stats`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; +CREATE TABLE `role_promotion_stats` ( + `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, + `user_id` int(10) unsigned NOT NULL, + `role_promotion_id` bigint(20) unsigned NOT NULL, + `role_id` int(10) unsigned NOT NULL, + `days_added` int(11) NOT NULL COMMENT 'Number of days added to role expiry', + `previous_expiry_date` datetime DEFAULT NULL COMMENT 'Previous role expiry date before promotion', + `new_expiry_date` datetime DEFAULT NULL COMMENT 'New role expiry date after promotion', + `applied_at` timestamp NOT NULL COMMENT 'When the promotion was applied', + `created_at` timestamp NULL DEFAULT NULL, + `updated_at` timestamp NULL DEFAULT NULL, + PRIMARY KEY (`id`), + KEY `role_promotion_stats_user_id_index` (`user_id`), + KEY `role_promotion_stats_role_promotion_id_index` (`role_promotion_id`), + KEY `role_promotion_stats_role_id_index` (`role_id`), + KEY `role_promotion_stats_applied_at_index` (`applied_at`), + CONSTRAINT `role_promotion_stats_role_id_foreign` FOREIGN KEY (`role_id`) REFERENCES `roles` (`id`) ON DELETE CASCADE, + CONSTRAINT `role_promotion_stats_role_promotion_id_foreign` FOREIGN KEY (`role_promotion_id`) REFERENCES `role_promotions` (`id`) ON DELETE CASCADE, + CONSTRAINT `role_promotion_stats_user_id_foreign` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC; +/*!40101 SET character_set_client = @saved_cs_client */; +DROP TABLE IF EXISTS `role_promotions`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; +CREATE TABLE `role_promotions` ( + `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, + `name` varchar(255) NOT NULL, + `description` text DEFAULT NULL, + `applicable_roles` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL COMMENT 'JSON array of role IDs this promotion applies to' CHECK (json_valid(`applicable_roles`)), + `additional_days` int(11) NOT NULL DEFAULT 0 COMMENT 'Additional days added to role expiry', + `start_date` date DEFAULT NULL COMMENT 'Promotion start date', + `end_date` date DEFAULT NULL COMMENT 'Promotion end date', + `is_active` tinyint(1) NOT NULL DEFAULT 1, + `created_at` timestamp NULL DEFAULT NULL, + `updated_at` timestamp NULL DEFAULT NULL, + PRIMARY KEY (`id`), + KEY `ix_role_promotions_is_active` (`is_active`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC; +/*!40101 SET character_set_client = @saved_cs_client */; +DROP TABLE IF EXISTS `role_stats`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; +CREATE TABLE `role_stats` ( + `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, + `created_at` timestamp NULL DEFAULT NULL, + `updated_at` timestamp NULL DEFAULT NULL, + `role` varchar(255) DEFAULT NULL, + `users` int(11) NOT NULL DEFAULT 0, + PRIMARY KEY (`id`), + KEY `ix_role_stats_created_at` (`created_at`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC; +/*!40101 SET character_set_client = @saved_cs_client */; DROP TABLE IF EXISTS `roles`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; CREATE TABLE `roles` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `name` varchar(255) NOT NULL, @@ -1003,8 +1474,11 @@ CREATE TABLE `roles` ( `created_at` timestamp NULL DEFAULT NULL, `updated_at` timestamp NULL DEFAULT NULL, PRIMARY KEY (`id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; DROP TABLE IF EXISTS `root_categories`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; CREATE TABLE `root_categories` ( `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, `title` varchar(255) NOT NULL, @@ -1014,14 +1488,106 @@ CREATE TABLE `root_categories` ( `updated_at` timestamp NULL DEFAULT NULL, PRIMARY KEY (`id`), KEY `ix_root_categories_status` (`status`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; +DROP TABLE IF EXISTS `search_index_failures`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; +CREATE TABLE `search_index_failures` ( + `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, + `release_id` bigint(20) unsigned NOT NULL, + `operation` varchar(32) NOT NULL DEFAULT 'upsert', + `attempts` int(10) unsigned NOT NULL DEFAULT 0, + `last_error` text DEFAULT NULL, + `next_attempt_at` timestamp NULL DEFAULT NULL, + `resolved_at` timestamp NULL DEFAULT NULL, + `created_at` timestamp NULL DEFAULT NULL, + `updated_at` timestamp NULL DEFAULT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `search_index_failures_release_id_unique` (`release_id`), + KEY `search_index_failures_next_attempt_at_index` (`next_attempt_at`), + KEY `search_index_failures_resolved_at_index` (`resolved_at`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC; +/*!40101 SET character_set_client = @saved_cs_client */; +DROP TABLE IF EXISTS `service_incident_service_status`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; +CREATE TABLE `service_incident_service_status` ( + `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, + `service_incident_id` bigint(20) unsigned NOT NULL, + `service_status_id` bigint(20) unsigned NOT NULL, + `created_at` timestamp NULL DEFAULT NULL, + `updated_at` timestamp NULL DEFAULT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `svc_incident_svc_pair_unique` (`service_incident_id`,`service_status_id`), + KEY `service_incident_service_status_service_status_id_index` (`service_status_id`), + CONSTRAINT `service_incident_service_status_service_incident_id_foreign` FOREIGN KEY (`service_incident_id`) REFERENCES `service_incidents` (`id`) ON DELETE CASCADE, + CONSTRAINT `service_incident_service_status_service_status_id_foreign` FOREIGN KEY (`service_status_id`) REFERENCES `service_statuses` (`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC; +/*!40101 SET character_set_client = @saved_cs_client */; +DROP TABLE IF EXISTS `service_incidents`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; +CREATE TABLE `service_incidents` ( + `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, + `title` varchar(255) NOT NULL, + `description` text NOT NULL, + `status` varchar(255) NOT NULL, + `impact` varchar(255) NOT NULL, + `started_at` timestamp NOT NULL, + `resolved_at` timestamp NULL DEFAULT NULL, + `created_by` int(10) unsigned DEFAULT NULL, + `is_auto` tinyint(1) NOT NULL DEFAULT 0, + `created_at` timestamp NULL DEFAULT NULL, + `updated_at` timestamp NULL DEFAULT NULL, + PRIMARY KEY (`id`), + KEY `service_incidents_status_index` (`status`), + KEY `service_incidents_impact_index` (`impact`), + KEY `service_incidents_started_at_index` (`started_at`), + KEY `service_incidents_resolved_at_index` (`resolved_at`), + KEY `service_incidents_status_started_at_index` (`status`,`started_at`), + KEY `service_incidents_created_by_index` (`created_by`), + KEY `service_incidents_is_auto_index` (`is_auto`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC; +/*!40101 SET character_set_client = @saved_cs_client */; +DROP TABLE IF EXISTS `service_statuses`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; +CREATE TABLE `service_statuses` ( + `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, + `name` varchar(255) NOT NULL, + `slug` varchar(255) NOT NULL, + `endpoint_url` varchar(255) DEFAULT NULL, + `check_type` varchar(255) NOT NULL DEFAULT 'http', + `probe_identifier` varchar(255) DEFAULT NULL, + `status` varchar(255) NOT NULL, + `last_checked_at` timestamp NULL DEFAULT NULL, + `uptime_percentage` decimal(5,2) NOT NULL DEFAULT 100.00, + `response_time_ms` int(10) unsigned DEFAULT NULL, + `is_enabled` tinyint(1) NOT NULL DEFAULT 1, + `sort_order` int(11) NOT NULL DEFAULT 0, + `created_at` timestamp NULL DEFAULT NULL, + `updated_at` timestamp NULL DEFAULT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `service_statuses_slug_unique` (`slug`), + KEY `service_statuses_status_index` (`status`), + KEY `service_statuses_is_enabled_sort_order_index` (`is_enabled`,`sort_order`), + KEY `service_statuses_check_type_index` (`check_type`), + KEY `service_statuses_probe_identifier_index` (`probe_identifier`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC; +/*!40101 SET character_set_client = @saved_cs_client */; DROP TABLE IF EXISTS `settings`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; CREATE TABLE `settings` ( `name` varchar(25) NOT NULL DEFAULT '', `value` varchar(1000) NOT NULL DEFAULT '', PRIMARY KEY (`name`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; DROP TABLE IF EXISTS `short_groups`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; CREATE TABLE `short_groups` ( `id` int(11) NOT NULL AUTO_INCREMENT, `name` varchar(255) NOT NULL DEFAULT '', @@ -1030,8 +1596,25 @@ CREATE TABLE `short_groups` ( `updated` datetime DEFAULT NULL, PRIMARY KEY (`id`), KEY `ix_shortgroups_name` (`name`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; +DROP TABLE IF EXISTS `signup_stats`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; +CREATE TABLE `signup_stats` ( + `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, + `created_at` timestamp NULL DEFAULT NULL, + `updated_at` timestamp NULL DEFAULT NULL, + `month` varchar(255) DEFAULT NULL, + `sort_date` date DEFAULT NULL, + `signups` int(11) NOT NULL DEFAULT 0, + PRIMARY KEY (`id`), + KEY `signup_stats_sort_date_index` (`sort_date`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC; +/*!40101 SET character_set_client = @saved_cs_client */; DROP TABLE IF EXISTS `steam_apps`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; CREATE TABLE `steam_apps` ( `name` varchar(255) NOT NULL DEFAULT '' COMMENT 'Steam application name', `appid` int(10) unsigned NOT NULL COMMENT 'Steam application id', @@ -1039,8 +1622,28 @@ CREATE TABLE `steam_apps` ( PRIMARY KEY (`id`), KEY `ix_name_appid` (`name`,`appid`), FULLTEXT KEY `ix_name_ft` (`name`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; +DROP TABLE IF EXISTS `system_metrics`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; +CREATE TABLE `system_metrics` ( + `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, + `metric_type` varchar(50) NOT NULL, + `value` decimal(8,2) NOT NULL, + `recorded_at` timestamp NOT NULL, + `created_at` timestamp NULL DEFAULT NULL, + `updated_at` timestamp NULL DEFAULT NULL, + PRIMARY KEY (`id`), + KEY `system_metrics_metric_type_recorded_at_index` (`metric_type`,`recorded_at`), + KEY `system_metrics_metric_type_index` (`metric_type`), + KEY `system_metrics_recorded_at_index` (`recorded_at`), + KEY `ix_system_metrics_type_recorded` (`metric_type`,`recorded_at`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC; +/*!40101 SET character_set_client = @saved_cs_client */; DROP TABLE IF EXISTS `telescope_entries`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; CREATE TABLE `telescope_entries` ( `sequence` bigint(20) unsigned NOT NULL AUTO_INCREMENT, `uuid` char(36) NOT NULL, @@ -1055,24 +1658,49 @@ CREATE TABLE `telescope_entries` ( KEY `telescope_entries_batch_id_index` (`batch_id`), KEY `telescope_entries_type_should_display_on_index_index` (`type`,`should_display_on_index`), KEY `telescope_entries_family_hash_index` (`family_hash`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci ROW_FORMAT=DYNAMIC; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC; +/*!40101 SET character_set_client = @saved_cs_client */; DROP TABLE IF EXISTS `telescope_entries_tags`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; CREATE TABLE `telescope_entries_tags` ( `entry_uuid` char(36) NOT NULL, `tag` varchar(255) NOT NULL, KEY `telescope_entries_tags_entry_uuid_tag_index` (`entry_uuid`,`tag`), KEY `telescope_entries_tags_tag_index` (`tag`), CONSTRAINT `telescope_entries_tags_entry_uuid_foreign` FOREIGN KEY (`entry_uuid`) REFERENCES `telescope_entries` (`uuid`) ON DELETE CASCADE -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci ROW_FORMAT=DYNAMIC; - +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC; +/*!40101 SET character_set_client = @saved_cs_client */; DROP TABLE IF EXISTS `telescope_monitoring`; - +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; CREATE TABLE `telescope_monitoring` ( `tag` varchar(255) NOT NULL -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci ROW_FORMAT=DYNAMIC; - +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC; +/*!40101 SET character_set_client = @saved_cs_client */; +DROP TABLE IF EXISTS `trusted_devices`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; +CREATE TABLE `trusted_devices` ( + `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, + `user_id` int(10) unsigned NOT NULL, + `token_hash` varchar(64) NOT NULL, + `expires_at` timestamp NOT NULL, + `last_used_at` timestamp NULL DEFAULT NULL, + `ip_address` varchar(45) DEFAULT NULL, + `user_agent` varchar(500) DEFAULT NULL, + `created_at` timestamp NULL DEFAULT NULL, + `updated_at` timestamp NULL DEFAULT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `trusted_devices_token_hash_unique` (`token_hash`), + KEY `trusted_devices_user_id_expires_at_index` (`user_id`,`expires_at`), + KEY `trusted_devices_expires_at_index` (`expires_at`), + CONSTRAINT `trusted_devices_user_id_foreign` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC; +/*!40101 SET character_set_client = @saved_cs_client */; DROP TABLE IF EXISTS `tv_episodes`; - +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; CREATE TABLE `tv_episodes` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `videos_id` int(10) unsigned NOT NULL COMMENT 'FK to videos.id of the parent series.', @@ -1081,25 +1709,27 @@ CREATE TABLE `tv_episodes` ( `se_complete` varchar(10) NOT NULL COMMENT 'String version of Series/Episode as taken from release subject (i.e. S02E21+22).', `title` varchar(180) NOT NULL COMMENT 'Title of the episode.', `firstaired` date DEFAULT NULL COMMENT 'Date of original airing/release.', - `summary` text NOT NULL COMMENT 'Description/summary of the episode.', + `summary` mediumtext NOT NULL COMMENT 'Description/summary of the episode.', PRIMARY KEY (`id`), UNIQUE KEY `videos_id` (`videos_id`,`series`,`episode`,`firstaired`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci; - +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; DROP TABLE IF EXISTS `tv_info`; - +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; CREATE TABLE `tv_info` ( `videos_id` int(10) unsigned NOT NULL DEFAULT 0 COMMENT 'FK to video.id', - `summary` text NOT NULL COMMENT 'Description/summary of the show.', + `summary` mediumtext NOT NULL COMMENT 'Description/summary of the show.', `publisher` varchar(50) NOT NULL COMMENT 'The channel/network of production/release (ABC, BBC, Showtime, etc.).', `localzone` varchar(50) NOT NULL DEFAULT '' COMMENT 'The linux tz style identifier', `image` tinyint(1) NOT NULL DEFAULT 0 COMMENT 'Does the video have a cover image?', PRIMARY KEY (`videos_id`), KEY `ix_tv_info_image` (`image`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci; - +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; DROP TABLE IF EXISTS `usenet_groups`; - +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; CREATE TABLE `usenet_groups` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `name` varchar(255) NOT NULL DEFAULT '', @@ -1116,11 +1746,62 @@ CREATE TABLE `usenet_groups` ( `description` varchar(255) DEFAULT '', PRIMARY KEY (`id`), UNIQUE KEY `ix_groups_name` (`name`), - KEY `active` (`active`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci; - + KEY `active` (`active`), + KEY `ix_usenet_groups_active_name_admin` (`active`,`name`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; +DROP TABLE IF EXISTS `user_activities`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; +CREATE TABLE `user_activities` ( + `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, + `user_id` bigint(20) unsigned DEFAULT NULL, + `username` varchar(255) NOT NULL, + `activity_type` varchar(50) NOT NULL, + `description` text NOT NULL, + `metadata` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL CHECK (json_valid(`metadata`)), + `is_permanent` tinyint(1) NOT NULL DEFAULT 0, + `created_at` timestamp NOT NULL DEFAULT current_timestamp(), + PRIMARY KEY (`id`), + KEY `user_activities_activity_type_created_at_index` (`activity_type`,`created_at`), + KEY `user_activities_created_at_index` (`created_at`), + KEY `ix_user_activities_type_permanent` (`activity_type`,`is_permanent`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC; +/*!40101 SET character_set_client = @saved_cs_client */; +DROP TABLE IF EXISTS `user_activity_stats`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; +CREATE TABLE `user_activity_stats` ( + `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, + `stat_date` date NOT NULL, + `downloads_count` int(11) NOT NULL DEFAULT 0, + `api_hits_count` int(11) NOT NULL DEFAULT 0, + `created_at` timestamp NULL DEFAULT NULL, + `updated_at` timestamp NULL DEFAULT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `user_activity_stats_stat_date_unique` (`stat_date`), + KEY `user_activity_stats_stat_date_index` (`stat_date`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC; +/*!40101 SET character_set_client = @saved_cs_client */; +DROP TABLE IF EXISTS `user_activity_stats_hourly`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; +CREATE TABLE `user_activity_stats_hourly` ( + `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, + `stat_hour` datetime NOT NULL, + `downloads_count` int(11) NOT NULL DEFAULT 0, + `api_hits_count` int(11) NOT NULL DEFAULT 0, + `created_at` timestamp NULL DEFAULT NULL, + `updated_at` timestamp NULL DEFAULT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `user_activity_stats_hourly_stat_hour_unique` (`stat_hour`), + KEY `user_activity_stats_hourly_stat_hour_index` (`stat_hour`), + KEY `ix_hourly_stats_hour` (`stat_hour`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC; +/*!40101 SET character_set_client = @saved_cs_client */; DROP TABLE IF EXISTS `user_downloads`; - +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; CREATE TABLE `user_downloads` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `users_id` int(10) unsigned NOT NULL, @@ -1130,11 +1811,31 @@ CREATE TABLE `user_downloads` ( PRIMARY KEY (`id`), KEY `userid` (`users_id`), KEY `timestamp` (`timestamp`), + KEY `ix_user_downloads_users_timestamp` (`users_id`,`timestamp`), + KEY `ix_user_downloads_releases_id` (`releases_id`), CONSTRAINT `FK_users_ud` FOREIGN KEY (`users_id`) REFERENCES `users` (`id`) ON DELETE CASCADE ON UPDATE CASCADE -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci; - +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; +DROP TABLE IF EXISTS `user_excluded_categories`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; +CREATE TABLE `user_excluded_categories` ( + `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, + `users_id` int(10) unsigned NOT NULL, + `categories_id` int(10) unsigned NOT NULL, + `created_at` timestamp NULL DEFAULT NULL, + `updated_at` timestamp NULL DEFAULT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `user_excluded_categories_users_id_categories_id_unique` (`users_id`,`categories_id`), + KEY `user_excluded_categories_categories_id_foreign` (`categories_id`), + KEY `user_excluded_categories_users_id_index` (`users_id`), + CONSTRAINT `user_excluded_categories_categories_id_foreign` FOREIGN KEY (`categories_id`) REFERENCES `categories` (`id`) ON DELETE CASCADE, + CONSTRAINT `user_excluded_categories_users_id_foreign` FOREIGN KEY (`users_id`) REFERENCES `users` (`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC; +/*!40101 SET character_set_client = @saved_cs_client */; DROP TABLE IF EXISTS `user_invitations`; - +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; CREATE TABLE `user_invitations` ( `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, `code` varchar(255) NOT NULL, @@ -1146,10 +1847,11 @@ CREATE TABLE `user_invitations` ( `updated_at` timestamp NULL DEFAULT NULL, PRIMARY KEY (`id`), KEY `user_invitations_code_index` (`code`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci ROW_FORMAT=DYNAMIC; - +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC; +/*!40101 SET character_set_client = @saved_cs_client */; DROP TABLE IF EXISTS `user_movies`; - +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; CREATE TABLE `user_movies` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `users_id` int(10) unsigned NOT NULL, @@ -1160,10 +1862,11 @@ CREATE TABLE `user_movies` ( PRIMARY KEY (`id`), KEY `ix_usermovies_userid` (`users_id`,`imdbid`), CONSTRAINT `FK_users_um` FOREIGN KEY (`users_id`) REFERENCES `users` (`id`) ON DELETE CASCADE ON UPDATE CASCADE -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci; - +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; DROP TABLE IF EXISTS `user_requests`; - +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; CREATE TABLE `user_requests` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `users_id` int(10) unsigned NOT NULL, @@ -1173,11 +1876,35 @@ CREATE TABLE `user_requests` ( PRIMARY KEY (`id`), KEY `userid` (`users_id`), KEY `timestamp` (`timestamp`), + KEY `ix_user_requests_users_timestamp` (`users_id`,`timestamp`), CONSTRAINT `FK_users_urq` FOREIGN KEY (`users_id`) REFERENCES `users` (`id`) ON DELETE CASCADE ON UPDATE CASCADE -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci; - +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; +DROP TABLE IF EXISTS `user_role_history`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; +CREATE TABLE `user_role_history` ( + `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, + `user_id` bigint(20) unsigned NOT NULL, + `old_role_id` int(11) DEFAULT NULL, + `new_role_id` int(11) NOT NULL, + `old_expiry_date` datetime DEFAULT NULL COMMENT 'Previous role expiry date', + `new_expiry_date` datetime DEFAULT NULL COMMENT 'New role expiry date', + `effective_date` datetime NOT NULL COMMENT 'When this role change became active', + `is_stacked` tinyint(1) NOT NULL DEFAULT 0 COMMENT 'Was this role stacked after previous expiry', + `change_reason` varchar(255) DEFAULT NULL COMMENT 'Reason for role change (upgrade, downgrade, expiry, admin, etc)', + `changed_by` bigint(20) unsigned DEFAULT NULL COMMENT 'Admin user ID who made the change', + `created_at` timestamp NULL DEFAULT NULL, + `updated_at` timestamp NULL DEFAULT NULL, + PRIMARY KEY (`id`), + KEY `user_role_history_user_id_index` (`user_id`), + KEY `ix_user_role_history_changed_by` (`changed_by`), + KEY `ix_user_role_history_roles` (`old_role_id`,`new_role_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC; +/*!40101 SET character_set_client = @saved_cs_client */; DROP TABLE IF EXISTS `user_series`; - +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; CREATE TABLE `user_series` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `users_id` int(10) unsigned NOT NULL, @@ -1188,10 +1915,11 @@ CREATE TABLE `user_series` ( PRIMARY KEY (`id`), KEY `ix_userseries_videos_id` (`users_id`,`videos_id`), CONSTRAINT `FK_users_us` FOREIGN KEY (`users_id`) REFERENCES `users` (`id`) ON DELETE CASCADE ON UPDATE CASCADE -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci; - +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; DROP TABLE IF EXISTS `users`; - +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; CREATE TABLE `users` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `username` varchar(50) NOT NULL, @@ -1208,6 +1936,7 @@ CREATE TABLE `users` ( `resetguid` varchar(50) DEFAULT NULL, `lastlogin` datetime DEFAULT NULL, `apiaccess` datetime DEFAULT NULL, + `lastdownload` datetime DEFAULT NULL, `invites` int(11) NOT NULL DEFAULT 0, `invitedby` int(11) DEFAULT NULL, `movieview` int(11) NOT NULL DEFAULT 1, @@ -1218,23 +1947,45 @@ CREATE TABLE `users` ( `gameview` int(11) NOT NULL DEFAULT 1, `rate_limit` int(11) NOT NULL DEFAULT 60, `notes` varchar(255) DEFAULT NULL, + `theme_preference` varchar(10) NOT NULL DEFAULT 'light', + `color_scheme` varchar(20) NOT NULL DEFAULT 'blue', `style` varchar(255) DEFAULT NULL, `rolechangedate` datetime DEFAULT NULL COMMENT 'When does the role expire', + `pending_role_start_date` datetime DEFAULT NULL COMMENT 'When the pending role change takes effect', + `pending_roles_id` int(11) DEFAULT NULL COMMENT 'The role that will be applied after current role expires', `next_rolechangedate` datetime DEFAULT NULL, `email_verified_at` timestamp NULL DEFAULT NULL, `remember_token` varchar(100) DEFAULT NULL, + `session_token` varchar(60) DEFAULT NULL, `timezone` varchar(255) DEFAULT NULL, + `movie_layout` tinyint(4) NOT NULL DEFAULT 2 COMMENT '1=1-column, 2=2-columns', `created_at` timestamp NULL DEFAULT NULL, `updated_at` timestamp NULL DEFAULT NULL, `verified` tinyint(1) NOT NULL DEFAULT 0, `verification_token` varchar(255) DEFAULT NULL, + `deleted_at` timestamp NULL DEFAULT NULL, + `deleted_by` varchar(255) DEFAULT NULL, + `bad_user` tinyint(1) NOT NULL DEFAULT 0, + `can_post` tinyint(1) NOT NULL DEFAULT 0, PRIMARY KEY (`id`), UNIQUE KEY `ux_users_api_token` (`api_token`), - KEY `ix_user_roles` (`roles_id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci; - + KEY `ix_user_roles` (`roles_id`), + KEY `ix_users_created_at` (`created_at`), + KEY `ix_users_roles_created` (`roles_id`,`created_at`), + KEY `ix_users_deleted_at` (`deleted_at`), + KEY `ix_users_username` (`username`), + KEY `ix_users_email` (`email`), + KEY `ix_users_host` (`host`), + KEY `ix_users_lastlogin` (`lastlogin`), + KEY `ix_users_apiaccess` (`apiaccess`), + KEY `ix_users_grabs` (`grabs`), + KEY `ix_users_rolechangedate` (`rolechangedate`), + KEY `ix_users_verified` (`verified`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; DROP TABLE IF EXISTS `users_releases`; - +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; CREATE TABLE `users_releases` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `users_id` int(10) unsigned NOT NULL, @@ -1247,9 +1998,10 @@ CREATE TABLE `users_releases` ( CONSTRAINT `FK_ur_releases` FOREIGN KEY (`releases_id`) REFERENCES `releases` (`id`) ON DELETE CASCADE ON UPDATE CASCADE, CONSTRAINT `FK_users_ur` FOREIGN KEY (`users_id`) REFERENCES `users` (`id`) ON DELETE CASCADE ON UPDATE CASCADE ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci; - +/*!40101 SET character_set_client = @saved_cs_client */; DROP TABLE IF EXISTS `video_data`; - +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; CREATE TABLE `video_data` ( `releases_id` int(10) unsigned NOT NULL COMMENT 'FK to releases.id', `containerformat` varchar(50) DEFAULT NULL, @@ -1264,10 +2016,11 @@ CREATE TABLE `video_data` ( `videolibrary` varchar(50) DEFAULT NULL, PRIMARY KEY (`releases_id`), CONSTRAINT `FK_vd_releases` FOREIGN KEY (`releases_id`) REFERENCES `releases` (`id`) ON DELETE CASCADE ON UPDATE CASCADE -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci; - +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; DROP TABLE IF EXISTS `videos`; - +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; CREATE TABLE `videos` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT 'Show ID to be used in other tables as reference ', `type` tinyint(1) NOT NULL DEFAULT 0 COMMENT '0 = TV, 1 = Film, 2 = Anime', @@ -1291,18 +2044,27 @@ CREATE TABLE `videos` ( KEY `ix_videos_tvdb` (`tvdb`), KEY `ix_videos_tvmaze` (`tvmaze`), KEY `ix_videos_tvrage` (`tvrage`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci; - +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; DROP TABLE IF EXISTS `videos_aliases`; - +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; CREATE TABLE `videos_aliases` ( `videos_id` int(10) unsigned NOT NULL COMMENT 'FK to videos.id of the parent title.', `title` varchar(180) NOT NULL COMMENT 'AKA of the video.', `created_at` timestamp NULL DEFAULT NULL, `updated_at` timestamp NULL DEFAULT NULL, PRIMARY KEY (`videos_id`,`title`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; +/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */; +/*!40101 SET SQL_MODE=@OLD_SQL_MODE */; +/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; +/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; +/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; + +/*M!999999\- enable the sandbox mode */ INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (1,'2014_01_16_195548_create_users_table',1); INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (2,'2014_02_01_311070_create_firewall_table',1); INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (3,'2017_11_29_223842_create_countries_table',1); @@ -1356,6 +2118,7 @@ INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (52,'2018_01_20_200 INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (53,'2018_01_20_200346_create_video_data_table',1); INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (54,'2018_01_20_200353_create_videos_table',1); INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (55,'2018_01_20_200403_create_videos_aliases_table',1); +INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (56,'2018_01_20_200417_create_xxxinfo_table',1); INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (58,'2018_04_24_132758_create_cache_table',1); INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (59,'2018_08_08_100000_create_telescope_entries_table',1); INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (60,'2018_09_13_070520_add_verification_to_user_table',1); @@ -1411,5 +2174,111 @@ INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (110,'2020_03_22_05 INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (111,'2020_03_22_055827_add_post_id_to_threads',5); INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (112,'2020_12_02_233754_add_first_post_id_to_threads',5); INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (113,'2021_07_31_094750_add_fk_indices',5); - -SET FOREIGN_KEY_CHECKS = 1; +INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (114,'2024_03_30_095622_update_forum_category_colors',6); +INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (115,'2024_04_27_124202_create_media_infos_table',6); +INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (116,'2024_05_12_192646_add_uuid_column_to_failed_jobs_table',6); +INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (117,'2024_05_25_122046_drop_stored_procedure',6); +INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (118,'2024_09_08_151127_create_grab_stats_table',6); +INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (119,'2024_09_08_151135_create_signup_stats_table',6); +INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (120,'2024_09_08_151158_create_role_stats_table',6); +INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (121,'2024_09_08_151214_create_download_stats_table',6); +INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (122,'2024_09_08_151223_create_release_stats_table',6); +INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (123,'2024_09_15_184830_add_xxx_vr_category',6); +INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (124,'2024_10_28_092803_drop_columns_from_settings_table',6); +INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (125,'2024_10_28_115551_rename_lookuptvrage_to_lookuptv',6); +INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (126,'2024_11_16_114640_add_invoice_status_to_payments_table',6); +INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (127,'2025_01_01_000000_create_invitations_table',6); +INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (128,'2025_01_30_115835_drop_triggers',6); +INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (129,'2025_04_10_202844_add_created_at_updated_at_columns',6); +INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (130,'2025_05_04_212955_drop_userseed',6); +INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (131,'2025_06_06_000000_add_soft_deletes_to_users_table',6); +INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (132,'2025_08_10_195505_fix_invitations_column_names',6); +INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (133,'2025_08_14_000001_add_indexes_to_collections_table',6); +INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (134,'2025_08_22_133811_drop_reqidstatus_column',6); +INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (135,'2025_08_28_000000_add_onlyfans_category_to_categories_table',6); +INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (136,'2025_10_16_101145_add_dark_mode_to_users_table',6); +INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (137,'2025_10_16_120000_update_dark_mode_to_theme_preference',6); +INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (138,'2025_10_20_132950_convert_content_table_to_utf8mb4',6); +INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (139,'2025_10_22_000000_create_system_metrics_table',6); +INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (140,'2025_10_22_204937_add_sort_date_to_signup_stats_table',6); +INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (141,'2025_10_23_094253_add_timezone_to_users_table',6); +INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (142,'2025_10_23_100000_add_movie_layout_to_users_table',6); +INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (143,'2025_10_24_000000_create_user_activity_stats_table',6); +INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (144,'2025_10_27_165328_create_user_activities_table',6); +INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (145,'2025_10_29_000000_create_user_activity_stats_hourly_table',6); +INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (146,'2025_11_05_095815_fix_release_files_collation',6); +INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (147,'2025_11_05_104146_fix_all_tables_collation_to_utf8mb4',6); +INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (148,'2025_11_05_120504_fix_telescope_tables_collation',6); +INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (149,'2025_11_28_000000_create_role_promotions_table',6); +INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (150,'2025_11_29_000000_create_role_promotion_stats_table',6); +INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (151,'2025_11_29_120000_add_role_stacking_fields_to_users_table',6); +INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (152,'2025_11_29_120001_create_user_role_history_table',6); +INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (153,'2025_12_04_000000_add_redis_args_to_settings_table',6); +INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (154,'2025_12_05_000000_replace_anidb_with_anilist',6); +INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (155,'2025_12_21_000000_add_categories_postdate_index_to_releases_table',6); +INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (156,'2025_12_21_000001_add_covering_index_for_tv_search',6); +INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (157,'2025_12_22_000000_add_additional_performance_indexes_to_releases_table',6); +INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (158,'2025_12_22_120000_add_composite_indexes_for_admin_performance',6); +INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (159,'2026_01_15_000000_fix_invitations_foreign_key',7); +INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (160,'2026_01_15_000001_cleanup_invitations_legacy_columns',8); +INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (161,'2026_01_23_150053_create_user_excluded_categories_table',9); +INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (162,'2026_01_26_105835_add_releases_categories_videos_index',10); +INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (163,'2019_10_03_112445_add_bad_user_to_users_table',11); +INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (164,'2024_03_14_201011_add_nzb_password_column_to_releases_table',11); +INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (165,'2024_08_13_232303_add_index_to_media_infos_table',11); +INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (166,'2024_12_20_113606_add_can_post_column_to_users_table',11); +INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (167,'2025_01_13_000000_create_nzb_backup_tables',11); +INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (168,'2025_11_07_164001__add_predb_scrape_setting',11); +INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (169,'2026_01_21_134835_create_api_token_ip_addresses_table',11); +INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (170,'2026_02_01_000000_create_release_reports_table',12); +INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (171,'2024_08_31_084308_add_content_approval_support',13); +INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (172,'2026_02_09_114731_add_deleted_by_to_users_table',14); +INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (173,'2026_02_11_000000_remove_ishashed_and_dehashstatus_columns',14); +INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (174,'2026_02_12_000000_add_covering_index_for_movie_browse',15); +INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (175,'2026_02_13_000000_drop_xxxinfo_and_releases_xxxinfo_id',16); +INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (176,'2026_02_19_000000_add_pp_timeout_count_to_releases_table',17); +INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (180,'2026_03_03_120000_add_color_scheme_to_users_table',18); +INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (181,'2026_03_10_000000_create_registration_periods_table',19); +INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (182,'2026_03_10_000001_create_registration_status_history_table',19); +INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (183,'2026_03_11_120000_add_lastdownload_to_users_table',19); +INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (184,'2026_03_18_000000_replace_vendor_countries_table',20); +INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (185,'2026_03_31_000000_add_missing_performance_indexes',21); +INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (186,'2026_04_01_000000_create_service_statuses_table',22); +INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (187,'2026_04_01_000001_create_service_incidents_table',22); +INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (188,'2026_04_01_120000_add_user_sort_column_indexes',22); +INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (189,'2026_04_01_120000_service_incidents_many_services',22); +INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (190,'2026_04_01_130000_add_endpoint_url_to_service_statuses',22); +INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (191,'2026_04_01_130001_add_is_auto_to_service_incidents',22); +INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (192,'2026_03_19_150944_add_depth_to_categories',23); +INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (193,'2026_04_02_170000_fix_add_depth_to_forum_categories',23); +INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (194,'2026_04_09_120000_normalize_padded_imdb_ids',23); +INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (195,'2026_04_13_000002_add_probe_fields_to_service_statuses_table',24); +INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (196,'2026_04_24_000000_create_passkeys_table',24); +INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (197,'2026_04_27_000000_add_is_permanent_to_user_activities',25); +INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (198,'2026_05_05_123242_add_cbp_query_indexes',25); +INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (199,'2026_05_05_124540_phase2_shrink_binaries_binaryhash',25); +INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (200,'2026_05_06_093900_restore_cbp_foreign_keys',25); +INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (201,'2026_05_08_154620_add_session_token_to_users_table',25); +INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (202,'2026_05_08_180000_drop_nzb_guid_columns',25); +INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (203,'2026_05_12_000000_add_ix_releases_searchname',25); +INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (204,'2026_06_08_000000_add_response_fields_to_release_reports_table',26); +INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (205,'2026_06_10_000000_create_trusted_devices_table',26); +INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (206,'2026_06_11_000000_create_password_reset_tokens_table',26); +INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (207,'2026_06_11_000001_add_resetguid_to_users_table',26); +INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (208,'2026_06_17_000000_create_gdpr_requests_table',27); +INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (209,'2026_06_17_000001_create_gdpr_consents_table',27); +INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (210,'2026_06_17_000002_create_gdpr_audit_logs_table',27); +INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (211,'2026_06_17_000000_update_rss_service_status_endpoint_to_health',28); +INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (212,'2026_06_17_000001_neutralize_rss_http_400_false_positive_incidents',28); +INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (213,'2026_07_12_000000_add_additional_postprocessing_claim_fields',29); +INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (214,'2026_07_12_000001_add_release_creation_claim_fields_to_collections_table',30); +INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (215,'2026_07_12_000002_add_release_creation_failure_fields_to_collections_table',31); +INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (216,'2026_07_13_000000_add_admin_list_performance_indexes',32); +INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (217,'2026_07_13_000000_add_nzb_creation_claim_fields',32); +INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (218,'2026_07_15_000000_add_releases_adddate_id_index',33); +INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (219,'2026_08_02_000000_create_search_index_failures_table',34); +INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (220,'2026_08_03_000000_prepare_cbp_optimized_storage',35); +INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (221,'2026_08_03_000001_finalize_cbp_binary_hash_storage',36); +INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (222,'2026_08_04_082439_add_fix_release_name_query_indexes',37); +INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (223,'2026_08_10_192527_convert_size_settings_to_bytes',38); +INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (224,'2026_08_13_001652_normalize_and_optimize_releases_table',39); diff --git a/database/schema/mysql-schema.sql b/database/schema/mysql-schema.sql index c32dcd4dc..041702d56 100644 --- a/database/schema/mysql-schema.sql +++ b/database/schema/mysql-schema.sql @@ -1,7 +1,13 @@ -SET FOREIGN_KEY_CHECKS = 0; - +/*M!999999\- enable the sandbox mode */ +/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */; +/*!40103 SET TIME_ZONE='+00:00' */; +/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */; +/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; +/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; +/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; DROP TABLE IF EXISTS `anidb_info`; - +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; CREATE TABLE `anidb_info` ( `anidbid` int(10) unsigned NOT NULL COMMENT 'ID of title from AniDB', `anilist_id` int(10) unsigned DEFAULT NULL COMMENT 'ID from AniList', @@ -20,7 +26,7 @@ CREATE TABLE `anidb_info` ( `related` varchar(1024) DEFAULT NULL, `similar` varchar(1024) DEFAULT NULL, `creators` varchar(1024) DEFAULT NULL, - `description` text DEFAULT NULL, + `description` mediumtext DEFAULT NULL, `rating` varchar(5) DEFAULT NULL, `picture` varchar(255) DEFAULT NULL, `categories` varchar(1024) DEFAULT NULL, @@ -30,20 +36,46 @@ CREATE TABLE `anidb_info` ( KEY `ix_anidb_info_anilist_id` (`anilist_id`), KEY `ix_anidb_info_mal_id` (`mal_id`), KEY `ix_anidb_info_country` (`country`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci; - +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; DROP TABLE IF EXISTS `anidb_titles`; - +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; CREATE TABLE `anidb_titles` ( `anidbid` int(10) unsigned NOT NULL COMMENT 'ID of title from AniDB', `type` varchar(25) NOT NULL COMMENT 'type of title.', `lang` varchar(25) NOT NULL, `title` varchar(255) NOT NULL, PRIMARY KEY (`anidbid`,`type`,`lang`,`title`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci; - +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; +DROP TABLE IF EXISTS `api_token_ip_addresses`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; +CREATE TABLE `api_token_ip_addresses` ( + `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, + `users_id` int(10) unsigned NOT NULL, + `api_token` varchar(64) NOT NULL, + `ip_address` varchar(45) NOT NULL, + `first_seen_at` timestamp NOT NULL DEFAULT current_timestamp(), + `last_seen_at` timestamp NOT NULL DEFAULT current_timestamp(), + `request_count` int(10) unsigned NOT NULL DEFAULT 1, + `is_rate_limited` tinyint(1) NOT NULL DEFAULT 0, + `rate_limited_until` timestamp NULL DEFAULT NULL, + `created_at` timestamp NULL DEFAULT NULL, + `updated_at` timestamp NULL DEFAULT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `api_token_ip_addresses_api_token_ip_address_unique` (`api_token`,`ip_address`), + KEY `api_token_ip_addresses_users_id_index` (`users_id`), + KEY `api_token_ip_addresses_is_rate_limited_index` (`is_rate_limited`), + KEY `api_token_ip_addresses_last_seen_at_index` (`last_seen_at`), + KEY `api_token_ip_addresses_api_token_index` (`api_token`), + CONSTRAINT `api_token_ip_addresses_users_id_foreign` FOREIGN KEY (`users_id`) REFERENCES `users` (`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC; +/*!40101 SET character_set_client = @saved_cs_client */; DROP TABLE IF EXISTS `audio_data`; - +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; CREATE TABLE `audio_data` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `releases_id` int(10) unsigned NOT NULL COMMENT 'FK to releases.id', @@ -60,10 +92,11 @@ CREATE TABLE `audio_data` ( PRIMARY KEY (`id`), UNIQUE KEY `ix_releaseaudio_releaseid_audioid` (`releases_id`,`audioid`), CONSTRAINT `FK_ad_releases` FOREIGN KEY (`releases_id`) REFERENCES `releases` (`id`) ON DELETE CASCADE ON UPDATE CASCADE -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci; - +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; DROP TABLE IF EXISTS `binaries`; - +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; CREATE TABLE `binaries` ( `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, `binaryhash` binary(16) NOT NULL, @@ -76,14 +109,15 @@ CREATE TABLE `binaries` ( `partsize` bigint(20) unsigned NOT NULL DEFAULT 0, PRIMARY KEY (`id`), UNIQUE KEY `ux_binaries_collection_hash` (`collections_id`,`binaryhash`), - KEY `ix_binaries_collection_filenumber` (`collections_id`,`filenumber`), KEY `ix_binaries_collection` (`collections_id`), KEY `ix_binaries_partcheck` (`partcheck`), + KEY `ix_binaries_collection_filenumber` (`collections_id`,`filenumber`), CONSTRAINT `FK_Collections` FOREIGN KEY (`collections_id`) REFERENCES `collections` (`id`) ON DELETE CASCADE ON UPDATE CASCADE -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci; - +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; DROP TABLE IF EXISTS `binaryblacklist`; - +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; CREATE TABLE `binaryblacklist` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `groupname` varchar(255) DEFAULT NULL, @@ -96,10 +130,11 @@ CREATE TABLE `binaryblacklist` ( PRIMARY KEY (`id`), KEY `ix_binaryblacklist_groupname` (`groupname`), KEY `ix_binaryblacklist_status` (`status`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci; - +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; DROP TABLE IF EXISTS `bookinfo`; - +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; CREATE TABLE `bookinfo` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `title` varchar(255) NOT NULL, @@ -120,19 +155,21 @@ CREATE TABLE `bookinfo` ( PRIMARY KEY (`id`), UNIQUE KEY `ix_bookinfo_asin` (`asin`), FULLTEXT KEY `ix_bookinfo_author_title_ft` (`author`,`title`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci; - +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; DROP TABLE IF EXISTS `cache`; - +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; CREATE TABLE `cache` ( `key` varchar(255) NOT NULL, - `value` mediumtext NOT NULL, + `value` longtext NOT NULL, `expiration` int(11) NOT NULL, UNIQUE KEY `cache_key_unique` (`key`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci ROW_FORMAT=DYNAMIC; - +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC; +/*!40101 SET character_set_client = @saved_cs_client */; DROP TABLE IF EXISTS `categories`; - +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; CREATE TABLE `categories` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `title` varchar(255) NOT NULL, @@ -146,10 +183,11 @@ CREATE TABLE `categories` ( KEY `ix_categories_parentid` (`root_categories_id`), KEY `ix_categories_status` (`status`), CONSTRAINT `fk_root_categories_id` FOREIGN KEY (`root_categories_id`) REFERENCES `root_categories` (`id`) ON DELETE CASCADE ON UPDATE CASCADE -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci; - +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; DROP TABLE IF EXISTS `category_regexes`; - +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; CREATE TABLE `category_regexes` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `group_regex` varchar(255) NOT NULL DEFAULT '' COMMENT 'This is a regex to match against usenet groups', @@ -163,10 +201,30 @@ CREATE TABLE `category_regexes` ( KEY `ix_category_regexes_status` (`status`), KEY `ix_category_regexes_ordinal` (`ordinal`), KEY `ix_category_regexes_categories_id` (`categories_id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci; - +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; +DROP TABLE IF EXISTS `cbp_optimization_checkpoints`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; +CREATE TABLE `cbp_optimization_checkpoints` ( + `step_name` varchar(64) CHARACTER SET ascii COLLATE ascii_bin NOT NULL, + `completed_at` datetime NOT NULL, + PRIMARY KEY (`step_name`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_uca1400_ai_ci; +/*!40101 SET character_set_client = @saved_cs_client */; +DROP TABLE IF EXISTS `collection_groups`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; +CREATE TABLE `collection_groups` ( + `collections_id` int(10) unsigned NOT NULL, + `group_name` varchar(255) NOT NULL, + PRIMARY KEY (`collections_id`,`group_name`), + CONSTRAINT `fk_collection_groups_collection` FOREIGN KEY (`collections_id`) REFERENCES `collections` (`id`) ON DELETE CASCADE ON UPDATE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC; +/*!40101 SET character_set_client = @saved_cs_client */; DROP TABLE IF EXISTS `collection_regexes`; - +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; CREATE TABLE `collection_regexes` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `group_regex` varchar(255) NOT NULL DEFAULT '' COMMENT 'This is a regex to match against usenet groups', @@ -178,10 +236,11 @@ CREATE TABLE `collection_regexes` ( KEY `ix_collection_regexes_group_regex` (`group_regex`), KEY `ix_collection_regexes_status` (`status`), KEY `ix_collection_regexes_ordinal` (`ordinal`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci; - +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; DROP TABLE IF EXISTS `collections`; - +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; CREATE TABLE `collections` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `subject` varchar(255) NOT NULL DEFAULT '', @@ -198,6 +257,11 @@ CREATE TABLE `collections` ( `filecheck` tinyint(1) NOT NULL DEFAULT 0, `filesize` bigint(20) unsigned NOT NULL DEFAULT 0, `releases_id` int(11) DEFAULT NULL, + `release_creation_claimed_at` timestamp NULL DEFAULT NULL, + `release_creation_claim_token` varchar(64) DEFAULT NULL, + `release_creation_failures` smallint(5) unsigned NOT NULL DEFAULT 0, + `release_creation_failed_at` timestamp NULL DEFAULT NULL, + `release_creation_error` varchar(512) DEFAULT NULL, `noise` char(32) NOT NULL DEFAULT '', PRIMARY KEY (`id`), UNIQUE KEY `ix_collection_collectionhash` (`collectionhash`), @@ -207,20 +271,16 @@ CREATE TABLE `collections` ( KEY `ix_collection_dateadded` (`dateadded`), KEY `ix_collection_filecheck` (`filecheck`), KEY `ix_collection_releaseid` (`releases_id`), + KEY `collections_groups_added_idx` (`groups_id`,`added`), + KEY `collections_dateadded_idx` (`dateadded`), + KEY `ix_collections_filecheck_filesize_groups` (`filecheck`,`filesize`,`groups_id`), + KEY `ix_collections_release_creation_claim_queue` (`filecheck`,`groups_id`,`release_creation_claimed_at`,`id`), KEY `ix_collections_group_filecheck_seen_id` (`groups_id`,`filecheck`,`last_seen_at`,`id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci; - -DROP TABLE IF EXISTS `collection_groups`; - -CREATE TABLE `collection_groups` ( - `collections_id` int(10) unsigned NOT NULL, - `group_name` varchar(255) NOT NULL, - PRIMARY KEY (`collections_id`,`group_name`), - CONSTRAINT `fk_collection_groups_collection` FOREIGN KEY (`collections_id`) REFERENCES `collections` (`id`) ON DELETE CASCADE ON UPDATE CASCADE -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci; - +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; DROP TABLE IF EXISTS `consoleinfo`; - +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; CREATE TABLE `consoleinfo` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `title` varchar(255) NOT NULL, @@ -238,52 +298,46 @@ CREATE TABLE `consoleinfo` ( `updated_at` timestamp NULL DEFAULT NULL, PRIMARY KEY (`id`), UNIQUE KEY `ix_consoleinfo_asin` (`asin`), + KEY `ix_consoleinfo_genres_id` (`genres_id`), FULLTEXT KEY `ix_consoleinfo_title_platform_ft` (`title`,`platform`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci; - +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; DROP TABLE IF EXISTS `content`; - +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; CREATE TABLE `content` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `title` varchar(255) NOT NULL, `url` varchar(2000) DEFAULT NULL, - `body` text DEFAULT NULL, + `body` mediumtext DEFAULT NULL, `metadescription` varchar(1000) NOT NULL, `metakeywords` varchar(1000) NOT NULL, `contenttype` int(11) NOT NULL, `status` int(11) NOT NULL, `ordinal` int(11) DEFAULT NULL, `role` int(11) NOT NULL DEFAULT 0, + `created_at` timestamp NULL DEFAULT NULL, + `updated_at` timestamp NULL DEFAULT NULL, PRIMARY KEY (`id`), - KEY `ix_status_contenttype_role` (`status`,`contenttype`,`role`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci; - + KEY `ix_status_contenttype_role` (`status`,`contenttype`,`role`), + KEY `ix_content_type_ordinal` (`contenttype`,`ordinal`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; DROP TABLE IF EXISTS `countries`; - +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; CREATE TABLE `countries` ( - `id` int(10) unsigned NOT NULL AUTO_INCREMENT, - `capital` varchar(255) DEFAULT NULL, - `citizenship` varchar(255) DEFAULT NULL, - `country_code` char(3) NOT NULL DEFAULT '', - `currency` varchar(255) DEFAULT NULL, - `currency_code` varchar(255) DEFAULT NULL, - `currency_sub_unit` varchar(255) DEFAULT NULL, - `currency_symbol` varchar(3) DEFAULT NULL, - `currency_decimals` int(11) DEFAULT NULL, + `iso_3166_2` char(2) NOT NULL, + `name` varchar(255) NOT NULL, `full_name` varchar(255) DEFAULT NULL, - `iso_3166_2` char(2) NOT NULL DEFAULT '', - `iso_3166_3` char(3) NOT NULL DEFAULT '', - `name` varchar(255) NOT NULL DEFAULT '', - `region_code` char(3) NOT NULL DEFAULT '', - `sub_region_code` char(3) NOT NULL DEFAULT '', - `eea` tinyint(1) NOT NULL DEFAULT 0, - `calling_code` varchar(3) DEFAULT NULL, - `flag` varchar(6) DEFAULT NULL, - PRIMARY KEY (`id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci ROW_FORMAT=DYNAMIC; - + PRIMARY KEY (`iso_3166_2`), + KEY `countries_name_index` (`name`), + KEY `countries_full_name_index` (`full_name`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC; +/*!40101 SET character_set_client = @saved_cs_client */; DROP TABLE IF EXISTS `dnzb_failures`; - +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; CREATE TABLE `dnzb_failures` ( `release_id` int(10) unsigned NOT NULL, `users_id` int(10) unsigned NOT NULL, @@ -293,21 +347,40 @@ CREATE TABLE `dnzb_failures` ( CONSTRAINT `FK_df_releases` FOREIGN KEY (`release_id`) REFERENCES `releases` (`id`) ON DELETE CASCADE ON UPDATE CASCADE, CONSTRAINT `FK_users_df` FOREIGN KEY (`users_id`) REFERENCES `users` (`id`) ON DELETE CASCADE ON UPDATE CASCADE ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci; - +/*!40101 SET character_set_client = @saved_cs_client */; +DROP TABLE IF EXISTS `download_stats`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; +CREATE TABLE `download_stats` ( + `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, + `created_at` timestamp NULL DEFAULT NULL, + `updated_at` timestamp NULL DEFAULT NULL, + `searchname` varchar(255) DEFAULT NULL, + `grabs` int(11) NOT NULL DEFAULT 0, + `guid` varchar(255) DEFAULT NULL, + `adddate` datetime DEFAULT NULL, + PRIMARY KEY (`id`), + KEY `ix_download_stats_created_at` (`created_at`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC; +/*!40101 SET character_set_client = @saved_cs_client */; DROP TABLE IF EXISTS `failed_jobs`; - +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; CREATE TABLE `failed_jobs` ( `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, - `connection` text NOT NULL, - `queue` text NOT NULL, + `connection` mediumtext NOT NULL, + `queue` mediumtext NOT NULL, `payload` longtext NOT NULL, `exception` longtext NOT NULL, `failed_at` timestamp NOT NULL DEFAULT current_timestamp(), - PRIMARY KEY (`id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci ROW_FORMAT=DYNAMIC; - + `uuid` varchar(255) NOT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `failed_jobs_uuid_unique` (`uuid`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC; +/*!40101 SET character_set_client = @saved_cs_client */; DROP TABLE IF EXISTS `firewall`; - +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; CREATE TABLE `firewall` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `ip_address` varchar(39) NOT NULL, @@ -316,10 +389,11 @@ CREATE TABLE `firewall` ( `updated_at` timestamp NULL DEFAULT NULL, PRIMARY KEY (`id`), UNIQUE KEY `firewall_ip_address_unique` (`ip_address`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci ROW_FORMAT=DYNAMIC; - +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC; +/*!40101 SET character_set_client = @saved_cs_client */; DROP TABLE IF EXISTS `forum_categories`; - +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; CREATE TABLE `forum_categories` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `title` varchar(255) NOT NULL, @@ -330,34 +404,41 @@ CREATE TABLE `forum_categories` ( `thread_count` int(11) NOT NULL DEFAULT 0, `post_count` int(11) NOT NULL DEFAULT 0, `is_private` tinyint(1) NOT NULL DEFAULT 0, + `thread_approval_enabled` tinyint(1) NOT NULL DEFAULT 0, + `post_approval_enabled` tinyint(1) NOT NULL DEFAULT 0, `created_at` timestamp NULL DEFAULT NULL, `updated_at` timestamp NULL DEFAULT NULL, `_lft` int(10) unsigned NOT NULL DEFAULT 0, `_rgt` int(10) unsigned NOT NULL DEFAULT 0, `parent_id` int(10) unsigned DEFAULT NULL, - `color` varchar(255) DEFAULT NULL, + `color_light_mode` varchar(255) DEFAULT NULL, + `color_dark_mode` varchar(255) DEFAULT NULL, + `depth` smallint(6) NOT NULL DEFAULT 0, PRIMARY KEY (`id`), KEY `forum_categories__lft__rgt_parent_id_index` (`_lft`,`_rgt`,`parent_id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci ROW_FORMAT=DYNAMIC; - +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC; +/*!40101 SET character_set_client = @saved_cs_client */; DROP TABLE IF EXISTS `forum_posts`; - +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; CREATE TABLE `forum_posts` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `thread_id` int(10) unsigned NOT NULL, `author_id` bigint(20) unsigned NOT NULL, - `content` text NOT NULL, + `content` mediumtext NOT NULL, `post_id` int(10) unsigned DEFAULT NULL, `sequence` int(10) unsigned NOT NULL DEFAULT 0, `created_at` timestamp NULL DEFAULT NULL, `updated_at` timestamp NULL DEFAULT NULL, + `approved_at` timestamp NULL DEFAULT NULL, `deleted_at` timestamp NULL DEFAULT NULL, PRIMARY KEY (`id`), KEY `forum_posts_thread_id_index` (`thread_id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci ROW_FORMAT=DYNAMIC; - +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC; +/*!40101 SET character_set_client = @saved_cs_client */; DROP TABLE IF EXISTS `forum_threads`; - +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; CREATE TABLE `forum_threads` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `category_id` int(10) unsigned NOT NULL, @@ -370,29 +451,34 @@ CREATE TABLE `forum_threads` ( `reply_count` int(11) NOT NULL DEFAULT 0, `created_at` timestamp NULL DEFAULT NULL, `updated_at` timestamp NULL DEFAULT NULL, + `approved_at` timestamp NULL DEFAULT NULL, `deleted_at` timestamp NULL DEFAULT NULL, PRIMARY KEY (`id`), - KEY `forum_threads_category_id_index` (`category_id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci ROW_FORMAT=DYNAMIC; - + KEY `forum_threads_category_id_index` (`category_id`), + KEY `ix_forum_threads_author_id` (`author_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC; +/*!40101 SET character_set_client = @saved_cs_client */; DROP TABLE IF EXISTS `forum_threads_read`; - +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; CREATE TABLE `forum_threads_read` ( `thread_id` int(10) unsigned NOT NULL, `user_id` bigint(20) unsigned NOT NULL, `created_at` timestamp NULL DEFAULT NULL, - `updated_at` timestamp NULL DEFAULT NULL + `updated_at` timestamp NULL DEFAULT NULL, + KEY `ix_forum_threads_read_user_thread` (`user_id`,`thread_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci ROW_FORMAT=DYNAMIC; - +/*!40101 SET character_set_client = @saved_cs_client */; DROP TABLE IF EXISTS `forumpost`; - +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; CREATE TABLE `forumpost` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `forumid` int(11) NOT NULL DEFAULT 1, `parentid` int(11) NOT NULL DEFAULT 0, `users_id` int(10) unsigned NOT NULL, `subject` varchar(255) NOT NULL, - `message` text NOT NULL, + `message` mediumtext NOT NULL, `locked` tinyint(1) NOT NULL DEFAULT 0, `sticky` tinyint(1) NOT NULL DEFAULT 0, `replies` int(10) unsigned NOT NULL DEFAULT 0, @@ -402,10 +488,11 @@ CREATE TABLE `forumpost` ( KEY `parentid` (`parentid`), KEY `userid` (`users_id`), CONSTRAINT `FK_users_fp` FOREIGN KEY (`users_id`) REFERENCES `users` (`id`) ON DELETE CASCADE ON UPDATE CASCADE -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci; - +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; DROP TABLE IF EXISTS `gamesinfo`; - +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; CREATE TABLE `gamesinfo` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `title` varchar(255) NOT NULL, @@ -424,34 +511,129 @@ CREATE TABLE `gamesinfo` ( `updated_at` timestamp NULL DEFAULT NULL, PRIMARY KEY (`id`), UNIQUE KEY `ix_gamesinfo_asin` (`asin`), + KEY `ix_gamesinfo_genres_id` (`genres_id`), FULLTEXT KEY `ix_title_ft` (`title`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci; - +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; +DROP TABLE IF EXISTS `gdpr_audit_logs`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; +CREATE TABLE `gdpr_audit_logs` ( + `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, + `gdpr_request_id` bigint(20) unsigned DEFAULT NULL, + `user_id` bigint(20) unsigned DEFAULT NULL, + `actor_id` bigint(20) unsigned DEFAULT NULL, + `event` varchar(64) NOT NULL, + `description` text NOT NULL, + `metadata` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL CHECK (json_valid(`metadata`)), + `created_at` timestamp NOT NULL DEFAULT current_timestamp(), + PRIMARY KEY (`id`), + KEY `ix_gdpr_audit_request_created` (`gdpr_request_id`,`created_at`), + KEY `ix_gdpr_audit_user_created` (`user_id`,`created_at`), + KEY `ix_gdpr_audit_event_created` (`event`,`created_at`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC; +/*!40101 SET character_set_client = @saved_cs_client */; +DROP TABLE IF EXISTS `gdpr_consents`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; +CREATE TABLE `gdpr_consents` ( + `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, + `user_id` bigint(20) unsigned DEFAULT NULL, + `consent_type` varchar(64) NOT NULL, + `status` varchar(32) NOT NULL DEFAULT 'granted', + `policy_version` varchar(64) DEFAULT NULL, + `consented_at` timestamp NULL DEFAULT NULL, + `withdrawn_at` timestamp NULL DEFAULT NULL, + `ip_address` varchar(45) DEFAULT NULL, + `user_agent_hash` varchar(64) DEFAULT NULL, + `metadata` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL CHECK (json_valid(`metadata`)), + `created_at` timestamp NULL DEFAULT NULL, + `updated_at` timestamp NULL DEFAULT NULL, + PRIMARY KEY (`id`), + KEY `ix_gdpr_consents_user_type` (`user_id`,`consent_type`), + KEY `ix_gdpr_consents_type_status` (`consent_type`,`status`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC; +/*!40101 SET character_set_client = @saved_cs_client */; +DROP TABLE IF EXISTS `gdpr_requests`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; +CREATE TABLE `gdpr_requests` ( + `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, + `user_id` bigint(20) unsigned DEFAULT NULL, + `requester_username` varchar(255) DEFAULT NULL, + `requester_email` varchar(255) DEFAULT NULL, + `type` varchar(32) NOT NULL, + `status` varchar(32) NOT NULL DEFAULT 'pending', + `request_payload` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL CHECK (json_valid(`request_payload`)), + `response_payload` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL CHECK (json_valid(`response_payload`)), + `notes` text DEFAULT NULL, + `admin_notes` text DEFAULT NULL, + `export_disk` varchar(255) DEFAULT NULL, + `export_path` varchar(255) DEFAULT NULL, + `export_expires_at` timestamp NULL DEFAULT NULL, + `processed_by` bigint(20) unsigned DEFAULT NULL, + `completed_at` timestamp NULL DEFAULT NULL, + `created_at` timestamp NULL DEFAULT NULL, + `updated_at` timestamp NULL DEFAULT NULL, + PRIMARY KEY (`id`), + KEY `ix_gdpr_requests_user_type_status` (`user_id`,`type`,`status`), + KEY `ix_gdpr_requests_status_created` (`status`,`created_at`), + KEY `ix_gdpr_requests_type_created` (`type`,`created_at`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC; +/*!40101 SET character_set_client = @saved_cs_client */; DROP TABLE IF EXISTS `genres`; - +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; CREATE TABLE `genres` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `title` varchar(255) NOT NULL, `type` int(11) DEFAULT NULL, `disabled` tinyint(1) NOT NULL DEFAULT 0, - PRIMARY KEY (`id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci; - + PRIMARY KEY (`id`), + KEY `ix_genres_type_disabled` (`type`,`disabled`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; +DROP TABLE IF EXISTS `grab_stats`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; +CREATE TABLE `grab_stats` ( + `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, + `created_at` timestamp NULL DEFAULT NULL, + `updated_at` timestamp NULL DEFAULT NULL, + `username` varchar(255) DEFAULT NULL, + `grabs` int(11) NOT NULL DEFAULT 0, + PRIMARY KEY (`id`), + KEY `ix_grab_stats_created_at` (`created_at`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC; +/*!40101 SET character_set_client = @saved_cs_client */; DROP TABLE IF EXISTS `invitations`; - +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; CREATE TABLE `invitations` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, - `guid` varchar(50) NOT NULL, - `users_id` int(10) unsigned NOT NULL, + `token` varchar(64) NOT NULL, + `email` varchar(255) NOT NULL, + `invited_by` bigint(20) unsigned NOT NULL, + `expires_at` timestamp NOT NULL, + `used_at` timestamp NULL DEFAULT NULL, + `used_by` bigint(20) unsigned DEFAULT NULL, + `is_active` tinyint(1) NOT NULL DEFAULT 1, + `metadata` longtext DEFAULT NULL CHECK (json_valid(`metadata`)), `created_at` timestamp NULL DEFAULT NULL, `updated_at` timestamp NULL DEFAULT NULL, PRIMARY KEY (`id`), - KEY `FK_users_inv` (`users_id`), - CONSTRAINT `FK_users_inv` FOREIGN KEY (`users_id`) REFERENCES `users` (`id`) ON DELETE CASCADE ON UPDATE CASCADE -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci; - + UNIQUE KEY `invitations_token_unique` (`token`), + KEY `invitations_invited_by_index` (`invited_by`), + KEY `invitations_used_by_index` (`used_by`), + KEY `invitations_token_is_active_index` (`token`,`is_active`), + KEY `invitations_email_is_active_index` (`email`,`is_active`), + KEY `invitations_expires_at_index` (`expires_at`), + KEY `ix_invitations_active_expires` (`is_active`,`expires_at`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; DROP TABLE IF EXISTS `jobs`; - +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; CREATE TABLE `jobs` ( `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, `queue` varchar(255) NOT NULL, @@ -462,29 +644,48 @@ CREATE TABLE `jobs` ( `created_at` int(10) unsigned NOT NULL, PRIMARY KEY (`id`), KEY `jobs_queue_index` (`queue`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci ROW_FORMAT=DYNAMIC; - +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC; +/*!40101 SET character_set_client = @saved_cs_client */; DROP TABLE IF EXISTS `logging`; - +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; CREATE TABLE `logging` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `time` datetime DEFAULT NULL, `username` varchar(50) DEFAULT NULL, `host` varchar(40) DEFAULT NULL, PRIMARY KEY (`id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci; - +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; +DROP TABLE IF EXISTS `media_infos`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; +CREATE TABLE `media_infos` ( + `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, + `releases_id` bigint(20) unsigned NOT NULL, + `movie_name` varchar(255) DEFAULT NULL, + `file_name` varchar(255) DEFAULT NULL, + `unique_id` varchar(255) DEFAULT NULL, + `created_at` timestamp NULL DEFAULT NULL, + `updated_at` timestamp NULL DEFAULT NULL, + PRIMARY KEY (`id`), + KEY `ix_media_infos_unique_id` (`unique_id`), + KEY `ix_media_infos_releases_id` (`releases_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC; +/*!40101 SET character_set_client = @saved_cs_client */; DROP TABLE IF EXISTS `migrations`; - +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; CREATE TABLE `migrations` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `migration` varchar(255) NOT NULL, `batch` int(11) NOT NULL, PRIMARY KEY (`id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci ROW_FORMAT=DYNAMIC; - +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC; +/*!40101 SET character_set_client = @saved_cs_client */; DROP TABLE IF EXISTS `missed_parts`; - +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; CREATE TABLE `missed_parts` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `numberid` bigint(20) unsigned NOT NULL, @@ -498,9 +699,10 @@ CREATE TABLE `missed_parts` ( KEY `ix_missed_parts_numberid_groupsid_attempts` (`numberid`,`groups_id`,`attempts`), KEY `ix_missed_parts_attempts` (`attempts`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci; - +/*!40101 SET character_set_client = @saved_cs_client */; DROP TABLE IF EXISTS `model_has_permissions`; - +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; CREATE TABLE `model_has_permissions` ( `permission_id` int(10) unsigned NOT NULL, `model_type` varchar(255) NOT NULL, @@ -508,10 +710,11 @@ CREATE TABLE `model_has_permissions` ( PRIMARY KEY (`permission_id`,`model_id`,`model_type`), KEY `model_has_permissions_model_type_model_id_index` (`model_type`,`model_id`), CONSTRAINT `model_has_permissions_permission_id_foreign` FOREIGN KEY (`permission_id`) REFERENCES `permissions` (`id`) ON DELETE CASCADE -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci; - +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; DROP TABLE IF EXISTS `model_has_roles`; - +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; CREATE TABLE `model_has_roles` ( `role_id` int(10) unsigned NOT NULL, `model_type` varchar(255) NOT NULL, @@ -519,10 +722,11 @@ CREATE TABLE `model_has_roles` ( PRIMARY KEY (`role_id`,`model_id`,`model_type`), KEY `model_has_roles_model_type_model_id_index` (`model_type`,`model_id`), CONSTRAINT `model_has_roles_role_id_foreign` FOREIGN KEY (`role_id`) REFERENCES `roles` (`id`) ON DELETE CASCADE -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci; - +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; DROP TABLE IF EXISTS `movieinfo`; - +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; CREATE TABLE `movieinfo` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `imdbid` varchar(100) NOT NULL, @@ -549,10 +753,11 @@ CREATE TABLE `movieinfo` ( KEY `ix_movieinfo_title` (`title`), KEY `ix_movieinfo_tmdbid` (`tmdbid`), KEY `ix_movieinfo_traktid` (`traktid`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci; - +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; DROP TABLE IF EXISTS `musicinfo`; - +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; CREATE TABLE `musicinfo` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `title` varchar(255) NOT NULL, @@ -571,23 +776,48 @@ CREATE TABLE `musicinfo` ( `updated_at` timestamp NULL DEFAULT NULL, PRIMARY KEY (`id`), UNIQUE KEY `ix_musicinfo_asin` (`asin`), + KEY `ix_musicinfo_genres_id` (`genres_id`), FULLTEXT KEY `ix_musicinfo_artist_title_ft` (`artist`,`title`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci; - +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; +DROP TABLE IF EXISTS `nzb_backup_runs`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; +CREATE TABLE `nzb_backup_runs` ( + `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, + `started_at` timestamp NOT NULL, + `completed_at` timestamp NULL DEFAULT NULL, + `status` enum('running','completed','failed','cancelled') NOT NULL DEFAULT 'running', + `files_found` int(10) unsigned NOT NULL DEFAULT 0 COMMENT 'Files checked by rclone', + `files_uploaded` int(10) unsigned NOT NULL DEFAULT 0 COMMENT 'Files transferred', + `files_skipped` int(10) unsigned NOT NULL DEFAULT 0, + `files_failed` int(10) unsigned NOT NULL DEFAULT 0 COMMENT 'Errors during transfer', + `bytes_uploaded` bigint(20) unsigned NOT NULL DEFAULT 0, + `error_message` text DEFAULT NULL, + `stats` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL COMMENT 'Rclone statistics' CHECK (json_valid(`stats`)), + `created_at` timestamp NULL DEFAULT NULL, + `updated_at` timestamp NULL DEFAULT NULL, + PRIMARY KEY (`id`), + KEY `nzb_backup_runs_started_at_index` (`started_at`), + KEY `nzb_backup_runs_status_index` (`status`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC; +/*!40101 SET character_set_client = @saved_cs_client */; DROP TABLE IF EXISTS `par_hashes`; - +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; CREATE TABLE `par_hashes` ( `releases_id` int(10) unsigned NOT NULL COMMENT 'FK to releases.id', `hash` varchar(32) NOT NULL COMMENT 'hash_16k block of par2', PRIMARY KEY (`releases_id`,`hash`), KEY `ix_par_hashes_hash_releases_id` (`hash`,`releases_id`), CONSTRAINT `FK_ph_releases` FOREIGN KEY (`releases_id`) REFERENCES `releases` (`id`) ON DELETE CASCADE ON UPDATE CASCADE -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci; - +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; DROP TABLE IF EXISTS `parts`; - +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; CREATE TABLE `parts` ( - `binaries_id` bigint(20) unsigned NOT NULL DEFAULT 0, + `binaries_id` bigint(20) unsigned NOT NULL, `messageid` varchar(255) CHARACTER SET ascii COLLATE ascii_bin NOT NULL DEFAULT '', `number` bigint(20) unsigned NOT NULL DEFAULT 0, `partnumber` int(10) unsigned NOT NULL DEFAULT 0, @@ -595,10 +825,38 @@ CREATE TABLE `parts` ( PRIMARY KEY (`binaries_id`,`partnumber`), KEY `ix_parts_number` (`number`), CONSTRAINT `FK_binaries` FOREIGN KEY (`binaries_id`) REFERENCES `binaries` (`id`) ON DELETE CASCADE ON UPDATE CASCADE -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci; - +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_uca1400_ai_ci; +/*!40101 SET character_set_client = @saved_cs_client */; +DROP TABLE IF EXISTS `passkeys`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; +CREATE TABLE `passkeys` ( + `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, + `authenticatable_id` int(10) unsigned NOT NULL, + `name` text NOT NULL, + `credential_id` text NOT NULL, + `data` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL CHECK (json_valid(`data`)), + `last_used_at` timestamp NULL DEFAULT NULL, + `created_at` timestamp NULL DEFAULT NULL, + `updated_at` timestamp NULL DEFAULT NULL, + PRIMARY KEY (`id`), + KEY `passkeys_authenticatable_fk` (`authenticatable_id`), + CONSTRAINT `passkeys_authenticatable_fk` FOREIGN KEY (`authenticatable_id`) REFERENCES `users` (`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC; +/*!40101 SET character_set_client = @saved_cs_client */; +DROP TABLE IF EXISTS `password_reset_tokens`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; +CREATE TABLE `password_reset_tokens` ( + `email` varchar(255) NOT NULL, + `token` varchar(255) NOT NULL, + `created_at` timestamp NULL DEFAULT NULL, + PRIMARY KEY (`email`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC; +/*!40101 SET character_set_client = @saved_cs_client */; DROP TABLE IF EXISTS `password_securities`; - +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; CREATE TABLE `password_securities` ( `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, `user_id` int(11) NOT NULL, @@ -606,11 +864,13 @@ CREATE TABLE `password_securities` ( `google2fa_secret` varchar(255) DEFAULT NULL, `created_at` timestamp NULL DEFAULT NULL, `updated_at` timestamp NULL DEFAULT NULL, - PRIMARY KEY (`id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci ROW_FORMAT=DYNAMIC; - + PRIMARY KEY (`id`), + KEY `ix_password_securities_user_id` (`user_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC; +/*!40101 SET character_set_client = @saved_cs_client */; DROP TABLE IF EXISTS `payments`; - +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; CREATE TABLE `payments` ( `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, `created_at` timestamp NULL DEFAULT NULL, @@ -621,27 +881,31 @@ CREATE TABLE `payments` ( `order_id` varchar(255) NOT NULL, `payment_id` varchar(255) NOT NULL, `payment_status` varchar(255) NOT NULL, + `invoice_status` varchar(255) DEFAULT 'Pending', `invoice_amount` varchar(255) NOT NULL, `payment_method` varchar(255) NOT NULL, `payment_value` varchar(255) NOT NULL, `webhook_id` varchar(255) NOT NULL, `invoice_id` varchar(255) NOT NULL, PRIMARY KEY (`id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci ROW_FORMAT=DYNAMIC; - +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC; +/*!40101 SET character_set_client = @saved_cs_client */; DROP TABLE IF EXISTS `paypal_payments`; - +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; CREATE TABLE `paypal_payments` ( `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, `users_id` int(11) NOT NULL, `transaction_id` varchar(255) NOT NULL, `created_at` timestamp NULL DEFAULT NULL, `updated_at` timestamp NULL DEFAULT NULL, - PRIMARY KEY (`id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci ROW_FORMAT=DYNAMIC; - + PRIMARY KEY (`id`), + KEY `ix_paypal_payments_users_id` (`users_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC; +/*!40101 SET character_set_client = @saved_cs_client */; DROP TABLE IF EXISTS `permissions`; - +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; CREATE TABLE `permissions` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `name` varchar(255) NOT NULL, @@ -649,17 +913,18 @@ CREATE TABLE `permissions` ( `created_at` timestamp NULL DEFAULT NULL, `updated_at` timestamp NULL DEFAULT NULL, PRIMARY KEY (`id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci; - +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; DROP TABLE IF EXISTS `personal_access_tokens`; - +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; CREATE TABLE `personal_access_tokens` ( `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, `tokenable_type` varchar(255) NOT NULL, `tokenable_id` bigint(20) unsigned NOT NULL, `name` varchar(255) NOT NULL, `token` varchar(64) NOT NULL, - `abilities` text DEFAULT NULL, + `abilities` mediumtext DEFAULT NULL, `last_used_at` timestamp NULL DEFAULT NULL, `expires_at` timestamp NULL DEFAULT NULL, `created_at` timestamp NULL DEFAULT NULL, @@ -667,10 +932,11 @@ CREATE TABLE `personal_access_tokens` ( PRIMARY KEY (`id`), UNIQUE KEY `personal_access_tokens_token_unique` (`token`), KEY `personal_access_tokens_tokenable_type_tokenable_id_index` (`tokenable_type`,`tokenable_id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci ROW_FORMAT=DYNAMIC; - +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC; +/*!40101 SET character_set_client = @saved_cs_client */; DROP TABLE IF EXISTS `poster_renames`; - +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; CREATE TABLE `poster_renames` ( `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, `created_at` timestamp NULL DEFAULT NULL, @@ -680,10 +946,11 @@ CREATE TABLE `poster_renames` ( `source` varchar(20) NOT NULL, PRIMARY KEY (`id`), UNIQUE KEY `poster_title` (`poster`,`title`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci ROW_FORMAT=DYNAMIC; - +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC; +/*!40101 SET character_set_client = @saved_cs_client */; DROP TABLE IF EXISTS `predb`; - +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; CREATE TABLE `predb` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT 'Primary key', `title` varchar(255) NOT NULL DEFAULT '', @@ -708,9 +975,11 @@ CREATE TABLE `predb` ( KEY `ix_predb_searched` (`searched`), KEY `ix_predb_searched_predate_id` (`searched`,`predate`,`id`), FULLTEXT KEY `ft_predb_filename` (`filename`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; DROP TABLE IF EXISTS `predb_crcs`; - +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; CREATE TABLE `predb_crcs` ( `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, `predb_id` int(10) unsigned NOT NULL COMMENT 'FK to predb.id', @@ -723,10 +992,11 @@ CREATE TABLE `predb_crcs` ( PRIMARY KEY (`id`), KEY `predb_crcs_crchash_filesize_filedate_index` (`crchash`,`filesize`,`filedate`), KEY `predb_crcs_osohash_index` (`osohash`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci ROW_FORMAT=DYNAMIC; - +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC; +/*!40101 SET character_set_client = @saved_cs_client */; DROP TABLE IF EXISTS `predb_imports`; - +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; CREATE TABLE `predb_imports` ( `title` varchar(255) NOT NULL DEFAULT '', `nfo` varchar(255) DEFAULT NULL, @@ -742,16 +1012,17 @@ CREATE TABLE `predb_imports` ( `filename` varchar(255) NOT NULL DEFAULT '', `searched` tinyint(1) NOT NULL DEFAULT 0, `groupname` varchar(255) DEFAULT NULL -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci; - +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; DROP TABLE IF EXISTS `pulse_aggregates`; - +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; CREATE TABLE `pulse_aggregates` ( `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, `bucket` int(10) unsigned NOT NULL, `period` mediumint(8) unsigned NOT NULL, `type` varchar(255) NOT NULL, - `key` text NOT NULL, + `key` mediumtext NOT NULL, `key_hash` binary(16) GENERATED ALWAYS AS (unhex(md5(`key`))) VIRTUAL, `aggregate` varchar(255) NOT NULL, `value` decimal(20,2) NOT NULL, @@ -761,15 +1032,16 @@ CREATE TABLE `pulse_aggregates` ( KEY `pulse_aggregates_period_bucket_index` (`period`,`bucket`), KEY `pulse_aggregates_type_index` (`type`), KEY `pulse_aggregates_period_type_aggregate_bucket_index` (`period`,`type`,`aggregate`,`bucket`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci ROW_FORMAT=DYNAMIC; - +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC; +/*!40101 SET character_set_client = @saved_cs_client */; DROP TABLE IF EXISTS `pulse_entries`; - +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; CREATE TABLE `pulse_entries` ( `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, `timestamp` int(10) unsigned NOT NULL, `type` varchar(255) NOT NULL, - `key` text NOT NULL, + `key` mediumtext NOT NULL, `key_hash` binary(16) GENERATED ALWAYS AS (unhex(md5(`key`))) VIRTUAL, `value` bigint(20) DEFAULT NULL, PRIMARY KEY (`id`), @@ -777,54 +1049,92 @@ CREATE TABLE `pulse_entries` ( KEY `pulse_entries_type_index` (`type`), KEY `pulse_entries_key_hash_index` (`key_hash`), KEY `pulse_entries_timestamp_type_key_hash_value_index` (`timestamp`,`type`,`key_hash`,`value`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci ROW_FORMAT=DYNAMIC; - +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC; +/*!40101 SET character_set_client = @saved_cs_client */; DROP TABLE IF EXISTS `pulse_values`; - +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; CREATE TABLE `pulse_values` ( `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, `timestamp` int(10) unsigned NOT NULL, `type` varchar(255) NOT NULL, - `key` text NOT NULL, + `key` mediumtext NOT NULL, `key_hash` binary(16) GENERATED ALWAYS AS (unhex(md5(`key`))) VIRTUAL, - `value` text NOT NULL, + `value` mediumtext NOT NULL, PRIMARY KEY (`id`), UNIQUE KEY `pulse_values_type_key_hash_unique` (`type`,`key_hash`), KEY `pulse_values_timestamp_index` (`timestamp`), KEY `pulse_values_type_index` (`type`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci ROW_FORMAT=DYNAMIC; - +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC; +/*!40101 SET character_set_client = @saved_cs_client */; +DROP TABLE IF EXISTS `registration_periods`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; +CREATE TABLE `registration_periods` ( + `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, + `name` varchar(255) NOT NULL, + `starts_at` datetime NOT NULL, + `ends_at` datetime NOT NULL, + `is_enabled` tinyint(1) NOT NULL DEFAULT 1, + `notes` text DEFAULT NULL, + `created_by` bigint(20) unsigned DEFAULT NULL, + `updated_by` bigint(20) unsigned DEFAULT NULL, + `created_at` timestamp NULL DEFAULT NULL, + `updated_at` timestamp NULL DEFAULT NULL, + PRIMARY KEY (`id`), + KEY `registration_periods_starts_at_index` (`starts_at`), + KEY `registration_periods_ends_at_index` (`ends_at`), + KEY `registration_periods_is_enabled_index` (`is_enabled`), + KEY `registration_periods_created_by_index` (`created_by`), + KEY `registration_periods_updated_by_index` (`updated_by`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC; +/*!40101 SET character_set_client = @saved_cs_client */; +DROP TABLE IF EXISTS `registration_status_history`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; +CREATE TABLE `registration_status_history` ( + `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, + `action` varchar(100) NOT NULL, + `old_status` tinyint(3) unsigned DEFAULT NULL, + `new_status` tinyint(3) unsigned DEFAULT NULL, + `registration_period_id` bigint(20) unsigned DEFAULT NULL, + `changed_by` bigint(20) unsigned DEFAULT NULL, + `description` varchar(255) NOT NULL, + `metadata` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL CHECK (json_valid(`metadata`)), + `created_at` timestamp NOT NULL DEFAULT current_timestamp(), + `updated_at` timestamp NULL DEFAULT NULL, + PRIMARY KEY (`id`), + KEY `registration_status_history_action_index` (`action`), + KEY `registration_status_history_registration_period_id_index` (`registration_period_id`), + KEY `registration_status_history_changed_by_index` (`changed_by`), + KEY `registration_status_history_created_at_index` (`created_at`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC; +/*!40101 SET character_set_client = @saved_cs_client */; DROP TABLE IF EXISTS `release_comments`; - +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; CREATE TABLE `release_comments` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `releases_id` int(10) unsigned NOT NULL COMMENT 'FK to releases.id', `text` varchar(2000) NOT NULL DEFAULT '', `isvisible` tinyint(1) NOT NULL DEFAULT 1, - `issynced` tinyint(1) NOT NULL DEFAULT 0, - `gid` varchar(32) DEFAULT NULL, - `cid` varchar(32) DEFAULT NULL, `username` varchar(255) NOT NULL DEFAULT '', `users_id` int(10) unsigned NOT NULL, `created_at` timestamp NULL DEFAULT NULL, `updated_at` timestamp NULL DEFAULT NULL, `host` varchar(15) DEFAULT NULL, - `shared` tinyint(1) NOT NULL DEFAULT 0, - `shareid` varchar(40) NOT NULL DEFAULT '', - `siteid` varchar(40) NOT NULL DEFAULT '', - `sourceid` bigint(20) unsigned DEFAULT NULL, - `nzb_guid` binary(16) NOT NULL DEFAULT '0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0', PRIMARY KEY (`id`), KEY `ix_releasecomment_releases_id` (`releases_id`), KEY `ix_releasecomment_userid` (`users_id`), CONSTRAINT `FK_rc_releases` FOREIGN KEY (`releases_id`) REFERENCES `releases` (`id`) ON DELETE CASCADE ON UPDATE CASCADE -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci; - +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; DROP TABLE IF EXISTS `release_files`; - +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; CREATE TABLE `release_files` ( `releases_id` int(10) unsigned NOT NULL COMMENT 'FK to releases.id', - `name` varchar(255) NOT NULL DEFAULT '', + `name` varchar(255) NOT NULL, `size` bigint(20) unsigned NOT NULL DEFAULT 0, `crc32` varchar(255) NOT NULL DEFAULT '', `created_at` timestamp NULL DEFAULT NULL, @@ -833,8 +1143,11 @@ CREATE TABLE `release_files` ( PRIMARY KEY (`releases_id`,`name`), KEY `ix_release_files_crc32_releases_id` (`crc32`,`releases_id`), CONSTRAINT `FK_rf_releases` FOREIGN KEY (`releases_id`) REFERENCES `releases` (`id`) ON DELETE CASCADE ON UPDATE CASCADE -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; DROP TABLE IF EXISTS `release_informs`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; CREATE TABLE `release_informs` ( `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, `created_at` timestamp NULL DEFAULT NULL, @@ -843,8 +1156,11 @@ CREATE TABLE `release_informs` ( `relPName` varchar(255) NOT NULL, `api_token` varchar(255) NOT NULL, PRIMARY KEY (`id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci ROW_FORMAT=DYNAMIC; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC; +/*!40101 SET character_set_client = @saved_cs_client */; DROP TABLE IF EXISTS `release_naming_regexes`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; CREATE TABLE `release_naming_regexes` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `group_regex` varchar(255) NOT NULL DEFAULT '' COMMENT 'This is a regex to match against usenet groups', @@ -856,22 +1172,99 @@ CREATE TABLE `release_naming_regexes` ( KEY `ix_release_naming_regexes_group_regex` (`group_regex`), KEY `ix_release_naming_regexes_status` (`status`), KEY `ix_release_naming_regexes_ordinal` (`ordinal`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; DROP TABLE IF EXISTS `release_nfos`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; CREATE TABLE `release_nfos` ( `releases_id` int(10) unsigned NOT NULL COMMENT 'FK to releases.id', `nfo` blob DEFAULT NULL, PRIMARY KEY (`releases_id`), CONSTRAINT `FK_rn_releases` FOREIGN KEY (`releases_id`) REFERENCES `releases` (`id`) ON DELETE CASCADE ON UPDATE CASCADE ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; +DROP TABLE IF EXISTS `release_nzb_creation_failures`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; +CREATE TABLE `release_nzb_creation_failures` ( + `releases_id` int(10) unsigned NOT NULL, + `attempts` smallint(5) unsigned NOT NULL DEFAULT 0, + `last_error` text DEFAULT NULL, + `created_at` timestamp NULL DEFAULT NULL, + `updated_at` timestamp NULL DEFAULT NULL, + PRIMARY KEY (`releases_id`), + CONSTRAINT `FK_rncf_releases` FOREIGN KEY (`releases_id`) REFERENCES `releases` (`id`) ON DELETE CASCADE ON UPDATE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC; +/*!40101 SET character_set_client = @saved_cs_client */; +DROP TABLE IF EXISTS `release_nzb_passwords`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; +CREATE TABLE `release_nzb_passwords` ( + `releases_id` int(10) unsigned NOT NULL, + `password` varchar(255) NOT NULL, + PRIMARY KEY (`releases_id`), + CONSTRAINT `FK_rnp_releases` FOREIGN KEY (`releases_id`) REFERENCES `releases` (`id`) ON DELETE CASCADE ON UPDATE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC; +/*!40101 SET character_set_client = @saved_cs_client */; DROP TABLE IF EXISTS `release_regexes`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; CREATE TABLE `release_regexes` ( `releases_id` int(10) unsigned NOT NULL DEFAULT 0, `collection_regex_id` int(11) NOT NULL DEFAULT 0, `naming_regex_id` int(11) NOT NULL DEFAULT 0, PRIMARY KEY (`releases_id`,`collection_regex_id`,`naming_regex_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; +DROP TABLE IF EXISTS `release_reports`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; +CREATE TABLE `release_reports` ( + `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, + `releases_id` int(10) unsigned NOT NULL, + `users_id` int(10) unsigned NOT NULL, + `reason` varchar(255) NOT NULL, + `description` text DEFAULT NULL, + `response` text DEFAULT NULL, + `status` enum('pending','reviewed','resolved','dismissed') NOT NULL DEFAULT 'pending', + `reviewed_by` int(10) unsigned DEFAULT NULL, + `reviewed_at` timestamp NULL DEFAULT NULL, + `responded_by` int(10) unsigned DEFAULT NULL, + `responded_at` timestamp NULL DEFAULT NULL, + `response_is_public` tinyint(1) NOT NULL DEFAULT 1, + `created_at` timestamp NULL DEFAULT NULL, + `updated_at` timestamp NULL DEFAULT NULL, + PRIMARY KEY (`id`), + KEY `release_reports_users_id_foreign` (`users_id`), + KEY `release_reports_reviewed_by_foreign` (`reviewed_by`), + KEY `release_reports_releases_id_status_index` (`releases_id`,`status`), + KEY `release_reports_status_created_at_index` (`status`,`created_at`), + KEY `release_reports_responded_by_foreign` (`responded_by`), + KEY `release_reports_response_lookup_idx` (`releases_id`,`response_is_public`,`responded_at`), + KEY `ix_release_reports_status_created_admin` (`status`,`created_at`), + CONSTRAINT `release_reports_releases_id_foreign` FOREIGN KEY (`releases_id`) REFERENCES `releases` (`id`) ON DELETE CASCADE, + CONSTRAINT `release_reports_responded_by_foreign` FOREIGN KEY (`responded_by`) REFERENCES `users` (`id`) ON DELETE SET NULL, + CONSTRAINT `release_reports_reviewed_by_foreign` FOREIGN KEY (`reviewed_by`) REFERENCES `users` (`id`) ON DELETE SET NULL, + CONSTRAINT `release_reports_users_id_foreign` FOREIGN KEY (`users_id`) REFERENCES `users` (`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC; +/*!40101 SET character_set_client = @saved_cs_client */; +DROP TABLE IF EXISTS `release_stats`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; +CREATE TABLE `release_stats` ( + `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, + `created_at` timestamp NULL DEFAULT NULL, + `updated_at` timestamp NULL DEFAULT NULL, + `category` varchar(255) NOT NULL, + `count` int(11) NOT NULL DEFAULT 0, + PRIMARY KEY (`id`), + KEY `release_stats_category_index` (`category`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC; +/*!40101 SET character_set_client = @saved_cs_client */; DROP TABLE IF EXISTS `release_subtitles`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; CREATE TABLE `release_subtitles` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `releases_id` int(10) unsigned NOT NULL COMMENT 'FK to releases.id', @@ -880,15 +1273,21 @@ CREATE TABLE `release_subtitles` ( PRIMARY KEY (`id`), UNIQUE KEY `ix_releasesubs_releases_id_subsid` (`releases_id`,`subsid`), CONSTRAINT `FK_rs_releases` FOREIGN KEY (`releases_id`) REFERENCES `releases` (`id`) ON DELETE CASCADE ON UPDATE CASCADE -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; DROP TABLE IF EXISTS `release_unique`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; CREATE TABLE `release_unique` ( `releases_id` int(10) unsigned NOT NULL COMMENT 'FK to releases.id.', `uniqueid` varchar(255) NOT NULL COMMENT 'Unique_ID from mediainfo.', PRIMARY KEY (`releases_id`,`uniqueid`), CONSTRAINT `FK_ru_releases` FOREIGN KEY (`releases_id`) REFERENCES `releases` (`id`) ON DELETE CASCADE ON UPDATE CASCADE -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; DROP TABLE IF EXISTS `releases`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; CREATE TABLE `releases` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `name` varchar(255) NOT NULL DEFAULT '', @@ -898,10 +1297,8 @@ CREATE TABLE `releases` ( `size` bigint(20) unsigned NOT NULL DEFAULT 0, `postdate` datetime DEFAULT NULL, `adddate` datetime DEFAULT NULL, - `updatetime` timestamp NOT NULL DEFAULT current_timestamp(), - `gid` varchar(32) DEFAULT NULL, - `guid` varchar(40) NOT NULL, - `leftguid` char(1) NOT NULL COMMENT 'The first letter of the release guid', + `guid` char(36) CHARACTER SET ascii COLLATE ascii_general_ci NOT NULL, + `leftguid` char(1) CHARACTER SET ascii COLLATE ascii_general_ci NOT NULL COMMENT 'The first letter of the release guid', `fromname` varchar(255) DEFAULT NULL, `completion` double NOT NULL DEFAULT 0, `categories_id` int(11) NOT NULL DEFAULT 10, @@ -923,13 +1320,12 @@ CREATE TABLE `releases` ( `nfostatus` tinyint(1) NOT NULL DEFAULT 0, `jpgstatus` tinyint(1) NOT NULL DEFAULT 0, `videostatus` tinyint(1) NOT NULL DEFAULT 0, - `audiostatus` tinyint(1) NOT NULL DEFAULT 0, - `reqidstatus` tinyint(1) NOT NULL DEFAULT 0, `nzbstatus` tinyint(1) NOT NULL DEFAULT 0, + `nzb_creation_claimed_at` timestamp NULL DEFAULT NULL, + `nzb_creation_claim_token` varchar(64) DEFAULT NULL, `iscategorized` tinyint(1) NOT NULL DEFAULT 0, `isrenamed` tinyint(1) NOT NULL DEFAULT 0, `proc_pp` tinyint(1) NOT NULL DEFAULT 0, - `proc_sorter` tinyint(1) NOT NULL DEFAULT 0, `proc_par2` tinyint(1) NOT NULL DEFAULT 0, `proc_nfo` tinyint(1) NOT NULL DEFAULT 0, `proc_files` tinyint(1) NOT NULL DEFAULT 0, @@ -937,37 +1333,51 @@ CREATE TABLE `releases` ( `proc_srr` tinyint(1) NOT NULL DEFAULT 0 COMMENT 'Has the release been srr\nprocessed', `proc_hash16k` tinyint(1) NOT NULL DEFAULT 0 COMMENT 'Has the release been hash16k\nprocessed', `proc_crc32` tinyint(1) NOT NULL DEFAULT 0 COMMENT 'Has the release been crc32 processed', - `nzb_guid` blob NOT NULL, - `source` smallint(5) unsigned DEFAULT NULL, - PRIMARY KEY (`id`,`categories_id`), + `pp_timeout_count` tinyint(4) NOT NULL DEFAULT 0 COMMENT 'Number of times this release timed out during additional post-processing', + `additional_pp_claimed_at` timestamp NULL DEFAULT NULL, + `additional_pp_claim_token` varchar(64) DEFAULT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `ux_releases_guid` (`guid`), KEY `ix_releases_groupsid` (`groups_id`,`passwordstatus`), - KEY `ix_releases_postdate_searchname` (`postdate`,`searchname`), KEY `ix_releases_leftguid` (`leftguid`,`predb_id`), KEY `ix_releases_musicinfo_id` (`musicinfo_id`,`passwordstatus`), - KEY `ix_releases_predb_id_searchname` (`predb_id`,`searchname`), - KEY `ix_releases_haspreview_passwordstatus` (`haspreview`,`passwordstatus`), KEY `ix_releases_nfostatus` (`nfostatus`,`size`), KEY `ix_releases_name` (`name`), - KEY `ix_releases_guid` (`guid`), - KEY `ix_releases_videos_id` (`videos_id`), KEY `ix_releases_tv_episodes_id` (`tv_episodes_id`), - KEY `ix_releases_imdbid` (`imdbid`), KEY `ix_releases_consoleinfo_id` (`consoleinfo_id`), KEY `ix_releases_gamesinfo_id` (`gamesinfo_id`), KEY `ix_releases_bookinfo_id` (`bookinfo_id`), KEY `ix_releases_anidbid` (`anidbid`), - KEY `ix_releases_movieinfo_id` (`movieinfo_id`), - KEY `ix_releases_passwordstatus` (`passwordstatus`), - KEY `ix_releases_nzb_guid` (`nzb_guid`(3072)) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci; + KEY `ix_releases_password_categories_postdate` (`passwordstatus`,`categories_id`,`postdate` DESC), + KEY `ix_releases_adddate` (`adddate`,`categories_id`), + KEY `ix_releases_grabs` (`grabs`,`categories_id`,`postdate`), + KEY `ix_releases_movieinfo_cat` (`movieinfo_id`,`categories_id`,`passwordstatus`,`postdate`), + KEY `ix_releases_videos_categories` (`videos_id`,`categories_id`), + KEY `ix_releases_imdbid_password_cat_postdate` (`imdbid`,`passwordstatus`,`categories_id`,`postdate` DESC), + KEY `ix_releases_searchname` (`searchname`), + KEY `ix_releases_categories_postdate_admin` (`categories_id`,`postdate`), + KEY `ix_releases_postdate_admin` (`postdate`), + KEY `ix_releases_adddate_id` (`adddate`,`id`), + KEY `ix_releases_predb_id` (`predb_id`), + KEY `ix_releases_size` (`size`), + KEY `ix_releases_add_pp_claim_queue` (`passwordstatus`,`haspreview`,`nzbstatus`,`leftguid`,`postdate` DESC,`id`,`additional_pp_claimed_at`), + KEY `ix_releases_nzb_creation_group_queue` (`nzbstatus`,`groups_id`,`postdate` DESC,`id`,`nzb_creation_claimed_at`), + KEY `ix_releases_nzb_creation_global_queue` (`nzbstatus`,`postdate` DESC,`id`,`nzb_creation_claimed_at`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; DROP TABLE IF EXISTS `releases_groups`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; CREATE TABLE `releases_groups` ( `releases_id` int(10) unsigned NOT NULL DEFAULT 0 COMMENT 'FK to releases.id', `groups_id` int(10) unsigned NOT NULL DEFAULT 0 COMMENT 'FK to groups.id', PRIMARY KEY (`releases_id`,`groups_id`), CONSTRAINT `FK_rg_releases` FOREIGN KEY (`releases_id`) REFERENCES `releases` (`id`) ON DELETE CASCADE ON UPDATE CASCADE ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; DROP TABLE IF EXISTS `role_expiration_emails`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; CREATE TABLE `role_expiration_emails` ( `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, `users_id` int(11) NOT NULL, @@ -979,7 +1389,10 @@ CREATE TABLE `role_expiration_emails` ( PRIMARY KEY (`id`), UNIQUE KEY `role_expiration_emails_users_id_unique` (`users_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci ROW_FORMAT=DYNAMIC; +/*!40101 SET character_set_client = @saved_cs_client */; DROP TABLE IF EXISTS `role_has_permissions`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; CREATE TABLE `role_has_permissions` ( `permission_id` int(10) unsigned NOT NULL, `role_id` int(10) unsigned NOT NULL, @@ -988,7 +1401,65 @@ CREATE TABLE `role_has_permissions` ( CONSTRAINT `role_has_permissions_permission_id_foreign` FOREIGN KEY (`permission_id`) REFERENCES `permissions` (`id`) ON DELETE CASCADE, CONSTRAINT `role_has_permissions_role_id_foreign` FOREIGN KEY (`role_id`) REFERENCES `roles` (`id`) ON DELETE CASCADE ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; +DROP TABLE IF EXISTS `role_promotion_stats`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; +CREATE TABLE `role_promotion_stats` ( + `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, + `user_id` int(10) unsigned NOT NULL, + `role_promotion_id` bigint(20) unsigned NOT NULL, + `role_id` int(10) unsigned NOT NULL, + `days_added` int(11) NOT NULL COMMENT 'Number of days added to role expiry', + `previous_expiry_date` datetime DEFAULT NULL COMMENT 'Previous role expiry date before promotion', + `new_expiry_date` datetime DEFAULT NULL COMMENT 'New role expiry date after promotion', + `applied_at` timestamp NOT NULL COMMENT 'When the promotion was applied', + `created_at` timestamp NULL DEFAULT NULL, + `updated_at` timestamp NULL DEFAULT NULL, + PRIMARY KEY (`id`), + KEY `role_promotion_stats_user_id_index` (`user_id`), + KEY `role_promotion_stats_role_promotion_id_index` (`role_promotion_id`), + KEY `role_promotion_stats_role_id_index` (`role_id`), + KEY `role_promotion_stats_applied_at_index` (`applied_at`), + CONSTRAINT `role_promotion_stats_role_id_foreign` FOREIGN KEY (`role_id`) REFERENCES `roles` (`id`) ON DELETE CASCADE, + CONSTRAINT `role_promotion_stats_role_promotion_id_foreign` FOREIGN KEY (`role_promotion_id`) REFERENCES `role_promotions` (`id`) ON DELETE CASCADE, + CONSTRAINT `role_promotion_stats_user_id_foreign` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC; +/*!40101 SET character_set_client = @saved_cs_client */; +DROP TABLE IF EXISTS `role_promotions`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; +CREATE TABLE `role_promotions` ( + `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, + `name` varchar(255) NOT NULL, + `description` text DEFAULT NULL, + `applicable_roles` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL COMMENT 'JSON array of role IDs this promotion applies to' CHECK (json_valid(`applicable_roles`)), + `additional_days` int(11) NOT NULL DEFAULT 0 COMMENT 'Additional days added to role expiry', + `start_date` date DEFAULT NULL COMMENT 'Promotion start date', + `end_date` date DEFAULT NULL COMMENT 'Promotion end date', + `is_active` tinyint(1) NOT NULL DEFAULT 1, + `created_at` timestamp NULL DEFAULT NULL, + `updated_at` timestamp NULL DEFAULT NULL, + PRIMARY KEY (`id`), + KEY `ix_role_promotions_is_active` (`is_active`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC; +/*!40101 SET character_set_client = @saved_cs_client */; +DROP TABLE IF EXISTS `role_stats`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; +CREATE TABLE `role_stats` ( + `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, + `created_at` timestamp NULL DEFAULT NULL, + `updated_at` timestamp NULL DEFAULT NULL, + `role` varchar(255) DEFAULT NULL, + `users` int(11) NOT NULL DEFAULT 0, + PRIMARY KEY (`id`), + KEY `ix_role_stats_created_at` (`created_at`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC; +/*!40101 SET character_set_client = @saved_cs_client */; DROP TABLE IF EXISTS `roles`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; CREATE TABLE `roles` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `name` varchar(255) NOT NULL, @@ -1003,8 +1474,11 @@ CREATE TABLE `roles` ( `created_at` timestamp NULL DEFAULT NULL, `updated_at` timestamp NULL DEFAULT NULL, PRIMARY KEY (`id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; DROP TABLE IF EXISTS `root_categories`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; CREATE TABLE `root_categories` ( `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, `title` varchar(255) NOT NULL, @@ -1014,14 +1488,106 @@ CREATE TABLE `root_categories` ( `updated_at` timestamp NULL DEFAULT NULL, PRIMARY KEY (`id`), KEY `ix_root_categories_status` (`status`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; +DROP TABLE IF EXISTS `search_index_failures`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; +CREATE TABLE `search_index_failures` ( + `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, + `release_id` bigint(20) unsigned NOT NULL, + `operation` varchar(32) NOT NULL DEFAULT 'upsert', + `attempts` int(10) unsigned NOT NULL DEFAULT 0, + `last_error` text DEFAULT NULL, + `next_attempt_at` timestamp NULL DEFAULT NULL, + `resolved_at` timestamp NULL DEFAULT NULL, + `created_at` timestamp NULL DEFAULT NULL, + `updated_at` timestamp NULL DEFAULT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `search_index_failures_release_id_unique` (`release_id`), + KEY `search_index_failures_next_attempt_at_index` (`next_attempt_at`), + KEY `search_index_failures_resolved_at_index` (`resolved_at`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC; +/*!40101 SET character_set_client = @saved_cs_client */; +DROP TABLE IF EXISTS `service_incident_service_status`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; +CREATE TABLE `service_incident_service_status` ( + `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, + `service_incident_id` bigint(20) unsigned NOT NULL, + `service_status_id` bigint(20) unsigned NOT NULL, + `created_at` timestamp NULL DEFAULT NULL, + `updated_at` timestamp NULL DEFAULT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `svc_incident_svc_pair_unique` (`service_incident_id`,`service_status_id`), + KEY `service_incident_service_status_service_status_id_index` (`service_status_id`), + CONSTRAINT `service_incident_service_status_service_incident_id_foreign` FOREIGN KEY (`service_incident_id`) REFERENCES `service_incidents` (`id`) ON DELETE CASCADE, + CONSTRAINT `service_incident_service_status_service_status_id_foreign` FOREIGN KEY (`service_status_id`) REFERENCES `service_statuses` (`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC; +/*!40101 SET character_set_client = @saved_cs_client */; +DROP TABLE IF EXISTS `service_incidents`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; +CREATE TABLE `service_incidents` ( + `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, + `title` varchar(255) NOT NULL, + `description` text NOT NULL, + `status` varchar(255) NOT NULL, + `impact` varchar(255) NOT NULL, + `started_at` timestamp NOT NULL, + `resolved_at` timestamp NULL DEFAULT NULL, + `created_by` int(10) unsigned DEFAULT NULL, + `is_auto` tinyint(1) NOT NULL DEFAULT 0, + `created_at` timestamp NULL DEFAULT NULL, + `updated_at` timestamp NULL DEFAULT NULL, + PRIMARY KEY (`id`), + KEY `service_incidents_status_index` (`status`), + KEY `service_incidents_impact_index` (`impact`), + KEY `service_incidents_started_at_index` (`started_at`), + KEY `service_incidents_resolved_at_index` (`resolved_at`), + KEY `service_incidents_status_started_at_index` (`status`,`started_at`), + KEY `service_incidents_created_by_index` (`created_by`), + KEY `service_incidents_is_auto_index` (`is_auto`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC; +/*!40101 SET character_set_client = @saved_cs_client */; +DROP TABLE IF EXISTS `service_statuses`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; +CREATE TABLE `service_statuses` ( + `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, + `name` varchar(255) NOT NULL, + `slug` varchar(255) NOT NULL, + `endpoint_url` varchar(255) DEFAULT NULL, + `check_type` varchar(255) NOT NULL DEFAULT 'http', + `probe_identifier` varchar(255) DEFAULT NULL, + `status` varchar(255) NOT NULL, + `last_checked_at` timestamp NULL DEFAULT NULL, + `uptime_percentage` decimal(5,2) NOT NULL DEFAULT 100.00, + `response_time_ms` int(10) unsigned DEFAULT NULL, + `is_enabled` tinyint(1) NOT NULL DEFAULT 1, + `sort_order` int(11) NOT NULL DEFAULT 0, + `created_at` timestamp NULL DEFAULT NULL, + `updated_at` timestamp NULL DEFAULT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `service_statuses_slug_unique` (`slug`), + KEY `service_statuses_status_index` (`status`), + KEY `service_statuses_is_enabled_sort_order_index` (`is_enabled`,`sort_order`), + KEY `service_statuses_check_type_index` (`check_type`), + KEY `service_statuses_probe_identifier_index` (`probe_identifier`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC; +/*!40101 SET character_set_client = @saved_cs_client */; DROP TABLE IF EXISTS `settings`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; CREATE TABLE `settings` ( `name` varchar(25) NOT NULL DEFAULT '', `value` varchar(1000) NOT NULL DEFAULT '', PRIMARY KEY (`name`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; DROP TABLE IF EXISTS `short_groups`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; CREATE TABLE `short_groups` ( `id` int(11) NOT NULL AUTO_INCREMENT, `name` varchar(255) NOT NULL DEFAULT '', @@ -1030,8 +1596,25 @@ CREATE TABLE `short_groups` ( `updated` datetime DEFAULT NULL, PRIMARY KEY (`id`), KEY `ix_shortgroups_name` (`name`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; +DROP TABLE IF EXISTS `signup_stats`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; +CREATE TABLE `signup_stats` ( + `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, + `created_at` timestamp NULL DEFAULT NULL, + `updated_at` timestamp NULL DEFAULT NULL, + `month` varchar(255) DEFAULT NULL, + `sort_date` date DEFAULT NULL, + `signups` int(11) NOT NULL DEFAULT 0, + PRIMARY KEY (`id`), + KEY `signup_stats_sort_date_index` (`sort_date`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC; +/*!40101 SET character_set_client = @saved_cs_client */; DROP TABLE IF EXISTS `steam_apps`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; CREATE TABLE `steam_apps` ( `name` varchar(255) NOT NULL DEFAULT '' COMMENT 'Steam application name', `appid` int(10) unsigned NOT NULL COMMENT 'Steam application id', @@ -1039,8 +1622,28 @@ CREATE TABLE `steam_apps` ( PRIMARY KEY (`id`), KEY `ix_name_appid` (`name`,`appid`), FULLTEXT KEY `ix_name_ft` (`name`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; +DROP TABLE IF EXISTS `system_metrics`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; +CREATE TABLE `system_metrics` ( + `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, + `metric_type` varchar(50) NOT NULL, + `value` decimal(8,2) NOT NULL, + `recorded_at` timestamp NOT NULL, + `created_at` timestamp NULL DEFAULT NULL, + `updated_at` timestamp NULL DEFAULT NULL, + PRIMARY KEY (`id`), + KEY `system_metrics_metric_type_recorded_at_index` (`metric_type`,`recorded_at`), + KEY `system_metrics_metric_type_index` (`metric_type`), + KEY `system_metrics_recorded_at_index` (`recorded_at`), + KEY `ix_system_metrics_type_recorded` (`metric_type`,`recorded_at`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC; +/*!40101 SET character_set_client = @saved_cs_client */; DROP TABLE IF EXISTS `telescope_entries`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; CREATE TABLE `telescope_entries` ( `sequence` bigint(20) unsigned NOT NULL AUTO_INCREMENT, `uuid` char(36) NOT NULL, @@ -1055,24 +1658,49 @@ CREATE TABLE `telescope_entries` ( KEY `telescope_entries_batch_id_index` (`batch_id`), KEY `telescope_entries_type_should_display_on_index_index` (`type`,`should_display_on_index`), KEY `telescope_entries_family_hash_index` (`family_hash`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci ROW_FORMAT=DYNAMIC; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC; +/*!40101 SET character_set_client = @saved_cs_client */; DROP TABLE IF EXISTS `telescope_entries_tags`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; CREATE TABLE `telescope_entries_tags` ( `entry_uuid` char(36) NOT NULL, `tag` varchar(255) NOT NULL, KEY `telescope_entries_tags_entry_uuid_tag_index` (`entry_uuid`,`tag`), KEY `telescope_entries_tags_tag_index` (`tag`), CONSTRAINT `telescope_entries_tags_entry_uuid_foreign` FOREIGN KEY (`entry_uuid`) REFERENCES `telescope_entries` (`uuid`) ON DELETE CASCADE -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci ROW_FORMAT=DYNAMIC; - +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC; +/*!40101 SET character_set_client = @saved_cs_client */; DROP TABLE IF EXISTS `telescope_monitoring`; - +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; CREATE TABLE `telescope_monitoring` ( `tag` varchar(255) NOT NULL -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci ROW_FORMAT=DYNAMIC; - +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC; +/*!40101 SET character_set_client = @saved_cs_client */; +DROP TABLE IF EXISTS `trusted_devices`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; +CREATE TABLE `trusted_devices` ( + `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, + `user_id` int(10) unsigned NOT NULL, + `token_hash` varchar(64) NOT NULL, + `expires_at` timestamp NOT NULL, + `last_used_at` timestamp NULL DEFAULT NULL, + `ip_address` varchar(45) DEFAULT NULL, + `user_agent` varchar(500) DEFAULT NULL, + `created_at` timestamp NULL DEFAULT NULL, + `updated_at` timestamp NULL DEFAULT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `trusted_devices_token_hash_unique` (`token_hash`), + KEY `trusted_devices_user_id_expires_at_index` (`user_id`,`expires_at`), + KEY `trusted_devices_expires_at_index` (`expires_at`), + CONSTRAINT `trusted_devices_user_id_foreign` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC; +/*!40101 SET character_set_client = @saved_cs_client */; DROP TABLE IF EXISTS `tv_episodes`; - +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; CREATE TABLE `tv_episodes` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `videos_id` int(10) unsigned NOT NULL COMMENT 'FK to videos.id of the parent series.', @@ -1081,25 +1709,27 @@ CREATE TABLE `tv_episodes` ( `se_complete` varchar(10) NOT NULL COMMENT 'String version of Series/Episode as taken from release subject (i.e. S02E21+22).', `title` varchar(180) NOT NULL COMMENT 'Title of the episode.', `firstaired` date DEFAULT NULL COMMENT 'Date of original airing/release.', - `summary` text NOT NULL COMMENT 'Description/summary of the episode.', + `summary` mediumtext NOT NULL COMMENT 'Description/summary of the episode.', PRIMARY KEY (`id`), UNIQUE KEY `videos_id` (`videos_id`,`series`,`episode`,`firstaired`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci; - +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; DROP TABLE IF EXISTS `tv_info`; - +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; CREATE TABLE `tv_info` ( `videos_id` int(10) unsigned NOT NULL DEFAULT 0 COMMENT 'FK to video.id', - `summary` text NOT NULL COMMENT 'Description/summary of the show.', + `summary` mediumtext NOT NULL COMMENT 'Description/summary of the show.', `publisher` varchar(50) NOT NULL COMMENT 'The channel/network of production/release (ABC, BBC, Showtime, etc.).', `localzone` varchar(50) NOT NULL DEFAULT '' COMMENT 'The linux tz style identifier', `image` tinyint(1) NOT NULL DEFAULT 0 COMMENT 'Does the video have a cover image?', PRIMARY KEY (`videos_id`), KEY `ix_tv_info_image` (`image`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci; - +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; DROP TABLE IF EXISTS `usenet_groups`; - +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; CREATE TABLE `usenet_groups` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `name` varchar(255) NOT NULL DEFAULT '', @@ -1116,11 +1746,62 @@ CREATE TABLE `usenet_groups` ( `description` varchar(255) DEFAULT '', PRIMARY KEY (`id`), UNIQUE KEY `ix_groups_name` (`name`), - KEY `active` (`active`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci; - + KEY `active` (`active`), + KEY `ix_usenet_groups_active_name_admin` (`active`,`name`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; +DROP TABLE IF EXISTS `user_activities`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; +CREATE TABLE `user_activities` ( + `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, + `user_id` bigint(20) unsigned DEFAULT NULL, + `username` varchar(255) NOT NULL, + `activity_type` varchar(50) NOT NULL, + `description` text NOT NULL, + `metadata` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL CHECK (json_valid(`metadata`)), + `is_permanent` tinyint(1) NOT NULL DEFAULT 0, + `created_at` timestamp NOT NULL DEFAULT current_timestamp(), + PRIMARY KEY (`id`), + KEY `user_activities_activity_type_created_at_index` (`activity_type`,`created_at`), + KEY `user_activities_created_at_index` (`created_at`), + KEY `ix_user_activities_type_permanent` (`activity_type`,`is_permanent`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC; +/*!40101 SET character_set_client = @saved_cs_client */; +DROP TABLE IF EXISTS `user_activity_stats`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; +CREATE TABLE `user_activity_stats` ( + `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, + `stat_date` date NOT NULL, + `downloads_count` int(11) NOT NULL DEFAULT 0, + `api_hits_count` int(11) NOT NULL DEFAULT 0, + `created_at` timestamp NULL DEFAULT NULL, + `updated_at` timestamp NULL DEFAULT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `user_activity_stats_stat_date_unique` (`stat_date`), + KEY `user_activity_stats_stat_date_index` (`stat_date`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC; +/*!40101 SET character_set_client = @saved_cs_client */; +DROP TABLE IF EXISTS `user_activity_stats_hourly`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; +CREATE TABLE `user_activity_stats_hourly` ( + `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, + `stat_hour` datetime NOT NULL, + `downloads_count` int(11) NOT NULL DEFAULT 0, + `api_hits_count` int(11) NOT NULL DEFAULT 0, + `created_at` timestamp NULL DEFAULT NULL, + `updated_at` timestamp NULL DEFAULT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `user_activity_stats_hourly_stat_hour_unique` (`stat_hour`), + KEY `user_activity_stats_hourly_stat_hour_index` (`stat_hour`), + KEY `ix_hourly_stats_hour` (`stat_hour`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC; +/*!40101 SET character_set_client = @saved_cs_client */; DROP TABLE IF EXISTS `user_downloads`; - +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; CREATE TABLE `user_downloads` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `users_id` int(10) unsigned NOT NULL, @@ -1130,11 +1811,31 @@ CREATE TABLE `user_downloads` ( PRIMARY KEY (`id`), KEY `userid` (`users_id`), KEY `timestamp` (`timestamp`), + KEY `ix_user_downloads_users_timestamp` (`users_id`,`timestamp`), + KEY `ix_user_downloads_releases_id` (`releases_id`), CONSTRAINT `FK_users_ud` FOREIGN KEY (`users_id`) REFERENCES `users` (`id`) ON DELETE CASCADE ON UPDATE CASCADE -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci; - +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; +DROP TABLE IF EXISTS `user_excluded_categories`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; +CREATE TABLE `user_excluded_categories` ( + `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, + `users_id` int(10) unsigned NOT NULL, + `categories_id` int(10) unsigned NOT NULL, + `created_at` timestamp NULL DEFAULT NULL, + `updated_at` timestamp NULL DEFAULT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `user_excluded_categories_users_id_categories_id_unique` (`users_id`,`categories_id`), + KEY `user_excluded_categories_categories_id_foreign` (`categories_id`), + KEY `user_excluded_categories_users_id_index` (`users_id`), + CONSTRAINT `user_excluded_categories_categories_id_foreign` FOREIGN KEY (`categories_id`) REFERENCES `categories` (`id`) ON DELETE CASCADE, + CONSTRAINT `user_excluded_categories_users_id_foreign` FOREIGN KEY (`users_id`) REFERENCES `users` (`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC; +/*!40101 SET character_set_client = @saved_cs_client */; DROP TABLE IF EXISTS `user_invitations`; - +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; CREATE TABLE `user_invitations` ( `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, `code` varchar(255) NOT NULL, @@ -1146,10 +1847,11 @@ CREATE TABLE `user_invitations` ( `updated_at` timestamp NULL DEFAULT NULL, PRIMARY KEY (`id`), KEY `user_invitations_code_index` (`code`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci ROW_FORMAT=DYNAMIC; - +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC; +/*!40101 SET character_set_client = @saved_cs_client */; DROP TABLE IF EXISTS `user_movies`; - +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; CREATE TABLE `user_movies` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `users_id` int(10) unsigned NOT NULL, @@ -1160,10 +1862,11 @@ CREATE TABLE `user_movies` ( PRIMARY KEY (`id`), KEY `ix_usermovies_userid` (`users_id`,`imdbid`), CONSTRAINT `FK_users_um` FOREIGN KEY (`users_id`) REFERENCES `users` (`id`) ON DELETE CASCADE ON UPDATE CASCADE -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci; - +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; DROP TABLE IF EXISTS `user_requests`; - +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; CREATE TABLE `user_requests` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `users_id` int(10) unsigned NOT NULL, @@ -1173,11 +1876,35 @@ CREATE TABLE `user_requests` ( PRIMARY KEY (`id`), KEY `userid` (`users_id`), KEY `timestamp` (`timestamp`), + KEY `ix_user_requests_users_timestamp` (`users_id`,`timestamp`), CONSTRAINT `FK_users_urq` FOREIGN KEY (`users_id`) REFERENCES `users` (`id`) ON DELETE CASCADE ON UPDATE CASCADE -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci; - +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; +DROP TABLE IF EXISTS `user_role_history`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; +CREATE TABLE `user_role_history` ( + `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, + `user_id` bigint(20) unsigned NOT NULL, + `old_role_id` int(11) DEFAULT NULL, + `new_role_id` int(11) NOT NULL, + `old_expiry_date` datetime DEFAULT NULL COMMENT 'Previous role expiry date', + `new_expiry_date` datetime DEFAULT NULL COMMENT 'New role expiry date', + `effective_date` datetime NOT NULL COMMENT 'When this role change became active', + `is_stacked` tinyint(1) NOT NULL DEFAULT 0 COMMENT 'Was this role stacked after previous expiry', + `change_reason` varchar(255) DEFAULT NULL COMMENT 'Reason for role change (upgrade, downgrade, expiry, admin, etc)', + `changed_by` bigint(20) unsigned DEFAULT NULL COMMENT 'Admin user ID who made the change', + `created_at` timestamp NULL DEFAULT NULL, + `updated_at` timestamp NULL DEFAULT NULL, + PRIMARY KEY (`id`), + KEY `user_role_history_user_id_index` (`user_id`), + KEY `ix_user_role_history_changed_by` (`changed_by`), + KEY `ix_user_role_history_roles` (`old_role_id`,`new_role_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC; +/*!40101 SET character_set_client = @saved_cs_client */; DROP TABLE IF EXISTS `user_series`; - +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; CREATE TABLE `user_series` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `users_id` int(10) unsigned NOT NULL, @@ -1188,10 +1915,11 @@ CREATE TABLE `user_series` ( PRIMARY KEY (`id`), KEY `ix_userseries_videos_id` (`users_id`,`videos_id`), CONSTRAINT `FK_users_us` FOREIGN KEY (`users_id`) REFERENCES `users` (`id`) ON DELETE CASCADE ON UPDATE CASCADE -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci; - +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; DROP TABLE IF EXISTS `users`; - +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; CREATE TABLE `users` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `username` varchar(50) NOT NULL, @@ -1208,6 +1936,7 @@ CREATE TABLE `users` ( `resetguid` varchar(50) DEFAULT NULL, `lastlogin` datetime DEFAULT NULL, `apiaccess` datetime DEFAULT NULL, + `lastdownload` datetime DEFAULT NULL, `invites` int(11) NOT NULL DEFAULT 0, `invitedby` int(11) DEFAULT NULL, `movieview` int(11) NOT NULL DEFAULT 1, @@ -1218,23 +1947,45 @@ CREATE TABLE `users` ( `gameview` int(11) NOT NULL DEFAULT 1, `rate_limit` int(11) NOT NULL DEFAULT 60, `notes` varchar(255) DEFAULT NULL, + `theme_preference` varchar(10) NOT NULL DEFAULT 'light', + `color_scheme` varchar(20) NOT NULL DEFAULT 'blue', `style` varchar(255) DEFAULT NULL, `rolechangedate` datetime DEFAULT NULL COMMENT 'When does the role expire', + `pending_role_start_date` datetime DEFAULT NULL COMMENT 'When the pending role change takes effect', + `pending_roles_id` int(11) DEFAULT NULL COMMENT 'The role that will be applied after current role expires', `next_rolechangedate` datetime DEFAULT NULL, `email_verified_at` timestamp NULL DEFAULT NULL, `remember_token` varchar(100) DEFAULT NULL, + `session_token` varchar(60) DEFAULT NULL, `timezone` varchar(255) DEFAULT NULL, + `movie_layout` tinyint(4) NOT NULL DEFAULT 2 COMMENT '1=1-column, 2=2-columns', `created_at` timestamp NULL DEFAULT NULL, `updated_at` timestamp NULL DEFAULT NULL, `verified` tinyint(1) NOT NULL DEFAULT 0, `verification_token` varchar(255) DEFAULT NULL, + `deleted_at` timestamp NULL DEFAULT NULL, + `deleted_by` varchar(255) DEFAULT NULL, + `bad_user` tinyint(1) NOT NULL DEFAULT 0, + `can_post` tinyint(1) NOT NULL DEFAULT 0, PRIMARY KEY (`id`), UNIQUE KEY `ux_users_api_token` (`api_token`), - KEY `ix_user_roles` (`roles_id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci; - + KEY `ix_user_roles` (`roles_id`), + KEY `ix_users_created_at` (`created_at`), + KEY `ix_users_roles_created` (`roles_id`,`created_at`), + KEY `ix_users_deleted_at` (`deleted_at`), + KEY `ix_users_username` (`username`), + KEY `ix_users_email` (`email`), + KEY `ix_users_host` (`host`), + KEY `ix_users_lastlogin` (`lastlogin`), + KEY `ix_users_apiaccess` (`apiaccess`), + KEY `ix_users_grabs` (`grabs`), + KEY `ix_users_rolechangedate` (`rolechangedate`), + KEY `ix_users_verified` (`verified`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; DROP TABLE IF EXISTS `users_releases`; - +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; CREATE TABLE `users_releases` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `users_id` int(10) unsigned NOT NULL, @@ -1247,9 +1998,10 @@ CREATE TABLE `users_releases` ( CONSTRAINT `FK_ur_releases` FOREIGN KEY (`releases_id`) REFERENCES `releases` (`id`) ON DELETE CASCADE ON UPDATE CASCADE, CONSTRAINT `FK_users_ur` FOREIGN KEY (`users_id`) REFERENCES `users` (`id`) ON DELETE CASCADE ON UPDATE CASCADE ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci; - +/*!40101 SET character_set_client = @saved_cs_client */; DROP TABLE IF EXISTS `video_data`; - +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; CREATE TABLE `video_data` ( `releases_id` int(10) unsigned NOT NULL COMMENT 'FK to releases.id', `containerformat` varchar(50) DEFAULT NULL, @@ -1264,10 +2016,11 @@ CREATE TABLE `video_data` ( `videolibrary` varchar(50) DEFAULT NULL, PRIMARY KEY (`releases_id`), CONSTRAINT `FK_vd_releases` FOREIGN KEY (`releases_id`) REFERENCES `releases` (`id`) ON DELETE CASCADE ON UPDATE CASCADE -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci; - +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; DROP TABLE IF EXISTS `videos`; - +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; CREATE TABLE `videos` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT 'Show ID to be used in other tables as reference ', `type` tinyint(1) NOT NULL DEFAULT 0 COMMENT '0 = TV, 1 = Film, 2 = Anime', @@ -1291,18 +2044,27 @@ CREATE TABLE `videos` ( KEY `ix_videos_tvdb` (`tvdb`), KEY `ix_videos_tvmaze` (`tvmaze`), KEY `ix_videos_tvrage` (`tvrage`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci; - +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; DROP TABLE IF EXISTS `videos_aliases`; - +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8mb4 */; CREATE TABLE `videos_aliases` ( `videos_id` int(10) unsigned NOT NULL COMMENT 'FK to videos.id of the parent title.', `title` varchar(180) NOT NULL COMMENT 'AKA of the video.', `created_at` timestamp NULL DEFAULT NULL, `updated_at` timestamp NULL DEFAULT NULL, PRIMARY KEY (`videos_id`,`title`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; +/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */; +/*!40101 SET SQL_MODE=@OLD_SQL_MODE */; +/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; +/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; +/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; + +/*M!999999\- enable the sandbox mode */ INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (1,'2014_01_16_195548_create_users_table',1); INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (2,'2014_02_01_311070_create_firewall_table',1); INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (3,'2017_11_29_223842_create_countries_table',1); @@ -1356,6 +2118,7 @@ INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (52,'2018_01_20_200 INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (53,'2018_01_20_200346_create_video_data_table',1); INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (54,'2018_01_20_200353_create_videos_table',1); INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (55,'2018_01_20_200403_create_videos_aliases_table',1); +INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (56,'2018_01_20_200417_create_xxxinfo_table',1); INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (58,'2018_04_24_132758_create_cache_table',1); INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (59,'2018_08_08_100000_create_telescope_entries_table',1); INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (60,'2018_09_13_070520_add_verification_to_user_table',1); @@ -1411,5 +2174,111 @@ INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (110,'2020_03_22_05 INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (111,'2020_03_22_055827_add_post_id_to_threads',5); INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (112,'2020_12_02_233754_add_first_post_id_to_threads',5); INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (113,'2021_07_31_094750_add_fk_indices',5); - -SET FOREIGN_KEY_CHECKS = 1; +INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (114,'2024_03_30_095622_update_forum_category_colors',6); +INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (115,'2024_04_27_124202_create_media_infos_table',6); +INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (116,'2024_05_12_192646_add_uuid_column_to_failed_jobs_table',6); +INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (117,'2024_05_25_122046_drop_stored_procedure',6); +INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (118,'2024_09_08_151127_create_grab_stats_table',6); +INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (119,'2024_09_08_151135_create_signup_stats_table',6); +INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (120,'2024_09_08_151158_create_role_stats_table',6); +INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (121,'2024_09_08_151214_create_download_stats_table',6); +INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (122,'2024_09_08_151223_create_release_stats_table',6); +INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (123,'2024_09_15_184830_add_xxx_vr_category',6); +INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (124,'2024_10_28_092803_drop_columns_from_settings_table',6); +INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (125,'2024_10_28_115551_rename_lookuptvrage_to_lookuptv',6); +INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (126,'2024_11_16_114640_add_invoice_status_to_payments_table',6); +INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (127,'2025_01_01_000000_create_invitations_table',6); +INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (128,'2025_01_30_115835_drop_triggers',6); +INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (129,'2025_04_10_202844_add_created_at_updated_at_columns',6); +INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (130,'2025_05_04_212955_drop_userseed',6); +INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (131,'2025_06_06_000000_add_soft_deletes_to_users_table',6); +INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (132,'2025_08_10_195505_fix_invitations_column_names',6); +INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (133,'2025_08_14_000001_add_indexes_to_collections_table',6); +INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (134,'2025_08_22_133811_drop_reqidstatus_column',6); +INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (135,'2025_08_28_000000_add_onlyfans_category_to_categories_table',6); +INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (136,'2025_10_16_101145_add_dark_mode_to_users_table',6); +INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (137,'2025_10_16_120000_update_dark_mode_to_theme_preference',6); +INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (138,'2025_10_20_132950_convert_content_table_to_utf8mb4',6); +INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (139,'2025_10_22_000000_create_system_metrics_table',6); +INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (140,'2025_10_22_204937_add_sort_date_to_signup_stats_table',6); +INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (141,'2025_10_23_094253_add_timezone_to_users_table',6); +INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (142,'2025_10_23_100000_add_movie_layout_to_users_table',6); +INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (143,'2025_10_24_000000_create_user_activity_stats_table',6); +INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (144,'2025_10_27_165328_create_user_activities_table',6); +INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (145,'2025_10_29_000000_create_user_activity_stats_hourly_table',6); +INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (146,'2025_11_05_095815_fix_release_files_collation',6); +INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (147,'2025_11_05_104146_fix_all_tables_collation_to_utf8mb4',6); +INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (148,'2025_11_05_120504_fix_telescope_tables_collation',6); +INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (149,'2025_11_28_000000_create_role_promotions_table',6); +INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (150,'2025_11_29_000000_create_role_promotion_stats_table',6); +INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (151,'2025_11_29_120000_add_role_stacking_fields_to_users_table',6); +INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (152,'2025_11_29_120001_create_user_role_history_table',6); +INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (153,'2025_12_04_000000_add_redis_args_to_settings_table',6); +INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (154,'2025_12_05_000000_replace_anidb_with_anilist',6); +INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (155,'2025_12_21_000000_add_categories_postdate_index_to_releases_table',6); +INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (156,'2025_12_21_000001_add_covering_index_for_tv_search',6); +INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (157,'2025_12_22_000000_add_additional_performance_indexes_to_releases_table',6); +INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (158,'2025_12_22_120000_add_composite_indexes_for_admin_performance',6); +INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (159,'2026_01_15_000000_fix_invitations_foreign_key',7); +INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (160,'2026_01_15_000001_cleanup_invitations_legacy_columns',8); +INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (161,'2026_01_23_150053_create_user_excluded_categories_table',9); +INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (162,'2026_01_26_105835_add_releases_categories_videos_index',10); +INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (163,'2019_10_03_112445_add_bad_user_to_users_table',11); +INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (164,'2024_03_14_201011_add_nzb_password_column_to_releases_table',11); +INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (165,'2024_08_13_232303_add_index_to_media_infos_table',11); +INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (166,'2024_12_20_113606_add_can_post_column_to_users_table',11); +INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (167,'2025_01_13_000000_create_nzb_backup_tables',11); +INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (168,'2025_11_07_164001__add_predb_scrape_setting',11); +INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (169,'2026_01_21_134835_create_api_token_ip_addresses_table',11); +INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (170,'2026_02_01_000000_create_release_reports_table',12); +INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (171,'2024_08_31_084308_add_content_approval_support',13); +INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (172,'2026_02_09_114731_add_deleted_by_to_users_table',14); +INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (173,'2026_02_11_000000_remove_ishashed_and_dehashstatus_columns',14); +INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (174,'2026_02_12_000000_add_covering_index_for_movie_browse',15); +INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (175,'2026_02_13_000000_drop_xxxinfo_and_releases_xxxinfo_id',16); +INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (176,'2026_02_19_000000_add_pp_timeout_count_to_releases_table',17); +INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (180,'2026_03_03_120000_add_color_scheme_to_users_table',18); +INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (181,'2026_03_10_000000_create_registration_periods_table',19); +INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (182,'2026_03_10_000001_create_registration_status_history_table',19); +INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (183,'2026_03_11_120000_add_lastdownload_to_users_table',19); +INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (184,'2026_03_18_000000_replace_vendor_countries_table',20); +INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (185,'2026_03_31_000000_add_missing_performance_indexes',21); +INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (186,'2026_04_01_000000_create_service_statuses_table',22); +INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (187,'2026_04_01_000001_create_service_incidents_table',22); +INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (188,'2026_04_01_120000_add_user_sort_column_indexes',22); +INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (189,'2026_04_01_120000_service_incidents_many_services',22); +INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (190,'2026_04_01_130000_add_endpoint_url_to_service_statuses',22); +INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (191,'2026_04_01_130001_add_is_auto_to_service_incidents',22); +INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (192,'2026_03_19_150944_add_depth_to_categories',23); +INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (193,'2026_04_02_170000_fix_add_depth_to_forum_categories',23); +INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (194,'2026_04_09_120000_normalize_padded_imdb_ids',23); +INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (195,'2026_04_13_000002_add_probe_fields_to_service_statuses_table',24); +INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (196,'2026_04_24_000000_create_passkeys_table',24); +INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (197,'2026_04_27_000000_add_is_permanent_to_user_activities',25); +INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (198,'2026_05_05_123242_add_cbp_query_indexes',25); +INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (199,'2026_05_05_124540_phase2_shrink_binaries_binaryhash',25); +INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (200,'2026_05_06_093900_restore_cbp_foreign_keys',25); +INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (201,'2026_05_08_154620_add_session_token_to_users_table',25); +INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (202,'2026_05_08_180000_drop_nzb_guid_columns',25); +INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (203,'2026_05_12_000000_add_ix_releases_searchname',25); +INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (204,'2026_06_08_000000_add_response_fields_to_release_reports_table',26); +INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (205,'2026_06_10_000000_create_trusted_devices_table',26); +INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (206,'2026_06_11_000000_create_password_reset_tokens_table',26); +INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (207,'2026_06_11_000001_add_resetguid_to_users_table',26); +INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (208,'2026_06_17_000000_create_gdpr_requests_table',27); +INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (209,'2026_06_17_000001_create_gdpr_consents_table',27); +INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (210,'2026_06_17_000002_create_gdpr_audit_logs_table',27); +INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (211,'2026_06_17_000000_update_rss_service_status_endpoint_to_health',28); +INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (212,'2026_06_17_000001_neutralize_rss_http_400_false_positive_incidents',28); +INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (213,'2026_07_12_000000_add_additional_postprocessing_claim_fields',29); +INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (214,'2026_07_12_000001_add_release_creation_claim_fields_to_collections_table',30); +INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (215,'2026_07_12_000002_add_release_creation_failure_fields_to_collections_table',31); +INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (216,'2026_07_13_000000_add_admin_list_performance_indexes',32); +INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (217,'2026_07_13_000000_add_nzb_creation_claim_fields',32); +INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (218,'2026_07_15_000000_add_releases_adddate_id_index',33); +INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (219,'2026_08_02_000000_create_search_index_failures_table',34); +INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (220,'2026_08_03_000000_prepare_cbp_optimized_storage',35); +INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (221,'2026_08_03_000001_finalize_cbp_binary_hash_storage',36); +INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (222,'2026_08_04_082439_add_fix_release_name_query_indexes',37); +INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (223,'2026_08_10_192527_convert_size_settings_to_bytes',38); +INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (224,'2026_08_13_001652_normalize_and_optimize_releases_table',39); diff --git a/tests/Feature/NzbCreationReliabilityTest.php b/tests/Feature/NzbCreationReliabilityTest.php index 99bea1ef8..367315089 100644 --- a/tests/Feature/NzbCreationReliabilityTest.php +++ b/tests/Feature/NzbCreationReliabilityTest.php @@ -69,6 +69,7 @@ class NzbCreationReliabilityTest extends TestCase parent::setUp(); $this->tempNzbPath = sys_get_temp_dir().'/nntmux-nzb-reliability-'.uniqid('', true); + mkdir($this->tempNzbPath, 0777, true); config(['database.default' => 'sqlite', 'database.connections.sqlite.database' => $this->databasePath]); config(['nntmux_settings.path_to_nzbs' => $this->tempNzbPath]); DB::purge(); @@ -140,8 +141,8 @@ class NzbCreationReliabilityTest extends TestCase $this->assertSame(0, $service->createNZBs(null)); $this->assertSame(1, DB::table('releases')->count()); - $this->assertSame(1, (int) DB::table('releases')->where('id', 1)->value('nzb_creation_attempts')); - $this->assertSame('Temporary filesystem failure.', DB::table('releases')->where('id', 1)->value('nzb_creation_last_error')); + $this->assertSame(1, (int) DB::table('release_nzb_creation_failures')->where('releases_id', 1)->value('attempts')); + $this->assertSame('Temporary filesystem failure.', DB::table('release_nzb_creation_failures')->where('releases_id', 1)->value('last_error')); $this->assertNull(DB::table('releases')->where('id', 1)->value('nzb_creation_claimed_at')); $this->assertSame(1, DB::table('collections')->count()); $this->assertSame('NZB creation failed; release will be retried', $nzbCreationLogger->warnings[0]['message']); @@ -174,6 +175,7 @@ class NzbCreationReliabilityTest extends TestCase $this->assertSame(0, DB::table('collections')->count()); $this->assertSame(0, DB::table('binaries')->count()); $this->assertSame(0, DB::table('parts')->count()); + $this->assertSame(0, DB::table('release_nzb_creation_failures')->count()); $this->assertSame('Deleting release after NZB creation failure', $nzbCreationLogger->warnings[0]['message']); $this->assertSame(1, $nzbCreationLogger->warnings[0]['context']['release_id']); $this->assertSame(str_repeat('a', 36), $nzbCreationLogger->warnings[0]['context']['guid']); @@ -203,6 +205,13 @@ class NzbCreationReliabilityTest extends TestCase public function test_writer_streams_multiple_keyset_pages_in_segment_order(): void { $this->insertRelease(1, 'h'); + DB::table('release_nzb_creation_failures')->insert([ + 'releases_id' => 1, + 'attempts' => 1, + 'last_error' => 'Earlier failure.', + 'created_at' => now(), + 'updated_at' => now(), + ]); $this->insertWritableCbp(200, 2000, 1); DB::table('parts')->where('binaries_id', 2000)->delete(); DB::table('parts')->insert([ @@ -222,6 +231,7 @@ class NzbCreationReliabilityTest extends TestCase $this->assertLessThan(strpos($xml, 'part02@example.test'), strpos($xml, 'part01@example.test')); $this->assertLessThan(strpos($xml, 'part03@example.test'), strpos($xml, 'part02@example.test')); $this->assertStringContainsString('alt.test', $xml); + $this->assertSame(0, DB::table('release_nzb_creation_failures')->count()); } public function test_stale_temporary_nzb_cleanup_deletes_only_old_temp_files(): void @@ -277,9 +287,17 @@ class NzbCreationReliabilityTest extends TestCase 'nzbstatus' => NzbService::NZB_NONE, 'nzb_creation_claimed_at' => $claimedAt?->format('Y-m-d H:i:s'), 'nzb_creation_claim_token' => $claimedAt === null ? null : 'claimed', - 'nzb_creation_attempts' => $attempts, - 'nzb_creation_last_error' => null, ]); + + if ($attempts > 0) { + DB::table('release_nzb_creation_failures')->insert([ + 'releases_id' => $id, + 'attempts' => $attempts, + 'last_error' => null, + 'created_at' => now(), + 'updated_at' => now(), + ]); + } } private function insertCbp(int $collectionId, int $binaryId, int $releaseId): void @@ -337,7 +355,7 @@ class NzbCreationReliabilityTest extends TestCase private function createSchema(): void { - foreach (['parts', 'binaries', 'collections', 'releases', 'categories', 'root_categories', 'usenet_groups', 'settings'] as $table) { + foreach (['parts', 'binaries', 'collections', 'release_nzb_creation_failures', 'releases', 'categories', 'root_categories', 'usenet_groups', 'settings'] as $table) { DB::statement("DROP TABLE IF EXISTS {$table}"); } @@ -355,9 +373,15 @@ class NzbCreationReliabilityTest extends TestCase postdate DATETIME NULL, nzbstatus INTEGER, nzb_creation_claimed_at DATETIME NULL, - nzb_creation_claim_token VARCHAR(64) NULL, - nzb_creation_attempts INTEGER DEFAULT 0, - nzb_creation_last_error TEXT NULL + nzb_creation_claim_token VARCHAR(64) NULL + )'); + DB::statement('CREATE TABLE release_nzb_creation_failures ( + releases_id INTEGER PRIMARY KEY, + attempts INTEGER DEFAULT 0, + last_error TEXT NULL, + created_at DATETIME NULL, + updated_at DATETIME NULL, + FOREIGN KEY (releases_id) REFERENCES releases(id) ON DELETE CASCADE )'); DB::statement('CREATE TABLE usenet_groups (id INTEGER PRIMARY KEY, name VARCHAR(255))'); DB::statement('CREATE TABLE collections ( diff --git a/tests/Feature/PostProcessRunnerAdditionalThreadsTest.php b/tests/Feature/PostProcessRunnerAdditionalThreadsTest.php index de585591a..746987957 100644 --- a/tests/Feature/PostProcessRunnerAdditionalThreadsTest.php +++ b/tests/Feature/PostProcessRunnerAdditionalThreadsTest.php @@ -352,7 +352,7 @@ class PostProcessRunnerAdditionalThreadsTest extends TestCase $this->assertContains('oversized-capacity', $warningCodes); } - public function test_additional_diagnostics_recognizes_the_covering_claim_index(): void + public function test_additional_diagnostics_recognizes_the_claim_queue_index(): void { Schema::table('releases', function (Blueprint $table): void { $table->index([ @@ -360,8 +360,9 @@ class PostProcessRunnerAdditionalThreadsTest extends TestCase 'haspreview', 'nzbstatus', 'leftguid', - 'additional_pp_claimed_at', 'postdate', + 'id', + 'additional_pp_claimed_at', ], 'ix_releases_add_pp_claim_queue'); }); @@ -370,7 +371,7 @@ class PostProcessRunnerAdditionalThreadsTest extends TestCase $this->assertSame(0, $status); $this->assertFalse($report['indexes']['missing']); - $this->assertSame('ix_releases_add_pp_claim_queue', $report['indexes']['covering_index']); + $this->assertSame('ix_releases_add_pp_claim_queue', $report['indexes']['claim_queue_index']); $this->assertNotContains('missing-index', array_column($report['warnings'], 'code')); } diff --git a/tests/Feature/ReleaseCommentLifecycleTest.php b/tests/Feature/ReleaseCommentLifecycleTest.php new file mode 100644 index 000000000..e1ad68a4d --- /dev/null +++ b/tests/Feature/ReleaseCommentLifecycleTest.php @@ -0,0 +1,97 @@ +databasePath = sys_get_temp_dir().'/nntmux-release-comments.sqlite'; + if (file_exists($this->databasePath)) { + unlink($this->databasePath); + } + $pdo = new PDO('sqlite:'.$this->databasePath); + $pdo->exec('CREATE TABLE settings (name VARCHAR PRIMARY KEY, value TEXT NULL)'); + $pdo->exec("INSERT INTO settings VALUES ('categorizeforeign', '0'), ('catwebdl', '0')"); + putenv('APP_ENV=testing'); + putenv('DB_CONNECTION=sqlite'); + putenv('DB_DATABASE='.$this->databasePath); + $_ENV['APP_ENV'] = $_SERVER['APP_ENV'] = 'testing'; + $_ENV['DB_CONNECTION'] = $_SERVER['DB_CONNECTION'] = 'sqlite'; + $_ENV['DB_DATABASE'] = $_SERVER['DB_DATABASE'] = $this->databasePath; + $app = require __DIR__.'/../../bootstrap/app.php'; + $app->make(Kernel::class)->bootstrap(); + + return $app; + } + + protected function setUp(): void + { + parent::setUp(); + config(['database.default' => 'sqlite', 'database.connections.sqlite.database' => $this->databasePath]); + DB::purge(); + DB::reconnect(); + DB::statement('CREATE TABLE users (id INTEGER PRIMARY KEY, username VARCHAR(255), deleted_at DATETIME NULL)'); + DB::statement('CREATE TABLE releases (id INTEGER PRIMARY KEY, comments INTEGER DEFAULT 0)'); + DB::statement('CREATE TABLE release_comments ( + id INTEGER PRIMARY KEY AUTOINCREMENT, releases_id INTEGER NOT NULL, text VARCHAR(2000), + isvisible INTEGER DEFAULT 1, username VARCHAR(255), users_id INTEGER, + created_at DATETIME, updated_at DATETIME, host VARCHAR(45) + )'); + DB::table('users')->insert(['id' => 7, 'username' => 'commenter']); + DB::table('releases')->insert(['id' => 10, 'comments' => 99]); + } + + protected function tearDown(): void + { + DB::disconnect(); + if (file_exists($this->databasePath)) { + unlink($this->databasePath); + } + parent::tearDown(); + } + + public function test_add_delete_and_visible_only_recount_use_release_id(): void + { + $commentId = ReleaseComment::addComment(10, 'Visible comment', 7, '127.0.0.1'); + DB::table('release_comments')->insert([ + 'releases_id' => 10, + 'text' => 'Hidden comment', + 'isvisible' => 0, + 'username' => 'commenter', + 'users_id' => 7, + ]); + ReleaseComment::updateReleaseCommentCount(10); + + $this->assertSame(1, (int) DB::table('releases')->where('id', 10)->value('comments')); + $this->assertDatabaseHas('release_comments', [ + 'id' => $commentId, + 'releases_id' => 10, + 'text' => 'Visible comment', + ]); + + ReleaseComment::deleteComment($commentId); + + $this->assertSame(0, (int) DB::table('releases')->where('id', 10)->value('comments')); + $this->assertDatabaseMissing('release_comments', ['id' => $commentId]); + } + + public function test_details_controller_submission_no_longer_reads_or_passes_gid(): void + { + $controller = file_get_contents(app_path('Http/Controllers/DetailsController.php')); + + $this->assertIsString($controller); + $this->assertStringContainsString('ReleaseComment::addComment((int) $data[\'id\']', $controller); + $this->assertStringNotContainsString("\$data['gid']", $controller); + } +} diff --git a/tests/Feature/ReleasesOptimizePreflightTest.php b/tests/Feature/ReleasesOptimizePreflightTest.php new file mode 100644 index 000000000..dcc5e23b1 --- /dev/null +++ b/tests/Feature/ReleasesOptimizePreflightTest.php @@ -0,0 +1,159 @@ +databasePath = sys_get_temp_dir().'/nntmux-releases-preflight.sqlite'; + if (file_exists($this->databasePath)) { + unlink($this->databasePath); + } + $pdo = new PDO('sqlite:'.$this->databasePath); + $pdo->exec('CREATE TABLE settings (name VARCHAR PRIMARY KEY, value TEXT NULL)'); + $pdo->exec("INSERT INTO settings VALUES ('categorizeforeign', '0'), ('catwebdl', '0')"); + $this->setEnvironmentValue('APP_ENV', 'testing'); + $this->setEnvironmentValue('DB_CONNECTION', 'sqlite'); + $this->setEnvironmentValue('DB_DATABASE', $this->databasePath); + + $app = require __DIR__.'/../../bootstrap/app.php'; + $app->make(Kernel::class)->bootstrap(); + + return $app; + } + + protected function setUp(): void + { + parent::setUp(); + config(['database.default' => 'sqlite', 'database.connections.sqlite.database' => $this->databasePath]); + DB::purge(); + DB::reconnect(); + $this->createSchema(); + } + + protected function tearDown(): void + { + DB::disconnect(); + if (file_exists($this->databasePath)) { + unlink($this->databasePath); + } + parent::tearDown(); + } + + public function test_valid_data_reports_storage_and_every_migration_data_category(): void + { + $this->insertRelease(1, '01234567-89ab-cdef-0123-456789abcdef', '0', [ + 'nzb_password' => 'secret', + 'nzb_creation_attempts' => 1, + 'nzb_creation_last_error' => 'temporary', + 'source' => 2, + ]); + DB::table('release_comments')->insert([ + ['id' => 1, 'releases_id' => 1, 'isvisible' => 1, 'gid' => 'legacy'], + ['id' => 2, 'releases_id' => 1, 'isvisible' => 0, 'gid' => null], + ]); + + $status = Artisan::call('releases:optimize-preflight', ['--json' => true]); + $report = json_decode(Artisan::output(), true, flags: JSON_THROW_ON_ERROR); + + $this->assertSame(0, $status); + $this->assertTrue($report['ok']); + $this->assertSame(1, $report['migration_data']['nzb_password_rows']); + $this->assertSame(1, $report['migration_data']['nzb_creation_failure_rows']); + $this->assertSame(1, $report['migration_data']['visible_comment_rows']); + $this->assertSame(1, $report['migration_data']['release_comment_counter_mismatches']); + $this->assertSame(1, $report['discarded_data']['releases.source']); + $this->assertSame(1, $report['discarded_data']['release_comments.gid']); + $this->assertNull($report['storage']['total_bytes']); + } + + public function test_every_identifier_blocker_fails_preflight(): void + { + $scenarios = [ + 'duplicate_ids' => function (): void { + $this->insertRelease(1, '01234567-89ab-cdef-0123-456789abcdef', '0'); + $this->insertRelease(1, '11234567-89ab-cdef-0123-456789abcdef', '1'); + }, + 'invalid_guids' => fn () => $this->insertRelease(1, 'not-a-guid', 'n'), + 'case_insensitive_duplicate_guids' => function (): void { + $this->insertRelease(1, 'abcdef01-2345-6789-abcd-ef0123456789', 'a'); + $this->insertRelease(2, 'ABCDEF01-2345-6789-ABCD-EF0123456789', 'A'); + }, + 'leftguid_mismatches' => fn () => $this->insertRelease(1, '01234567-89ab-cdef-0123-456789abcdef', 'f'), + ]; + + foreach ($scenarios as $expectedBlocker => $seed) { + DB::table('release_comments')->delete(); + DB::table('releases')->delete(); + $seed(); + + $status = Artisan::call('releases:optimize-preflight', ['--json' => true]); + $report = json_decode(Artisan::output(), true, flags: JSON_THROW_ON_ERROR); + + $this->assertSame(1, $status, $expectedBlocker); + $this->assertContains($expectedBlocker, array_column($report['blockers'], 'code')); + } + } + + /** @param array $overrides */ + private function insertRelease(int $id, string $guid, string $leftguid, array $overrides = []): void + { + DB::table('releases')->insert([ + 'id' => $id, + 'guid' => $guid, + 'leftguid' => $leftguid, + 'comments' => 0, + 'nzb_password' => null, + 'nzb_creation_attempts' => 0, + 'nzb_creation_last_error' => null, + 'updatetime' => now(), + 'gid' => null, + 'source' => null, + 'proc_sorter' => 0, + 'audiostatus' => 0, + ...$overrides, + ]); + } + + private function createSchema(): void + { + DB::statement('DROP TABLE IF EXISTS release_comments'); + DB::statement('DROP TABLE IF EXISTS releases'); + DB::statement('CREATE TABLE releases ( + id INTEGER NOT NULL, + guid VARCHAR(40), leftguid CHAR(1), comments INTEGER DEFAULT 0, + nzb_password VARCHAR(255), nzb_creation_attempts INTEGER DEFAULT 0, + nzb_creation_last_error TEXT, updatetime DATETIME, gid VARCHAR(32), + source INTEGER, proc_sorter INTEGER DEFAULT 0, audiostatus INTEGER DEFAULT 0 + )'); + DB::statement('CREATE TABLE release_comments ( + id INTEGER PRIMARY KEY, releases_id INTEGER NOT NULL, isvisible INTEGER DEFAULT 1, + gid VARCHAR(32), cid VARCHAR(32), issynced INTEGER DEFAULT 0, shared INTEGER DEFAULT 0, + shareid VARCHAR(40) DEFAULT \'\', siteid VARCHAR(40) DEFAULT \'\', sourceid INTEGER + )'); + } + + private function setEnvironmentValue(string $key, ?string $value): void + { + if ($value === null) { + putenv($key); + unset($_ENV[$key], $_SERVER[$key]); + + return; + } + + putenv("{$key}={$value}"); + $_ENV[$key] = $_SERVER[$key] = $value; + } +} diff --git a/tests/Integration/AdditionalCandidateQueryMariaDbTest.php b/tests/Integration/AdditionalCandidateQueryMariaDbTest.php index 9a37736e2..a575d2829 100644 --- a/tests/Integration/AdditionalCandidateQueryMariaDbTest.php +++ b/tests/Integration/AdditionalCandidateQueryMariaDbTest.php @@ -276,7 +276,7 @@ final class AdditionalCandidateQueryMariaDbTest extends TestCase additional_pp_claimed_at TIMESTAMP NULL, additional_pp_claim_token VARCHAR(64) NULL, KEY ix_releases_haspreview_passwordstatus (haspreview, passwordstatus), - KEY ix_releases_add_pp_claim_queue (passwordstatus, haspreview, nzbstatus, leftguid, additional_pp_claimed_at, postdate DESC) + KEY ix_releases_add_pp_claim_queue (passwordstatus, haspreview, nzbstatus, leftguid, postdate DESC, id, additional_pp_claimed_at) ) ENGINE=InnoDB SQL); DB::table('settings')->insert([ diff --git a/tests/Integration/NzbCreationClaimPlanMariaDbTest.php b/tests/Integration/NzbCreationClaimPlanMariaDbTest.php new file mode 100644 index 000000000..6974517c7 --- /dev/null +++ b/tests/Integration/NzbCreationClaimPlanMariaDbTest.php @@ -0,0 +1,158 @@ +safeLoad(); + $this->tablePrefix = 'nzb_claim_'.getmypid().'_'.bin2hex(random_bytes(4)).'_'; + $this->setEnvironmentValue('APP_ENV', 'testing'); + $this->setEnvironmentValue('DB_URL', null); + $this->setEnvironmentValue('DB_CONNECTION', 'mariadb'); + $app = require __DIR__.'/../../bootstrap/app.php'; + $app->make(Kernel::class)->bootstrap(); + $app->make('config')->set('database.connections.mariadb.prefix', $this->tablePrefix); + $app->make('db')->purge('mariadb'); + + return $app; + } + + protected function setUp(): void + { + parent::setUp(); + if (DB::getDriverName() !== 'mariadb') { + $this->markTestSkipped('MariaDB integration test.'); + } + $this->createSchema(); + } + + protected function tearDown(): void + { + if (isset($this->tablePrefix) && preg_match('/^nzb_claim_\d+_[a-f0-9]{8}_$/', $this->tablePrefix) === 1) { + DB::statement('SET FOREIGN_KEY_CHECKS=0'); + foreach (['release_nzb_creation_failures', 'releases', 'categories', 'root_categories', 'settings'] as $table) { + DB::statement('DROP TABLE IF EXISTS `'.$this->table($table).'`'); + } + DB::statement('SET FOREIGN_KEY_CHECKS=1'); + } + DB::disconnect(); + parent::tearDown(); + } + + #[Test] + public function grouped_and_global_claim_plans_use_their_indexes_without_filesort(): void + { + $releases = $this->table('releases'); + DB::statement(<<explain(<<explain(<<assertStringContainsString('ix_releases_nzb_creation_group_queue', $groupPlan); + $this->assertStringContainsString('ix_releases_nzb_creation_global_queue', $globalPlan); + $this->assertStringNotContainsString('filesort', strtolower($groupPlan)); + $this->assertStringNotContainsString('filesort', strtolower($globalPlan)); + } + + #[Test] + public function workers_cannot_claim_the_same_nzb_rows(): void + { + DB::table('releases')->insert(array_map(static fn (int $id): array => [ + 'id' => $id, + 'guid' => 'claim-'.$id, + 'groups_id' => 1, + 'categories_id' => 1, + 'nzbstatus' => 0, + 'postdate' => now()->subSeconds($id), + ], range(1, 10))); + + $first = NzbCreationCandidateQuery::claimBatch(1, 5, 'worker-one', ['id', 'categories_id']); + $second = NzbCreationCandidateQuery::claimBatch(1, 5, 'worker-two', ['id', 'categories_id']); + + $this->assertSame([1, 2, 3, 4, 5], $first->pluck('id')->all()); + $this->assertSame([6, 7, 8, 9, 10], $second->pluck('id')->all()); + $this->assertSame([], array_intersect($first->pluck('id')->all(), $second->pluck('id')->all())); + } + + private function createSchema(): void + { + DB::statement('CREATE TABLE `'.$this->table('settings').'` (name VARCHAR(255) PRIMARY KEY, value TEXT) ENGINE=InnoDB'); + DB::statement('CREATE TABLE `'.$this->table('root_categories').'` (id INT PRIMARY KEY, title VARCHAR(255)) ENGINE=InnoDB'); + DB::statement('CREATE TABLE `'.$this->table('categories').'` (id INT PRIMARY KEY, title VARCHAR(255), root_categories_id INT) ENGINE=InnoDB'); + DB::statement(<<table('releases')}` ( + id INT UNSIGNED PRIMARY KEY, guid VARCHAR(64) NOT NULL, groups_id INT UNSIGNED NOT NULL, + categories_id INT NOT NULL, nzbstatus TINYINT NOT NULL DEFAULT 0, postdate DATETIME, + nzb_creation_claimed_at TIMESTAMP NULL, nzb_creation_claim_token VARCHAR(64) NULL, + KEY ix_releases_nzb_creation_group_queue (nzbstatus, groups_id, postdate DESC, id, nzb_creation_claimed_at), + KEY ix_releases_nzb_creation_global_queue (nzbstatus, postdate DESC, id, nzb_creation_claimed_at) + ) ENGINE=InnoDB + SQL); + DB::statement('CREATE TABLE `'.$this->table('release_nzb_creation_failures').'` ( + releases_id INT UNSIGNED PRIMARY KEY, attempts SMALLINT UNSIGNED NOT NULL DEFAULT 0, + last_error TEXT NULL, created_at TIMESTAMP NULL, updated_at TIMESTAMP NULL, + FOREIGN KEY (releases_id) REFERENCES `'.$this->table('releases').'` (id) ON DELETE CASCADE + ) ENGINE=InnoDB'); + DB::table('settings')->insert([ + ['name' => 'categorizeforeign', 'value' => '0'], + ['name' => 'catwebdl', 'value' => '0'], + ['name' => 'releaseprocessingtimeout', 'value' => '120'], + ]); + DB::table('root_categories')->insert(['id' => 1, 'title' => 'Other']); + DB::table('categories')->insert(['id' => 1, 'title' => 'Misc', 'root_categories_id' => 1]); + } + + private function explain(string $sql): string + { + $row = DB::selectOne('EXPLAIN FORMAT=JSON '.$sql); + + return (string) reset($row); + } + + private function table(string $name): string + { + return $this->tablePrefix.$name; + } + + private function setEnvironmentValue(string $key, ?string $value): void + { + if ($value === null) { + putenv($key); + unset($_ENV[$key], $_SERVER[$key]); + + return; + } + putenv("{$key}={$value}"); + $_ENV[$key] = $_SERVER[$key] = $value; + } +} diff --git a/tests/Integration/ReleaseSchemaOptimizationMigrationMariaDbTest.php b/tests/Integration/ReleaseSchemaOptimizationMigrationMariaDbTest.php new file mode 100644 index 000000000..796f21261 --- /dev/null +++ b/tests/Integration/ReleaseSchemaOptimizationMigrationMariaDbTest.php @@ -0,0 +1,279 @@ +safeLoad(); + $this->tablePrefix = 'release_opt_'.getmypid().'_'.bin2hex(random_bytes(4)).'_'; + $this->setEnvironmentValue('APP_ENV', 'testing'); + $this->setEnvironmentValue('DB_URL', null); + $this->setEnvironmentValue('DB_CONNECTION', 'mariadb'); + $app = require __DIR__.'/../../bootstrap/app.php'; + $app->make(Kernel::class)->bootstrap(); + $app->make('config')->set('database.connections.mariadb.prefix', $this->tablePrefix); + $app->make('db')->purge('mariadb'); + + return $app; + } + + protected function setUp(): void + { + parent::setUp(); + if (DB::getDriverName() !== 'mariadb') { + $this->markTestSkipped('MariaDB integration test.'); + } + $this->createSchema(); + } + + protected function tearDown(): void + { + if (isset($this->tablePrefix) && preg_match('/^release_opt_\d+_[a-f0-9]{8}_$/', $this->tablePrefix) === 1) { + DB::statement('SET FOREIGN_KEY_CHECKS=0'); + foreach (['release_files', 'release_comments', 'release_nzb_creation_failures', 'release_nzb_passwords', 'releases'] as $table) { + DB::statement('DROP TABLE IF EXISTS `'.$this->table($table).'`'); + } + DB::statement('SET FOREIGN_KEY_CHECKS=1'); + } + DB::disconnect(); + parent::tearDown(); + } + + #[Test] + public function migration_up_and_down_preserve_sparse_state_and_incoming_foreign_keys(): void + { + $this->seedLegacyData(); + $migration = $this->migration(); + + $migration->up(); + + $this->assertTrue(Schema::hasTable('release_nzb_passwords')); + $this->assertTrue(Schema::hasTable('release_nzb_creation_failures')); + $this->assertSame('secret-one', DB::table('release_nzb_passwords')->where('releases_id', 1)->value('password')); + $this->assertSame(2, (int) DB::table('release_nzb_creation_failures')->where('releases_id', 1)->value('attempts')); + $this->assertSame('temporary', DB::table('release_nzb_creation_failures')->where('releases_id', 1)->value('last_error')); + $this->assertSame(['id'], $this->primaryKeyColumns()); + $this->assertSame('ascii_general_ci', $this->columnCollation('guid')); + $this->assertSame('char(36)', strtolower($this->columnType('guid'))); + $this->assertSame('ascii_general_ci', $this->columnCollation('leftguid')); + $this->assertSame(1, (int) DB::table('releases')->where('id', 1)->value('comments')); + $this->assertFalse(Schema::hasColumn('releases', 'nzb_password')); + $this->assertFalse(Schema::hasColumn('releases', 'proc_sorter')); + $this->assertFalse(Schema::hasColumn('release_comments', 'gid')); + $this->assertSame( + ['passwordstatus', 'haspreview', 'nzbstatus', 'leftguid', 'postdate', 'id', 'additional_pp_claimed_at'], + $this->indexColumns('ix_releases_add_pp_claim_queue'), + ); + $this->assertSame( + ['nzbstatus', 'groups_id', 'postdate', 'id', 'nzb_creation_claimed_at'], + $this->indexColumns('ix_releases_nzb_creation_group_queue'), + ); + $this->assertSame( + ['nzbstatus', 'postdate', 'id', 'nzb_creation_claimed_at'], + $this->indexColumns('ix_releases_nzb_creation_global_queue'), + ); + + try { + DB::table('releases')->insert($this->releaseRow(3, strtoupper('01234567-89ab-cdef-0123-456789abcdef'))); + $this->fail('ASCII case-insensitive GUID uniqueness was not enforced.'); + } catch (QueryException) { + $this->assertSame(2, DB::table('releases')->count()); + } + + DB::table('releases')->where('id', 2)->delete(); + $this->assertDatabaseMissing('release_nzb_passwords', ['releases_id' => 2]); + DB::table('release_files')->insert(['releases_id' => 1, 'name' => 'kept.nzb']); + + $migration->down(); + + $this->assertSame(['id', 'categories_id'], $this->primaryKeyColumns()); + $this->assertTrue(Schema::hasColumn('releases', 'nzb_password')); + $this->assertTrue(Schema::hasColumn('releases', 'nzb_creation_attempts')); + $this->assertTrue(Schema::hasColumn('release_comments', 'gid')); + $this->assertSame('secret-one', DB::table('releases')->where('id', 1)->value('nzb_password')); + $this->assertSame(2, (int) DB::table('releases')->where('id', 1)->value('nzb_creation_attempts')); + $this->assertSame('temporary', DB::table('releases')->where('id', 1)->value('nzb_creation_last_error')); + $this->assertFalse(Schema::hasTable('release_nzb_passwords')); + $this->assertFalse(Schema::hasTable('release_nzb_creation_failures')); + $this->assertDatabaseHas('release_files', ['releases_id' => 1, 'name' => 'kept.nzb']); + } + + private function createSchema(): void + { + $releases = $this->table('releases'); + $comments = $this->table('release_comments'); + $files = $this->table('release_files'); + DB::statement(<<insert([ + $this->releaseRow(1, '01234567-89ab-cdef-0123-456789abcdef', [ + 'nzb_password' => 'secret-one', + 'nzb_creation_attempts' => 2, + 'nzb_creation_last_error' => 'temporary', + 'comments' => 99, + ]), + $this->releaseRow(2, 'abcdef01-2345-6789-abcd-ef0123456789', ['nzb_password' => 'secret-two']), + ]); + DB::table('release_comments')->insert([ + ['releases_id' => 1, 'text' => 'visible', 'isvisible' => 1], + ['releases_id' => 1, 'text' => 'hidden', 'isvisible' => 0], + ]); + } + + /** @param array $overrides */ + private function releaseRow(int $id, string $guid, array $overrides = []): array + { + return [ + 'id' => $id, + 'categories_id' => 10, + 'name' => 'Release '.$id, + 'searchname' => 'Release '.$id, + 'groups_id' => 1, + 'size' => 1000, + 'postdate' => '2026-08-13 00:00:00', + 'adddate' => '2026-08-13 00:00:00', + 'guid' => $guid, + 'leftguid' => strtolower($guid[0]), + 'comments' => 0, + 'nzb_password' => null, + 'nzb_creation_attempts' => 0, + 'nzb_creation_last_error' => null, + ...$overrides, + ]; + } + + /** @return list */ + private function primaryKeyColumns(): array + { + return array_map( + static fn (object $row): string => (string) $row->COLUMN_NAME, + DB::select( + 'SELECT COLUMN_NAME FROM information_schema.KEY_COLUMN_USAGE + WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = ? AND CONSTRAINT_NAME = \'PRIMARY\' + ORDER BY ORDINAL_POSITION', + [$this->table('releases')], + ), + ); + } + + /** @return list */ + private function indexColumns(string $index): array + { + $rows = DB::select('SHOW INDEX FROM `'.$this->table('releases').'` WHERE Key_name = ?', [$index]); + usort($rows, static fn (object $left, object $right): int => (int) $left->Seq_in_index <=> (int) $right->Seq_in_index); + + return array_map( + static fn (object $row): string => (string) $row->Column_name, + $rows, + ); + } + + private function columnCollation(string $column): string + { + return (string) DB::selectOne( + 'SELECT COLLATION_NAME FROM information_schema.COLUMNS + WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = ? AND COLUMN_NAME = ?', + [$this->table('releases'), $column], + )->COLLATION_NAME; + } + + private function columnType(string $column): string + { + return (string) DB::selectOne( + 'SELECT COLUMN_TYPE FROM information_schema.COLUMNS + WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = ? AND COLUMN_NAME = ?', + [$this->table('releases'), $column], + )->COLUMN_TYPE; + } + + private function migration(): Migration + { + /** @var Migration $migration */ + $migration = require database_path('migrations/2026_08_13_001652_normalize_and_optimize_releases_table.php'); + + return $migration; + } + + private function table(string $name): string + { + return $this->tablePrefix.$name; + } + + private function setEnvironmentValue(string $key, ?string $value): void + { + if ($value === null) { + putenv($key); + unset($_ENV[$key], $_SERVER[$key]); + + return; + } + putenv("{$key}={$value}"); + $_ENV[$key] = $_SERVER[$key] = $value; + } +}