mirror of
https://github.com/RayLabsHQ/gitea-mirror.git
synced 2026-08-13 01:59:31 +08:00
feat: warn when Forgejo destination has known mirror-credential bug (refs #263)
Forgejo < 15.0.0 silently discards auth_username/auth_password sent to /api/v1/repos/migrate, causing subsequent pull-mirror sync of private repos to fail with `terminal prompts disabled`. Fix landed upstream in Forgejo v15.0.0 via codeberg.org/forgejo/forgejo/pulls/11909 and was not backported to v12/v13/v14. Test-connection endpoint now also probes /api/v1/version, detects Forgejo via the `+gitea-` suffix, and surfaces a warning Alert in the Gitea config form when the connected server reports a major version below 15.
This commit is contained in:
@@ -6,7 +6,9 @@ import {
|
||||
CardHeader,
|
||||
CardTitle,
|
||||
} from "@/components/ui/card";
|
||||
import { giteaApi } from "@/lib/api";
|
||||
import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert";
|
||||
import { AlertTriangle } from "lucide-react";
|
||||
import { giteaApi, type GiteaServerInfo } from "@/lib/api";
|
||||
import type { GiteaConfig, MirrorStrategy } from "@/types/config";
|
||||
import { toast } from "sonner";
|
||||
import { OrganizationStrategy } from "./OrganizationStrategy";
|
||||
@@ -23,6 +25,7 @@ interface GiteaConfigFormProps {
|
||||
|
||||
export function GiteaConfigForm({ config, setConfig, onAutoSave, isAutoSaving, githubUsername }: GiteaConfigFormProps) {
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [serverInfo, setServerInfo] = useState<GiteaServerInfo | null>(null);
|
||||
|
||||
// Derive the mirror strategy from existing config for backward compatibility
|
||||
const getMirrorStrategy = (): MirrorStrategy => {
|
||||
@@ -128,13 +131,16 @@ export function GiteaConfigForm({ config, setConfig, onAutoSave, isAutoSaving, g
|
||||
try {
|
||||
const result = await giteaApi.testConnection(config.url, config.token);
|
||||
if (result.success) {
|
||||
setServerInfo(result.serverInfo ?? null);
|
||||
toast.success("Successfully connected to Gitea!");
|
||||
} else {
|
||||
setServerInfo(null);
|
||||
toast.error(
|
||||
"Failed to connect to Gitea. Please check your URL and token."
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
setServerInfo(null);
|
||||
toast.error(
|
||||
error instanceof Error ? error.message : "An unknown error occurred"
|
||||
);
|
||||
@@ -162,6 +168,31 @@ export function GiteaConfigForm({ config, setConfig, onAutoSave, isAutoSaving, g
|
||||
</CardHeader>
|
||||
|
||||
<CardContent className="flex flex-col gap-y-6 flex-1">
|
||||
{serverInfo?.type === "forgejo" && serverInfo.hasMirrorCredBug && (
|
||||
<Alert variant="warning">
|
||||
<AlertTriangle className="h-4 w-4" />
|
||||
<AlertTitle>
|
||||
Forgejo {serverInfo.version} has a known mirror-credential bug
|
||||
</AlertTitle>
|
||||
<AlertDescription>
|
||||
<p>
|
||||
Pull-mirror credentials sent via Forgejo's migrate API aren't persisted on this version, so subsequent syncs of private repos fail with <code className="text-xs font-mono bg-amber-100 dark:bg-amber-900/40 px-1 py-0.5 rounded">terminal prompts disabled</code>. Fixed in Forgejo 15.0.0 (
|
||||
<a
|
||||
href="https://codeberg.org/forgejo/forgejo/pulls/11909"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="underline underline-offset-2"
|
||||
>
|
||||
PR #11909
|
||||
</a>
|
||||
).
|
||||
</p>
|
||||
<p>
|
||||
Upgrade Forgejo to 15.0.0 or later, then delete and re-mirror affected repos — or open each repo's Settings → Mirror Settings in Forgejo and re-enter the GitHub token once.
|
||||
</p>
|
||||
</AlertDescription>
|
||||
</Alert>
|
||||
)}
|
||||
<div>
|
||||
<label
|
||||
htmlFor="gitea-username"
|
||||
|
||||
+14
-4
@@ -87,12 +87,22 @@ export const githubApi = {
|
||||
};
|
||||
|
||||
// Gitea API
|
||||
export interface GiteaServerInfo {
|
||||
type: "forgejo" | "gitea";
|
||||
version: string;
|
||||
raw: string;
|
||||
hasMirrorCredBug: boolean;
|
||||
}
|
||||
|
||||
export const giteaApi = {
|
||||
testConnection: (url: string, token: string) =>
|
||||
apiRequest<{ success: boolean }>("/gitea/test-connection", {
|
||||
method: "POST",
|
||||
body: JSON.stringify({ url, token }),
|
||||
}),
|
||||
apiRequest<{ success: boolean; serverInfo?: GiteaServerInfo; message?: string }>(
|
||||
"/gitea/test-connection",
|
||||
{
|
||||
method: "POST",
|
||||
body: JSON.stringify({ url, token }),
|
||||
}
|
||||
),
|
||||
};
|
||||
|
||||
// Health API
|
||||
|
||||
@@ -2,6 +2,25 @@ import type { APIRoute } from 'astro';
|
||||
import { httpGet, HttpError } from '@/lib/http-client';
|
||||
import { createSecureErrorResponse } from '@/lib/utils';
|
||||
|
||||
// Forgejo reports `15.0.0+gitea-1.22.0`; pure Gitea reports just `1.22.0`.
|
||||
// Forgejo < 15.0.0 has a known bug where pull-mirror credentials sent via
|
||||
// /api/v1/repos/migrate are not persisted, so subsequent sync of private
|
||||
// repos fails with `terminal prompts disabled`. Fixed upstream in v15.0.0
|
||||
// via PR #11909 (codeberg.org/forgejo/forgejo/pulls/11909).
|
||||
function parseServerInfo(versionString: string) {
|
||||
const forgejoMatch = versionString.match(/^(\d+)\.(\d+)\.(\d+)\+gitea-/);
|
||||
if (forgejoMatch) {
|
||||
const major = Number(forgejoMatch[1]);
|
||||
return {
|
||||
type: 'forgejo' as const,
|
||||
version: `${forgejoMatch[1]}.${forgejoMatch[2]}.${forgejoMatch[3]}`,
|
||||
raw: versionString,
|
||||
hasMirrorCredBug: major < 15,
|
||||
};
|
||||
}
|
||||
return { type: 'gitea' as const, version: versionString, raw: versionString, hasMirrorCredBug: false };
|
||||
}
|
||||
|
||||
export const POST: APIRoute = async ({ request }) => {
|
||||
try {
|
||||
const body = await request.json();
|
||||
@@ -49,7 +68,18 @@ export const POST: APIRoute = async ({ request }) => {
|
||||
);
|
||||
}
|
||||
|
||||
// Return success response with user data
|
||||
let serverInfo: ReturnType<typeof parseServerInfo> | undefined;
|
||||
try {
|
||||
const versionResp = await httpGet(`${baseUrl}/api/v1/version`, {
|
||||
'Accept': 'application/json',
|
||||
});
|
||||
if (typeof versionResp.data?.version === 'string') {
|
||||
serverInfo = parseServerInfo(versionResp.data.version);
|
||||
}
|
||||
} catch {
|
||||
// Version probe is best-effort; older or non-standard servers may not expose it.
|
||||
}
|
||||
|
||||
return new Response(
|
||||
JSON.stringify({
|
||||
success: true,
|
||||
@@ -59,6 +89,7 @@ export const POST: APIRoute = async ({ request }) => {
|
||||
name: data.full_name,
|
||||
avatar_url: data.avatar_url,
|
||||
},
|
||||
serverInfo,
|
||||
}),
|
||||
{
|
||||
status: 200,
|
||||
|
||||
Reference in New Issue
Block a user