mirror of
https://github.com/RayLabsHQ/gitea-mirror.git
synced 2025-12-07 03:56:46 +03:00
241 lines
7.6 KiB
TypeScript
241 lines
7.6 KiB
TypeScript
import React, { useState, useEffect } from "react";
|
|
import { Button } from "@/components/ui/button";
|
|
import {
|
|
Card,
|
|
CardContent,
|
|
CardHeader,
|
|
CardTitle,
|
|
} from "@/components/ui/card";
|
|
import { giteaApi } from "@/lib/api";
|
|
import type { GiteaConfig, MirrorStrategy } from "@/types/config";
|
|
import { toast } from "sonner";
|
|
import { OrganizationStrategy } from "./OrganizationStrategy";
|
|
import { OrganizationConfiguration } from "./OrganizationConfiguration";
|
|
import { Separator } from "../ui/separator";
|
|
|
|
interface GiteaConfigFormProps {
|
|
config: GiteaConfig;
|
|
setConfig: React.Dispatch<React.SetStateAction<GiteaConfig>>;
|
|
onAutoSave?: (giteaConfig: GiteaConfig) => Promise<void>;
|
|
isAutoSaving?: boolean;
|
|
githubUsername?: string;
|
|
}
|
|
|
|
export function GiteaConfigForm({ config, setConfig, onAutoSave, isAutoSaving, githubUsername }: GiteaConfigFormProps) {
|
|
const [isLoading, setIsLoading] = useState(false);
|
|
|
|
// Derive the mirror strategy from existing config for backward compatibility
|
|
const getMirrorStrategy = (): MirrorStrategy => {
|
|
if (config.mirrorStrategy) return config.mirrorStrategy;
|
|
if (config.preserveOrgStructure) return "preserve";
|
|
if (config.organization && config.organization !== config.username) return "single-org";
|
|
return "flat-user";
|
|
};
|
|
|
|
const [mirrorStrategy, setMirrorStrategy] = useState<MirrorStrategy>(getMirrorStrategy());
|
|
|
|
// Update config when strategy changes
|
|
useEffect(() => {
|
|
const newConfig = { ...config };
|
|
|
|
switch (mirrorStrategy) {
|
|
case "preserve":
|
|
newConfig.preserveOrgStructure = true;
|
|
newConfig.mirrorStrategy = "preserve";
|
|
break;
|
|
case "single-org":
|
|
newConfig.preserveOrgStructure = false;
|
|
newConfig.mirrorStrategy = "single-org";
|
|
if (!newConfig.organization) {
|
|
newConfig.organization = "github-mirrors";
|
|
}
|
|
break;
|
|
case "flat-user":
|
|
newConfig.preserveOrgStructure = false;
|
|
newConfig.mirrorStrategy = "flat-user";
|
|
newConfig.organization = "";
|
|
break;
|
|
}
|
|
|
|
setConfig(newConfig);
|
|
if (onAutoSave) {
|
|
onAutoSave(newConfig);
|
|
}
|
|
}, [mirrorStrategy]);
|
|
|
|
const handleChange = (
|
|
e: React.ChangeEvent<HTMLInputElement | HTMLSelectElement>
|
|
) => {
|
|
const { name, value, type } = e.target;
|
|
const checked = type === "checkbox" ? (e.target as HTMLInputElement).checked : undefined;
|
|
|
|
// Special handling for preserveOrgStructure changes
|
|
if (
|
|
name === "preserveOrgStructure" &&
|
|
config.preserveOrgStructure !== checked
|
|
) {
|
|
toast.info(
|
|
"Changing this setting may affect how repositories are accessed in Gitea. " +
|
|
"Existing mirrored repositories will still be accessible during sync operations.",
|
|
{
|
|
duration: 6000,
|
|
position: "top-center",
|
|
}
|
|
);
|
|
}
|
|
|
|
const newConfig = {
|
|
...config,
|
|
[name]: type === "checkbox" ? checked : value,
|
|
};
|
|
setConfig(newConfig);
|
|
|
|
// Auto-save for all field changes
|
|
if (onAutoSave) {
|
|
onAutoSave(newConfig);
|
|
}
|
|
};
|
|
|
|
const testConnection = async () => {
|
|
if (!config.url || !config.token) {
|
|
toast.error("Gitea URL and token are required to test the connection");
|
|
return;
|
|
}
|
|
|
|
setIsLoading(true);
|
|
|
|
try {
|
|
const result = await giteaApi.testConnection(config.url, config.token);
|
|
if (result.success) {
|
|
toast.success("Successfully connected to Gitea!");
|
|
} else {
|
|
toast.error(
|
|
"Failed to connect to Gitea. Please check your URL and token."
|
|
);
|
|
}
|
|
} catch (error) {
|
|
toast.error(
|
|
error instanceof Error ? error.message : "An unknown error occurred"
|
|
);
|
|
} finally {
|
|
setIsLoading(false);
|
|
}
|
|
};
|
|
|
|
return (
|
|
<Card className="w-full h-full flex flex-col">
|
|
<CardHeader className="flex flex-row items-center justify-between gap-4">
|
|
<CardTitle className="text-lg font-semibold">
|
|
Gitea Configuration
|
|
</CardTitle>
|
|
<Button
|
|
type="button"
|
|
variant="outline"
|
|
onClick={testConnection}
|
|
disabled={isLoading || !config.url || !config.token}
|
|
>
|
|
{isLoading ? "Testing..." : "Test Connection"}
|
|
</Button>
|
|
</CardHeader>
|
|
|
|
<CardContent className="flex flex-col gap-y-6 flex-1">
|
|
<div>
|
|
<label
|
|
htmlFor="gitea-username"
|
|
className="block text-sm font-medium mb-1.5"
|
|
>
|
|
Gitea Username
|
|
</label>
|
|
<input
|
|
id="gitea-username"
|
|
name="username"
|
|
type="text"
|
|
value={config.username}
|
|
onChange={handleChange}
|
|
className="w-full rounded-md border border-input bg-background px-3 py-2 text-sm shadow-sm transition-colors placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring"
|
|
placeholder="Your Gitea username"
|
|
required
|
|
/>
|
|
</div>
|
|
|
|
<div>
|
|
<label
|
|
htmlFor="gitea-url"
|
|
className="block text-sm font-medium mb-1.5"
|
|
>
|
|
Gitea URL
|
|
</label>
|
|
<input
|
|
id="gitea-url"
|
|
name="url"
|
|
type="url"
|
|
value={config.url}
|
|
onChange={handleChange}
|
|
className="w-full rounded-md border border-input bg-background px-3 py-2 text-sm shadow-sm transition-colors placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring"
|
|
placeholder="https://your-gitea-instance.com"
|
|
required
|
|
/>
|
|
</div>
|
|
|
|
<div>
|
|
<label
|
|
htmlFor="gitea-token"
|
|
className="block text-sm font-medium mb-1.5"
|
|
>
|
|
Gitea Token
|
|
</label>
|
|
<input
|
|
id="gitea-token"
|
|
name="token"
|
|
type="password"
|
|
value={config.token}
|
|
onChange={handleChange}
|
|
className="w-full rounded-md border border-input bg-background px-3 py-2 text-sm shadow-sm transition-colors placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring"
|
|
placeholder="Your Gitea access token"
|
|
required
|
|
/>
|
|
<p className="text-xs text-muted-foreground mt-1">
|
|
Create a token in your Gitea instance under Settings >
|
|
Applications.
|
|
</p>
|
|
</div>
|
|
|
|
<Separator />
|
|
|
|
<OrganizationStrategy
|
|
strategy={mirrorStrategy}
|
|
destinationOrg={config.organization}
|
|
starredReposOrg={config.starredReposOrg}
|
|
onStrategyChange={setMirrorStrategy}
|
|
githubUsername={githubUsername}
|
|
giteaUsername={config.username}
|
|
/>
|
|
|
|
<Separator />
|
|
|
|
<OrganizationConfiguration
|
|
strategy={mirrorStrategy}
|
|
destinationOrg={config.organization}
|
|
starredReposOrg={config.starredReposOrg}
|
|
visibility={config.visibility}
|
|
onDestinationOrgChange={(org) => {
|
|
const newConfig = { ...config, organization: org };
|
|
setConfig(newConfig);
|
|
if (onAutoSave) onAutoSave(newConfig);
|
|
}}
|
|
onStarredReposOrgChange={(org) => {
|
|
const newConfig = { ...config, starredReposOrg: org };
|
|
setConfig(newConfig);
|
|
if (onAutoSave) onAutoSave(newConfig);
|
|
}}
|
|
onVisibilityChange={(visibility) => {
|
|
const newConfig = { ...config, visibility };
|
|
setConfig(newConfig);
|
|
if (onAutoSave) onAutoSave(newConfig);
|
|
}}
|
|
/>
|
|
</CardContent>
|
|
</Card>
|
|
);
|
|
}
|