feat: add repository management dialogs and store functionality

- Implement CreateRepoDialog for creating new repositories with form validation.
- Implement EditRepoDialog for editing existing repository details.
- Implement SyncRepoDialog for syncing repositories with Gitea, including repository creation if necessary.
- Implement WorkspaceDetailDialog for managing workspace links and actions.
- Enhance the repo store with new state management for repository actions, including creating, editing, and syncing repositories.
- Add build configuration utilities for repository synchronization.
- Create a new page for repository management, integrating all dialogs and functionalities.
- Add login route for authentication.
This commit is contained in:
2026-02-25 01:02:55 +08:00
parent f4643464ba
commit 7ec6428643
32 changed files with 3303 additions and 71 deletions

187
src/pages/config/page.tsx Normal file
View File

@@ -0,0 +1,187 @@
import { useConfigStore } from './store';
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card';
import { Input } from '@/components/ui/input';
import { Label } from '@/components/ui/label';
import { Button } from '@/components/ui/button';
import { Checkbox } from '@/components/ui/checkbox';
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/components/ui/tooltip';
import { Info } from 'lucide-react';
import { configSchema } from './store/schema';
import { toast } from 'sonner';
export const ConfigPage = () => {
const { config, setConfig, resetConfig } = useConfigStore();
const handleSubmit = (e: React.FormEvent) => {
e.preventDefault();
const result = configSchema.safeParse(config);
if (result.success) {
toast.success('配置已保存')
setTimeout(() => {
location.reload()
}, 400)
} else {
console.error('验证错误:', result.error.format());
}
};
const handleChange = (field: keyof typeof config, value: string | boolean) => {
setConfig({ [field]: value });
};
return (
<TooltipProvider>
<div className="container mx-auto max-w-2xl py-8">
<Card>
<CardHeader>
<CardTitle>CNB </CardTitle>
<CardDescription>
CNB API
</CardDescription>
</CardHeader>
<CardContent>
<form onSubmit={handleSubmit} className="space-y-6">
<div className="space-y-2">
<div className="flex items-center gap-2">
<Label htmlFor="api-key">API </Label>
<Tooltip>
<TooltipTrigger>
<Info className="h-4 w-4 text-muted-foreground cursor-help" />
</TooltipTrigger>
<TooltipContent>
<p>
访 CNB API
<a
href="https://cnb.cool/profile/token"
target="_blank"
rel="noopener noreferrer"
className="underline ml-1 hover:text-blue-400"
>
</a>
</p>
</TooltipContent>
</Tooltip>
</div>
<Input
id="api-key"
type="text"
value={config.CNB_API_KEY}
onChange={(e) => handleChange('CNB_API_KEY', e.target.value)}
placeholder="请输入您的 CNB API 密钥"
/>
</div>
<div className="space-y-2">
<div className="flex items-center gap-2">
<Label htmlFor="cookie">Cookie</Label>
<Tooltip>
<TooltipTrigger>
<Info className="h-4 w-4 text-muted-foreground cursor-help" />
</TooltipTrigger>
<TooltipContent>
<p>
Cookie
<a
href="https://cnb.cool/kevisual/cnb-live-extension"
target="_blank"
rel="noopener noreferrer"
className="underline ml-1 hover:text-blue-400"
>
</a>
</p>
</TooltipContent>
</Tooltip>
</div>
<Input
id="cookie"
type="text"
value={config.CNB_COOKIE}
onChange={(e) => handleChange('CNB_COOKIE', e.target.value)}
placeholder="请输入您的 CNB Cookie"
/>
</div>
<div className="space-y-2">
<Label htmlFor="cors-url"></Label>
<Input
id="cors-url"
type="url"
value={config.CNB_CORS_URL}
onChange={(e) => handleChange('CNB_CORS_URL', e.target.value)}
placeholder="https://cors.example.com"
/>
</div>
<div className="flex items-center space-x-2">
<Checkbox
id="enable-cors"
checked={config.ENABLE_CORS}
onCheckedChange={(checked) => handleChange('ENABLE_CORS', checked === true)}
/>
<Label htmlFor="enable-cors" className="cursor-pointer">
</Label>
</div>
<div className="space-y-2">
<Label htmlFor="ai-base-url">AI </Label>
<Input
id="ai-base-url"
type="url"
value={config.AI_BASE_URL}
onChange={(e) => handleChange('AI_BASE_URL', e.target.value)}
placeholder="请输入 AI 基础地址"
/>
</div>
<div className="space-y-2">
<Label htmlFor="ai-model">AI </Label>
<Input
id="ai-model"
type="text"
value={config.AI_MODEL}
onChange={(e) => handleChange('AI_MODEL', e.target.value)}
placeholder="请输入 AI 模型名称"
/>
</div>
<div className="space-y-2">
<div className="flex items-center gap-2">
<Label htmlFor="ai-api-key">AI </Label>
<Tooltip>
<TooltipTrigger>
<Info className="h-4 w-4 text-muted-foreground cursor-help" />
</TooltipTrigger>
<TooltipContent>
<p>
使 CNB AI API
</p>
</TooltipContent>
</Tooltip>
</div>
<Input
id="ai-api-key"
type="password"
value={config.AI_API_KEY}
onChange={(e) => handleChange('AI_API_KEY', e.target.value)}
placeholder="请输入您的 AI API 密钥"
/>
</div>
<div className="flex gap-4">
<Button type="submit"></Button>
<Button type="button" variant="outline" onClick={resetConfig}>
</Button>
</div>
</form>
</CardContent>
</Card>
</div>
</TooltipProvider>
);
};
export default ConfigPage;