mirror of
https://github.com/RayLabsHQ/gitea-mirror.git
synced 2026-03-26 15:47:43 +03:00
* feat: add custom sync start time scheduling * Updated UI * docs: add updated issue 240 UI screenshot * fix: improve schedule UI with client-side next run calc and timezone handling - Compute next scheduled run client-side via useMemo to avoid permanent "Calculating..." state when server hasn't set nextRun yet - Default to browser timezone when enabling syncing (not UTC) - Show actual saved timezone in badge, use it consistently in all handlers - Match time input background to select trigger in dark mode - Add clock icon to time picker with hidden native indicator
158 lines
5.2 KiB
TypeScript
158 lines
5.2 KiB
TypeScript
import { db, configs } from "@/lib/db";
|
|
import { eq } from "drizzle-orm";
|
|
import { v4 as uuidv4 } from "uuid";
|
|
import { encrypt } from "@/lib/utils/encryption";
|
|
import { getNextScheduledRun, normalizeTimezone } from "@/lib/utils/schedule-utils";
|
|
|
|
export interface DefaultConfigOptions {
|
|
userId: string;
|
|
envOverrides?: {
|
|
githubToken?: string;
|
|
githubUsername?: string;
|
|
giteaUrl?: string;
|
|
giteaExternalUrl?: string;
|
|
giteaToken?: string;
|
|
giteaUsername?: string;
|
|
scheduleEnabled?: boolean;
|
|
scheduleInterval?: number | string;
|
|
cleanupEnabled?: boolean;
|
|
cleanupRetentionDays?: number;
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Creates a default configuration for a new user with sensible defaults
|
|
* Environment variables can override these defaults
|
|
*/
|
|
export async function createDefaultConfig({ userId, envOverrides = {} }: DefaultConfigOptions) {
|
|
// Check if config already exists
|
|
const existingConfig = await db
|
|
.select()
|
|
.from(configs)
|
|
.where(eq(configs.userId, userId))
|
|
.limit(1);
|
|
|
|
if (existingConfig.length > 0) {
|
|
return existingConfig[0];
|
|
}
|
|
|
|
// Read environment variables for overrides
|
|
const githubToken = envOverrides.githubToken || process.env.GITHUB_TOKEN || "";
|
|
const githubUsername = envOverrides.githubUsername || process.env.GITHUB_USERNAME || "";
|
|
const giteaUrl = envOverrides.giteaUrl || process.env.GITEA_URL || "";
|
|
const giteaExternalUrl =
|
|
envOverrides.giteaExternalUrl || process.env.GITEA_EXTERNAL_URL || "";
|
|
const giteaToken = envOverrides.giteaToken || process.env.GITEA_TOKEN || "";
|
|
const giteaUsername = envOverrides.giteaUsername || process.env.GITEA_USERNAME || "";
|
|
|
|
// Schedule config from env - default to ENABLED
|
|
const scheduleEnabled = envOverrides.scheduleEnabled ??
|
|
(process.env.SCHEDULE_ENABLED === "false" ? false : true); // Default: ENABLED
|
|
const scheduleInterval = envOverrides.scheduleInterval ??
|
|
(process.env.SCHEDULE_INTERVAL || 86400); // Default: daily
|
|
const scheduleTimezone = normalizeTimezone(process.env.SCHEDULE_TIMEZONE || "UTC");
|
|
let scheduleNextRun: Date | null = null;
|
|
if (scheduleEnabled) {
|
|
try {
|
|
scheduleNextRun = getNextScheduledRun(scheduleInterval, new Date(), scheduleTimezone);
|
|
} catch {
|
|
scheduleNextRun = new Date(Date.now() + 86400 * 1000);
|
|
}
|
|
}
|
|
|
|
// Cleanup config from env - default to ENABLED
|
|
const cleanupEnabled = envOverrides.cleanupEnabled ??
|
|
(process.env.CLEANUP_ENABLED === "false" ? false : true); // Default: ENABLED
|
|
const cleanupRetentionDays = envOverrides.cleanupRetentionDays ??
|
|
(process.env.CLEANUP_RETENTION_DAYS ? parseInt(process.env.CLEANUP_RETENTION_DAYS, 10) * 86400 : 604800); // Default: 7 days
|
|
|
|
// Create default configuration
|
|
const configId = uuidv4();
|
|
const defaultConfig = {
|
|
id: configId,
|
|
userId,
|
|
name: "Default Configuration",
|
|
isActive: true,
|
|
githubConfig: {
|
|
owner: githubUsername,
|
|
type: "personal",
|
|
token: githubToken ? encrypt(githubToken) : "",
|
|
includeStarred: false,
|
|
includeForks: true,
|
|
includeArchived: false,
|
|
includePrivate: false,
|
|
includePublic: true,
|
|
includeOrganizations: [],
|
|
starredReposOrg: "starred",
|
|
starredReposMode: "dedicated-org",
|
|
mirrorStrategy: "preserve",
|
|
defaultOrg: "github-mirrors",
|
|
},
|
|
giteaConfig: {
|
|
url: giteaUrl,
|
|
externalUrl: giteaExternalUrl || undefined,
|
|
token: giteaToken ? encrypt(giteaToken) : "",
|
|
defaultOwner: giteaUsername,
|
|
mirrorInterval: "8h",
|
|
lfs: false,
|
|
wiki: false,
|
|
visibility: "public",
|
|
createOrg: true,
|
|
addTopics: true,
|
|
preserveVisibility: false,
|
|
forkStrategy: "reference",
|
|
issueConcurrency: 3,
|
|
pullRequestConcurrency: 5,
|
|
backupStrategy: "on-force-push",
|
|
backupBeforeSync: true, // Deprecated: kept for backward compat
|
|
backupRetentionCount: 5,
|
|
backupRetentionDays: 30,
|
|
backupDirectory: "data/repo-backups",
|
|
blockSyncOnBackupFailure: true,
|
|
},
|
|
include: [],
|
|
exclude: [],
|
|
scheduleConfig: {
|
|
enabled: scheduleEnabled,
|
|
interval: String(scheduleInterval),
|
|
timezone: scheduleTimezone,
|
|
concurrent: false,
|
|
batchSize: 5, // Reduced from 10 to be more conservative with GitHub API limits
|
|
lastRun: null,
|
|
nextRun: scheduleNextRun,
|
|
},
|
|
cleanupConfig: {
|
|
enabled: cleanupEnabled,
|
|
retentionDays: cleanupRetentionDays,
|
|
deleteFromGitea: false,
|
|
deleteIfNotInGitHub: true,
|
|
protectedRepos: [],
|
|
dryRun: false,
|
|
orphanedRepoAction: "archive",
|
|
batchSize: 10,
|
|
pauseBetweenDeletes: 2000,
|
|
lastRun: null,
|
|
nextRun: cleanupEnabled ? new Date(Date.now() + getCleanupInterval(cleanupRetentionDays) * 1000) : null,
|
|
},
|
|
createdAt: new Date(),
|
|
updatedAt: new Date(),
|
|
};
|
|
|
|
// Insert the default config
|
|
await db.insert(configs).values(defaultConfig);
|
|
|
|
return defaultConfig;
|
|
}
|
|
|
|
/**
|
|
* Calculate cleanup interval based on retention period
|
|
*/
|
|
function getCleanupInterval(retentionSeconds: number): number {
|
|
const days = retentionSeconds / 86400;
|
|
if (days <= 1) return 21600; // 6 hours
|
|
if (days <= 3) return 43200; // 12 hours
|
|
if (days <= 7) return 86400; // 24 hours
|
|
if (days <= 30) return 172800; // 48 hours
|
|
return 604800; // 1 week
|
|
}
|