#!/usr/bin/env php name = $name; $task->type = 'sql'; $task->command = $command; } function msz_sched_task_func(string $name, bool $slow, callable $command): void { global $schedTasks, $runSlow; if($slow && !$runSlow) return; $schedTasks[] = $task = new stdClass; $task->name = $name; $task->type = 'func'; $task->command = $command; } for($i = 1; $i < count($argv); ++$i) { $argvi = $argv[$i]; if($argvi === 'slow') $runSlow = true; else die('Unknown argument specified.' . PHP_EOL); } if($runSlow) echo 'Also running slow tasks!' . PHP_EOL; else echo 'Only running quick tasks!' . PHP_EOL; echo PHP_EOL; msz_sched_task_sql('Ensure main role exists.', true, 'INSERT IGNORE INTO msz_roles (role_id, role_name, role_hierarchy, role_colour, role_description, role_created) VALUES (1, "Member", 1, 1073741824, NULL, NOW())'); msz_sched_task_sql('Ensure all users have the main role.', true, 'INSERT INTO msz_users_roles (user_id, role_id) SELECT user_id, 1 FROM msz_users AS u WHERE NOT EXISTS (SELECT 1 FROM msz_users_roles AS ur WHERE role_id = 1 AND u.user_id = ur.user_id)'); msz_sched_task_sql('Ensure all display_role field values are correct with msz_users_roles.', true, 'UPDATE msz_users AS u SET display_role = (SELECT ur.role_id FROM msz_users_roles AS ur LEFT JOIN msz_roles AS r ON r.role_id = ur.role_id WHERE ur.user_id = u.user_id ORDER BY role_hierarchy DESC LIMIT 1) WHERE NOT EXISTS (SELECT 1 FROM msz_users_roles AS ur WHERE ur.role_id = u.display_role AND ur.user_id = u.user_id)'); msz_sched_task_sql('Remove expired sessions.', false, 'DELETE FROM msz_sessions WHERE session_expires < NOW()'); msz_sched_task_sql('Remove old password reset tokens.', false, 'DELETE FROM msz_users_password_resets WHERE reset_requested < NOW() - INTERVAL 1 WEEK'); msz_sched_task_sql('Remove old login attempt logs.', false, 'DELETE FROM msz_login_attempts WHERE attempt_created < NOW() - INTERVAL 1 MONTH'); msz_sched_task_sql('Remove old audit log entries.', false, 'DELETE FROM msz_audit_log WHERE log_created < NOW() - INTERVAL 3 MONTH'); msz_sched_task_sql('Remove stale forum tracking entries.', false, 'DELETE tt FROM msz_forum_topics_track AS tt LEFT JOIN msz_forum_topics AS t ON t.topic_id = tt.topic_id WHERE t.topic_bumped < NOW() - INTERVAL 1 MONTH'); msz_sched_task_sql('Synchronise forum_id.', true, 'UPDATE msz_forum_posts AS p INNER JOIN msz_forum_topics AS t ON t.topic_id = p.topic_id SET p.forum_id = t.forum_id'); msz_sched_task_func('Recount forum topics and posts.', true, function() use ($msz) { $msz->getForumContext()->getCategories()->syncForumCounters(); }); msz_sched_task_sql('Clean up expired 2fa tokens.', false, 'DELETE FROM msz_auth_tfa WHERE tfa_created < NOW() - INTERVAL 15 MINUTE'); // very heavy stuff that should msz_sched_task_func('Resync statistics counters.', true, function() use ($msz) { $dbConn = $msz->getDbConn(); $counters = $msz->getCounters(); $stats = [ 'users:total' => 'SELECT COUNT(*) FROM msz_users', 'users:active' => 'SELECT COUNT(*) FROM msz_users WHERE user_active IS NOT NULL AND user_deleted IS NULL', 'users:inactive' => 'SELECT COUNT(*) FROM msz_users WHERE user_active IS NULL OR user_deleted IS NOT NULL', 'users:online:recent' => 'SELECT COUNT(*) FROM msz_users WHERE user_active >= NOW() - INTERVAL 1 HOUR', // used to be 5 minutes, but this script runs every hour soooo 'users:online:today' => 'SELECT COUNT(*) FROM msz_users WHERE user_active >= NOW() - INTERVAL 24 HOUR', 'auditlogs:total' => 'SELECT COUNT(*) FROM msz_audit_log', 'changelog:changes:total' => 'SELECT COUNT(*) FROM msz_changelog_changes', 'changelog:tags:total' => 'SELECT COUNT(*) FROM msz_changelog_tags', 'comments:cats:total' => 'SELECT COUNT(*) FROM msz_comments_categories', 'comments:cats:locked' => 'SELECT COUNT(*) FROM msz_comments_categories WHERE category_locked IS NOT NULL', 'comments:posts:total' => 'SELECT COUNT(*) FROM msz_comments_posts', 'comments:posts:visible' => 'SELECT COUNT(*) FROM msz_comments_posts WHERE comment_deleted IS NULL', 'comments:posts:deleted' => 'SELECT COUNT(*) FROM msz_comments_posts WHERE comment_deleted IS NOT NULL', 'comments:posts:replies' => 'SELECT COUNT(*) FROM msz_comments_posts WHERE comment_reply_to IS NOT NULL', 'comments:posts:pinned' => 'SELECT COUNT(*) FROM msz_comments_posts WHERE comment_pinned IS NOT NULL', 'comments:posts:edited' => 'SELECT COUNT(*) FROM msz_comments_posts WHERE comment_edited IS NOT NULL', 'comments:votes:likes' => 'SELECT COUNT(*) FROM msz_comments_votes WHERE comment_vote > 0', 'comments:votes:dislikes' => 'SELECT COUNT(*) FROM msz_comments_votes WHERE comment_vote < 0', 'forum:posts:total' => 'SELECT COUNT(*) FROM msz_forum_posts', 'forum:posts:visible' => 'SELECT COUNT(*) FROM msz_forum_posts WHERE post_deleted IS NULL', 'forum:posts:deleted' => 'SELECT COUNT(*) FROM msz_forum_posts WHERE post_deleted IS NOT NULL', 'forum:posts:edited' => 'SELECT COUNT(*) FROM msz_forum_posts WHERE post_edited IS NOT NULL', 'forum:posts:parse:plain' => 'SELECT COUNT(*) FROM msz_forum_posts WHERE post_parse = 0', 'forum:posts:parse:bbcode' => 'SELECT COUNT(*) FROM msz_forum_posts WHERE post_parse = 1', 'forum:posts:parse:markdown' => 'SELECT COUNT(*) FROM msz_forum_posts WHERE post_parse = 2', 'forum:posts:signature' => 'SELECT COUNT(*) FROM msz_forum_posts WHERE post_display_signature <> 0', 'forum:topics:total' => 'SELECT COUNT(*) FROM msz_forum_topics', 'forum:topics:type:normal' => 'SELECT COUNT(*) FROM msz_forum_topics WHERE topic_type = 0', 'forum:topics:type:pinned' => 'SELECT COUNT(*) FROM msz_forum_topics WHERE topic_type = 1', 'forum:topics:type:announce' => 'SELECT COUNT(*) FROM msz_forum_topics WHERE topic_type = 2', 'forum:topics:type:global' => 'SELECT COUNT(*) FROM msz_forum_topics WHERE topic_type = 3', 'forum:topics:visible' => 'SELECT COUNT(*) FROM msz_forum_topics WHERE topic_deleted IS NULL', 'forum:topics:deleted' => 'SELECT COUNT(*) FROM msz_forum_topics WHERE topic_deleted IS NOT NULL', 'forum:topics:locked' => 'SELECT COUNT(*) FROM msz_forum_topics WHERE topic_locked IS NOT NULL', 'auth:attempts:total' => 'SELECT COUNT(*) FROM msz_login_attempts', 'auth:attempts:failed' => 'SELECT COUNT(*) FROM msz_login_attempts WHERE attempt_success = 0', 'auth:sessions:total' => 'SELECT COUNT(*) FROM msz_sessions', 'auth:tfasessions:total' => 'SELECT COUNT(*) FROM msz_auth_tfa', 'auth:recovery:total' => 'SELECT COUNT(*) FROM msz_users_password_resets', 'users:modnotes:total' => 'SELECT COUNT(*) FROM msz_users_modnotes', 'users:warnings:total' => 'SELECT COUNT(*) FROM msz_users_warnings', 'users:warnings:visible' => 'SELECT COUNT(*) FROM msz_users_warnings WHERE warn_created > NOW() - INTERVAL 90 DAY', 'users:bans:total' => 'SELECT COUNT(*) FROM msz_users_bans', 'users:bans:active' => 'SELECT COUNT(*) FROM msz_users_bans WHERE ban_expires IS NULL OR ban_expires > NOW()', ]; foreach($stats as $name => $query) { $result = $dbConn->query($query); $counters->set($name, $result->next() ? $result->getInteger(0) : 0); } }); msz_sched_task_func('Recalculate permissions (maybe)...', false, function() use ($msz) { $needsRecalc = $msz->getConfig()->getBoolean('perms.needsRecalc'); if(!$needsRecalc) return; $msz->getConfig()->removeValues('perms.needsRecalc'); $msz->getPerms()->precalculatePermissions($msz->getForumContext()->getCategories()); }); echo 'Running ' . count($schedTasks) . ' tasks...' . PHP_EOL; $dbConn = $msz->getDbConn(); foreach($schedTasks as $task) { echo '=> ' . $task->name . PHP_EOL; $success = true; try { switch($task->type) { case 'sql': $affected = $dbConn->execute($task->command); echo $affected . ' rows affected. ' . PHP_EOL; break; case 'func': $result = ($task->command)(); if(is_bool($result)) $success = $result; break; default: $success = false; echo 'Unknown task type.' . PHP_EOL; break; } } catch(Exception $ex) { $success = false; echo (string)$ex; } finally { if($success) echo 'Done!'; else echo '!!! FAILED !!!'; echo PHP_EOL; } echo PHP_EOL; } echo 'Took ' . number_format(microtime(true) - MSZ_STARTUP, 5) . 'ms.' . PHP_EOL;