mirror of
https://github.com/RayLabsHQ/gitea-mirror.git
synced 2025-12-10 21:46:45 +03:00
feat: add support for mirroring wiki pages in configuration
This commit is contained in:
@@ -238,6 +238,7 @@ export const GET: APIRoute = async ({ request }) => {
|
||||
skipForks: false,
|
||||
privateRepositories: false,
|
||||
mirrorIssues: false,
|
||||
mirrorWiki: false,
|
||||
mirrorStarred: true,
|
||||
useSpecificUser: false,
|
||||
preserveOrgStructure: true,
|
||||
|
||||
@@ -1,35 +1,75 @@
|
||||
import { describe, test, expect, mock, beforeEach, afterEach } from "bun:test";
|
||||
import type { MirrorRepoRequest } from "@/types/mirror";
|
||||
|
||||
// Create a mock POST function
|
||||
const mockPOST = mock(async ({ request }) => {
|
||||
const body = await request.json();
|
||||
|
||||
// Check for missing userId or repositoryIds
|
||||
if (!body.userId || !body.repositoryIds) {
|
||||
return new Response(
|
||||
JSON.stringify({
|
||||
error: "Missing userId or repositoryIds."
|
||||
}),
|
||||
{ status: 400 }
|
||||
);
|
||||
}
|
||||
|
||||
// Success case
|
||||
return new Response(
|
||||
JSON.stringify({
|
||||
success: true,
|
||||
message: "Repository mirroring started",
|
||||
batchId: "test-batch-id"
|
||||
}),
|
||||
{ status: 200 }
|
||||
);
|
||||
});
|
||||
|
||||
// Create a mock module
|
||||
const mockModule = {
|
||||
POST: mockPOST
|
||||
// Mock the database module
|
||||
const mockDb = {
|
||||
select: mock(() => ({
|
||||
from: mock(() => ({
|
||||
where: mock(() => ({
|
||||
limit: mock(() => Promise.resolve([{
|
||||
id: "config-id",
|
||||
userId: "user-id",
|
||||
githubConfig: {
|
||||
token: "github-token",
|
||||
preserveOrgStructure: false,
|
||||
mirrorIssues: false
|
||||
},
|
||||
giteaConfig: {
|
||||
url: "https://gitea.example.com",
|
||||
token: "gitea-token",
|
||||
username: "giteauser"
|
||||
}
|
||||
}]))
|
||||
}))
|
||||
}))
|
||||
}))
|
||||
};
|
||||
|
||||
mock.module("@/lib/db", () => ({
|
||||
db: mockDb,
|
||||
configs: {},
|
||||
repositories: {}
|
||||
}));
|
||||
|
||||
// Mock the gitea module
|
||||
const mockMirrorGithubRepoToGitea = mock(() => Promise.resolve());
|
||||
const mockMirrorGitHubOrgRepoToGiteaOrg = mock(() => Promise.resolve());
|
||||
|
||||
mock.module("@/lib/gitea", () => ({
|
||||
mirrorGithubRepoToGitea: mockMirrorGithubRepoToGitea,
|
||||
mirrorGitHubOrgRepoToGiteaOrg: mockMirrorGitHubOrgRepoToGiteaOrg
|
||||
}));
|
||||
|
||||
// Mock the github module
|
||||
const mockCreateGitHubClient = mock(() => ({}));
|
||||
|
||||
mock.module("@/lib/github", () => ({
|
||||
createGitHubClient: mockCreateGitHubClient
|
||||
}));
|
||||
|
||||
// Mock the concurrency module
|
||||
const mockProcessWithResilience = mock(() => Promise.resolve([]));
|
||||
|
||||
mock.module("@/lib/utils/concurrency", () => ({
|
||||
processWithResilience: mockProcessWithResilience
|
||||
}));
|
||||
|
||||
// Mock drizzle-orm
|
||||
mock.module("drizzle-orm", () => ({
|
||||
eq: mock(() => ({})),
|
||||
inArray: mock(() => ({}))
|
||||
}));
|
||||
|
||||
// Mock the types
|
||||
mock.module("@/types/Repository", () => ({
|
||||
repositoryVisibilityEnum: {
|
||||
parse: mock((value: string) => value)
|
||||
},
|
||||
repoStatusEnum: {
|
||||
parse: mock((value: string) => value)
|
||||
}
|
||||
}));
|
||||
|
||||
describe("Repository Mirroring API", () => {
|
||||
// Mock console.log and console.error to prevent test output noise
|
||||
let originalConsoleLog: typeof console.log;
|
||||
|
||||
@@ -9,7 +9,6 @@ import {
|
||||
} from "@/lib/gitea";
|
||||
import { createGitHubClient } from "@/lib/github";
|
||||
import { processWithResilience } from "@/lib/utils/concurrency";
|
||||
import { v4 as uuidv4 } from "uuid";
|
||||
|
||||
export const POST: APIRoute = async ({ request }) => {
|
||||
try {
|
||||
@@ -77,9 +76,6 @@ export const POST: APIRoute = async ({ request }) => {
|
||||
// Define the concurrency limit - adjust based on API rate limits
|
||||
const CONCURRENCY_LIMIT = 3;
|
||||
|
||||
// Generate a batch ID to group related repositories
|
||||
const batchId = uuidv4();
|
||||
|
||||
// Process repositories in parallel with resilience to container restarts
|
||||
await processWithResilience(
|
||||
repos,
|
||||
@@ -120,7 +116,6 @@ export const POST: APIRoute = async ({ request }) => {
|
||||
{
|
||||
userId: config.userId || "",
|
||||
jobType: "mirror",
|
||||
batchId,
|
||||
getItemId: (repo) => repo.id,
|
||||
getItemName: (repo) => repo.name,
|
||||
concurrencyLimit: CONCURRENCY_LIMIT,
|
||||
@@ -129,15 +124,19 @@ export const POST: APIRoute = async ({ request }) => {
|
||||
checkpointInterval: 5, // Checkpoint every 5 repositories to reduce event frequency
|
||||
onProgress: (completed, total, result) => {
|
||||
const percentComplete = Math.round((completed / total) * 100);
|
||||
console.log(`Mirroring progress: ${percentComplete}% (${completed}/${total})`);
|
||||
console.log(
|
||||
`Mirroring progress: ${percentComplete}% (${completed}/${total})`
|
||||
);
|
||||
|
||||
if (result) {
|
||||
console.log(`Successfully mirrored repository: ${result.name}`);
|
||||
}
|
||||
},
|
||||
onRetry: (repo, error, attempt) => {
|
||||
console.log(`Retrying repository ${repo.name} (attempt ${attempt}): ${error.message}`);
|
||||
}
|
||||
console.log(
|
||||
`Retrying repository ${repo.name} (attempt ${attempt}): ${error.message}`
|
||||
);
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
@@ -168,7 +167,10 @@ export const POST: APIRoute = async ({ request }) => {
|
||||
// Enhanced error logging for better debugging
|
||||
console.error("=== ERROR MIRRORING REPOSITORIES ===");
|
||||
console.error("Error type:", error?.constructor?.name);
|
||||
console.error("Error message:", error instanceof Error ? error.message : String(error));
|
||||
console.error(
|
||||
"Error message:",
|
||||
error instanceof Error ? error.message : String(error)
|
||||
);
|
||||
|
||||
if (error instanceof Error) {
|
||||
console.error("Error stack:", error.stack);
|
||||
@@ -181,9 +183,11 @@ export const POST: APIRoute = async ({ request }) => {
|
||||
console.error("- Headers:", Object.fromEntries(request.headers.entries()));
|
||||
|
||||
// If it's a JSON parsing error, provide more context
|
||||
if (error instanceof SyntaxError && error.message.includes('JSON')) {
|
||||
if (error instanceof SyntaxError && error.message.includes("JSON")) {
|
||||
console.error("🚨 JSON PARSING ERROR DETECTED:");
|
||||
console.error("This suggests the response from Gitea API is not valid JSON");
|
||||
console.error(
|
||||
"This suggests the response from Gitea API is not valid JSON"
|
||||
);
|
||||
console.error("Common causes:");
|
||||
console.error("- Gitea server returned HTML error page instead of JSON");
|
||||
console.error("- Network connection interrupted");
|
||||
@@ -196,14 +200,16 @@ export const POST: APIRoute = async ({ request }) => {
|
||||
|
||||
return new Response(
|
||||
JSON.stringify({
|
||||
error: error instanceof Error ? error.message : "An unknown error occurred",
|
||||
error:
|
||||
error instanceof Error ? error.message : "An unknown error occurred",
|
||||
errorType: error?.constructor?.name || "Unknown",
|
||||
timestamp: new Date().toISOString(),
|
||||
troubleshooting: error instanceof SyntaxError && error.message.includes('JSON')
|
||||
? "JSON parsing error detected. Check Gitea server status and logs. Ensure Gitea is returning valid JSON responses."
|
||||
: "Check application logs for more details"
|
||||
troubleshooting:
|
||||
error instanceof SyntaxError && error.message.includes("JSON")
|
||||
? "JSON parsing error detected. Check Gitea server status and logs. Ensure Gitea is returning valid JSON responses."
|
||||
: "Check application logs for more details",
|
||||
}),
|
||||
{ status: 500, headers: { "Content-Type": "application/json" } }
|
||||
);
|
||||
}
|
||||
};
|
||||
};
|
||||
Reference in New Issue
Block a user