@allbluecn/web-app 0.23.1 → 0.23.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 +3 -3
- package/src/components/desktop/__tests__/desktop-state-listener.test.tsx +183 -0
- package/src/components/desktop/__tests__/sync-indicator.test.tsx +130 -0
- package/src/components/desktop/cloud-login.tsx +108 -6
- package/src/components/desktop/desktop-state-listener.tsx +76 -0
- package/src/components/desktop/shell-widgets.tsx +12 -5
- package/src/components/desktop/sync-indicator.tsx +62 -13
- package/src/components/layout/sidebar-03/__tests__/user-menu-anonymous.test.tsx +4 -0
- package/src/components/layout/sidebar-03/user-menu.tsx +63 -0
- package/src/lib/__tests__/session.test.ts +95 -0
- package/src/lib/auth/auth.config.ts +13 -0
- package/src/lib/changelog/sdk-versions.ts +1 -1
- package/src/lib/desktop-bridge-types.ts +36 -0
- package/src/lib/session.ts +15 -1
- package/src/paraglide/messages/_index.d.ts +11 -0
- package/src/paraglide/messages/_index.js +11 -0
- package/src/paraglide/messages/desktop_cloud_exchange_failed.d.ts +16 -0
- package/src/paraglide/messages/desktop_cloud_exchange_failed.js +29 -0
- package/src/paraglide/messages/desktop_state_cloud.d.ts +16 -0
- package/src/paraglide/messages/desktop_state_cloud.js +29 -0
- package/src/paraglide/messages/desktop_state_guest.d.ts +16 -0
- package/src/paraglide/messages/desktop_state_guest.js +29 -0
- package/src/paraglide/messages/desktop_state_transferring.d.ts +16 -0
- package/src/paraglide/messages/desktop_state_transferring.js +29 -0
- package/src/paraglide/messages/sync_bind_failed.d.ts +16 -0
- package/src/paraglide/messages/sync_bind_failed.js +29 -0
- package/src/paraglide/messages/sync_bind_in_progress.d.ts +16 -0
- package/src/paraglide/messages/sync_bind_in_progress.js +29 -0
- package/src/paraglide/messages/sync_bind_nonempty.d.ts +16 -0
- package/src/paraglide/messages/sync_bind_nonempty.js +29 -0
- package/src/paraglide/messages/sync_bind_probe_unreachable.d.ts +16 -0
- package/src/paraglide/messages/sync_bind_probe_unreachable.js +29 -0
- package/src/paraglide/messages/sync_bind_shell_unsupported.d.ts +16 -0
- package/src/paraglide/messages/sync_bind_shell_unsupported.js +29 -0
- package/src/paraglide/messages/sync_logout_failed.d.ts +16 -0
- package/src/paraglide/messages/sync_logout_failed.js +29 -0
- package/src/paraglide/messages/sync_logout_in_progress.d.ts +16 -0
- package/src/paraglide/messages/sync_logout_in_progress.js +29 -0
- package/src/routeTree.gen.ts +21 -0
- package/src/routes/api/sync/__tests__/account-status.test.ts +109 -0
- package/src/routes/api/sync/account-status.ts +39 -0
- package/src/server/__tests__/exchange-remote-token.test.ts +113 -0
- package/src/server/__tests__/remote-auth.test.ts +131 -1
- package/src/server/ai-gateway/__tests__/tools-authorization.test.ts +4 -2
- package/src/server/ai-gateway/tools/server.ts +7 -0
- package/src/server/desktop-bootstrap.ts +19 -3
- package/src/server/desktop-session-merge.ts +51 -0
- package/src/server/remote-auth.ts +61 -3
- package/src/server/serverFns/__tests__/knowledge-readable-kb.test.ts +21 -0
- package/src/server/serverFns/__tests__/workspace.test.ts +46 -0
- package/src/server/serverFns/auth/session.ts +28 -28
- package/src/server/serverFns/harness/attach-to-kb.ts +3 -1
- package/src/server/serverFns/knowledge.ts +4 -1
- package/src/server/serverFns/sync/transfer.ts +168 -0
- package/src/server/serverFns/workspace.ts +15 -0
- package/src/server/serverfn-public-allowlist.ts +4 -0
- package/src/server/sync/__tests__/ownership-transfer.test.ts +896 -0
- package/src/server/sync/account-status.ts +36 -0
- package/src/server/sync/ownership-transfer.ts +291 -0
- package/src/server/sync/saas-profile.ts +70 -0
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/* eslint-disable */
|
|
2
|
+
import { getLocale, experimentalStaticLocale } from '../runtime.js';
|
|
3
|
+
|
|
4
|
+
/** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
|
|
5
|
+
|
|
6
|
+
/** @typedef {{}} Desktop_State_TransferringInputs */
|
|
7
|
+
|
|
8
|
+
const zh_desktop_state_transferring = /** @type {(inputs: Desktop_State_TransferringInputs) => LocalizedString} */ () => {
|
|
9
|
+
return /** @type {LocalizedString} */ (`归属转移中…`)
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
const en_desktop_state_transferring = /** @type {(inputs: Desktop_State_TransferringInputs) => LocalizedString} */ () => {
|
|
13
|
+
return /** @type {LocalizedString} */ (`Transferring data…`)
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* | output |
|
|
18
|
+
* | --- |
|
|
19
|
+
* | "Transferring data…" |
|
|
20
|
+
*
|
|
21
|
+
* @param {Desktop_State_TransferringInputs} inputs
|
|
22
|
+
* @param {{ locale?: "zh" | "en" }} options
|
|
23
|
+
* @returns {LocalizedString}
|
|
24
|
+
*/
|
|
25
|
+
export const desktop_state_transferring = /** @type {((inputs?: Desktop_State_TransferringInputs, options?: { locale?: "zh" | "en" }) => LocalizedString) & import('../runtime.js').MessageMetadata<Desktop_State_TransferringInputs, { locale?: "zh" | "en" }, {}>} */ ((inputs = {}, options = {}) => {
|
|
26
|
+
const locale = experimentalStaticLocale ?? options.locale ?? getLocale()
|
|
27
|
+
if (locale === "en") return en_desktop_state_transferring(inputs)
|
|
28
|
+
return zh_desktop_state_transferring(inputs)
|
|
29
|
+
});
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* | output |
|
|
3
|
+
* | --- |
|
|
4
|
+
* | "Local data transfer failed. Local guest state restored and cloud sign-out applied. Please retry." |
|
|
5
|
+
*
|
|
6
|
+
* @param {Sync_Bind_FailedInputs} inputs
|
|
7
|
+
* @param {{ locale?: "zh" | "en" }} options
|
|
8
|
+
* @returns {LocalizedString}
|
|
9
|
+
*/
|
|
10
|
+
export const sync_bind_failed: ((inputs?: Sync_Bind_FailedInputs, options?: {
|
|
11
|
+
locale?: "zh" | "en";
|
|
12
|
+
}) => LocalizedString) & import("../runtime.js").MessageMetadata<Sync_Bind_FailedInputs, {
|
|
13
|
+
locale?: "zh" | "en";
|
|
14
|
+
}, {}>;
|
|
15
|
+
export type LocalizedString = import("../runtime.js").LocalizedString;
|
|
16
|
+
export type Sync_Bind_FailedInputs = {};
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/* eslint-disable */
|
|
2
|
+
import { getLocale, experimentalStaticLocale } from '../runtime.js';
|
|
3
|
+
|
|
4
|
+
/** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
|
|
5
|
+
|
|
6
|
+
/** @typedef {{}} Sync_Bind_FailedInputs */
|
|
7
|
+
|
|
8
|
+
const zh_sync_bind_failed = /** @type {(inputs: Sync_Bind_FailedInputs) => LocalizedString} */ () => {
|
|
9
|
+
return /** @type {LocalizedString} */ (`本地数据转移失败,已恢复本地 guest 状态并退出云登录,请重试`)
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
const en_sync_bind_failed = /** @type {(inputs: Sync_Bind_FailedInputs) => LocalizedString} */ () => {
|
|
13
|
+
return /** @type {LocalizedString} */ (`Local data transfer failed. Local guest state restored and cloud sign-out applied. Please retry.`)
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* | output |
|
|
18
|
+
* | --- |
|
|
19
|
+
* | "Local data transfer failed. Local guest state restored and cloud sign-out applied. Please retry." |
|
|
20
|
+
*
|
|
21
|
+
* @param {Sync_Bind_FailedInputs} inputs
|
|
22
|
+
* @param {{ locale?: "zh" | "en" }} options
|
|
23
|
+
* @returns {LocalizedString}
|
|
24
|
+
*/
|
|
25
|
+
export const sync_bind_failed = /** @type {((inputs?: Sync_Bind_FailedInputs, options?: { locale?: "zh" | "en" }) => LocalizedString) & import('../runtime.js').MessageMetadata<Sync_Bind_FailedInputs, { locale?: "zh" | "en" }, {}>} */ ((inputs = {}, options = {}) => {
|
|
26
|
+
const locale = experimentalStaticLocale ?? options.locale ?? getLocale()
|
|
27
|
+
if (locale === "en") return en_sync_bind_failed(inputs)
|
|
28
|
+
return zh_sync_bind_failed(inputs)
|
|
29
|
+
});
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* | output |
|
|
3
|
+
* | --- |
|
|
4
|
+
* | "Transferring local data to your cloud account…" |
|
|
5
|
+
*
|
|
6
|
+
* @param {Sync_Bind_In_ProgressInputs} inputs
|
|
7
|
+
* @param {{ locale?: "zh" | "en" }} options
|
|
8
|
+
* @returns {LocalizedString}
|
|
9
|
+
*/
|
|
10
|
+
export const sync_bind_in_progress: ((inputs?: Sync_Bind_In_ProgressInputs, options?: {
|
|
11
|
+
locale?: "zh" | "en";
|
|
12
|
+
}) => LocalizedString) & import("../runtime.js").MessageMetadata<Sync_Bind_In_ProgressInputs, {
|
|
13
|
+
locale?: "zh" | "en";
|
|
14
|
+
}, {}>;
|
|
15
|
+
export type LocalizedString = import("../runtime.js").LocalizedString;
|
|
16
|
+
export type Sync_Bind_In_ProgressInputs = {};
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/* eslint-disable */
|
|
2
|
+
import { getLocale, experimentalStaticLocale } from '../runtime.js';
|
|
3
|
+
|
|
4
|
+
/** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
|
|
5
|
+
|
|
6
|
+
/** @typedef {{}} Sync_Bind_In_ProgressInputs */
|
|
7
|
+
|
|
8
|
+
const zh_sync_bind_in_progress = /** @type {(inputs: Sync_Bind_In_ProgressInputs) => LocalizedString} */ () => {
|
|
9
|
+
return /** @type {LocalizedString} */ (`正在转移本地数据到云端账号…`)
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
const en_sync_bind_in_progress = /** @type {(inputs: Sync_Bind_In_ProgressInputs) => LocalizedString} */ () => {
|
|
13
|
+
return /** @type {LocalizedString} */ (`Transferring local data to your cloud account…`)
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* | output |
|
|
18
|
+
* | --- |
|
|
19
|
+
* | "Transferring local data to your cloud account…" |
|
|
20
|
+
*
|
|
21
|
+
* @param {Sync_Bind_In_ProgressInputs} inputs
|
|
22
|
+
* @param {{ locale?: "zh" | "en" }} options
|
|
23
|
+
* @returns {LocalizedString}
|
|
24
|
+
*/
|
|
25
|
+
export const sync_bind_in_progress = /** @type {((inputs?: Sync_Bind_In_ProgressInputs, options?: { locale?: "zh" | "en" }) => LocalizedString) & import('../runtime.js').MessageMetadata<Sync_Bind_In_ProgressInputs, { locale?: "zh" | "en" }, {}>} */ ((inputs = {}, options = {}) => {
|
|
26
|
+
const locale = experimentalStaticLocale ?? options.locale ?? getLocale()
|
|
27
|
+
if (locale === "en") return en_sync_bind_in_progress(inputs)
|
|
28
|
+
return zh_sync_bind_in_progress(inputs)
|
|
29
|
+
});
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* | output |
|
|
3
|
+
* | --- |
|
|
4
|
+
* | "This cloud account already has data. Binding transfer is not supported yet (empty accounts only in phase one)." |
|
|
5
|
+
*
|
|
6
|
+
* @param {Sync_Bind_NonemptyInputs} inputs
|
|
7
|
+
* @param {{ locale?: "zh" | "en" }} options
|
|
8
|
+
* @returns {LocalizedString}
|
|
9
|
+
*/
|
|
10
|
+
export const sync_bind_nonempty: ((inputs?: Sync_Bind_NonemptyInputs, options?: {
|
|
11
|
+
locale?: "zh" | "en";
|
|
12
|
+
}) => LocalizedString) & import("../runtime.js").MessageMetadata<Sync_Bind_NonemptyInputs, {
|
|
13
|
+
locale?: "zh" | "en";
|
|
14
|
+
}, {}>;
|
|
15
|
+
export type LocalizedString = import("../runtime.js").LocalizedString;
|
|
16
|
+
export type Sync_Bind_NonemptyInputs = {};
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/* eslint-disable */
|
|
2
|
+
import { getLocale, experimentalStaticLocale } from '../runtime.js';
|
|
3
|
+
|
|
4
|
+
/** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
|
|
5
|
+
|
|
6
|
+
/** @typedef {{}} Sync_Bind_NonemptyInputs */
|
|
7
|
+
|
|
8
|
+
const zh_sync_bind_nonempty = /** @type {(inputs: Sync_Bind_NonemptyInputs) => LocalizedString} */ () => {
|
|
9
|
+
return /** @type {LocalizedString} */ (`该云账号已有数据,暂不支持绑定转移(一期仅支持空账号)`)
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
const en_sync_bind_nonempty = /** @type {(inputs: Sync_Bind_NonemptyInputs) => LocalizedString} */ () => {
|
|
13
|
+
return /** @type {LocalizedString} */ (`This cloud account already has data. Binding transfer is not supported yet (empty accounts only in phase one).`)
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* | output |
|
|
18
|
+
* | --- |
|
|
19
|
+
* | "This cloud account already has data. Binding transfer is not supported yet (empty accounts only in phase one)." |
|
|
20
|
+
*
|
|
21
|
+
* @param {Sync_Bind_NonemptyInputs} inputs
|
|
22
|
+
* @param {{ locale?: "zh" | "en" }} options
|
|
23
|
+
* @returns {LocalizedString}
|
|
24
|
+
*/
|
|
25
|
+
export const sync_bind_nonempty = /** @type {((inputs?: Sync_Bind_NonemptyInputs, options?: { locale?: "zh" | "en" }) => LocalizedString) & import('../runtime.js').MessageMetadata<Sync_Bind_NonemptyInputs, { locale?: "zh" | "en" }, {}>} */ ((inputs = {}, options = {}) => {
|
|
26
|
+
const locale = experimentalStaticLocale ?? options.locale ?? getLocale()
|
|
27
|
+
if (locale === "en") return en_sync_bind_nonempty(inputs)
|
|
28
|
+
return zh_sync_bind_nonempty(inputs)
|
|
29
|
+
});
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* | output |
|
|
3
|
+
* | --- |
|
|
4
|
+
* | "Could not verify the cloud account status (network error). Local data was not transferred this time." |
|
|
5
|
+
*
|
|
6
|
+
* @param {Sync_Bind_Probe_UnreachableInputs} inputs
|
|
7
|
+
* @param {{ locale?: "zh" | "en" }} options
|
|
8
|
+
* @returns {LocalizedString}
|
|
9
|
+
*/
|
|
10
|
+
export const sync_bind_probe_unreachable: ((inputs?: Sync_Bind_Probe_UnreachableInputs, options?: {
|
|
11
|
+
locale?: "zh" | "en";
|
|
12
|
+
}) => LocalizedString) & import("../runtime.js").MessageMetadata<Sync_Bind_Probe_UnreachableInputs, {
|
|
13
|
+
locale?: "zh" | "en";
|
|
14
|
+
}, {}>;
|
|
15
|
+
export type LocalizedString = import("../runtime.js").LocalizedString;
|
|
16
|
+
export type Sync_Bind_Probe_UnreachableInputs = {};
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/* eslint-disable */
|
|
2
|
+
import { getLocale, experimentalStaticLocale } from '../runtime.js';
|
|
3
|
+
|
|
4
|
+
/** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
|
|
5
|
+
|
|
6
|
+
/** @typedef {{}} Sync_Bind_Probe_UnreachableInputs */
|
|
7
|
+
|
|
8
|
+
const zh_sync_bind_probe_unreachable = /** @type {(inputs: Sync_Bind_Probe_UnreachableInputs) => LocalizedString} */ () => {
|
|
9
|
+
return /** @type {LocalizedString} */ (`暂时无法确认云账号状态(网络异常),本次未转移本地数据`)
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
const en_sync_bind_probe_unreachable = /** @type {(inputs: Sync_Bind_Probe_UnreachableInputs) => LocalizedString} */ () => {
|
|
13
|
+
return /** @type {LocalizedString} */ (`Could not verify the cloud account status (network error). Local data was not transferred this time.`)
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* | output |
|
|
18
|
+
* | --- |
|
|
19
|
+
* | "Could not verify the cloud account status (network error). Local data was not transferred this time." |
|
|
20
|
+
*
|
|
21
|
+
* @param {Sync_Bind_Probe_UnreachableInputs} inputs
|
|
22
|
+
* @param {{ locale?: "zh" | "en" }} options
|
|
23
|
+
* @returns {LocalizedString}
|
|
24
|
+
*/
|
|
25
|
+
export const sync_bind_probe_unreachable = /** @type {((inputs?: Sync_Bind_Probe_UnreachableInputs, options?: { locale?: "zh" | "en" }) => LocalizedString) & import('../runtime.js').MessageMetadata<Sync_Bind_Probe_UnreachableInputs, { locale?: "zh" | "en" }, {}>} */ ((inputs = {}, options = {}) => {
|
|
26
|
+
const locale = experimentalStaticLocale ?? options.locale ?? getLocale()
|
|
27
|
+
if (locale === "en") return en_sync_bind_probe_unreachable(inputs)
|
|
28
|
+
return zh_sync_bind_probe_unreachable(inputs)
|
|
29
|
+
});
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* | output |
|
|
3
|
+
* | --- |
|
|
4
|
+
* | "This desktop shell version does not support binding transfer. Please upgrade the app." |
|
|
5
|
+
*
|
|
6
|
+
* @param {Sync_Bind_Shell_UnsupportedInputs} inputs
|
|
7
|
+
* @param {{ locale?: "zh" | "en" }} options
|
|
8
|
+
* @returns {LocalizedString}
|
|
9
|
+
*/
|
|
10
|
+
export const sync_bind_shell_unsupported: ((inputs?: Sync_Bind_Shell_UnsupportedInputs, options?: {
|
|
11
|
+
locale?: "zh" | "en";
|
|
12
|
+
}) => LocalizedString) & import("../runtime.js").MessageMetadata<Sync_Bind_Shell_UnsupportedInputs, {
|
|
13
|
+
locale?: "zh" | "en";
|
|
14
|
+
}, {}>;
|
|
15
|
+
export type LocalizedString = import("../runtime.js").LocalizedString;
|
|
16
|
+
export type Sync_Bind_Shell_UnsupportedInputs = {};
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/* eslint-disable */
|
|
2
|
+
import { getLocale, experimentalStaticLocale } from '../runtime.js';
|
|
3
|
+
|
|
4
|
+
/** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
|
|
5
|
+
|
|
6
|
+
/** @typedef {{}} Sync_Bind_Shell_UnsupportedInputs */
|
|
7
|
+
|
|
8
|
+
const zh_sync_bind_shell_unsupported = /** @type {(inputs: Sync_Bind_Shell_UnsupportedInputs) => LocalizedString} */ () => {
|
|
9
|
+
return /** @type {LocalizedString} */ (`当前桌面壳版本不支持绑定转移,请升级桌面应用`)
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
const en_sync_bind_shell_unsupported = /** @type {(inputs: Sync_Bind_Shell_UnsupportedInputs) => LocalizedString} */ () => {
|
|
13
|
+
return /** @type {LocalizedString} */ (`This desktop shell version does not support binding transfer. Please upgrade the app.`)
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* | output |
|
|
18
|
+
* | --- |
|
|
19
|
+
* | "This desktop shell version does not support binding transfer. Please upgrade the app." |
|
|
20
|
+
*
|
|
21
|
+
* @param {Sync_Bind_Shell_UnsupportedInputs} inputs
|
|
22
|
+
* @param {{ locale?: "zh" | "en" }} options
|
|
23
|
+
* @returns {LocalizedString}
|
|
24
|
+
*/
|
|
25
|
+
export const sync_bind_shell_unsupported = /** @type {((inputs?: Sync_Bind_Shell_UnsupportedInputs, options?: { locale?: "zh" | "en" }) => LocalizedString) & import('../runtime.js').MessageMetadata<Sync_Bind_Shell_UnsupportedInputs, { locale?: "zh" | "en" }, {}>} */ ((inputs = {}, options = {}) => {
|
|
26
|
+
const locale = experimentalStaticLocale ?? options.locale ?? getLocale()
|
|
27
|
+
if (locale === "en") return en_sync_bind_shell_unsupported(inputs)
|
|
28
|
+
return zh_sync_bind_shell_unsupported(inputs)
|
|
29
|
+
});
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* | output |
|
|
3
|
+
* | --- |
|
|
4
|
+
* | "Sign-out failed: cloud data transfer back to this device did not complete. Local data is untouched. Please retry." |
|
|
5
|
+
*
|
|
6
|
+
* @param {Sync_Logout_FailedInputs} inputs
|
|
7
|
+
* @param {{ locale?: "zh" | "en" }} options
|
|
8
|
+
* @returns {LocalizedString}
|
|
9
|
+
*/
|
|
10
|
+
export const sync_logout_failed: ((inputs?: Sync_Logout_FailedInputs, options?: {
|
|
11
|
+
locale?: "zh" | "en";
|
|
12
|
+
}) => LocalizedString) & import("../runtime.js").MessageMetadata<Sync_Logout_FailedInputs, {
|
|
13
|
+
locale?: "zh" | "en";
|
|
14
|
+
}, {}>;
|
|
15
|
+
export type LocalizedString = import("../runtime.js").LocalizedString;
|
|
16
|
+
export type Sync_Logout_FailedInputs = {};
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/* eslint-disable */
|
|
2
|
+
import { getLocale, experimentalStaticLocale } from '../runtime.js';
|
|
3
|
+
|
|
4
|
+
/** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
|
|
5
|
+
|
|
6
|
+
/** @typedef {{}} Sync_Logout_FailedInputs */
|
|
7
|
+
|
|
8
|
+
const zh_sync_logout_failed = /** @type {(inputs: Sync_Logout_FailedInputs) => LocalizedString} */ () => {
|
|
9
|
+
return /** @type {LocalizedString} */ (`登出失败:云端数据转回本机未完成,本机数据未受影响,请重试`)
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
const en_sync_logout_failed = /** @type {(inputs: Sync_Logout_FailedInputs) => LocalizedString} */ () => {
|
|
13
|
+
return /** @type {LocalizedString} */ (`Sign-out failed: cloud data transfer back to this device did not complete. Local data is untouched. Please retry.`)
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* | output |
|
|
18
|
+
* | --- |
|
|
19
|
+
* | "Sign-out failed: cloud data transfer back to this device did not complete. Local data is untouched. Please retry." |
|
|
20
|
+
*
|
|
21
|
+
* @param {Sync_Logout_FailedInputs} inputs
|
|
22
|
+
* @param {{ locale?: "zh" | "en" }} options
|
|
23
|
+
* @returns {LocalizedString}
|
|
24
|
+
*/
|
|
25
|
+
export const sync_logout_failed = /** @type {((inputs?: Sync_Logout_FailedInputs, options?: { locale?: "zh" | "en" }) => LocalizedString) & import('../runtime.js').MessageMetadata<Sync_Logout_FailedInputs, { locale?: "zh" | "en" }, {}>} */ ((inputs = {}, options = {}) => {
|
|
26
|
+
const locale = experimentalStaticLocale ?? options.locale ?? getLocale()
|
|
27
|
+
if (locale === "en") return en_sync_logout_failed(inputs)
|
|
28
|
+
return zh_sync_logout_failed(inputs)
|
|
29
|
+
});
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* | output |
|
|
3
|
+
* | --- |
|
|
4
|
+
* | "Transferring cloud data back to this device…" |
|
|
5
|
+
*
|
|
6
|
+
* @param {Sync_Logout_In_ProgressInputs} inputs
|
|
7
|
+
* @param {{ locale?: "zh" | "en" }} options
|
|
8
|
+
* @returns {LocalizedString}
|
|
9
|
+
*/
|
|
10
|
+
export const sync_logout_in_progress: ((inputs?: Sync_Logout_In_ProgressInputs, options?: {
|
|
11
|
+
locale?: "zh" | "en";
|
|
12
|
+
}) => LocalizedString) & import("../runtime.js").MessageMetadata<Sync_Logout_In_ProgressInputs, {
|
|
13
|
+
locale?: "zh" | "en";
|
|
14
|
+
}, {}>;
|
|
15
|
+
export type LocalizedString = import("../runtime.js").LocalizedString;
|
|
16
|
+
export type Sync_Logout_In_ProgressInputs = {};
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/* eslint-disable */
|
|
2
|
+
import { getLocale, experimentalStaticLocale } from '../runtime.js';
|
|
3
|
+
|
|
4
|
+
/** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
|
|
5
|
+
|
|
6
|
+
/** @typedef {{}} Sync_Logout_In_ProgressInputs */
|
|
7
|
+
|
|
8
|
+
const zh_sync_logout_in_progress = /** @type {(inputs: Sync_Logout_In_ProgressInputs) => LocalizedString} */ () => {
|
|
9
|
+
return /** @type {LocalizedString} */ (`正在把云端数据转回本机…`)
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
const en_sync_logout_in_progress = /** @type {(inputs: Sync_Logout_In_ProgressInputs) => LocalizedString} */ () => {
|
|
13
|
+
return /** @type {LocalizedString} */ (`Transferring cloud data back to this device…`)
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* | output |
|
|
18
|
+
* | --- |
|
|
19
|
+
* | "Transferring cloud data back to this device…" |
|
|
20
|
+
*
|
|
21
|
+
* @param {Sync_Logout_In_ProgressInputs} inputs
|
|
22
|
+
* @param {{ locale?: "zh" | "en" }} options
|
|
23
|
+
* @returns {LocalizedString}
|
|
24
|
+
*/
|
|
25
|
+
export const sync_logout_in_progress = /** @type {((inputs?: Sync_Logout_In_ProgressInputs, options?: { locale?: "zh" | "en" }) => LocalizedString) & import('../runtime.js').MessageMetadata<Sync_Logout_In_ProgressInputs, { locale?: "zh" | "en" }, {}>} */ ((inputs = {}, options = {}) => {
|
|
26
|
+
const locale = experimentalStaticLocale ?? options.locale ?? getLocale()
|
|
27
|
+
if (locale === "en") return en_sync_logout_in_progress(inputs)
|
|
28
|
+
return zh_sync_logout_in_progress(inputs)
|
|
29
|
+
});
|
package/src/routeTree.gen.ts
CHANGED
|
@@ -105,6 +105,7 @@ import { Route as ApiLicenseActivateRouteRouteImport } from './routes/api/licens
|
|
|
105
105
|
import { Route as ApiLicenseHeartbeatRouteRouteImport } from './routes/api/license/heartbeat/route'
|
|
106
106
|
import { Route as ApiMcpBugsRouteImport } from './routes/api/mcp/bugs'
|
|
107
107
|
import { Route as ApiNotificationsStreamRouteImport } from './routes/api/notifications/stream'
|
|
108
|
+
import { Route as ApiSyncAccountStatusRouteImport } from './routes/api/sync/account-status'
|
|
108
109
|
import { Route as ApiSyncBootstrapRouteImport } from './routes/api/sync/bootstrap'
|
|
109
110
|
import { Route as ApiSyncPullRouteImport } from './routes/api/sync/pull'
|
|
110
111
|
import { Route as ApiSyncPushRouteImport } from './routes/api/sync/push'
|
|
@@ -751,6 +752,11 @@ const ApiNotificationsStreamRoute = ApiNotificationsStreamRouteImport.update({
|
|
|
751
752
|
path: '/api/notifications/stream',
|
|
752
753
|
getParentRoute: () => rootRouteImport,
|
|
753
754
|
} as any)
|
|
755
|
+
const ApiSyncAccountStatusRoute = ApiSyncAccountStatusRouteImport.update({
|
|
756
|
+
id: '/api/sync/account-status',
|
|
757
|
+
path: '/api/sync/account-status',
|
|
758
|
+
getParentRoute: () => rootRouteImport,
|
|
759
|
+
} as any)
|
|
754
760
|
const ApiSyncBootstrapRoute = ApiSyncBootstrapRouteImport.update({
|
|
755
761
|
id: '/api/sync/bootstrap',
|
|
756
762
|
path: '/api/sync/bootstrap',
|
|
@@ -1194,6 +1200,7 @@ export interface FileRoutesByFullPath {
|
|
|
1194
1200
|
'/api/knowledge/sync-github': typeof ApiKnowledgeSyncGithubRoute
|
|
1195
1201
|
'/api/mcp/bugs': typeof ApiMcpBugsRoute
|
|
1196
1202
|
'/api/notifications/stream': typeof ApiNotificationsStreamRoute
|
|
1203
|
+
'/api/sync/account-status': typeof ApiSyncAccountStatusRoute
|
|
1197
1204
|
'/api/sync/bootstrap': typeof ApiSyncBootstrapRoute
|
|
1198
1205
|
'/api/sync/pull': typeof ApiSyncPullRoute
|
|
1199
1206
|
'/api/sync/push': typeof ApiSyncPushRoute
|
|
@@ -1317,6 +1324,7 @@ export interface FileRoutesByTo {
|
|
|
1317
1324
|
'/api/knowledge/sync-github': typeof ApiKnowledgeSyncGithubRoute
|
|
1318
1325
|
'/api/mcp/bugs': typeof ApiMcpBugsRoute
|
|
1319
1326
|
'/api/notifications/stream': typeof ApiNotificationsStreamRoute
|
|
1327
|
+
'/api/sync/account-status': typeof ApiSyncAccountStatusRoute
|
|
1320
1328
|
'/api/sync/bootstrap': typeof ApiSyncBootstrapRoute
|
|
1321
1329
|
'/api/sync/pull': typeof ApiSyncPullRoute
|
|
1322
1330
|
'/api/sync/push': typeof ApiSyncPushRoute
|
|
@@ -1459,6 +1467,7 @@ export interface FileRoutesById {
|
|
|
1459
1467
|
'/api/knowledge/sync-github': typeof ApiKnowledgeSyncGithubRoute
|
|
1460
1468
|
'/api/mcp/bugs': typeof ApiMcpBugsRoute
|
|
1461
1469
|
'/api/notifications/stream': typeof ApiNotificationsStreamRoute
|
|
1470
|
+
'/api/sync/account-status': typeof ApiSyncAccountStatusRoute
|
|
1462
1471
|
'/api/sync/bootstrap': typeof ApiSyncBootstrapRoute
|
|
1463
1472
|
'/api/sync/pull': typeof ApiSyncPullRoute
|
|
1464
1473
|
'/api/sync/push': typeof ApiSyncPushRoute
|
|
@@ -1599,6 +1608,7 @@ export interface FileRouteTypes {
|
|
|
1599
1608
|
| '/api/knowledge/sync-github'
|
|
1600
1609
|
| '/api/mcp/bugs'
|
|
1601
1610
|
| '/api/notifications/stream'
|
|
1611
|
+
| '/api/sync/account-status'
|
|
1602
1612
|
| '/api/sync/bootstrap'
|
|
1603
1613
|
| '/api/sync/pull'
|
|
1604
1614
|
| '/api/sync/push'
|
|
@@ -1722,6 +1732,7 @@ export interface FileRouteTypes {
|
|
|
1722
1732
|
| '/api/knowledge/sync-github'
|
|
1723
1733
|
| '/api/mcp/bugs'
|
|
1724
1734
|
| '/api/notifications/stream'
|
|
1735
|
+
| '/api/sync/account-status'
|
|
1725
1736
|
| '/api/sync/bootstrap'
|
|
1726
1737
|
| '/api/sync/pull'
|
|
1727
1738
|
| '/api/sync/push'
|
|
@@ -1863,6 +1874,7 @@ export interface FileRouteTypes {
|
|
|
1863
1874
|
| '/api/knowledge/sync-github'
|
|
1864
1875
|
| '/api/mcp/bugs'
|
|
1865
1876
|
| '/api/notifications/stream'
|
|
1877
|
+
| '/api/sync/account-status'
|
|
1866
1878
|
| '/api/sync/bootstrap'
|
|
1867
1879
|
| '/api/sync/pull'
|
|
1868
1880
|
| '/api/sync/push'
|
|
@@ -1949,6 +1961,7 @@ export interface RootRouteChildren {
|
|
|
1949
1961
|
ApiKnowledgeSyncGithubRoute: typeof ApiKnowledgeSyncGithubRoute
|
|
1950
1962
|
ApiMcpBugsRoute: typeof ApiMcpBugsRoute
|
|
1951
1963
|
ApiNotificationsStreamRoute: typeof ApiNotificationsStreamRoute
|
|
1964
|
+
ApiSyncAccountStatusRoute: typeof ApiSyncAccountStatusRoute
|
|
1952
1965
|
ApiSyncBootstrapRoute: typeof ApiSyncBootstrapRoute
|
|
1953
1966
|
ApiSyncPullRoute: typeof ApiSyncPullRoute
|
|
1954
1967
|
ApiSyncPushRoute: typeof ApiSyncPushRoute
|
|
@@ -2620,6 +2633,13 @@ declare module '@tanstack/react-router' {
|
|
|
2620
2633
|
preLoaderRoute: typeof ApiNotificationsStreamRouteImport
|
|
2621
2634
|
parentRoute: typeof rootRouteImport
|
|
2622
2635
|
}
|
|
2636
|
+
'/api/sync/account-status': {
|
|
2637
|
+
id: '/api/sync/account-status'
|
|
2638
|
+
path: '/api/sync/account-status'
|
|
2639
|
+
fullPath: '/api/sync/account-status'
|
|
2640
|
+
preLoaderRoute: typeof ApiSyncAccountStatusRouteImport
|
|
2641
|
+
parentRoute: typeof rootRouteImport
|
|
2642
|
+
}
|
|
2623
2643
|
'/api/sync/bootstrap': {
|
|
2624
2644
|
id: '/api/sync/bootstrap'
|
|
2625
2645
|
path: '/api/sync/bootstrap'
|
|
@@ -3445,6 +3465,7 @@ const rootRouteChildren: RootRouteChildren = {
|
|
|
3445
3465
|
ApiKnowledgeSyncGithubRoute: ApiKnowledgeSyncGithubRoute,
|
|
3446
3466
|
ApiMcpBugsRoute: ApiMcpBugsRoute,
|
|
3447
3467
|
ApiNotificationsStreamRoute: ApiNotificationsStreamRoute,
|
|
3468
|
+
ApiSyncAccountStatusRoute: ApiSyncAccountStatusRoute,
|
|
3448
3469
|
ApiSyncBootstrapRoute: ApiSyncBootstrapRoute,
|
|
3449
3470
|
ApiSyncPullRoute: ApiSyncPullRoute,
|
|
3450
3471
|
ApiSyncPushRoute: ApiSyncPushRoute,
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
// SPDX-License-Identifier: AGPL-3.0-or-later
|
|
2
|
+
// AllBlue - 理想之海
|
|
3
|
+
// Copyright (C) 2026 AllBlue Contributors
|
|
4
|
+
|
|
5
|
+
// account-status 端点(归属转移前置探测,桌面本地优先 P2-A5)回归哨兵:
|
|
6
|
+
// - 空账号口径 = 当前用户六表 count 全 0(复审⑥ P1-9 扩口径:知识库两表计入,
|
|
7
|
+
// 防仅有知识库数据的云账号被误判空走归属转移);
|
|
8
|
+
// - 属主字段钉死:story/bug/testCase → createdById;project → ownerId;
|
|
9
|
+
// knowledgeBase → userId;knowledgeDocument → createdBy(以 schema 为准,
|
|
10
|
+
// 改错字段 = 探测失真,测试即报);
|
|
11
|
+
// - 未登录 401 且零查库。
|
|
12
|
+
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
|
13
|
+
|
|
14
|
+
const mocks = vi.hoisted(() => {
|
|
15
|
+
const count = () => vi.fn()
|
|
16
|
+
return {
|
|
17
|
+
prisma: {
|
|
18
|
+
story: { count: count() },
|
|
19
|
+
bug: { count: count() },
|
|
20
|
+
testCase: { count: count() },
|
|
21
|
+
project: { count: count() },
|
|
22
|
+
knowledgeBase: { count: count() },
|
|
23
|
+
knowledgeDocument: { count: count() },
|
|
24
|
+
},
|
|
25
|
+
requireAuth: vi.fn(),
|
|
26
|
+
}
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
vi.mock('@allbluecn/database/get-prisma', () => ({
|
|
30
|
+
getPrisma: async () => mocks.prisma,
|
|
31
|
+
}))
|
|
32
|
+
vi.mock('@/server/auth-helpers', () => ({
|
|
33
|
+
requireAuth: mocks.requireAuth,
|
|
34
|
+
UnauthorizedError: class UnauthorizedError extends Error {
|
|
35
|
+
constructor(message = 'UNAUTHORIZED') {
|
|
36
|
+
super(message)
|
|
37
|
+
this.name = 'UnauthorizedError'
|
|
38
|
+
}
|
|
39
|
+
},
|
|
40
|
+
}))
|
|
41
|
+
|
|
42
|
+
import { Route } from '../account-status'
|
|
43
|
+
import { UnauthorizedError } from '@/server/auth-helpers'
|
|
44
|
+
|
|
45
|
+
type Handler = (ctx: { request: Request }) => Promise<Response>
|
|
46
|
+
const GET = (Route.options.server as { handlers?: { GET?: Handler } } | undefined)?.handlers?.GET
|
|
47
|
+
if (!GET) throw new Error('GET handler missing')
|
|
48
|
+
|
|
49
|
+
const call = () =>
|
|
50
|
+
GET({ request: new Request('https://allblue.test/api/sync/account-status') })
|
|
51
|
+
|
|
52
|
+
/** 六表 count 桩:按 { 表名: 值 } 设定,未提及的表默认 0 */
|
|
53
|
+
const seedCounts = (values: Partial<Record<keyof typeof mocks.prisma, number>>) => {
|
|
54
|
+
for (const [table, model] of Object.entries(mocks.prisma)) {
|
|
55
|
+
model.count.mockReset()
|
|
56
|
+
model.count.mockResolvedValue(values[table as keyof typeof values] ?? 0)
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
beforeEach(() => {
|
|
61
|
+
vi.clearAllMocks()
|
|
62
|
+
mocks.requireAuth.mockResolvedValue({ user: { id: 'u1' } })
|
|
63
|
+
seedCounts({})
|
|
64
|
+
})
|
|
65
|
+
|
|
66
|
+
describe('/api/sync/account-status', () => {
|
|
67
|
+
it('空账号:六表 count 全 0 → { empty: true },各表按属主字段过滤', async () => {
|
|
68
|
+
const res = await call()
|
|
69
|
+
|
|
70
|
+
expect(res.status).toBe(200)
|
|
71
|
+
expect(await res.json()).toEqual({ empty: true })
|
|
72
|
+
|
|
73
|
+
// 属主字段口径钉死(改字段 = 探测失真,此处即报)
|
|
74
|
+
expect(mocks.prisma.story.count).toHaveBeenCalledWith({ where: { createdById: 'u1' } })
|
|
75
|
+
expect(mocks.prisma.bug.count).toHaveBeenCalledWith({ where: { createdById: 'u1' } })
|
|
76
|
+
expect(mocks.prisma.testCase.count).toHaveBeenCalledWith({ where: { createdById: 'u1' } })
|
|
77
|
+
expect(mocks.prisma.project.count).toHaveBeenCalledWith({ where: { ownerId: 'u1' } })
|
|
78
|
+
expect(mocks.prisma.knowledgeBase.count).toHaveBeenCalledWith({ where: { userId: 'u1' } })
|
|
79
|
+
expect(mocks.prisma.knowledgeDocument.count).toHaveBeenCalledWith({ where: { createdBy: 'u1' } })
|
|
80
|
+
})
|
|
81
|
+
|
|
82
|
+
it('任一表有数据(单表 >0 其余 0)→ { empty: false }(六表逐一参数化)', async () => {
|
|
83
|
+
for (const table of Object.keys(mocks.prisma) as Array<keyof typeof mocks.prisma>) {
|
|
84
|
+
seedCounts({ [table]: 2 })
|
|
85
|
+
const res = await call()
|
|
86
|
+
expect(res.status).toBe(200)
|
|
87
|
+
expect(await res.json()).toEqual({ empty: false })
|
|
88
|
+
}
|
|
89
|
+
})
|
|
90
|
+
|
|
91
|
+
it('仅知识库有数据(复审⑥ P1-9 扩口径):kb + 文档 >0、业务四表 0 → { empty: false }', async () => {
|
|
92
|
+
seedCounts({ knowledgeBase: 1, knowledgeDocument: 5 })
|
|
93
|
+
|
|
94
|
+
const res = await call()
|
|
95
|
+
|
|
96
|
+
expect(res.status).toBe(200)
|
|
97
|
+
expect(await res.json()).toEqual({ empty: false })
|
|
98
|
+
})
|
|
99
|
+
|
|
100
|
+
it('未登录 → 401,且零查库', async () => {
|
|
101
|
+
mocks.requireAuth.mockRejectedValue(new UnauthorizedError())
|
|
102
|
+
|
|
103
|
+
const res = await call()
|
|
104
|
+
|
|
105
|
+
expect(res.status).toBe(401)
|
|
106
|
+
expect(mocks.prisma.story.count).not.toHaveBeenCalled()
|
|
107
|
+
expect(mocks.prisma.knowledgeDocument.count).not.toHaveBeenCalled()
|
|
108
|
+
})
|
|
109
|
+
})
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
// SPDX-License-Identifier: AGPL-3.0-or-later
|
|
2
|
+
// AllBlue - 理想之海
|
|
3
|
+
// Copyright (C) 2026 AllBlue Contributors
|
|
4
|
+
|
|
5
|
+
// account-status 端点(桌面本地优先 P2-A5,迁移设计归属转移前置核对):桌面端绑定
|
|
6
|
+
// 云账号前探测目标账号是否「空账号」——仅空账号才走归属转移(一期唯一路径)。
|
|
7
|
+
// 六表 count 口径与属主字段实查收敛在 server/sync/account-status.ts 的
|
|
8
|
+
// isAccountEmpty(A8a 抽取,transferOwnershipFn 复核复用同源),此处只做
|
|
9
|
+
// requireAuth + 响应映射。
|
|
10
|
+
import { createFileRoute } from "@tanstack/react-router"
|
|
11
|
+
import { getPrisma } from "@allbluecn/database/get-prisma"
|
|
12
|
+
import { requireAuth, UnauthorizedError } from "@/server/auth-helpers"
|
|
13
|
+
import { isAccountEmpty } from "@/server/sync/account-status"
|
|
14
|
+
|
|
15
|
+
export const Route = createFileRoute("/api/sync/account-status")({
|
|
16
|
+
server: {
|
|
17
|
+
handlers: {
|
|
18
|
+
GET: async ({ request }) => {
|
|
19
|
+
try {
|
|
20
|
+
const session = await requireAuth(request)
|
|
21
|
+
const userId = session.user.id
|
|
22
|
+
|
|
23
|
+
const prisma = await getPrisma()
|
|
24
|
+
const empty = await isAccountEmpty(prisma, userId)
|
|
25
|
+
|
|
26
|
+
return Response.json({ empty })
|
|
27
|
+
} catch (e) {
|
|
28
|
+
// server.handlers 路由不走 serverFn middleware 链:401 映射在 handler 内
|
|
29
|
+
// (与 attachments/content 同款;message 兜底防 requireAuth 抛裸 UNAUTHORIZED)
|
|
30
|
+
if (e instanceof UnauthorizedError || (e instanceof Error && e.message === "UNAUTHORIZED")) {
|
|
31
|
+
return new Response("Unauthorized", { status: 401 })
|
|
32
|
+
}
|
|
33
|
+
console.error("[sync/account-status] 探测失败", e)
|
|
34
|
+
return new Response("Internal Server Error", { status: 500 })
|
|
35
|
+
}
|
|
36
|
+
},
|
|
37
|
+
},
|
|
38
|
+
},
|
|
39
|
+
})
|