tsc issues

This commit is contained in:
Arunavo Ray
2025-08-28 08:34:27 +05:30
parent 389f8dd292
commit ad7418aef2
10 changed files with 128 additions and 151 deletions

View File

@@ -47,7 +47,6 @@ async function createTestJob(): Promise<string> {
jobType: "mirror", jobType: "mirror",
totalItems: 10, totalItems: 10,
itemIds: ['item-1', 'item-2', 'item-3', 'item-4', 'item-5'], itemIds: ['item-1', 'item-2', 'item-3', 'item-4', 'item-5'],
completedItems: 2, // Simulate partial completion
inProgress: true, inProgress: true,
}); });

View File

@@ -74,7 +74,11 @@ export function extractUserFromHeaders(headers: Headers): {
} }
} }
return { username, email, name }; return {
username: username || undefined,
email: email || undefined,
name: name || undefined
};
} }
// Find or create user from header auth // Find or create user from header auth

View File

@@ -53,7 +53,7 @@ async function cleanupForUser(userId: string, retentionSeconds: number): Promise
let mirrorJobsDeleted = 0; let mirrorJobsDeleted = 0;
// Clean up old events // Clean up old events
const eventsResult = await db await db
.delete(events) .delete(events)
.where( .where(
and( and(
@@ -61,10 +61,10 @@ async function cleanupForUser(userId: string, retentionSeconds: number): Promise
lt(events.createdAt, cutoffDate) lt(events.createdAt, cutoffDate)
) )
); );
eventsDeleted = eventsResult.changes || 0; eventsDeleted = 0; // SQLite delete doesn't return count
// Clean up old mirror jobs (only completed ones) // Clean up old mirror jobs (only completed ones)
const jobsResult = await db await db
.delete(mirrorJobs) .delete(mirrorJobs)
.where( .where(
and( and(
@@ -73,7 +73,7 @@ async function cleanupForUser(userId: string, retentionSeconds: number): Promise
lt(mirrorJobs.timestamp, cutoffDate) lt(mirrorJobs.timestamp, cutoffDate)
) )
); );
mirrorJobsDeleted = jobsResult.changes || 0; mirrorJobsDeleted = 0; // SQLite delete doesn't return count
console.log(`Cleanup completed for user ${userId}: ${eventsDeleted} events, ${mirrorJobsDeleted} jobs deleted`); console.log(`Cleanup completed for user ${userId}: ${eventsDeleted} events, ${mirrorJobsDeleted} jobs deleted`);

View File

@@ -19,6 +19,7 @@ export const githubConfigSchema = z.object({
token: z.string(), token: z.string(),
includeStarred: z.boolean().default(false), includeStarred: z.boolean().default(false),
includeForks: z.boolean().default(true), includeForks: z.boolean().default(true),
skipForks: z.boolean().default(false),
includeArchived: z.boolean().default(false), includeArchived: z.boolean().default(false),
includePrivate: z.boolean().default(true), includePrivate: z.boolean().default(true),
includePublic: z.boolean().default(true), includePublic: z.boolean().default(true),
@@ -33,6 +34,7 @@ export const giteaConfigSchema = z.object({
url: z.url(), url: z.url(),
token: z.string(), token: z.string(),
defaultOwner: z.string(), defaultOwner: z.string(),
organization: z.string().optional(),
mirrorInterval: z.string().default("8h"), mirrorInterval: z.string().default("8h"),
lfs: z.boolean().default(false), lfs: z.boolean().default(false),
wiki: z.boolean().default(false), wiki: z.boolean().default(false),
@@ -45,6 +47,7 @@ export const giteaConfigSchema = z.object({
addTopics: z.boolean().default(true), addTopics: z.boolean().default(true),
topicPrefix: z.string().optional(), topicPrefix: z.string().optional(),
preserveVisibility: z.boolean().default(true), preserveVisibility: z.boolean().default(true),
preserveOrgStructure: z.boolean().default(false),
forkStrategy: z forkStrategy: z
.enum(["skip", "reference", "full-copy"]) .enum(["skip", "reference", "full-copy"])
.default("reference"), .default("reference"),
@@ -76,6 +79,8 @@ export const scheduleConfigSchema = z.object({
updateInterval: z.number().default(86400000), updateInterval: z.number().default(86400000),
skipRecentlyMirrored: z.boolean().default(true), skipRecentlyMirrored: z.boolean().default(true),
recentThreshold: z.number().default(3600000), recentThreshold: z.number().default(3600000),
lastRun: z.coerce.date().optional(),
nextRun: z.coerce.date().optional(),
}); });
export const cleanupConfigSchema = z.object({ export const cleanupConfigSchema = z.object({
@@ -90,6 +95,8 @@ export const cleanupConfigSchema = z.object({
.default("archive"), .default("archive"),
batchSize: z.number().default(10), batchSize: z.number().default(10),
pauseBetweenDeletes: z.number().default(2000), pauseBetweenDeletes: z.number().default(2000),
lastRun: z.coerce.date().optional(),
nextRun: z.coerce.date().optional(),
}); });
export const configSchema = z.object({ export const configSchema = z.object({
@@ -243,7 +250,7 @@ export const users = sqliteTable("users", {
.default(sql`(unixepoch())`), .default(sql`(unixepoch())`),
// Custom fields // Custom fields
username: text("username"), username: text("username"),
}); }, (_table) => []);
export const events = sqliteTable("events", { export const events = sqliteTable("events", {
id: text("id").primaryKey(), id: text("id").primaryKey(),
@@ -256,13 +263,11 @@ export const events = sqliteTable("events", {
createdAt: integer("created_at", { mode: "timestamp" }) createdAt: integer("created_at", { mode: "timestamp" })
.notNull() .notNull()
.default(sql`(unixepoch())`), .default(sql`(unixepoch())`),
}, (table) => { }, (table) => [
return { index("idx_events_user_channel").on(table.userId, table.channel),
userChannelIdx: index("idx_events_user_channel").on(table.userId, table.channel), index("idx_events_created_at").on(table.createdAt),
createdAtIdx: index("idx_events_created_at").on(table.createdAt), index("idx_events_read").on(table.read),
readIdx: index("idx_events_read").on(table.read), ]);
};
});
export const configs = sqliteTable("configs", { export const configs = sqliteTable("configs", {
id: text("id").primaryKey(), id: text("id").primaryKey(),
@@ -305,7 +310,7 @@ export const configs = sqliteTable("configs", {
updatedAt: integer("updated_at", { mode: "timestamp" }) updatedAt: integer("updated_at", { mode: "timestamp" })
.notNull() .notNull()
.default(sql`(unixepoch())`), .default(sql`(unixepoch())`),
}); }, (_table) => []);
export const repositories = sqliteTable("repositories", { export const repositories = sqliteTable("repositories", {
id: text("id").primaryKey(), id: text("id").primaryKey(),
@@ -362,17 +367,15 @@ export const repositories = sqliteTable("repositories", {
updatedAt: integer("updated_at", { mode: "timestamp" }) updatedAt: integer("updated_at", { mode: "timestamp" })
.notNull() .notNull()
.default(sql`(unixepoch())`), .default(sql`(unixepoch())`),
}, (table) => { }, (table) => [
return { index("idx_repositories_user_id").on(table.userId),
userIdIdx: index("idx_repositories_user_id").on(table.userId), index("idx_repositories_config_id").on(table.configId),
configIdIdx: index("idx_repositories_config_id").on(table.configId), index("idx_repositories_status").on(table.status),
statusIdx: index("idx_repositories_status").on(table.status), index("idx_repositories_owner").on(table.owner),
ownerIdx: index("idx_repositories_owner").on(table.owner), index("idx_repositories_organization").on(table.organization),
organizationIdx: index("idx_repositories_organization").on(table.organization), index("idx_repositories_is_fork").on(table.isForked),
isForkedIdx: index("idx_repositories_is_fork").on(table.isForked), index("idx_repositories_is_starred").on(table.isStarred),
isStarredIdx: index("idx_repositories_is_starred").on(table.isStarred), ]);
};
});
export const mirrorJobs = sqliteTable("mirror_jobs", { export const mirrorJobs = sqliteTable("mirror_jobs", {
id: text("id").primaryKey(), id: text("id").primaryKey(),
@@ -405,15 +408,13 @@ export const mirrorJobs = sqliteTable("mirror_jobs", {
startedAt: integer("started_at", { mode: "timestamp" }), startedAt: integer("started_at", { mode: "timestamp" }),
completedAt: integer("completed_at", { mode: "timestamp" }), completedAt: integer("completed_at", { mode: "timestamp" }),
lastCheckpoint: integer("last_checkpoint", { mode: "timestamp" }), lastCheckpoint: integer("last_checkpoint", { mode: "timestamp" }),
}, (table) => { }, (table) => [
return { index("idx_mirror_jobs_user_id").on(table.userId),
userIdIdx: index("idx_mirror_jobs_user_id").on(table.userId), index("idx_mirror_jobs_batch_id").on(table.batchId),
batchIdIdx: index("idx_mirror_jobs_batch_id").on(table.batchId), index("idx_mirror_jobs_in_progress").on(table.inProgress),
inProgressIdx: index("idx_mirror_jobs_in_progress").on(table.inProgress), index("idx_mirror_jobs_job_type").on(table.jobType),
jobTypeIdx: index("idx_mirror_jobs_job_type").on(table.jobType), index("idx_mirror_jobs_timestamp").on(table.timestamp),
timestampIdx: index("idx_mirror_jobs_timestamp").on(table.timestamp), ]);
};
});
export const organizations = sqliteTable("organizations", { export const organizations = sqliteTable("organizations", {
id: text("id").primaryKey(), id: text("id").primaryKey(),
@@ -447,14 +448,12 @@ export const organizations = sqliteTable("organizations", {
updatedAt: integer("updated_at", { mode: "timestamp" }) updatedAt: integer("updated_at", { mode: "timestamp" })
.notNull() .notNull()
.default(sql`(unixepoch())`), .default(sql`(unixepoch())`),
}, (table) => { }, (table) => [
return { index("idx_organizations_user_id").on(table.userId),
userIdIdx: index("idx_organizations_user_id").on(table.userId), index("idx_organizations_config_id").on(table.configId),
configIdIdx: index("idx_organizations_config_id").on(table.configId), index("idx_organizations_status").on(table.status),
statusIdx: index("idx_organizations_status").on(table.status), index("idx_organizations_is_included").on(table.isIncluded),
isIncludedIdx: index("idx_organizations_is_included").on(table.isIncluded), ]);
};
});
// ===== Better Auth Tables ===== // ===== Better Auth Tables =====
@@ -472,13 +471,11 @@ export const sessions = sqliteTable("sessions", {
updatedAt: integer("updated_at", { mode: "timestamp" }) updatedAt: integer("updated_at", { mode: "timestamp" })
.notNull() .notNull()
.default(sql`(unixepoch())`), .default(sql`(unixepoch())`),
}, (table) => { }, (table) => [
return { index("idx_sessions_user_id").on(table.userId),
userIdIdx: index("idx_sessions_user_id").on(table.userId), index("idx_sessions_token").on(table.token),
tokenIdx: index("idx_sessions_token").on(table.token), index("idx_sessions_expires_at").on(table.expiresAt),
expiresAtIdx: index("idx_sessions_expires_at").on(table.expiresAt), ]);
};
});
// Accounts table (for OAuth providers and credentials) // Accounts table (for OAuth providers and credentials)
export const accounts = sqliteTable("accounts", { export const accounts = sqliteTable("accounts", {
@@ -497,13 +494,11 @@ export const accounts = sqliteTable("accounts", {
updatedAt: integer("updated_at", { mode: "timestamp" }) updatedAt: integer("updated_at", { mode: "timestamp" })
.notNull() .notNull()
.default(sql`(unixepoch())`), .default(sql`(unixepoch())`),
}, (table) => { }, (table) => [
return { index("idx_accounts_account_id").on(table.accountId),
accountIdIdx: index("idx_accounts_account_id").on(table.accountId), index("idx_accounts_user_id").on(table.userId),
userIdIdx: index("idx_accounts_user_id").on(table.userId), index("idx_accounts_provider").on(table.providerId, table.providerUserId),
providerIdx: index("idx_accounts_provider").on(table.providerId, table.providerUserId), ]);
};
});
// Verification tokens table // Verification tokens table
export const verificationTokens = sqliteTable("verification_tokens", { export const verificationTokens = sqliteTable("verification_tokens", {
@@ -515,12 +510,10 @@ export const verificationTokens = sqliteTable("verification_tokens", {
createdAt: integer("created_at", { mode: "timestamp" }) createdAt: integer("created_at", { mode: "timestamp" })
.notNull() .notNull()
.default(sql`(unixepoch())`), .default(sql`(unixepoch())`),
}, (table) => { }, (table) => [
return { index("idx_verification_tokens_token").on(table.token),
tokenIdx: index("idx_verification_tokens_token").on(table.token), index("idx_verification_tokens_identifier").on(table.identifier),
identifierIdx: index("idx_verification_tokens_identifier").on(table.identifier), ]);
};
});
// Verifications table (for Better Auth) // Verifications table (for Better Auth)
export const verifications = sqliteTable("verifications", { export const verifications = sqliteTable("verifications", {
@@ -534,11 +527,9 @@ export const verifications = sqliteTable("verifications", {
updatedAt: integer("updated_at", { mode: "timestamp" }) updatedAt: integer("updated_at", { mode: "timestamp" })
.notNull() .notNull()
.default(sql`(unixepoch())`), .default(sql`(unixepoch())`),
}, (table) => { }, (table) => [
return { index("idx_verifications_identifier").on(table.identifier),
identifierIdx: index("idx_verifications_identifier").on(table.identifier), ]);
};
});
// ===== OIDC Provider Tables ===== // ===== OIDC Provider Tables =====
@@ -559,12 +550,10 @@ export const oauthApplications = sqliteTable("oauth_applications", {
updatedAt: integer("updated_at", { mode: "timestamp" }) updatedAt: integer("updated_at", { mode: "timestamp" })
.notNull() .notNull()
.default(sql`(unixepoch())`), .default(sql`(unixepoch())`),
}, (table) => { }, (table) => [
return { index("idx_oauth_applications_client_id").on(table.clientId),
clientIdIdx: index("idx_oauth_applications_client_id").on(table.clientId), index("idx_oauth_applications_user_id").on(table.userId),
userIdIdx: index("idx_oauth_applications_user_id").on(table.userId), ]);
};
});
// OAuth Access Tokens table // OAuth Access Tokens table
export const oauthAccessTokens = sqliteTable("oauth_access_tokens", { export const oauthAccessTokens = sqliteTable("oauth_access_tokens", {
@@ -582,13 +571,11 @@ export const oauthAccessTokens = sqliteTable("oauth_access_tokens", {
updatedAt: integer("updated_at", { mode: "timestamp" }) updatedAt: integer("updated_at", { mode: "timestamp" })
.notNull() .notNull()
.default(sql`(unixepoch())`), .default(sql`(unixepoch())`),
}, (table) => { }, (table) => [
return { index("idx_oauth_access_tokens_access_token").on(table.accessToken),
accessTokenIdx: index("idx_oauth_access_tokens_access_token").on(table.accessToken), index("idx_oauth_access_tokens_user_id").on(table.userId),
userIdIdx: index("idx_oauth_access_tokens_user_id").on(table.userId), index("idx_oauth_access_tokens_client_id").on(table.clientId),
clientIdIdx: index("idx_oauth_access_tokens_client_id").on(table.clientId), ]);
};
});
// OAuth Consent table // OAuth Consent table
export const oauthConsent = sqliteTable("oauth_consent", { export const oauthConsent = sqliteTable("oauth_consent", {
@@ -603,13 +590,11 @@ export const oauthConsent = sqliteTable("oauth_consent", {
updatedAt: integer("updated_at", { mode: "timestamp" }) updatedAt: integer("updated_at", { mode: "timestamp" })
.notNull() .notNull()
.default(sql`(unixepoch())`), .default(sql`(unixepoch())`),
}, (table) => { }, (table) => [
return { index("idx_oauth_consent_user_id").on(table.userId),
userIdIdx: index("idx_oauth_consent_user_id").on(table.userId), index("idx_oauth_consent_client_id").on(table.clientId),
clientIdIdx: index("idx_oauth_consent_client_id").on(table.clientId), index("idx_oauth_consent_user_client").on(table.userId, table.clientId),
userClientIdx: index("idx_oauth_consent_user_client").on(table.userId, table.clientId), ]);
};
});
// ===== SSO Provider Tables ===== // ===== SSO Provider Tables =====
@@ -628,13 +613,11 @@ export const ssoProviders = sqliteTable("sso_providers", {
updatedAt: integer("updated_at", { mode: "timestamp" }) updatedAt: integer("updated_at", { mode: "timestamp" })
.notNull() .notNull()
.default(sql`(unixepoch())`), .default(sql`(unixepoch())`),
}, (table) => { }, (table) => [
return { index("idx_sso_providers_provider_id").on(table.providerId),
providerIdIdx: index("idx_sso_providers_provider_id").on(table.providerId), index("idx_sso_providers_domain").on(table.domain),
domainIdx: index("idx_sso_providers_domain").on(table.domain), index("idx_sso_providers_issuer").on(table.issuer),
issuerIdx: index("idx_sso_providers_issuer").on(table.issuer), ]);
};
});
// Export type definitions // Export type definitions
export type User = z.infer<typeof userSchema>; export type User = z.infer<typeof userSchema>;

View File

@@ -240,6 +240,7 @@ export async function initializeConfigFromEnv(): Promise<void> {
token: envConfig.github.token ? encrypt(envConfig.github.token) : existingConfig?.[0]?.githubConfig?.token || '', token: envConfig.github.token ? encrypt(envConfig.github.token) : existingConfig?.[0]?.githubConfig?.token || '',
includeStarred: envConfig.github.mirrorStarred ?? existingConfig?.[0]?.githubConfig?.includeStarred ?? false, includeStarred: envConfig.github.mirrorStarred ?? existingConfig?.[0]?.githubConfig?.includeStarred ?? false,
includeForks: !(envConfig.github.skipForks ?? false), includeForks: !(envConfig.github.skipForks ?? false),
skipForks: envConfig.github.skipForks ?? existingConfig?.[0]?.githubConfig?.skipForks ?? false,
includeArchived: envConfig.github.includeArchived ?? existingConfig?.[0]?.githubConfig?.includeArchived ?? false, includeArchived: envConfig.github.includeArchived ?? existingConfig?.[0]?.githubConfig?.includeArchived ?? false,
includePrivate: envConfig.github.privateRepositories ?? existingConfig?.[0]?.githubConfig?.includePrivate ?? false, includePrivate: envConfig.github.privateRepositories ?? existingConfig?.[0]?.githubConfig?.includePrivate ?? false,
includePublic: envConfig.github.publicRepositories ?? existingConfig?.[0]?.githubConfig?.includePublic ?? true, includePublic: envConfig.github.publicRepositories ?? existingConfig?.[0]?.githubConfig?.includePublic ?? true,
@@ -255,6 +256,8 @@ export async function initializeConfigFromEnv(): Promise<void> {
url: envConfig.gitea.url || existingConfig?.[0]?.giteaConfig?.url || '', url: envConfig.gitea.url || existingConfig?.[0]?.giteaConfig?.url || '',
token: envConfig.gitea.token ? encrypt(envConfig.gitea.token) : existingConfig?.[0]?.giteaConfig?.token || '', token: envConfig.gitea.token ? encrypt(envConfig.gitea.token) : existingConfig?.[0]?.giteaConfig?.token || '',
defaultOwner: envConfig.gitea.username || existingConfig?.[0]?.giteaConfig?.defaultOwner || '', defaultOwner: envConfig.gitea.username || existingConfig?.[0]?.giteaConfig?.defaultOwner || '',
organization: envConfig.gitea.organization || existingConfig?.[0]?.giteaConfig?.organization || undefined,
preserveOrgStructure: mirrorStrategy === 'preserve' || mirrorStrategy === 'mixed',
mirrorInterval: envConfig.gitea.mirrorInterval || existingConfig?.[0]?.giteaConfig?.mirrorInterval || '8h', mirrorInterval: envConfig.gitea.mirrorInterval || existingConfig?.[0]?.giteaConfig?.mirrorInterval || '8h',
lfs: envConfig.gitea.lfs ?? existingConfig?.[0]?.giteaConfig?.lfs ?? false, lfs: envConfig.gitea.lfs ?? existingConfig?.[0]?.giteaConfig?.lfs ?? false,
wiki: envConfig.mirror.mirrorWiki ?? existingConfig?.[0]?.giteaConfig?.wiki ?? false, wiki: envConfig.mirror.mirrorWiki ?? existingConfig?.[0]?.giteaConfig?.wiki ?? false,
@@ -296,8 +299,8 @@ export async function initializeConfigFromEnv(): Promise<void> {
updateInterval: envConfig.schedule.updateInterval ?? existingConfig?.[0]?.scheduleConfig?.updateInterval ?? 86400000, updateInterval: envConfig.schedule.updateInterval ?? existingConfig?.[0]?.scheduleConfig?.updateInterval ?? 86400000,
skipRecentlyMirrored: envConfig.schedule.skipRecentlyMirrored ?? existingConfig?.[0]?.scheduleConfig?.skipRecentlyMirrored ?? true, skipRecentlyMirrored: envConfig.schedule.skipRecentlyMirrored ?? existingConfig?.[0]?.scheduleConfig?.skipRecentlyMirrored ?? true,
recentThreshold: envConfig.schedule.recentThreshold ?? existingConfig?.[0]?.scheduleConfig?.recentThreshold ?? 3600000, recentThreshold: envConfig.schedule.recentThreshold ?? existingConfig?.[0]?.scheduleConfig?.recentThreshold ?? 3600000,
lastRun: existingConfig?.[0]?.scheduleConfig?.lastRun || null, lastRun: existingConfig?.[0]?.scheduleConfig?.lastRun || undefined,
nextRun: existingConfig?.[0]?.scheduleConfig?.nextRun || null, nextRun: existingConfig?.[0]?.scheduleConfig?.nextRun || undefined,
}; };
// Build cleanup config // Build cleanup config
@@ -311,8 +314,8 @@ export async function initializeConfigFromEnv(): Promise<void> {
orphanedRepoAction: envConfig.cleanup.orphanedRepoAction || existingConfig?.[0]?.cleanupConfig?.orphanedRepoAction || 'archive', orphanedRepoAction: envConfig.cleanup.orphanedRepoAction || existingConfig?.[0]?.cleanupConfig?.orphanedRepoAction || 'archive',
batchSize: envConfig.cleanup.batchSize ?? existingConfig?.[0]?.cleanupConfig?.batchSize ?? 10, batchSize: envConfig.cleanup.batchSize ?? existingConfig?.[0]?.cleanupConfig?.batchSize ?? 10,
pauseBetweenDeletes: envConfig.cleanup.pauseBetweenDeletes ?? existingConfig?.[0]?.cleanupConfig?.pauseBetweenDeletes ?? 2000, pauseBetweenDeletes: envConfig.cleanup.pauseBetweenDeletes ?? existingConfig?.[0]?.cleanupConfig?.pauseBetweenDeletes ?? 2000,
lastRun: existingConfig?.[0]?.cleanupConfig?.lastRun || null, lastRun: existingConfig?.[0]?.cleanupConfig?.lastRun || undefined,
nextRun: existingConfig?.[0]?.cleanupConfig?.nextRun || null, nextRun: existingConfig?.[0]?.cleanupConfig?.nextRun || undefined,
}; };
if (existingConfig.length > 0) { if (existingConfig.length > 0) {

View File

@@ -1618,8 +1618,8 @@ export async function mirrorGitRepoPullRequestsToGitea({
} }
}, },
{ {
maxConcurrency: 5, concurrencyLimit: 5,
retryAttempts: 3, maxRetries: 3,
retryDelay: 1000, retryDelay: 1000,
} }
); );
@@ -1840,8 +1840,8 @@ export async function deleteGiteaRepo(
} }
); );
if (!response.success) { if (response.status >= 400) {
throw new Error(`Failed to delete repository ${owner}/${repo}: ${response.statusCode}`); throw new Error(`Failed to delete repository ${owner}/${repo}: ${response.status} ${response.statusText}`);
} }
console.log(`Successfully deleted repository ${owner}/${repo} from Gitea`); console.log(`Successfully deleted repository ${owner}/${repo} from Gitea`);
@@ -1871,8 +1871,8 @@ export async function archiveGiteaRepo(
} }
); );
if (!response.success) { if (response.status >= 400) {
throw new Error(`Failed to archive repository ${owner}/${repo}: ${response.statusCode}`); throw new Error(`Failed to archive repository ${owner}/${repo}: ${response.status} ${response.statusText}`);
} }
console.log(`Successfully archived repository ${owner}/${repo} in Gitea`); console.log(`Successfully archived repository ${owner}/${repo} in Gitea`);

View File

@@ -4,8 +4,8 @@
*/ */
import { findInterruptedJobs, resumeInterruptedJob } from './helpers'; import { findInterruptedJobs, resumeInterruptedJob } from './helpers';
import { db, repositories, organizations, mirrorJobs } from './db'; import { db, repositories, organizations, mirrorJobs, configs } from './db';
import { eq, and, lt } from 'drizzle-orm'; import { eq, and, lt, inArray } from 'drizzle-orm';
import { mirrorGithubRepoToGitea, mirrorGitHubOrgRepoToGiteaOrg, syncGiteaRepo } from './gitea'; import { mirrorGithubRepoToGitea, mirrorGitHubOrgRepoToGiteaOrg, syncGiteaRepo } from './gitea';
import { createGitHubClient } from './github'; import { createGitHubClient } from './github';
import { processWithResilience } from './utils/concurrency'; import { processWithResilience } from './utils/concurrency';
@@ -217,26 +217,26 @@ async function recoverMirrorJob(job: any, remainingItemIds: string[]) {
try { try {
// Get the config for this user with better error handling // Get the config for this user with better error handling
const configs = await db const userConfigs = await db
.select() .select()
.from(repositories) .from(configs)
.where(eq(repositories.userId, job.userId)) .where(eq(configs.userId, job.userId))
.limit(1); .limit(1);
if (configs.length === 0) { if (userConfigs.length === 0) {
throw new Error(`No configuration found for user ${job.userId}`); throw new Error(`No configuration found for user ${job.userId}`);
} }
const config = configs[0]; const config = userConfigs[0];
if (!config.configId) { if (!config.id) {
throw new Error(`Configuration missing configId for user ${job.userId}`); throw new Error(`Configuration missing id for user ${job.userId}`);
} }
// Get repositories to process with validation // Get repositories to process with validation
const repos = await db const repos = await db
.select() .select()
.from(repositories) .from(repositories)
.where(eq(repositories.id, remainingItemIds)); .where(inArray(repositories.id, remainingItemIds));
if (repos.length === 0) { if (repos.length === 0) {
console.warn(`No repositories found for remaining item IDs: ${remainingItemIds.join(', ')}`); console.warn(`No repositories found for remaining item IDs: ${remainingItemIds.join(', ')}`);
@@ -286,7 +286,7 @@ async function recoverMirrorJob(job: any, remainingItemIds: string[]) {
}; };
// Mirror the repository based on whether it's in an organization // Mirror the repository based on whether it's in an organization
if (repo.organization && config.githubConfig.preserveOrgStructure) { if (repo.organization && config.giteaConfig.preserveOrgStructure) {
await mirrorGitHubOrgRepoToGiteaOrg({ await mirrorGitHubOrgRepoToGiteaOrg({
config, config,
octokit, octokit,
@@ -346,26 +346,26 @@ async function recoverSyncJob(job: any, remainingItemIds: string[]) {
try { try {
// Get the config for this user with better error handling // Get the config for this user with better error handling
const configs = await db const userConfigs = await db
.select() .select()
.from(repositories) .from(configs)
.where(eq(repositories.userId, job.userId)) .where(eq(configs.userId, job.userId))
.limit(1); .limit(1);
if (configs.length === 0) { if (userConfigs.length === 0) {
throw new Error(`No configuration found for user ${job.userId}`); throw new Error(`No configuration found for user ${job.userId}`);
} }
const config = configs[0]; const config = userConfigs[0];
if (!config.configId) { if (!config.id) {
throw new Error(`Configuration missing configId for user ${job.userId}`); throw new Error(`Configuration missing id for user ${job.userId}`);
} }
// Get repositories to process with validation // Get repositories to process with validation
const repos = await db const repos = await db
.select() .select()
.from(repositories) .from(repositories)
.where(eq(repositories.id, remainingItemIds)); .where(inArray(repositories.id, remainingItemIds));
if (repos.length === 0) { if (repos.length === 0) {
console.warn(`No repositories found for remaining item IDs: ${remainingItemIds.join(', ')}`); console.warn(`No repositories found for remaining item IDs: ${remainingItemIds.join(', ')}`);
@@ -397,6 +397,7 @@ async function recoverSyncJob(job: any, remainingItemIds: string[]) {
errorMessage: repo.errorMessage ?? undefined, errorMessage: repo.errorMessage ?? undefined,
forkedFrom: repo.forkedFrom ?? undefined, forkedFrom: repo.forkedFrom ?? undefined,
visibility: repositoryVisibilityEnum.parse(repo.visibility || "public"), visibility: repositoryVisibilityEnum.parse(repo.visibility || "public"),
mirroredLocation: repo.mirroredLocation || "",
}; };
// Sync the repository // Sync the repository

View File

@@ -10,7 +10,7 @@ import { syncGiteaRepo } from '@/lib/gitea';
import { createGitHubClient } from '@/lib/github'; import { createGitHubClient } from '@/lib/github';
import { getDecryptedGitHubToken } from '@/lib/utils/config-encryption'; import { getDecryptedGitHubToken } from '@/lib/utils/config-encryption';
import { parseInterval, formatDuration } from '@/lib/utils/duration-parser'; import { parseInterval, formatDuration } from '@/lib/utils/duration-parser';
import type { Repository } from '@/types'; import type { Repository } from '@/lib/db/schema';
import { repoStatusEnum, repositoryVisibilityEnum } from '@/types/Repository'; import { repoStatusEnum, repositoryVisibilityEnum } from '@/types/Repository';
let schedulerInterval: NodeJS.Timeout | null = null; let schedulerInterval: NodeJS.Timeout | null = null;

View File

@@ -35,6 +35,16 @@ export const GET: APIRoute = async ({ url }) => {
details: job.details ?? undefined, details: job.details ?? undefined,
message: job.message, message: job.message,
timestamp: job.timestamp, timestamp: job.timestamp,
jobType: job.jobType,
batchId: job.batchId ?? undefined,
totalItems: job.totalItems ?? undefined,
completedItems: job.completedItems,
itemIds: job.itemIds ?? undefined,
completedItemIds: job.completedItemIds,
inProgress: job.inProgress,
startedAt: job.startedAt ?? undefined,
completedAt: job.completedAt ?? undefined,
lastCheckpoint: job.lastCheckpoint ?? undefined,
})); }));
return new Response( return new Response(

View File

@@ -77,32 +77,9 @@ export const GET: APIRoute = async ({ request }) => {
repoCount: repoCount ?? 0, repoCount: repoCount ?? 0,
orgCount: orgCount ?? 0, orgCount: orgCount ?? 0,
mirroredCount: mirroredCount ?? 0, mirroredCount: mirroredCount ?? 0,
repositories: userRepos.map((repo) => ({ repositories: userRepos,
...repo, organizations: userOrgs,
organization: repo.organization ?? undefined, activities: userLogs,
lastMirrored: repo.lastMirrored ?? undefined,
errorMessage: repo.errorMessage ?? undefined,
forkedFrom: repo.forkedFrom ?? undefined,
status: repoStatusEnum.parse(repo.status),
visibility: repositoryVisibilityEnum.parse(repo.visibility),
})),
organizations: userOrgs.map((org) => ({
...org,
status: repoStatusEnum.parse(org.status),
membershipRole: membershipRoleEnum.parse(org.membershipRole),
lastMirrored: org.lastMirrored ?? undefined,
errorMessage: org.errorMessage ?? undefined,
})),
activities: userLogs.map((job) => ({
id: job.id,
userId: job.userId,
repositoryName: job.repositoryName ?? undefined,
organizationName: job.organizationName ?? undefined,
status: repoStatusEnum.parse(job.status),
details: job.details ?? undefined,
message: job.message,
timestamp: job.timestamp,
})),
lastSync: userConfig?.scheduleConfig.lastRun ?? null, lastSync: userConfig?.scheduleConfig.lastRun ?? null,
}; };