mirror of
https://github.com/RayLabsHQ/gitea-mirror.git
synced 2025-12-06 19:46:44 +03:00
some more fixes
This commit is contained in:
@@ -1,4 +1,3 @@
|
||||
import React from "react";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Card, CardContent, CardHeader } from "@/components/ui/card";
|
||||
import { Home, ArrowLeft, GitBranch, BookOpen, Settings, FileQuestion } from "lucide-react";
|
||||
|
||||
@@ -114,7 +114,7 @@ export function AuthProvider({ children }: { children: React.ReactNode }) {
|
||||
// Create the context value
|
||||
const contextValue = {
|
||||
user: user as AuthUser | null,
|
||||
session,
|
||||
session: session as Session | null,
|
||||
isLoading: isLoading || betterAuthSession.isPending,
|
||||
error,
|
||||
login,
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { createAuthClient } from "better-auth/react";
|
||||
import { oidcClient } from "better-auth/client/plugins";
|
||||
import { ssoClient } from "better-auth/client/plugins";
|
||||
import type { Session as BetterAuthSession, User as BetterAuthUser } from "better-auth";
|
||||
|
||||
export const authClient = createAuthClient({
|
||||
// The base URL is optional when running on the same domain
|
||||
@@ -23,6 +24,12 @@ export const {
|
||||
getSession
|
||||
} = authClient;
|
||||
|
||||
// Export types
|
||||
export type Session = Awaited<ReturnType<typeof authClient.getSession>>["data"];
|
||||
export type AuthUser = Session extends { user: infer U } ? U : never;
|
||||
// Export types - directly use the types from better-auth
|
||||
export type Session = BetterAuthSession & {
|
||||
user: BetterAuthUser & {
|
||||
username?: string | null;
|
||||
};
|
||||
};
|
||||
export type AuthUser = BetterAuthUser & {
|
||||
username?: string | null;
|
||||
};
|
||||
@@ -47,6 +47,13 @@ export const giteaConfigSchema = z.object({
|
||||
forkStrategy: z
|
||||
.enum(["skip", "reference", "full-copy"])
|
||||
.default("reference"),
|
||||
// Mirror options
|
||||
mirrorReleases: z.boolean().default(false),
|
||||
mirrorMetadata: z.boolean().default(false),
|
||||
mirrorIssues: z.boolean().default(false),
|
||||
mirrorPullRequests: z.boolean().default(false),
|
||||
mirrorLabels: z.boolean().default(false),
|
||||
mirrorMilestones: z.boolean().default(false),
|
||||
});
|
||||
|
||||
export const scheduleConfigSchema = z.object({
|
||||
|
||||
@@ -272,6 +272,9 @@ export const mirrorGithubRepoToGitea = async ({
|
||||
throw new Error("Gitea username is required.");
|
||||
}
|
||||
|
||||
// Decrypt config tokens for API usage
|
||||
const decryptedConfig = decryptConfigTokens(config as Config);
|
||||
|
||||
// Get the correct owner based on the strategy (with organization overrides)
|
||||
const repoOwner = await getGiteaRepoOwnerAsync({ config, repository });
|
||||
|
||||
@@ -347,7 +350,7 @@ export const mirrorGithubRepoToGitea = async ({
|
||||
|
||||
cloneAddress = repository.cloneUrl.replace(
|
||||
"https://",
|
||||
`https://${config.githubConfig.token}@`
|
||||
`https://${decryptedConfig.githubConfig.token}@`
|
||||
);
|
||||
}
|
||||
|
||||
@@ -644,6 +647,9 @@ export async function mirrorGitHubRepoToGiteaOrg({
|
||||
throw new Error("Gitea config is required.");
|
||||
}
|
||||
|
||||
// Decrypt config tokens for API usage
|
||||
const decryptedConfig = decryptConfigTokens(config as Config);
|
||||
|
||||
const isExisting = await isRepoPresentInGitea({
|
||||
config,
|
||||
owner: orgName,
|
||||
@@ -698,7 +704,7 @@ export async function mirrorGitHubRepoToGiteaOrg({
|
||||
|
||||
cloneAddress = repository.cloneUrl.replace(
|
||||
"https://",
|
||||
`https://${config.githubConfig.token}@`
|
||||
`https://${decryptedConfig.githubConfig.token}@`
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1125,7 +1131,7 @@ export const syncGiteaRepo = async ({
|
||||
const apiUrl = `${config.giteaConfig.url}/api/v1/repos/${actualOwner}/${repository.name}/mirror-sync`;
|
||||
|
||||
const response = await httpPost(apiUrl, undefined, {
|
||||
Authorization: `token ${config.giteaConfig.token}`,
|
||||
Authorization: `token ${decryptedConfig.giteaConfig.token}`,
|
||||
});
|
||||
|
||||
// Mark repo as "synced" in DB
|
||||
@@ -1243,7 +1249,7 @@ export const mirrorGitRepoIssuesToGitea = async ({
|
||||
const giteaLabelsRes = await httpGet(
|
||||
`${config.giteaConfig.url}/api/v1/repos/${giteaOwner}/${repository.name}/labels`,
|
||||
{
|
||||
Authorization: `token ${config.giteaConfig.token}`,
|
||||
Authorization: `token ${decryptedConfig.giteaConfig.token}`,
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
@@ -9,35 +9,14 @@ import type {
|
||||
AdvancedOptions,
|
||||
SaveConfigApiRequest
|
||||
} from "@/types/config";
|
||||
import { z } from "zod";
|
||||
import { githubConfigSchema, giteaConfigSchema, scheduleConfigSchema, cleanupConfigSchema } from "@/lib/db/schema";
|
||||
|
||||
interface DbGitHubConfig {
|
||||
username: string;
|
||||
token?: string;
|
||||
skipForks: boolean;
|
||||
privateRepositories: boolean;
|
||||
mirrorIssues: boolean;
|
||||
mirrorWiki: boolean;
|
||||
mirrorStarred: boolean;
|
||||
useSpecificUser: boolean;
|
||||
singleRepo?: string;
|
||||
includeOrgs: string[];
|
||||
excludeOrgs: string[];
|
||||
mirrorPublicOrgs: boolean;
|
||||
publicOrgs: string[];
|
||||
skipStarredIssues: boolean;
|
||||
}
|
||||
|
||||
interface DbGiteaConfig {
|
||||
username: string;
|
||||
url: string;
|
||||
token: string;
|
||||
organization?: string;
|
||||
visibility: "public" | "private" | "limited";
|
||||
starredReposOrg: string;
|
||||
preserveOrgStructure: boolean;
|
||||
mirrorStrategy?: "preserve" | "single-org" | "flat-user" | "mixed";
|
||||
personalReposOrg?: string;
|
||||
}
|
||||
// Use the actual database schema types
|
||||
type DbGitHubConfig = z.infer<typeof githubConfigSchema>;
|
||||
type DbGiteaConfig = z.infer<typeof giteaConfigSchema>;
|
||||
type DbScheduleConfig = z.infer<typeof scheduleConfigSchema>;
|
||||
type DbCleanupConfig = z.infer<typeof cleanupConfigSchema>;
|
||||
|
||||
/**
|
||||
* Maps UI config structure to database schema structure
|
||||
@@ -48,32 +27,67 @@ export function mapUiToDbConfig(
|
||||
mirrorOptions: MirrorOptions,
|
||||
advancedOptions: AdvancedOptions
|
||||
): { githubConfig: DbGitHubConfig; giteaConfig: DbGiteaConfig } {
|
||||
// Map GitHub config with fields from mirrorOptions and advancedOptions
|
||||
// Map GitHub config to match database schema fields
|
||||
const dbGithubConfig: DbGitHubConfig = {
|
||||
username: githubConfig.username,
|
||||
token: githubConfig.token,
|
||||
privateRepositories: githubConfig.privateRepositories,
|
||||
mirrorStarred: githubConfig.mirrorStarred,
|
||||
// Map username to owner field
|
||||
owner: githubConfig.username,
|
||||
type: "personal", // Default to personal, could be made configurable
|
||||
token: githubConfig.token || "",
|
||||
|
||||
// From mirrorOptions
|
||||
mirrorIssues: mirrorOptions.mirrorMetadata && mirrorOptions.metadataComponents.issues,
|
||||
mirrorWiki: mirrorOptions.mirrorMetadata && mirrorOptions.metadataComponents.wiki,
|
||||
// Map checkbox fields with proper names
|
||||
includeStarred: githubConfig.mirrorStarred,
|
||||
includePrivate: githubConfig.privateRepositories,
|
||||
includeForks: !advancedOptions.skipForks, // Note: UI has skipForks, DB has includeForks
|
||||
includeArchived: false, // Not in UI yet, default to false
|
||||
includePublic: true, // Not in UI yet, default to true
|
||||
|
||||
// From advancedOptions
|
||||
skipForks: advancedOptions.skipForks,
|
||||
skipStarredIssues: advancedOptions.skipStarredIssues,
|
||||
// Organization related fields
|
||||
includeOrganizations: [], // Not in UI yet
|
||||
|
||||
// Default values for fields not in UI
|
||||
useSpecificUser: false,
|
||||
includeOrgs: [],
|
||||
excludeOrgs: [],
|
||||
mirrorPublicOrgs: false,
|
||||
publicOrgs: [],
|
||||
// Starred repos organization
|
||||
starredReposOrg: giteaConfig.starredReposOrg,
|
||||
|
||||
// Mirror strategy
|
||||
mirrorStrategy: giteaConfig.mirrorStrategy || "preserve",
|
||||
defaultOrg: giteaConfig.organization,
|
||||
};
|
||||
|
||||
// Gitea config remains mostly the same
|
||||
// Map Gitea config to match database schema
|
||||
const dbGiteaConfig: DbGiteaConfig = {
|
||||
...giteaConfig,
|
||||
url: giteaConfig.url,
|
||||
token: giteaConfig.token,
|
||||
defaultOwner: giteaConfig.username, // Map username to defaultOwner
|
||||
|
||||
// Mirror interval and options
|
||||
mirrorInterval: "8h", // Default value, could be made configurable
|
||||
lfs: false, // Not in UI yet
|
||||
wiki: mirrorOptions.mirrorMetadata && mirrorOptions.metadataComponents.wiki,
|
||||
|
||||
// Visibility settings
|
||||
visibility: giteaConfig.visibility || "default",
|
||||
preserveVisibility: giteaConfig.preserveOrgStructure,
|
||||
|
||||
// Organization creation
|
||||
createOrg: true, // Default to true
|
||||
|
||||
// Template settings (not in UI yet)
|
||||
templateOwner: undefined,
|
||||
templateRepo: undefined,
|
||||
|
||||
// Topics
|
||||
addTopics: true, // Default to true
|
||||
topicPrefix: undefined,
|
||||
|
||||
// Fork strategy
|
||||
forkStrategy: advancedOptions.skipForks ? "skip" : "reference",
|
||||
|
||||
// Mirror options from UI
|
||||
mirrorReleases: mirrorOptions.mirrorReleases,
|
||||
mirrorMetadata: mirrorOptions.mirrorMetadata,
|
||||
mirrorIssues: mirrorOptions.mirrorMetadata && mirrorOptions.metadataComponents.issues,
|
||||
mirrorPullRequests: mirrorOptions.mirrorMetadata && mirrorOptions.metadataComponents.pullRequests,
|
||||
mirrorLabels: mirrorOptions.mirrorMetadata && mirrorOptions.metadataComponents.labels,
|
||||
mirrorMilestones: mirrorOptions.mirrorMetadata && mirrorOptions.metadataComponents.milestones,
|
||||
};
|
||||
|
||||
return {
|
||||
@@ -91,40 +105,44 @@ export function mapDbToUiConfig(dbConfig: any): {
|
||||
mirrorOptions: MirrorOptions;
|
||||
advancedOptions: AdvancedOptions;
|
||||
} {
|
||||
// Map from database GitHub config to UI fields
|
||||
const githubConfig: GitHubConfig = {
|
||||
username: dbConfig.githubConfig?.username || "",
|
||||
username: dbConfig.githubConfig?.owner || "", // Map owner to username
|
||||
token: dbConfig.githubConfig?.token || "",
|
||||
privateRepositories: dbConfig.githubConfig?.privateRepositories || false,
|
||||
mirrorStarred: dbConfig.githubConfig?.mirrorStarred || false,
|
||||
privateRepositories: dbConfig.githubConfig?.includePrivate || false, // Map includePrivate to privateRepositories
|
||||
mirrorStarred: dbConfig.githubConfig?.includeStarred || false, // Map includeStarred to mirrorStarred
|
||||
};
|
||||
|
||||
// Map from database Gitea config to UI fields
|
||||
const giteaConfig: GiteaConfig = {
|
||||
url: dbConfig.giteaConfig?.url || "",
|
||||
username: dbConfig.giteaConfig?.username || "",
|
||||
username: dbConfig.giteaConfig?.defaultOwner || "", // Map defaultOwner to username
|
||||
token: dbConfig.giteaConfig?.token || "",
|
||||
organization: dbConfig.giteaConfig?.organization || "github-mirrors",
|
||||
visibility: dbConfig.giteaConfig?.visibility || "public",
|
||||
starredReposOrg: dbConfig.giteaConfig?.starredReposOrg || "github",
|
||||
preserveOrgStructure: dbConfig.giteaConfig?.preserveOrgStructure || false,
|
||||
mirrorStrategy: dbConfig.giteaConfig?.mirrorStrategy,
|
||||
personalReposOrg: dbConfig.giteaConfig?.personalReposOrg,
|
||||
organization: dbConfig.githubConfig?.defaultOrg || "github-mirrors", // Get from GitHub config
|
||||
visibility: dbConfig.giteaConfig?.visibility === "default" ? "public" : dbConfig.giteaConfig?.visibility || "public",
|
||||
starredReposOrg: dbConfig.githubConfig?.starredReposOrg || "github", // Get from GitHub config
|
||||
preserveOrgStructure: dbConfig.giteaConfig?.preserveVisibility || false, // Map preserveVisibility
|
||||
mirrorStrategy: dbConfig.githubConfig?.mirrorStrategy || "preserve", // Get from GitHub config
|
||||
personalReposOrg: undefined, // Not stored in current schema
|
||||
};
|
||||
|
||||
// Map mirror options from various database fields
|
||||
const mirrorOptions: MirrorOptions = {
|
||||
mirrorReleases: false, // Not stored in DB yet
|
||||
mirrorMetadata: dbConfig.githubConfig?.mirrorIssues || dbConfig.githubConfig?.mirrorWiki || false,
|
||||
mirrorReleases: dbConfig.giteaConfig?.mirrorReleases || false,
|
||||
mirrorMetadata: dbConfig.giteaConfig?.mirrorMetadata || false,
|
||||
metadataComponents: {
|
||||
issues: dbConfig.githubConfig?.mirrorIssues || false,
|
||||
pullRequests: false, // Not stored in DB yet
|
||||
labels: false, // Not stored in DB yet
|
||||
milestones: false, // Not stored in DB yet
|
||||
wiki: dbConfig.githubConfig?.mirrorWiki || false,
|
||||
issues: dbConfig.giteaConfig?.mirrorIssues || false,
|
||||
pullRequests: dbConfig.giteaConfig?.mirrorPullRequests || false,
|
||||
labels: dbConfig.giteaConfig?.mirrorLabels || false,
|
||||
milestones: dbConfig.giteaConfig?.mirrorMilestones || false,
|
||||
wiki: dbConfig.giteaConfig?.wiki || false,
|
||||
},
|
||||
};
|
||||
|
||||
// Map advanced options
|
||||
const advancedOptions: AdvancedOptions = {
|
||||
skipForks: dbConfig.githubConfig?.skipForks || false,
|
||||
skipStarredIssues: dbConfig.githubConfig?.skipStarredIssues || false,
|
||||
skipForks: !(dbConfig.githubConfig?.includeForks ?? true), // Invert includeForks to get skipForks
|
||||
skipStarredIssues: false, // Not stored in current schema
|
||||
};
|
||||
|
||||
return {
|
||||
@@ -133,4 +151,73 @@ export function mapDbToUiConfig(dbConfig: any): {
|
||||
mirrorOptions,
|
||||
advancedOptions,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Maps UI schedule config to database schema
|
||||
*/
|
||||
export function mapUiScheduleToDb(uiSchedule: any): DbScheduleConfig {
|
||||
return {
|
||||
enabled: uiSchedule.enabled || false,
|
||||
interval: uiSchedule.interval ? `0 */${Math.floor(uiSchedule.interval / 3600)} * * *` : "0 2 * * *", // Convert seconds to cron expression
|
||||
concurrent: false,
|
||||
batchSize: 10,
|
||||
pauseBetweenBatches: 5000,
|
||||
retryAttempts: 3,
|
||||
retryDelay: 60000,
|
||||
timeout: 3600000,
|
||||
autoRetry: true,
|
||||
cleanupBeforeMirror: false,
|
||||
notifyOnFailure: true,
|
||||
notifyOnSuccess: false,
|
||||
logLevel: "info",
|
||||
timezone: "UTC",
|
||||
onlyMirrorUpdated: false,
|
||||
updateInterval: 86400000,
|
||||
skipRecentlyMirrored: true,
|
||||
recentThreshold: 3600000,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Maps database schedule config to UI format
|
||||
*/
|
||||
export function mapDbScheduleToUi(dbSchedule: DbScheduleConfig): any {
|
||||
// Extract hours from cron expression if possible
|
||||
let intervalSeconds = 3600; // Default 1 hour
|
||||
const cronMatch = dbSchedule.interval.match(/0 \*\/(\d+) \* \* \*/);
|
||||
if (cronMatch) {
|
||||
intervalSeconds = parseInt(cronMatch[1]) * 3600;
|
||||
}
|
||||
|
||||
return {
|
||||
enabled: dbSchedule.enabled,
|
||||
interval: intervalSeconds,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Maps UI cleanup config to database schema
|
||||
*/
|
||||
export function mapUiCleanupToDb(uiCleanup: any): DbCleanupConfig {
|
||||
return {
|
||||
enabled: uiCleanup.enabled || false,
|
||||
deleteFromGitea: false,
|
||||
deleteIfNotInGitHub: true,
|
||||
protectedRepos: [],
|
||||
dryRun: true,
|
||||
orphanedRepoAction: "archive",
|
||||
batchSize: 10,
|
||||
pauseBetweenDeletes: 2000,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Maps database cleanup config to UI format
|
||||
*/
|
||||
export function mapDbCleanupToUi(dbCleanup: DbCleanupConfig): any {
|
||||
return {
|
||||
enabled: dbCleanup.enabled,
|
||||
retentionDays: 604800, // 7 days in seconds (kept for compatibility)
|
||||
};
|
||||
}
|
||||
@@ -4,7 +4,14 @@ import { v4 as uuidv4 } from "uuid";
|
||||
import { eq } from "drizzle-orm";
|
||||
import { calculateCleanupInterval } from "@/lib/cleanup-service";
|
||||
import { createSecureErrorResponse } from "@/lib/utils";
|
||||
import { mapUiToDbConfig, mapDbToUiConfig } from "@/lib/utils/config-mapper";
|
||||
import {
|
||||
mapUiToDbConfig,
|
||||
mapDbToUiConfig,
|
||||
mapUiScheduleToDb,
|
||||
mapUiCleanupToDb,
|
||||
mapDbScheduleToUi,
|
||||
mapDbCleanupToUi
|
||||
} from "@/lib/utils/config-mapper";
|
||||
import { encrypt, decrypt, migrateToken } from "@/lib/utils/encryption";
|
||||
|
||||
export const POST: APIRoute = async ({ request }) => {
|
||||
@@ -78,62 +85,9 @@ export const POST: APIRoute = async ({ request }) => {
|
||||
mappedGiteaConfig.token = encrypt(mappedGiteaConfig.token);
|
||||
}
|
||||
|
||||
// Process schedule config - set/update nextRun if enabled, clear if disabled
|
||||
const processedScheduleConfig = { ...scheduleConfig };
|
||||
if (scheduleConfig.enabled) {
|
||||
const now = new Date();
|
||||
const interval = scheduleConfig.interval || 3600; // Default to 1 hour
|
||||
|
||||
// Check if we need to recalculate nextRun
|
||||
// Recalculate if: no nextRun exists, or interval changed from existing config
|
||||
let shouldRecalculate = !scheduleConfig.nextRun;
|
||||
|
||||
if (existingConfig && existingConfig.scheduleConfig) {
|
||||
const existingScheduleConfig = existingConfig.scheduleConfig;
|
||||
const existingInterval = existingScheduleConfig.interval || 3600;
|
||||
|
||||
// If interval changed, recalculate nextRun
|
||||
if (interval !== existingInterval) {
|
||||
shouldRecalculate = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (shouldRecalculate) {
|
||||
processedScheduleConfig.nextRun = new Date(now.getTime() + interval * 1000);
|
||||
}
|
||||
} else {
|
||||
// Clear nextRun when disabled
|
||||
processedScheduleConfig.nextRun = null;
|
||||
}
|
||||
|
||||
// Process cleanup config - set/update nextRun if enabled, clear if disabled
|
||||
const processedCleanupConfig = { ...cleanupConfig };
|
||||
if (cleanupConfig.enabled) {
|
||||
const now = new Date();
|
||||
const retentionSeconds = cleanupConfig.retentionDays || 604800; // Default 7 days in seconds
|
||||
const cleanupIntervalHours = calculateCleanupInterval(retentionSeconds);
|
||||
|
||||
// Check if we need to recalculate nextRun
|
||||
// Recalculate if: no nextRun exists, or retention period changed from existing config
|
||||
let shouldRecalculate = !cleanupConfig.nextRun;
|
||||
|
||||
if (existingConfig && existingConfig.cleanupConfig) {
|
||||
const existingCleanupConfig = existingConfig.cleanupConfig;
|
||||
const existingRetentionSeconds = existingCleanupConfig.retentionDays || 604800;
|
||||
|
||||
// If retention period changed, recalculate nextRun
|
||||
if (retentionSeconds !== existingRetentionSeconds) {
|
||||
shouldRecalculate = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (shouldRecalculate) {
|
||||
processedCleanupConfig.nextRun = new Date(now.getTime() + cleanupIntervalHours * 60 * 60 * 1000);
|
||||
}
|
||||
} else {
|
||||
// Clear nextRun when disabled
|
||||
processedCleanupConfig.nextRun = null;
|
||||
}
|
||||
// Map schedule and cleanup configs to database schema
|
||||
const processedScheduleConfig = mapUiScheduleToDb(scheduleConfig);
|
||||
const processedCleanupConfig = mapUiCleanupToDb(cleanupConfig);
|
||||
|
||||
if (existingConfig) {
|
||||
// Update path
|
||||
@@ -234,28 +188,34 @@ export const GET: APIRoute = async ({ request }) => {
|
||||
.limit(1);
|
||||
|
||||
if (config.length === 0) {
|
||||
// Return a default empty configuration with UI structure
|
||||
// Return a default empty configuration with database structure
|
||||
const defaultDbConfig = {
|
||||
githubConfig: {
|
||||
username: "",
|
||||
owner: "",
|
||||
type: "personal",
|
||||
token: "",
|
||||
skipForks: false,
|
||||
privateRepositories: false,
|
||||
mirrorIssues: false,
|
||||
mirrorWiki: false,
|
||||
mirrorStarred: false,
|
||||
useSpecificUser: false,
|
||||
preserveOrgStructure: false,
|
||||
skipStarredIssues: false,
|
||||
includeStarred: false,
|
||||
includeForks: true,
|
||||
includeArchived: false,
|
||||
includePrivate: false,
|
||||
includePublic: true,
|
||||
includeOrganizations: [],
|
||||
starredReposOrg: "github",
|
||||
mirrorStrategy: "preserve",
|
||||
defaultOrg: "github-mirrors",
|
||||
},
|
||||
giteaConfig: {
|
||||
url: "",
|
||||
token: "",
|
||||
username: "",
|
||||
organization: "github-mirrors",
|
||||
defaultOwner: "",
|
||||
mirrorInterval: "8h",
|
||||
lfs: false,
|
||||
wiki: false,
|
||||
visibility: "public",
|
||||
starredReposOrg: "github",
|
||||
preserveOrgStructure: false,
|
||||
createOrg: true,
|
||||
addTopics: true,
|
||||
preserveVisibility: false,
|
||||
forkStrategy: "reference",
|
||||
},
|
||||
};
|
||||
|
||||
@@ -319,9 +279,23 @@ export const GET: APIRoute = async ({ request }) => {
|
||||
|
||||
const uiConfig = mapDbToUiConfig(decryptedConfig);
|
||||
|
||||
// Map schedule and cleanup configs to UI format
|
||||
const uiScheduleConfig = mapDbScheduleToUi(dbConfig.scheduleConfig);
|
||||
const uiCleanupConfig = mapDbCleanupToUi(dbConfig.cleanupConfig);
|
||||
|
||||
return new Response(JSON.stringify({
|
||||
...dbConfig,
|
||||
...uiConfig,
|
||||
scheduleConfig: {
|
||||
...uiScheduleConfig,
|
||||
lastRun: dbConfig.scheduleConfig.lastRun,
|
||||
nextRun: dbConfig.scheduleConfig.nextRun,
|
||||
},
|
||||
cleanupConfig: {
|
||||
...uiCleanupConfig,
|
||||
lastRun: dbConfig.cleanupConfig.lastRun,
|
||||
nextRun: dbConfig.cleanupConfig.nextRun,
|
||||
},
|
||||
}), {
|
||||
status: 200,
|
||||
headers: { "Content-Type": "application/json" },
|
||||
@@ -330,9 +304,22 @@ export const GET: APIRoute = async ({ request }) => {
|
||||
console.error("Failed to decrypt tokens:", error);
|
||||
// Return config without decrypting tokens if there's an error
|
||||
const uiConfig = mapDbToUiConfig(dbConfig);
|
||||
const uiScheduleConfig = mapDbScheduleToUi(dbConfig.scheduleConfig);
|
||||
const uiCleanupConfig = mapDbCleanupToUi(dbConfig.cleanupConfig);
|
||||
|
||||
return new Response(JSON.stringify({
|
||||
...dbConfig,
|
||||
...uiConfig,
|
||||
scheduleConfig: {
|
||||
...uiScheduleConfig,
|
||||
lastRun: dbConfig.scheduleConfig.lastRun,
|
||||
nextRun: dbConfig.scheduleConfig.nextRun,
|
||||
},
|
||||
cleanupConfig: {
|
||||
...uiCleanupConfig,
|
||||
lastRun: dbConfig.cleanupConfig.lastRun,
|
||||
nextRun: dbConfig.cleanupConfig.nextRun,
|
||||
},
|
||||
}), {
|
||||
status: 200,
|
||||
headers: { "Content-Type": "application/json" },
|
||||
|
||||
@@ -45,17 +45,10 @@ export const GET: APIRoute = async ({ request }) => {
|
||||
// Build query conditions based on config
|
||||
const conditions = [eq(repositories.userId, userId)];
|
||||
|
||||
if (!githubConfig.mirrorStarred) {
|
||||
conditions.push(eq(repositories.isStarred, false));
|
||||
}
|
||||
|
||||
if (githubConfig.skipForks) {
|
||||
conditions.push(eq(repositories.isForked, false));
|
||||
}
|
||||
|
||||
if (!githubConfig.privateRepositories) {
|
||||
conditions.push(eq(repositories.isPrivate, false));
|
||||
}
|
||||
// Note: We show ALL repositories in the list
|
||||
// The mirrorStarred and privateRepositories flags only control what gets mirrored,
|
||||
// not what's displayed in the repository list
|
||||
// Only skipForks is used for filtering the display since forked repos are often noise
|
||||
|
||||
const rawRepositories = await db
|
||||
.select()
|
||||
|
||||
@@ -9,6 +9,8 @@ import type {
|
||||
} from "@/types/organizations";
|
||||
import type { RepositoryVisibility, RepoStatus } from "@/types/Repository";
|
||||
import { v4 as uuidv4 } from "uuid";
|
||||
import { decryptConfigTokens } from "@/lib/utils/config-encryption";
|
||||
import { createGitHubClient } from "@/lib/github";
|
||||
|
||||
export const POST: APIRoute = async ({ request }) => {
|
||||
try {
|
||||
@@ -44,32 +46,71 @@ export const POST: APIRoute = async ({ request }) => {
|
||||
const [config] = await db
|
||||
.select()
|
||||
.from(configs)
|
||||
.where(eq(configs.userId, userId))
|
||||
.where(and(eq(configs.userId, userId), eq(configs.isActive, true)))
|
||||
.limit(1);
|
||||
|
||||
if (!config) {
|
||||
return jsonResponse({
|
||||
data: { error: "No configuration found for this user" },
|
||||
data: { error: "No active configuration found for this user" },
|
||||
status: 404,
|
||||
});
|
||||
}
|
||||
|
||||
const configId = config.id;
|
||||
|
||||
const octokit = new Octokit();
|
||||
|
||||
// Decrypt the config to get tokens
|
||||
const decryptedConfig = decryptConfigTokens(config);
|
||||
|
||||
// Check if we have a GitHub token
|
||||
if (!decryptedConfig.githubConfig?.token) {
|
||||
return jsonResponse({
|
||||
data: { error: "GitHub token not configured" },
|
||||
status: 401,
|
||||
});
|
||||
}
|
||||
|
||||
// Create authenticated Octokit instance
|
||||
const octokit = createGitHubClient(decryptedConfig.githubConfig.token);
|
||||
|
||||
// Fetch org metadata
|
||||
const { data: orgData } = await octokit.orgs.get({ org });
|
||||
|
||||
// Fetch public repos using Octokit paginator
|
||||
// Fetch repos based on config settings
|
||||
const allRepos = [];
|
||||
|
||||
// Always fetch public repos
|
||||
const publicRepos = await octokit.paginate(octokit.repos.listForOrg, {
|
||||
org,
|
||||
type: "public",
|
||||
per_page: 100,
|
||||
});
|
||||
allRepos.push(...publicRepos);
|
||||
|
||||
// Fetch private repos if enabled in config
|
||||
if (decryptedConfig.githubConfig?.includePrivate) {
|
||||
const privateRepos = await octokit.paginate(octokit.repos.listForOrg, {
|
||||
org,
|
||||
type: "private",
|
||||
per_page: 100,
|
||||
});
|
||||
allRepos.push(...privateRepos);
|
||||
}
|
||||
|
||||
// Also fetch member repos (includes private repos the user has access to)
|
||||
if (decryptedConfig.githubConfig?.includePrivate) {
|
||||
const memberRepos = await octokit.paginate(octokit.repos.listForOrg, {
|
||||
org,
|
||||
type: "member",
|
||||
per_page: 100,
|
||||
});
|
||||
// Filter out duplicates
|
||||
const existingIds = new Set(allRepos.map(r => r.id));
|
||||
const uniqueMemberRepos = memberRepos.filter(r => !existingIds.has(r.id));
|
||||
allRepos.push(...uniqueMemberRepos);
|
||||
}
|
||||
|
||||
// Insert repositories
|
||||
const repoRecords = publicRepos.map((repo) => ({
|
||||
const repoRecords = allRepos.map((repo) => ({
|
||||
id: uuidv4(),
|
||||
userId,
|
||||
configId,
|
||||
@@ -110,7 +151,7 @@ export const POST: APIRoute = async ({ request }) => {
|
||||
membershipRole: role,
|
||||
isIncluded: false,
|
||||
status: "imported" as RepoStatus,
|
||||
repositoryCount: publicRepos.length,
|
||||
repositoryCount: allRepos.length,
|
||||
createdAt: orgData.created_at ? new Date(orgData.created_at) : new Date(),
|
||||
updatedAt: orgData.updated_at ? new Date(orgData.updated_at) : new Date(),
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user