xaizek / rocketgit (License: AGPLv3+) (since 2018-12-09)
Light and fast Git hosting solution suitable to serve both as a hub or as a personal code storage with its tickets, pull requests, API and much more.
<root> / scripts / cron.php (11e10b282a7264b8d6a811d046b0c5e3bdbcb906) (7,807B) (mode 100644) [raw]
<?php
// This is called by cron
error_reporting(E_ALL);
ini_set('track_errors', 'On');
set_time_limit(0);

require_once('/etc/rocketgit/config.php');

$INC = dirname(__FILE__) . '/../inc';
require_once($INC . '/init.inc.php');
require_once($INC . '/log.inc.php');
require_once($INC . '/sql.inc.php');
require_once($INC . '/struct.inc.php');
require_once($INC . '/repo.inc.php');
require_once($INC . '/keys.inc.php');
require_once($INC . '/fixes.inc.php');
require_once($INC . '/mr.inc.php');
require_once($INC . '/admin.inc.php');
require_once($INC . '/stats.inc.php');
require_once($INC . '/ver.php');

$now = time();

rg_log_set_file($rg_log_dir . '/cron.log');
rg_log_set_sid('000000'); // to spread the logs

// locking
rg_lock_or_exit('cron.lock');

rg_log('Start (ver=' . $rocketgit_version . ')...');

rg_sql_app('rg-cron');
$db = rg_sql_open($rg_sql);
if ($db === FALSE) {
	rg_log('Cannot connect to database (' . rg_sql_error() . ')!');
	// TODO: inform admin - already by e-mail?
	exit(1);
}

if (rg_struct_ok($db) === FALSE)
	exit(1);

$first_install = rg_state_get($db, 'first_install');
if ($first_install === FALSE)
	exit(1);
if (empty($first_install)) {
	rg_log('Admin did not created the first user, so no need to run this code.');
	exit(0);
}

$rg['hostname'] = rg_state_get($db, 'hostname');
$rg['http_allow'] = rg_state_get($db, 'http_allow');
$rg['https_allow'] = rg_state_get($db, 'https_allow');
rg_base_url_build($rg['hostname'], $rg['http_allow'], $rg['https_allow']);
$rg['base_url'] = rg_base_url();
rg_log('DEBUG: base_url=' . rg_base_url());
$rg['debug'] = rg_state_get($db, 'debug');

// From now on, we give up the lock because some tasks take a long time to run.
rg_unlock('cron.lock');

rg_stats_insert($db);

$today_00 = gmmktime(0, 0, 0, gmdate('m'), gmdate('d'), gmdate('Y'));
$today_01 = gmmktime(1, 0, 0, gmdate('m'), gmdate('d'), gmdate('Y'));

$ldru = rg_state_get($db, 'last_disk_repo_usage');
while (($ldru < $today_00) && (rg_load() < 30)) {
	rg_log('Compute repository sizes if dirty...');
	$sql = 'SELECT uid, repo_id, master, disk_used_mb, git_mb'
		. ', artifacts_mb'
		. ' FROM repos'
		. ' WHERE deleted = 0';
	$res = rg_sql_query($db, $sql);
	if ($res === FALSE) {
		rg_internal_error('Cannot get repo info'
			. ' (' . rg_sql_error() . ')!');
		break;
	}

	$ok = TRUE;
	while (($row = rg_sql_fetch_array($res))) {
		$di = rg_repo_size($row['uid'], $row['repo_id']);
		if ($di === FALSE) {
			rg_internal_error('Cannot compute the repo size: '
				. rg_repo_error());
			$ok = FALSE;
			continue;
		}
		if (($di['disk_used_mb'] != $row['disk_used_mb'])
			|| ($di['git_mb'] != $row['git_mb'])
			|| ($di['artifacts_mb'] != $row['artifacts_mb'])) {
			$sql = 'UPDATE repos'
				. ' SET disk_used_mb = ' . $di['disk_used_mb']
				. ', git_mb = ' . $di['git_mb']
				. ', artifacts_mb = ' . $di['artifacts_mb']
				. ' WHERE repo_id = ' . $row['repo_id'];
			$res2 = rg_sql_query($db, $sql);
			if ($res2 === FALSE) {
				rg_internal_error('Cannot update repo: ' . rg_sql_error());
				$ok = FALSE;
				break;
			}
			rg_sql_free_result($res2);
		}
	}
	rg_sql_free_result($res);
	if ($ok) {
		$ldru = time();
		rg_state_set($db, 'last_disk_repo_usage', $ldru);
	}
	break;
}

$r = rg_state_get($db, 'last_disk_user_usage');
while (($r < $ldru) && (rg_load() < 30)) {
	rg_log('Compute repository sizes per user...');
	$sql = 'SELECT uid, SUM(disk_used_mb) AS disk_used_mb'
		. ', SUM(git_mb) AS git_mb'
		. ', SUM(artifacts_mb) AS artifacts_mb'
		. ' FROM repos'
		. ' GROUP BY uid';
	$res = rg_sql_query($db, $sql);
	if ($res === FALSE) {
		rg_internal_error('Cannot get repo info (' . rg_sql_error() . ')!');
		break;
	}

	$ok = TRUE;
	while (($row = rg_sql_fetch_array($res))) {
		$sql = 'UPDATE users'
			. ' SET disk_used_mb = ' . $row['disk_used_mb']
			. ', git_mb = ' . $row['git_mb']
			. ', artifacts_mb = ' . $row['artifacts_mb']
			. ' WHERE uid = ' . $row['uid']
			. ' AND (disk_used_mb != ' . $row['disk_used_mb']
			. ' OR git_mb != ' . $row['git_mb']
			. ' OR artifacts_mb != ' . $row['artifacts_mb']
			. ')';
		$res2 = rg_sql_query($db, $sql);
		if ($res2 === FALSE) {
			rg_internal_error('Cannot update (' . rg_sql_error() . ')!');
			$ok = FALSE;
			break;
		}
		rg_sql_free_result($res2);
	}
	rg_sql_free_result($res);
	if ($ok)
		rg_state_set($db, 'last_disk_user_usage', time());
	break;
}

$r = rg_state_get($db, 'last_clean_forget_pass');
while (($r < $today_00) && (rg_load() < 50)) {
	rg_log_enter('Clean old forget_pass entries...');
	$sql = 'DELETE FROM forgot_pass WHERE expire < ' . $now;
	$res = rg_sql_query($db, $sql);
	if ($res === FALSE) {
		rg_internal_error('Cannot delete forgot_pass'
			. ' (' . rg_sql_error() . ')!');
	} else {
		rg_sql_free_result($res);
		rg_state_set($db, 'last_clean_forget_pass', time());
	}
	rg_log_exit();
	break;
}

if (gmdate('i') == '30') {
	rg_log_enter('Clean old tokens...');
	$sql = 'DELETE FROM tokens WHERE expire < ' . $now;
	$res = rg_sql_query($db, $sql);
	if ($res === FALSE)
		rg_internal_error('Cannot delete expired tokens (' . rg_sql_error() . ')!');
	else
		rg_sql_free_result($res);
	rg_log_exit();
}

if (gmdate('i') == '01') {
	rg_log_enter('Clean old sess entries...');
	$sql = 'DELETE FROM sess WHERE expire < ' . $now;
	$res = rg_sql_query($db, $sql);
	if ($res === FALSE)
		rg_internal_error('Cannot delete expired sessions'
			. ' (' . rg_sql_error() . ')!');
	else
		rg_sql_free_result($res);
	rg_log_exit();
}

$r = rg_state_get($db, 'last_clean_tokens_ip');
while (($r < $today_00) && (rg_load() < 50)) {
	rg_log_enter('Clean old login_tokens_ip entries...');
	$sql = 'DELETE FROM login_tokens_ip WHERE expire < ' . $now;
	$res = rg_sql_query($db, $sql);
	if ($res === FALSE) {
		rg_internal_error('Cannot delete login_tokens_ip'
			. ' (' . rg_sql_error() . ')!');
	} else {
		rg_sql_free_result($res);
		rg_state_set($db, 'last_clean_tokens_ip', time());
	}
	rg_log_exit();
	break;
}

$r = rg_state_get($db, 'part_clean_last_run');
$when = gmmktime(0, 0, 0, gmdate('m'), 1, gmdate('Y'));
while (($r < $today_00) && (rg_load() < 50)) {
	rg_log_enter('Clean old part tables...');
	$ok = TRUE;
	foreach ($rg_sql_struct_parts as $table => $ti) {
		// value is in 'months' = how many months we do want to keep the table
		$r = rg_state_get($db, 'part_clean_table_' . $table);
		if ($r === FALSE) {
			$ok = FALSE;
			break;
		}
		if ($r === '') // no expiration set => forever
			continue;
		if ($r == 0) // no expiration
			continue;

		$ts = gmmktime(0, 0, 0, gmdate('m') - $r, 1, gmdate('Y'));
		$limit = gmdate('Y_m', $ts);
		rg_log('DEBUG: limit=' . $limit);

		$sql = 'SELECT relname FROM pg_class'
			. ' WHERE relname LIKE \'' . $table . '_%\''
			. ' AND relkind = \'r\''
			. ' AND relname < \'' . $table . '_' . $limit . '\''
			. ' ORDER BY relname';
		$res = rg_sql_query($db, $sql);
		if ($res === FALSE) {
			rg_internal_error('Cannot check if table exists'
				. ' (' . rg_sql_error() . ')!');
			$ok = FALSE;
			break;
		}
		while (($row = rg_sql_fetch_array($res))) {
			$sql = 'DROP TABLE ' . $row['relname'];
			$res2 = rg_sql_query($db, $sql);
			if ($res2 === FALSE) {
				rg_internal_error('Cannot drop table (' . rg_sql_error() . ')!');
				$ok = FALSE;
				break;
			}
			rg_sql_free_result($res2);
		}
		rg_sql_free_result($res);
	}

	if ($ok)
		rg_state_set($db, 'part_clean_last_run', time());
	rg_log_exit();
	break;
}

$r = rg_state_get($db, 'report1');
while (($now >= $today_01) && ($r < $today_01) && (rg_load() < 30)) {
	rg_log('Sending report1...');
	$r = rg_admin_report1($db, $rg);
	if ($r === FALSE)
		break;
	rg_state_set($db, 'report1', time());
	break;
}

// TODO: move it as an event after the push
rg_mr_queue_process($db);

if (gmdate('Hi') == '0305')
	rg_clean_logs('/var/log/rocketgit');

rg_log_cron();

rg_log('Done!');
Hints

Before first commit, do not forget to setup your git environment:
git config --global user.name "your_name_here"
git config --global user.email "your@email_here"

Clone this repository using HTTP(S):
git clone https://code.reversed.top/user/xaizek/rocketgit

Clone this repository using ssh (do not forget to upload a key first):
git clone ssh://rocketgit@code.reversed.top/user/xaizek/rocketgit

You are allowed to anonymously push to this repository.
This means that your pushed commits will automatically be transformed into a pull request:
... clone the repository ...
... make some changes and some commits ...
git push origin master