@allbluecn/web-app 0.22.0 → 0.22.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +6 -8
- package/public/logo/allblue-logo-app-icon.svg +1 -1
- package/src/components/changelog/changelog-page.tsx +24 -5
- package/src/components/desktop/cloud-login.tsx +91 -0
- package/src/components/desktop/conflict-center.tsx +148 -0
- package/src/components/desktop/renamed-toast.tsx +23 -0
- package/src/components/desktop/shell-widgets.tsx +19 -0
- package/src/components/desktop/sync-indicator.tsx +60 -0
- package/src/components/desktop/sync-status-card.tsx +60 -0
- package/src/components/layout/sidebar-03/app-sidebar.tsx +38 -3
- package/src/components/shared-kit/avatars/__tests__/avatar-shape.test.ts +17 -1
- package/src/components/shared-kit/avatars/avatar-shape.ts +10 -0
- package/src/components/shared-kit/avatars/user-avatar.tsx +2 -2
- package/src/lib/auth/auth.config.ts +4 -1
- package/src/lib/changelog/sdk-versions.ts +33 -2
- package/src/lib/desktop-bridge-types.ts +30 -0
- package/src/lib/desktop-detect.ts +2 -0
- package/src/lib/vector-store/__tests__/factory.test.ts +0 -7
- package/src/lib/vector-store/index.ts +2 -4
- package/src/paraglide/messages/_index.d.ts +1 -1
- package/src/paraglide/messages/_index.js +32 -16
- package/src/paraglide/messages/common_sidebar_usermenu_loginsaas3.d.ts +1 -1
- package/src/paraglide/messages/common_sidebar_usermenu_loginsaas3.js +3 -3
- package/src/paraglide/messages/en.js +11 -6
- package/src/paraglide/messages/{tasks_ai_desktop_notice.d.ts → settings_tabs_sync.d.ts} +5 -5
- package/src/paraglide/messages/settings_tabs_sync.js +29 -0
- package/src/paraglide/messages/zh.js +11 -6
- package/src/providers/index.tsx +6 -0
- package/src/routeTree.gen.ts +107 -49
- package/src/routes/_nav/route.lazy.tsx +12 -0
- package/src/routes/_nav/settings/-settings-tabs.tsx +1 -1
- package/src/routes/_nav/settings/sync/route.lazy.tsx +40 -0
- package/src/routes/_nav/settings/sync/route.tsx +15 -0
- package/src/routes/_nav/tasks/index.lazy.tsx +1 -11
- package/src/routes/api/billing-webhook.ts +6 -2
- package/src/routes/api/sync/bootstrap.ts +38 -0
- package/src/routes/api/sync/pull.ts +96 -0
- package/src/routes/api/sync/push.ts +169 -0
- package/src/routes/api/sync/snapshot.ts +104 -0
- package/src/server/__tests__/fetch-guard.test.ts +2 -2
- package/src/server/__tests__/resource-registry.test.ts +4 -2
- package/src/server/__tests__/sandbox-entitlement.test.ts +5 -0
- package/src/server/__tests__/security-headers.test.ts +25 -0
- package/src/server/__tests__/task-assigned-query.test.ts +0 -11
- package/src/server/__tests__/task-serverfn-crud.test.ts +1 -22
- package/src/server/__tests__/task-serverfn-queries.test.ts +1 -9
- package/src/server/ai-gateway/__tests__/chat-stream-builder-memory.test.ts +0 -11
- package/src/server/ai-gateway/__tests__/embedding-request.test.ts +1 -1
- package/src/server/ai-gateway/__tests__/provider-adapter.test.ts +2 -2
- package/src/server/ai-gateway/__tests__/run-store-durable.test.ts +1 -1
- package/src/server/ai-gateway/durability/__tests__/pg-stream.test.ts +1 -1
- package/src/server/ai-gateway/durability/__tests__/reaper-real-db.test.ts +1 -1
- package/src/server/ai-gateway/memory/index.ts +2 -2
- package/src/server/ai-gateway/sandbox/__tests__/instance-store.test.ts +1 -1
- package/src/server/ai-task/assigned-query.ts +4 -15
- package/src/server/desktop-guest.ts +12 -0
- package/src/server/fetch-guard.ts +3 -3
- package/src/server/middlewares/desktop-token.ts +23 -0
- package/src/server/remote-auth.ts +6 -0
- package/src/server/serverFns/__tests__/billing.test.ts +58 -1
- package/src/server/serverFns/__tests__/update-seats.test.ts +8 -8
- package/src/server/serverFns/agents/agent-crud.ts +3 -1
- package/src/server/serverFns/auth/session.ts +9 -0
- package/src/server/serverFns/billing.ts +10 -4
- package/src/server/serverFns/bug/bug-crud.ts +7 -2
- package/src/server/serverFns/story/__tests__/labels.test.ts +37 -15
- package/src/server/serverFns/story/story-comments.ts +2 -1
- package/src/server/serverFns/story/story-crud/activities.ts +1 -1
- package/src/server/serverFns/story/story-crud/user-brief.ts +1 -1
- package/src/server/serverFns/story/story-crud.ts +2 -1
- package/src/server/serverFns/story/story-transfer.ts +2 -1
- package/src/server/serverFns/tasks/task-crud.ts +0 -13
- package/src/server/serverFns/tasks/task-queries.ts +1 -12
- package/src/server/serverFns/testcase/plans.ts +1 -1
- package/src/server/serverFns/testcase/run-cases.ts +1 -1
- package/src/server/serverFns/testcase/runs.ts +1 -1
- package/src/server/serverFns/testcase/testcase-activities.ts +1 -1
- package/src/server/serverFns/testcase/testcase-board.ts +1 -1
- package/src/server/serverFns/testcase/testcase-crud.ts +1 -1
- package/src/server/serverFns/workspace.ts +1 -1
- package/src/server/story/types.ts +7 -22
- package/src/server/story/user-brief.ts +48 -0
- package/src/server/sync/scopes.ts +29 -0
- package/src/start.ts +10 -2
- package/src/styles/globals.css +10 -0
- package/src/__tests__/desktop-connection-validate.test.ts +0 -66
- package/src/lib/vector-store/__tests__/sqlite-vec-store.test.ts +0 -143
- package/src/lib/vector-store/sqlite-vec-store.ts +0 -224
- package/src/paraglide/messages/tasks_ai_desktop_notice.js +0 -29
- package/src/routes/__desktop/onboarding/route.lazy.tsx +0 -175
- package/src/routes/__desktop/onboarding/route.tsx +0 -11
- package/src/routes/_nav/settings/connection/route.lazy.tsx +0 -64
- package/src/routes/_nav/settings/connection/route.tsx +0 -10
|
@@ -1,175 +0,0 @@
|
|
|
1
|
-
import { createLazyFileRoute, useNavigate } from '@tanstack/react-router'
|
|
2
|
-
import { useState, type KeyboardEvent } from 'react'
|
|
3
|
-
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
|
|
4
|
-
import { Button } from '@/components/ui/button'
|
|
5
|
-
import {
|
|
6
|
-
Dialog,
|
|
7
|
-
DialogContent,
|
|
8
|
-
DialogFooter,
|
|
9
|
-
DialogHeader,
|
|
10
|
-
DialogTitle,
|
|
11
|
-
} from '@/components/ui/dialog'
|
|
12
|
-
import { Input } from '@/components/ui/input'
|
|
13
|
-
import { Label } from '@/components/ui/label'
|
|
14
|
-
import { Cloud, Server, HardDrive, Loader2 } from 'lucide-react'
|
|
15
|
-
import { useConnection } from '@desktop/connection-manager'
|
|
16
|
-
import { connectionTest } from '@desktop/connection-manager/connection-test'
|
|
17
|
-
import { toast } from '@/components/ui/toast'
|
|
18
|
-
import { m } from "@/paraglide/messages.js";
|
|
19
|
-
import { errorMessage } from "@/lib/toast-error";
|
|
20
|
-
|
|
21
|
-
export const Route = createLazyFileRoute('/__desktop/onboarding')({
|
|
22
|
-
component: OnboardingPage,
|
|
23
|
-
})
|
|
24
|
-
|
|
25
|
-
// SaaS 入口地址(桌面端默认云端模式落点,环境切换只需改这里)
|
|
26
|
-
const SAAS_URL = 'https://allblue.litsex.cn'
|
|
27
|
-
|
|
28
|
-
function OnboardingPage() {
|
|
29
|
-
const { switchMode } = useConnection()
|
|
30
|
-
const navigate = useNavigate()
|
|
31
|
-
const [selfHostedDialogOpen, setSelfHostedDialogOpen] = useState(false)
|
|
32
|
-
const [selfHostedUrl, setSelfHostedUrl] = useState('')
|
|
33
|
-
const [selfHostedLabel, setSelfHostedLabel] = useState('')
|
|
34
|
-
const [testing, setTesting] = useState(false)
|
|
35
|
-
|
|
36
|
-
const handleSelectSaas = async () => {
|
|
37
|
-
try {
|
|
38
|
-
await switchMode({ mode: 'saas', url: SAAS_URL })
|
|
39
|
-
} catch (e) {
|
|
40
|
-
toast.error(m.desktop_onboarding_errors_switchFailed({ message: errorMessage(e, m.desktop_onboarding_errors_unknownError()) }))
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
const handleSelectLocal = async () => {
|
|
45
|
-
try {
|
|
46
|
-
await switchMode({ mode: 'local' })
|
|
47
|
-
await navigate({ to: '/workspace' })
|
|
48
|
-
} catch (e) {
|
|
49
|
-
toast.error(m.desktop_onboarding_errors_localFailed({ message: errorMessage(e, m.desktop_onboarding_errors_unknownError()) }))
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
const handleConfirmSelfHosted = async () => {
|
|
54
|
-
setTesting(true)
|
|
55
|
-
try {
|
|
56
|
-
const result = await connectionTest(selfHostedUrl)
|
|
57
|
-
if (!result.ok) {
|
|
58
|
-
toast.error(m.desktop_onboarding_errors_connectFailed({ message: result.error ?? m.desktop_onboarding_errors_timeout() }))
|
|
59
|
-
return
|
|
60
|
-
}
|
|
61
|
-
await switchMode({ mode: 'self-hosted', url: selfHostedUrl, label: selfHostedLabel })
|
|
62
|
-
} catch (e) {
|
|
63
|
-
toast.error(m.desktop_onboarding_errors_switchFailed({ message: errorMessage(e, m.desktop_onboarding_errors_unknownError()) }))
|
|
64
|
-
} finally {
|
|
65
|
-
setTesting(false)
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
// 键盘可达性:Card 为 div+onClick,补 role/tabIndex/Enter·Space 激活
|
|
70
|
-
const handleCardKeyDown = (handler: () => void) => (e: KeyboardEvent) => {
|
|
71
|
-
if (e.key === 'Enter' || e.key === ' ') {
|
|
72
|
-
e.preventDefault()
|
|
73
|
-
handler()
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
return (
|
|
78
|
-
<div className="flex min-h-screen items-center justify-center bg-background p-8">
|
|
79
|
-
<div className="w-full max-w-4xl space-y-6">
|
|
80
|
-
<div className="text-center space-y-2">
|
|
81
|
-
<h1 className="text-3xl font-bold">{m.desktop_onboarding_title()}</h1>
|
|
82
|
-
<p className="text-muted-foreground">{m.desktop_onboarding_subtitle()}</p>
|
|
83
|
-
</div>
|
|
84
|
-
|
|
85
|
-
<div className="grid gap-4 md:grid-cols-3">
|
|
86
|
-
<Card
|
|
87
|
-
className="cursor-pointer hover:border-primary transition-colors"
|
|
88
|
-
onClick={handleSelectSaas}
|
|
89
|
-
role="button"
|
|
90
|
-
tabIndex={0}
|
|
91
|
-
onKeyDown={handleCardKeyDown(handleSelectSaas)}
|
|
92
|
-
>
|
|
93
|
-
<CardHeader>
|
|
94
|
-
<Cloud className="size-10 text-primary mb-2" />
|
|
95
|
-
<CardTitle>{m.desktop_onboarding_saas_title()}</CardTitle>
|
|
96
|
-
<CardDescription>{m.desktop_onboarding_saas_desc()}</CardDescription>
|
|
97
|
-
</CardHeader>
|
|
98
|
-
<CardContent>
|
|
99
|
-
<Button className="w-full" variant="default" tabIndex={-1}>{m.desktop_onboarding_saas_button()}</Button>
|
|
100
|
-
</CardContent>
|
|
101
|
-
</Card>
|
|
102
|
-
|
|
103
|
-
<Card
|
|
104
|
-
className="cursor-pointer hover:border-primary transition-colors"
|
|
105
|
-
onClick={() => setSelfHostedDialogOpen(true)}
|
|
106
|
-
role="button"
|
|
107
|
-
tabIndex={0}
|
|
108
|
-
onKeyDown={handleCardKeyDown(() => setSelfHostedDialogOpen(true))}
|
|
109
|
-
>
|
|
110
|
-
<CardHeader>
|
|
111
|
-
<Server className="size-10 text-primary mb-2" />
|
|
112
|
-
<CardTitle>{m.desktop_onboarding_selfHosted_title()}</CardTitle>
|
|
113
|
-
<CardDescription>{m.desktop_onboarding_selfHosted_desc()}</CardDescription>
|
|
114
|
-
</CardHeader>
|
|
115
|
-
<CardContent>
|
|
116
|
-
<Button className="w-full" variant="outline" tabIndex={-1}>{m.desktop_onboarding_selfHosted_button()}</Button>
|
|
117
|
-
</CardContent>
|
|
118
|
-
</Card>
|
|
119
|
-
|
|
120
|
-
<Card
|
|
121
|
-
className="cursor-pointer hover:border-primary transition-colors"
|
|
122
|
-
onClick={handleSelectLocal}
|
|
123
|
-
role="button"
|
|
124
|
-
tabIndex={0}
|
|
125
|
-
onKeyDown={handleCardKeyDown(handleSelectLocal)}
|
|
126
|
-
>
|
|
127
|
-
<CardHeader>
|
|
128
|
-
<HardDrive className="size-10 text-primary mb-2" />
|
|
129
|
-
<CardTitle>{m.desktop_onboarding_local_title()}</CardTitle>
|
|
130
|
-
<CardDescription>{m.desktop_onboarding_local_desc()}</CardDescription>
|
|
131
|
-
</CardHeader>
|
|
132
|
-
<CardContent>
|
|
133
|
-
<Button className="w-full" variant="outline" tabIndex={-1}>{m.desktop_onboarding_local_button()}</Button>
|
|
134
|
-
</CardContent>
|
|
135
|
-
</Card>
|
|
136
|
-
</div>
|
|
137
|
-
</div>
|
|
138
|
-
|
|
139
|
-
<Dialog open={selfHostedDialogOpen} onOpenChange={setSelfHostedDialogOpen}>
|
|
140
|
-
<DialogContent>
|
|
141
|
-
<DialogHeader>
|
|
142
|
-
<DialogTitle>{m.desktop_onboarding_selfHosted_dialogTitle()}</DialogTitle>
|
|
143
|
-
</DialogHeader>
|
|
144
|
-
<div className="space-y-4 py-4">
|
|
145
|
-
<div className="space-y-2">
|
|
146
|
-
<Label htmlFor="url">{m.desktop_onboarding_selfHosted_urlLabel()}</Label>
|
|
147
|
-
<Input
|
|
148
|
-
id="url"
|
|
149
|
-
placeholder="https://prd.mycompany.com"
|
|
150
|
-
value={selfHostedUrl}
|
|
151
|
-
onChange={(e) => setSelfHostedUrl(e.target.value)}
|
|
152
|
-
/>
|
|
153
|
-
</div>
|
|
154
|
-
<div className="space-y-2">
|
|
155
|
-
<Label htmlFor="label">{m.desktop_onboarding_selfHosted_labelField()}</Label>
|
|
156
|
-
<Input
|
|
157
|
-
id="label"
|
|
158
|
-
placeholder={m.desktop_onboarding_selfHosted_labelPlaceholder()}
|
|
159
|
-
value={selfHostedLabel}
|
|
160
|
-
onChange={(e) => setSelfHostedLabel(e.target.value)}
|
|
161
|
-
/>
|
|
162
|
-
</div>
|
|
163
|
-
</div>
|
|
164
|
-
<DialogFooter>
|
|
165
|
-
<Button variant="outline" onClick={() => setSelfHostedDialogOpen(false)}>{m.desktop_onboarding_selfHosted_cancel()}</Button>
|
|
166
|
-
<Button onClick={handleConfirmSelfHosted} disabled={testing || !selfHostedUrl.trim()}>
|
|
167
|
-
{testing ? <Loader2 className="size-4 animate-spin mr-2" /> : null}
|
|
168
|
-
{testing ? m.desktop_onboarding_selfHosted_connecting() : m.desktop_onboarding_selfHosted_connect()}
|
|
169
|
-
</Button>
|
|
170
|
-
</DialogFooter>
|
|
171
|
-
</DialogContent>
|
|
172
|
-
</Dialog>
|
|
173
|
-
</div>
|
|
174
|
-
)
|
|
175
|
-
}
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
import { createFileRoute, redirect } from '@tanstack/react-router'
|
|
2
|
-
|
|
3
|
-
export const Route = createFileRoute('/__desktop/onboarding')({
|
|
4
|
-
// web 生产部署时该路由不可访问(仅桌面端 sidecar)
|
|
5
|
-
beforeLoad: () => {
|
|
6
|
-
const isDesktopBuild = import.meta.env.VITE_DEPLOY_TARGET === 'desktop'
|
|
7
|
-
if (!isDesktopBuild) {
|
|
8
|
-
throw redirect({ to: '/' })
|
|
9
|
-
}
|
|
10
|
-
},
|
|
11
|
-
})
|
|
@@ -1,64 +0,0 @@
|
|
|
1
|
-
import { createLazyFileRoute } from '@tanstack/react-router'
|
|
2
|
-
import { useState } from 'react'
|
|
3
|
-
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
|
|
4
|
-
import { Button } from '@/components/ui/button'
|
|
5
|
-
import { Badge } from '@/components/ui/badge'
|
|
6
|
-
import { useConnection, ModeSwitchDialog } from '@desktop/connection-manager'
|
|
7
|
-
import type { ConnectionMode } from '@desktop/connection-manager'
|
|
8
|
-
import { m } from "@/paraglide/messages.js";
|
|
9
|
-
|
|
10
|
-
export const Route = createLazyFileRoute('/_nav/settings/connection')({
|
|
11
|
-
component: ConnectionSettingsPage,
|
|
12
|
-
})
|
|
13
|
-
|
|
14
|
-
const MODE_VARIANT: Record<ConnectionMode, 'default' | 'secondary' | 'outline'> = {
|
|
15
|
-
saas: 'default',
|
|
16
|
-
'self-hosted': 'secondary',
|
|
17
|
-
local: 'outline',
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
function ConnectionSettingsPage() {
|
|
21
|
-
const { config, switchMode } = useConnection()
|
|
22
|
-
const [dialogOpen, setDialogOpen] = useState(false)
|
|
23
|
-
|
|
24
|
-
const mode = config?.mode ?? 'local'
|
|
25
|
-
const MODE_LABELS: Record<string, () => string> = {
|
|
26
|
-
local: m.settings_connection_local,
|
|
27
|
-
cloud: m.settings_connection_cloud,
|
|
28
|
-
selfHosted: m.settings_connection_selfHosted,
|
|
29
|
-
}
|
|
30
|
-
const modeLabel = MODE_LABELS[mode]?.() ?? mode
|
|
31
|
-
|
|
32
|
-
return (
|
|
33
|
-
<div className="space-y-6 p-6">
|
|
34
|
-
<div>
|
|
35
|
-
<h1 className="text-2xl font-bold">{m.settings_connection_mode()}</h1>
|
|
36
|
-
<p className="text-muted-foreground mt-1">{m.settings_connection_modeDescription()}</p>
|
|
37
|
-
</div>
|
|
38
|
-
|
|
39
|
-
<Card>
|
|
40
|
-
<CardHeader>
|
|
41
|
-
<CardTitle className="flex items-center gap-2">
|
|
42
|
-
{m.settings_connection_currentMode()}
|
|
43
|
-
<Badge variant={MODE_VARIANT[mode]}>{modeLabel}</Badge>
|
|
44
|
-
</CardTitle>
|
|
45
|
-
<CardDescription>
|
|
46
|
-
{mode === 'saas' && m.settings_connection_cloudDescription()}
|
|
47
|
-
{mode === 'self-hosted' && m.settings_connection_selfHostedDescription({ label: (config?.label ?? config?.url) ?? "" })}
|
|
48
|
-
{mode === 'local' && m.settings_connection_localDescription()}
|
|
49
|
-
</CardDescription>
|
|
50
|
-
</CardHeader>
|
|
51
|
-
<CardContent>
|
|
52
|
-
<Button onClick={() => setDialogOpen(true)}>{m.settings_connection_switchMode()}</Button>
|
|
53
|
-
</CardContent>
|
|
54
|
-
</Card>
|
|
55
|
-
|
|
56
|
-
<ModeSwitchDialog
|
|
57
|
-
open={dialogOpen}
|
|
58
|
-
onOpenChange={setDialogOpen}
|
|
59
|
-
currentMode={mode}
|
|
60
|
-
onConfirm={switchMode}
|
|
61
|
-
/>
|
|
62
|
-
</div>
|
|
63
|
-
)
|
|
64
|
-
}
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
import { createFileRoute, redirect } from '@tanstack/react-router'
|
|
2
|
-
|
|
3
|
-
export const Route = createFileRoute('/_nav/settings/connection')({
|
|
4
|
-
beforeLoad: () => {
|
|
5
|
-
// 仅桌面端可访问
|
|
6
|
-
if (import.meta.env.VITE_DEPLOY_TARGET !== 'desktop') {
|
|
7
|
-
throw redirect({ to: '/settings' })
|
|
8
|
-
}
|
|
9
|
-
},
|
|
10
|
-
})
|