diff --git a/bun.lock b/bun.lock
index dcfb329..b10d434 100644
--- a/bun.lock
+++ b/bun.lock
@@ -18,7 +18,7 @@
"@radix-ui/react-select": "^2.2.4",
"@radix-ui/react-slot": "^1.2.2",
"@radix-ui/react-switch": "^1.2.5",
- "@radix-ui/react-tabs": "^1.1.11",
+ "@radix-ui/react-tabs": "^1.1.12",
"@radix-ui/react-tooltip": "^1.2.6",
"@tailwindcss/vite": "^4.1.7",
"@tanstack/react-virtual": "^3.13.8",
diff --git a/package.json b/package.json
index 24d4f74..c7c9da4 100644
--- a/package.json
+++ b/package.json
@@ -16,7 +16,6 @@
"check-db": "bun scripts/manage-db.ts check",
"fix-db": "bun scripts/manage-db.ts fix",
"reset-users": "bun scripts/manage-db.ts reset-users",
-
"startup-recovery": "bun scripts/startup-recovery.ts",
"startup-recovery-force": "bun scripts/startup-recovery.ts --force",
"test-recovery": "bun scripts/test-recovery.ts",
@@ -46,7 +45,7 @@
"@radix-ui/react-select": "^2.2.4",
"@radix-ui/react-slot": "^1.2.2",
"@radix-ui/react-switch": "^1.2.5",
- "@radix-ui/react-tabs": "^1.1.11",
+ "@radix-ui/react-tabs": "^1.1.12",
"@radix-ui/react-tooltip": "^1.2.6",
"@tailwindcss/vite": "^4.1.7",
"@tanstack/react-virtual": "^3.13.8",
diff --git a/src/components/config/AdvancedOptionsForm.tsx b/src/components/config/AdvancedOptionsForm.tsx
new file mode 100644
index 0000000..76abfc8
--- /dev/null
+++ b/src/components/config/AdvancedOptionsForm.tsx
@@ -0,0 +1,90 @@
+import React from "react";
+import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
+import { Checkbox } from "../ui/checkbox";
+import type { AdvancedOptions } from "@/types/config";
+import { RefreshCw } from "lucide-react";
+
+interface AdvancedOptionsFormProps {
+ config: AdvancedOptions;
+ setConfig: React.Dispatch>;
+ onAutoSave?: (config: AdvancedOptions) => Promise;
+ isAutoSaving?: boolean;
+}
+
+export function AdvancedOptionsForm({
+ config,
+ setConfig,
+ onAutoSave,
+ isAutoSaving = false,
+}: AdvancedOptionsFormProps) {
+ const handleChange = (name: string, checked: boolean) => {
+ const newConfig = {
+ ...config,
+ [name]: checked,
+ };
+
+ setConfig(newConfig);
+
+ // Auto-save
+ if (onAutoSave) {
+ onAutoSave(newConfig);
+ }
+ };
+
+ return (
+
+
+
+ Advanced Options
+ {isAutoSaving && (
+
+
+ Auto-saving...
+
+ )}
+
+
+
+
+
+
+ handleChange("skipForks", Boolean(checked))
+ }
+ />
+
+
+
+ Don't mirror repositories that are forks of other repositories
+
+
+
+
+ handleChange("skipStarredIssues", Boolean(checked))
+ }
+ />
+
+
+
+ Skip mirroring issues and pull requests for starred repositories
+
+
+
+
+ );
+}
diff --git a/src/components/config/ConfigTabs.tsx b/src/components/config/ConfigTabs.tsx
index 07069e5..5929c60 100644
--- a/src/components/config/ConfigTabs.tsx
+++ b/src/components/config/ConfigTabs.tsx
@@ -3,6 +3,9 @@ import { GitHubConfigForm } from './GitHubConfigForm';
import { GiteaConfigForm } from './GiteaConfigForm';
import { ScheduleConfigForm } from './ScheduleConfigForm';
import { DatabaseCleanupConfigForm } from './DatabaseCleanupConfigForm';
+import { MirrorOptionsForm } from './MirrorOptionsForm';
+import { AdvancedOptionsForm } from './AdvancedOptionsForm';
+import { Tabs, TabsContent, TabsList, TabsTrigger } from '../ui/tabs';
import type {
ConfigApiResponse,
GiteaConfig,
@@ -11,6 +14,8 @@ import type {
SaveConfigApiResponse,
ScheduleConfig,
DatabaseCleanupConfig,
+ MirrorOptions,
+ AdvancedOptions,
} from '@/types/config';
import { Button } from '../ui/button';
import { useAuth } from '@/hooks/useAuth';
@@ -25,6 +30,8 @@ type ConfigState = {
giteaConfig: GiteaConfig;
scheduleConfig: ScheduleConfig;
cleanupConfig: DatabaseCleanupConfig;
+ mirrorOptions: MirrorOptions;
+ advancedOptions: AdvancedOptions;
};
export function ConfigTabs() {
@@ -32,13 +39,8 @@ export function ConfigTabs() {
githubConfig: {
username: '',
token: '',
- skipForks: false,
privateRepositories: false,
- mirrorIssues: false,
- mirrorWiki: false,
mirrorStarred: false,
- preserveOrgStructure: false,
- skipStarredIssues: false,
},
giteaConfig: {
url: '',
@@ -47,6 +49,7 @@ export function ConfigTabs() {
organization: 'github-mirrors',
visibility: 'public',
starredReposOrg: 'github',
+ preserveOrgStructure: false,
},
scheduleConfig: {
enabled: false,
@@ -56,6 +59,21 @@ export function ConfigTabs() {
enabled: false,
retentionDays: 604800, // 7 days in seconds
},
+ mirrorOptions: {
+ mirrorReleases: false,
+ mirrorMetadata: false,
+ metadataComponents: {
+ issues: false,
+ pullRequests: false,
+ labels: false,
+ milestones: false,
+ wiki: false,
+ },
+ },
+ advancedOptions: {
+ skipForks: false,
+ skipStarredIssues: false,
+ },
});
const { user } = useAuth();
const [isLoading, setIsLoading] = useState(true);
@@ -65,10 +83,14 @@ export function ConfigTabs() {
const [isAutoSavingCleanup, setIsAutoSavingCleanup] = useState(false);
const [isAutoSavingGitHub, setIsAutoSavingGitHub] = useState(false);
const [isAutoSavingGitea, setIsAutoSavingGitea] = useState(false);
+ const [isAutoSavingMirrorOptions, setIsAutoSavingMirrorOptions] = useState(false);
+ const [isAutoSavingAdvancedOptions, setIsAutoSavingAdvancedOptions] = useState(false);
const autoSaveScheduleTimeoutRef = useRef(null);
const autoSaveCleanupTimeoutRef = useRef(null);
const autoSaveGitHubTimeoutRef = useRef(null);
const autoSaveGiteaTimeoutRef = useRef(null);
+ const autoSaveMirrorOptionsTimeoutRef = useRef(null);
+ const autoSaveAdvancedOptionsTimeoutRef = useRef(null);
const isConfigFormValid = (): boolean => {
const { githubConfig, giteaConfig } = config;
@@ -133,6 +155,8 @@ export function ConfigTabs() {
giteaConfig: config.giteaConfig,
scheduleConfig: scheduleConfig,
cleanupConfig: config.cleanupConfig,
+ mirrorOptions: config.mirrorOptions,
+ advancedOptions: config.advancedOptions,
};
try {
@@ -197,6 +221,8 @@ export function ConfigTabs() {
giteaConfig: config.giteaConfig,
scheduleConfig: config.scheduleConfig,
cleanupConfig: cleanupConfig,
+ mirrorOptions: config.mirrorOptions,
+ advancedOptions: config.advancedOptions,
};
try {
@@ -260,6 +286,8 @@ export function ConfigTabs() {
giteaConfig: config.giteaConfig,
scheduleConfig: config.scheduleConfig,
cleanupConfig: config.cleanupConfig,
+ mirrorOptions: config.mirrorOptions,
+ advancedOptions: config.advancedOptions,
};
try {
@@ -307,6 +335,8 @@ export function ConfigTabs() {
giteaConfig: giteaConfig,
scheduleConfig: config.scheduleConfig,
cleanupConfig: config.cleanupConfig,
+ mirrorOptions: config.mirrorOptions,
+ advancedOptions: config.advancedOptions,
};
try {
@@ -335,6 +365,104 @@ export function ConfigTabs() {
}, 500); // 500ms debounce
}, [user?.id, config.githubConfig, config.scheduleConfig, config.cleanupConfig]);
+ // Auto-save function specifically for mirror options changes
+ const autoSaveMirrorOptions = useCallback(async (mirrorOptions: MirrorOptions) => {
+ if (!user?.id) return;
+
+ // Clear any existing timeout
+ if (autoSaveMirrorOptionsTimeoutRef.current) {
+ clearTimeout(autoSaveMirrorOptionsTimeoutRef.current);
+ }
+
+ // Debounce the auto-save to prevent excessive API calls
+ autoSaveMirrorOptionsTimeoutRef.current = setTimeout(async () => {
+ setIsAutoSavingMirrorOptions(true);
+
+ const reqPayload: SaveConfigApiRequest = {
+ userId: user.id!,
+ githubConfig: config.githubConfig,
+ giteaConfig: config.giteaConfig,
+ scheduleConfig: config.scheduleConfig,
+ cleanupConfig: config.cleanupConfig,
+ mirrorOptions: mirrorOptions,
+ advancedOptions: config.advancedOptions,
+ };
+
+ try {
+ const response = await fetch('/api/config', {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify(reqPayload),
+ });
+ const result: SaveConfigApiResponse = await response.json();
+
+ if (result.success) {
+ // Silent success - no toast for auto-save
+ // Invalidate config cache so other components get fresh data
+ invalidateConfigCache();
+ } else {
+ showErrorToast(
+ `Auto-save failed: ${result.message || 'Unknown error'}`,
+ toast
+ );
+ }
+ } catch (error) {
+ showErrorToast(error, toast);
+ } finally {
+ setIsAutoSavingMirrorOptions(false);
+ }
+ }, 500); // 500ms debounce
+ }, [user?.id, config.githubConfig, config.giteaConfig, config.scheduleConfig, config.cleanupConfig, config.advancedOptions]);
+
+ // Auto-save function specifically for advanced options changes
+ const autoSaveAdvancedOptions = useCallback(async (advancedOptions: AdvancedOptions) => {
+ if (!user?.id) return;
+
+ // Clear any existing timeout
+ if (autoSaveAdvancedOptionsTimeoutRef.current) {
+ clearTimeout(autoSaveAdvancedOptionsTimeoutRef.current);
+ }
+
+ // Debounce the auto-save to prevent excessive API calls
+ autoSaveAdvancedOptionsTimeoutRef.current = setTimeout(async () => {
+ setIsAutoSavingAdvancedOptions(true);
+
+ const reqPayload: SaveConfigApiRequest = {
+ userId: user.id!,
+ githubConfig: config.githubConfig,
+ giteaConfig: config.giteaConfig,
+ scheduleConfig: config.scheduleConfig,
+ cleanupConfig: config.cleanupConfig,
+ mirrorOptions: config.mirrorOptions,
+ advancedOptions: advancedOptions,
+ };
+
+ try {
+ const response = await fetch('/api/config', {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify(reqPayload),
+ });
+ const result: SaveConfigApiResponse = await response.json();
+
+ if (result.success) {
+ // Silent success - no toast for auto-save
+ // Invalidate config cache so other components get fresh data
+ invalidateConfigCache();
+ } else {
+ showErrorToast(
+ `Auto-save failed: ${result.message || 'Unknown error'}`,
+ toast
+ );
+ }
+ } catch (error) {
+ showErrorToast(error, toast);
+ } finally {
+ setIsAutoSavingAdvancedOptions(false);
+ }
+ }, 500); // 500ms debounce
+ }, [user?.id, config.githubConfig, config.giteaConfig, config.scheduleConfig, config.cleanupConfig, config.mirrorOptions]);
+
// Cleanup timeouts on unmount
useEffect(() => {
return () => {
@@ -350,6 +478,12 @@ export function ConfigTabs() {
if (autoSaveGiteaTimeoutRef.current) {
clearTimeout(autoSaveGiteaTimeoutRef.current);
}
+ if (autoSaveMirrorOptionsTimeoutRef.current) {
+ clearTimeout(autoSaveMirrorOptionsTimeoutRef.current);
+ }
+ if (autoSaveAdvancedOptionsTimeoutRef.current) {
+ clearTimeout(autoSaveAdvancedOptionsTimeoutRef.current);
+ }
};
}, []);
@@ -373,6 +507,10 @@ export function ConfigTabs() {
response.scheduleConfig || config.scheduleConfig,
cleanupConfig:
response.cleanupConfig || config.cleanupConfig,
+ mirrorOptions:
+ response.mirrorOptions || config.mirrorOptions,
+ advancedOptions:
+ response.advancedOptions || config.advancedOptions,
});
}
@@ -496,72 +634,118 @@ export function ConfigTabs() {
{/* Content section */}
-
-
-
- setConfig(prev => ({
- ...prev,
- githubConfig:
- typeof update === 'function'
- ? update(prev.githubConfig)
- : update,
- }))
- }
- onAutoSave={autoSaveGitHubConfig}
- isAutoSaving={isAutoSavingGitHub}
- />
-
- setConfig(prev => ({
- ...prev,
- giteaConfig:
- typeof update === 'function'
- ? update(prev.giteaConfig)
- : update,
- }))
- }
- onAutoSave={autoSaveGiteaConfig}
- isAutoSaving={isAutoSavingGitea}
- />
-
-
-
-
+
+ Connections
+ Mirror Options
+ Schedule & Cleanup
+ Advanced
+
+
+
+
+
setConfig(prev => ({
...prev,
- scheduleConfig:
+ githubConfig:
typeof update === 'function'
- ? update(prev.scheduleConfig)
+ ? update(prev.githubConfig)
: update,
}))
}
- onAutoSave={autoSaveScheduleConfig}
- isAutoSaving={isAutoSavingSchedule}
+ onAutoSave={autoSaveGitHubConfig}
+ isAutoSaving={isAutoSavingGitHub}
/>
-
-
-
setConfig(prev => ({
...prev,
- cleanupConfig:
+ giteaConfig:
typeof update === 'function'
- ? update(prev.cleanupConfig)
+ ? update(prev.giteaConfig)
: update,
}))
}
- onAutoSave={autoSaveCleanupConfig}
- isAutoSaving={isAutoSavingCleanup}
+ onAutoSave={autoSaveGiteaConfig}
+ isAutoSaving={isAutoSavingGitea}
/>
-
-
+
+
+
+
+ setConfig(prev => ({
+ ...prev,
+ mirrorOptions:
+ typeof update === 'function'
+ ? update(prev.mirrorOptions)
+ : update,
+ }))
+ }
+ onAutoSave={autoSaveMirrorOptions}
+ isAutoSaving={isAutoSavingMirrorOptions}
+ />
+
+
+
+
+
+
+ setConfig(prev => ({
+ ...prev,
+ scheduleConfig:
+ typeof update === 'function'
+ ? update(prev.scheduleConfig)
+ : update,
+ }))
+ }
+ onAutoSave={autoSaveScheduleConfig}
+ isAutoSaving={isAutoSavingSchedule}
+ />
+
+
+
+ setConfig(prev => ({
+ ...prev,
+ cleanupConfig:
+ typeof update === 'function'
+ ? update(prev.cleanupConfig)
+ : update,
+ }))
+ }
+ onAutoSave={autoSaveCleanupConfig}
+ isAutoSaving={isAutoSavingCleanup}
+ />
+
+
+
+
+
+
+ setConfig(prev => ({
+ ...prev,
+ advancedOptions:
+ typeof update === 'function'
+ ? update(prev.advancedOptions)
+ : update,
+ }))
+ }
+ onAutoSave={autoSaveAdvancedOptions}
+ isAutoSaving={isAutoSavingAdvancedOptions}
+ />
+
+
);
}
diff --git a/src/components/config/GitHubConfigForm.tsx b/src/components/config/GitHubConfigForm.tsx
index 6356205..7c820a4 100644
--- a/src/components/config/GitHubConfigForm.tsx
+++ b/src/components/config/GitHubConfigForm.tsx
@@ -30,21 +30,6 @@ export function GitHubConfigForm({ config, setConfig, onAutoSave, isAutoSaving }
const handleChange = (e: React.ChangeEvent) => {
const { name, value, type, checked } = e.target;
- // 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,
@@ -140,32 +125,10 @@ export function GitHubConfigForm({ config, setConfig, onAutoSave, isAutoSaving }
-
-
-
-
- handleChange({
- target: {
- name: "skipForks",
- type: "checkbox",
- checked: Boolean(checked),
- value: "",
- },
- } as React.ChangeEvent)
- }
- />
-
-
+
+
Repository Access
+
- Mirror Private Repos
+ Include private repos
@@ -210,121 +173,7 @@ export function GitHubConfigForm({ config, setConfig, onAutoSave, isAutoSaving }
htmlFor="mirror-starred"
className="ml-2 block text-sm select-none"
>
- Mirror Starred Repos
-
-
-
-
-
-
-
- handleChange({
- target: {
- name: "mirrorIssues",
- type: "checkbox",
- checked: Boolean(checked),
- value: "",
- },
- } as React.ChangeEvent)
- }
- />
-
-
-
-
-
- handleChange({
- target: {
- name: "mirrorWiki",
- type: "checkbox",
- checked: Boolean(checked),
- value: "",
- },
- } as React.ChangeEvent)
- }
- />
-
-
-
-
-
- handleChange({
- target: {
- name: "preserveOrgStructure",
- type: "checkbox",
- checked: Boolean(checked),
- value: "",
- },
- } as React.ChangeEvent)
- }
- />
-
-
-
-
-
- handleChange({
- target: {
- name: "skipStarredIssues",
- type: "checkbox",
- checked: Boolean(checked),
- value: "",
- },
- } as React.ChangeEvent)
- }
- />
-
diff --git a/src/components/config/GiteaConfigForm.tsx b/src/components/config/GiteaConfigForm.tsx
index f86bead..0f85fa7 100644
--- a/src/components/config/GiteaConfigForm.tsx
+++ b/src/components/config/GiteaConfigForm.tsx
@@ -14,9 +14,12 @@ import {
SelectTrigger,
SelectValue,
} from "../ui/select";
+import { Checkbox } from "../ui/checkbox";
import { giteaApi } from "@/lib/api";
import type { GiteaConfig, GiteaOrgVisibility } from "@/types/config";
import { toast } from "sonner";
+import { Info } from "lucide-react";
+import { Tooltip, TooltipContent, TooltipTrigger } from "../ui/tooltip";
interface GiteaConfigFormProps {
config: GiteaConfig;
@@ -31,10 +34,27 @@ export function GiteaConfigForm({ config, setConfig, onAutoSave, isAutoSaving }:
const handleChange = (
e: React.ChangeEvent
) => {
- const { name, value } = e.target;
+ 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]: value,
+ [name]: type === "checkbox" ? checked : value,
};
setConfig(newConfig);
@@ -153,7 +173,7 @@ export function GiteaConfigForm({ config, setConfig, onAutoSave, isAutoSaving }:
htmlFor="organization"
className="block text-sm font-medium mb-1.5"
>
- Default Organization (Optional)
+ Destination organisation (optional)
- If specified, repositories will be mirrored to this organization.
+ Repos are created here if no per-repo org is set.
+
+
+ handleChange({
+ target: {
+ name: "preserveOrgStructure",
+ type: "checkbox",
+ checked: Boolean(checked),
+ value: "",
+ },
+ } as React.ChangeEvent)
+ }
+ />
+
+
+
- Organization for starred repositories (default: github)
+ Leave blank to use 'github'.
diff --git a/src/components/config/MirrorOptionsForm.tsx b/src/components/config/MirrorOptionsForm.tsx
new file mode 100644
index 0000000..ed0cd8c
--- /dev/null
+++ b/src/components/config/MirrorOptionsForm.tsx
@@ -0,0 +1,226 @@
+import React from "react";
+import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
+import { Checkbox } from "../ui/checkbox";
+import type { MirrorOptions } from "@/types/config";
+import { RefreshCw, Info } from "lucide-react";
+import { Tooltip, TooltipContent, TooltipTrigger } from "../ui/tooltip";
+
+interface MirrorOptionsFormProps {
+ config: MirrorOptions;
+ setConfig: React.Dispatch
>;
+ onAutoSave?: (config: MirrorOptions) => Promise;
+ isAutoSaving?: boolean;
+}
+
+export function MirrorOptionsForm({
+ config,
+ setConfig,
+ onAutoSave,
+ isAutoSaving = false,
+}: MirrorOptionsFormProps) {
+ const handleChange = (name: string, checked: boolean) => {
+ let newConfig = { ...config };
+
+ if (name === "mirrorMetadata") {
+ newConfig.mirrorMetadata = checked;
+ // If disabling metadata, also disable all components
+ if (!checked) {
+ newConfig.metadataComponents = {
+ issues: false,
+ pullRequests: false,
+ labels: false,
+ milestones: false,
+ wiki: false,
+ };
+ }
+ } else if (name.startsWith("metadataComponents.")) {
+ const componentName = name.split(".")[1] as keyof typeof config.metadataComponents;
+ newConfig.metadataComponents = {
+ ...config.metadataComponents,
+ [componentName]: checked,
+ };
+ } else {
+ newConfig = {
+ ...config,
+ [name]: checked,
+ };
+ }
+
+ setConfig(newConfig);
+
+ // Auto-save
+ if (onAutoSave) {
+ onAutoSave(newConfig);
+ }
+ };
+
+ return (
+
+
+
+ Mirror Options
+ {isAutoSaving && (
+
+
+ Auto-saving...
+
+ )}
+
+
+
+ {/* Repository Content */}
+
+
Repository Content
+
+
+
+ handleChange("mirrorReleases", Boolean(checked))
+ }
+ />
+
+
+
+
+
+ handleChange("mirrorMetadata", Boolean(checked))
+ }
+ />
+
+
+
+ {/* Metadata Components */}
+ {config.mirrorMetadata && (
+
+
+ Metadata Components
+
+
+
+
+
+ handleChange("metadataComponents.issues", Boolean(checked))
+ }
+ disabled={!config.mirrorMetadata}
+ />
+
+
+
+
+
+ handleChange("metadataComponents.pullRequests", Boolean(checked))
+ }
+ disabled={!config.mirrorMetadata}
+ />
+
+
+
+
+
+ handleChange("metadataComponents.labels", Boolean(checked))
+ }
+ disabled={!config.mirrorMetadata}
+ />
+
+
+
+
+
+ handleChange("metadataComponents.milestones", Boolean(checked))
+ }
+ disabled={!config.mirrorMetadata}
+ />
+
+
+
+
+
+ handleChange("metadataComponents.wiki", Boolean(checked))
+ }
+ disabled={!config.mirrorMetadata}
+ />
+
+
+
+
+ )}
+
+
+
+ );
+}
diff --git a/src/types/config.ts b/src/types/config.ts
index fecbe0d..dbdd1b1 100644
--- a/src/types/config.ts
+++ b/src/types/config.ts
@@ -9,6 +9,7 @@ export interface GiteaConfig {
organization: string;
visibility: GiteaOrgVisibility;
starredReposOrg: string;
+ preserveOrgStructure: boolean;
}
export interface ScheduleConfig {
@@ -28,12 +29,24 @@ export interface DatabaseCleanupConfig {
export interface GitHubConfig {
username: string;
token: string;
- skipForks: boolean;
privateRepositories: boolean;
- mirrorIssues: boolean;
- mirrorWiki: boolean;
mirrorStarred: boolean;
- preserveOrgStructure: boolean;
+}
+
+export interface MirrorOptions {
+ mirrorReleases: boolean;
+ mirrorMetadata: boolean;
+ metadataComponents: {
+ issues: boolean;
+ pullRequests: boolean;
+ labels: boolean;
+ milestones: boolean;
+ wiki: boolean;
+ };
+}
+
+export interface AdvancedOptions {
+ skipForks: boolean;
skipStarredIssues: boolean;
}
@@ -43,6 +56,8 @@ export interface SaveConfigApiRequest {
giteaConfig: GiteaConfig;
scheduleConfig: ScheduleConfig;
cleanupConfig: DatabaseCleanupConfig;
+ mirrorOptions?: MirrorOptions;
+ advancedOptions?: AdvancedOptions;
}
export interface SaveConfigApiResponse {
@@ -65,6 +80,8 @@ export interface ConfigApiResponse {
giteaConfig: GiteaConfig;
scheduleConfig: ScheduleConfig;
cleanupConfig: DatabaseCleanupConfig;
+ mirrorOptions?: MirrorOptions;
+ advancedOptions?: AdvancedOptions;
include: string[];
exclude: string[];
createdAt: Date;