@capybara-hq/kernel 0.1.0-rc.13 → 0.1.0-rc.15
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/dist/admin/admin.schemas.d.ts +5 -13
- package/dist/catalog/admin-catalog.controller.d.ts +39 -5
- package/dist/catalog/admin-catalog.controller.js +75 -6
- package/dist/catalog/admin-catalog.schemas.d.ts +10 -0
- package/dist/catalog/admin-catalog.schemas.js +7 -0
- package/dist/catalog/appearance.schemas.d.ts +10 -0
- package/dist/catalog/appearance.schemas.js +20 -0
- package/dist/catalog/appearance.service.d.ts +105 -0
- package/dist/catalog/appearance.service.js +306 -0
- package/dist/catalog/catalog-content.d.ts +9 -1
- package/dist/catalog/catalog-content.js +2 -1
- package/dist/catalog/catalog-files.d.ts +3 -2
- package/dist/catalog/catalog-files.js +8 -5
- package/dist/catalog/catalog-import.module.js +3 -0
- package/dist/catalog/catalog-items.d.ts +11 -0
- package/dist/catalog/catalog-items.js +21 -0
- package/dist/catalog/catalog-scope.service.d.ts +7 -0
- package/dist/catalog/catalog-scope.service.js +55 -15
- package/dist/catalog/catalog.service.js +10 -2
- package/dist/catalog/cli.js +3 -0
- package/dist/catalog/component-scope.service.d.ts +14 -1
- package/dist/catalog/component-scope.service.js +12 -2
- package/dist/catalog/marketplaces.service.d.ts +1 -1
- package/dist/catalog/org-catalog.controller.d.ts +5 -4
- package/dist/catalog/org-catalog.controller.js +5 -4
- package/dist/catalog/org-catalog.schemas.d.ts +18 -2
- package/dist/catalog/org-catalog.schemas.js +16 -2
- package/dist/catalog/org-catalog.service.js +8 -2
- package/dist/catalog/org-extension.controller.d.ts +27 -7
- package/dist/catalog/org-extension.controller.js +48 -6
- package/dist/catalog/plugin-import.d.ts +28 -4
- package/dist/catalog/plugin-import.js +96 -20
- package/dist/catalog/plugin-package.d.ts +4 -2
- package/dist/catalog/plugin-package.js +66 -12
- package/dist/catalog/plugin-upgrade.d.ts +15 -1
- package/dist/catalog/plugin-upgrade.js +18 -5
- package/dist/channels/connections.controller.d.ts +1 -1
- package/dist/channels/connections.controller.js +1 -1
- package/dist/channels/connections.service.d.ts +22 -5
- package/dist/channels/connections.service.js +56 -10
- package/dist/channels/webhook.d.ts +21 -1
- package/dist/channels/webhook.js +39 -4
- package/dist/client/client-access.service.d.ts +101 -0
- package/dist/client/client-access.service.js +294 -0
- package/dist/client/client-builds.service.d.ts +109 -0
- package/dist/client/client-builds.service.js +229 -0
- package/dist/client/client-delivery.controller.d.ts +25 -0
- package/dist/client/client-delivery.controller.js +66 -0
- package/dist/client/client-files.d.ts +48 -0
- package/dist/client/client-files.js +136 -0
- package/dist/client/client-reports.cron.d.ts +13 -0
- package/dist/client/client-reports.cron.js +42 -0
- package/dist/client/client-reports.service.d.ts +30 -0
- package/dist/client/client-reports.service.js +102 -0
- package/dist/client/client.controller.d.ts +45 -0
- package/dist/client/client.controller.js +121 -0
- package/dist/client/client.module.d.ts +7 -0
- package/dist/client/client.module.js +26 -0
- package/dist/client/client.schemas.d.ts +55 -0
- package/dist/client/client.schemas.js +27 -0
- package/dist/ctx/create-ctx.d.ts +19 -4
- package/dist/ctx/create-ctx.js +51 -3
- package/dist/db/kernel-schema.d.ts +58 -2
- package/dist/errors.d.ts +33 -0
- package/dist/errors.js +33 -0
- package/dist/extensions/admin-ops.d.ts +4 -1
- package/dist/extensions/admin-ops.js +22 -1
- package/dist/extensions/admin-view.d.ts +8 -0
- package/dist/extensions/admin-view.js +78 -0
- package/dist/extensions/blobs.d.ts +14 -1
- package/dist/extensions/blobs.js +48 -5
- package/dist/extensions/guide-places.d.ts +66 -0
- package/dist/extensions/guide-places.js +152 -0
- package/dist/extensions/host.d.ts +6 -1
- package/dist/extensions/host.js +23 -12
- package/dist/extensions/instance.d.ts +5 -1
- package/dist/extensions/instance.js +8 -0
- package/dist/extensions/ledger.d.ts +1 -1
- package/dist/extensions/package.d.ts +4 -3
- package/dist/extensions/package.js +5 -1
- package/dist/extensions/permission-set.d.ts +35 -5
- package/dist/extensions/permission-set.js +36 -5
- package/dist/extensions/permission-view.d.ts +38 -7
- package/dist/extensions/permission-view.js +27 -8
- package/dist/extensions/plugin-guides.d.ts +207 -54
- package/dist/extensions/plugin-guides.js +436 -165
- package/dist/extensions/sources.d.ts +6 -0
- package/dist/extensions/sources.js +2 -0
- package/dist/extensions/state.module.js +9 -1
- package/dist/http/api.module.js +16 -0
- package/dist/identity/settings.d.ts +41 -26
- package/dist/identity/settings.js +87 -4
- package/dist/infra/lock/lock.service.d.ts +8 -0
- package/dist/infra/lock/lock.service.js +27 -0
- package/dist/infra/throttler/named-throttlers.d.ts +8 -1
- package/dist/infra/throttler/named-throttlers.js +30 -1
- package/dist/infra/throttler/throttler.guard.js +1 -1
- package/dist/kernel.module.js +3 -0
- package/dist/orgs/settings.service.d.ts +2 -26
- package/dist/overrides.d.ts +3 -1
- package/dist/overrides.js +2 -0
- package/dist/platform/exclusions.js +37 -6
- package/dist/platform/operation.js +4 -0
- package/dist/platform/ops/admin.ops.js +17 -2
- package/dist/platform/ops/catalog-admin.ops.d.ts +19 -1
- package/dist/platform/ops/catalog-admin.ops.js +85 -2
- package/dist/platform/ops/connectors.ops.js +2 -0
- package/dist/platform/ops/org-plugins.ops.d.ts +7 -1
- package/dist/platform/ops/org-plugins.ops.js +63 -4
- package/dist/platform/ops/org-settings.ops.js +2 -2
- package/dist/platform/ops/orgs.ops.js +1 -0
- package/dist/platform/ops/setup.ops.d.ts +24 -0
- package/dist/platform/ops/setup.ops.js +212 -0
- package/dist/platform/ops/ui.ops.js +1 -1
- package/dist/platform/platform-ops.module.d.ts +2 -1
- package/dist/platform/platform-ops.module.js +7 -0
- package/dist/platform/route-schemas.js +8 -0
- package/dist/roles/worker.module.js +4 -0
- package/dist/setup/kernel-checks.d.ts +91 -0
- package/dist/setup/kernel-checks.js +295 -0
- package/dist/setup/oauth-token-probe.d.ts +47 -0
- package/dist/setup/oauth-token-probe.js +108 -0
- package/dist/setup/redact-check.d.ts +18 -0
- package/dist/setup/redact-check.js +46 -0
- package/dist/setup/setup-check-registry.d.ts +86 -0
- package/dist/setup/setup-check-registry.js +75 -0
- package/dist/setup/setup-checks.controller.d.ts +28 -0
- package/dist/setup/setup-checks.controller.js +78 -0
- package/dist/setup/setup-checks.schemas.d.ts +38 -0
- package/dist/setup/setup-checks.schemas.js +43 -0
- package/dist/setup/setup-checks.service.d.ts +144 -0
- package/dist/setup/setup-checks.service.js +683 -0
- package/dist/setup/setup-guides.controller.d.ts +18 -0
- package/dist/setup/setup-guides.controller.js +48 -0
- package/dist/setup/setup-guides.schemas.d.ts +10 -0
- package/dist/setup/setup-guides.schemas.js +6 -0
- package/dist/setup/setup-guides.service.d.ts +126 -0
- package/dist/setup/setup-guides.service.js +315 -0
- package/dist/setup/setup.module.d.ts +8 -0
- package/dist/setup/setup.module.js +31 -0
- package/dist/themes/render-css.d.ts +2 -1
- package/dist/themes/render-css.js +5 -2
- package/dist/themes/theme-delivery.service.d.ts +4 -2
- package/dist/themes/theme-delivery.service.js +8 -3
- package/dist/themes/theme-state.d.ts +28 -7
- package/dist/themes/theme-state.js +24 -6
- package/dist/themes/themes-public.controller.d.ts +7 -2
- package/dist/themes/themes-public.controller.js +11 -5
- package/dist/themes/themes.service.js +2 -2
- package/dist/tools/connectors.controller.d.ts +28 -11
- package/dist/tools/connectors.controller.js +49 -14
- package/dist/tools/connectors.schemas.d.ts +4 -0
- package/dist/tools/connectors.schemas.js +4 -0
- package/dist/tools/connectors.service.d.ts +30 -2
- package/dist/tools/connectors.service.js +98 -3
- package/dist/tools/credentials.d.ts +20 -0
- package/dist/tools/credentials.js +90 -1
- package/dist/tools/oauth/tokens.d.ts +20 -0
- package/dist/tools/oauth/tokens.js +49 -24
- package/dist/tools/pipeline.d.ts +3 -2
- package/dist/ui/arrangement.d.ts +41 -50
- package/dist/ui/arrangement.js +88 -15
- package/dist/ui/registry.d.ts +13 -3
- package/dist/ui/registry.js +15 -4
- package/dist/ui/ui.schemas.d.ts +4 -0
- package/dist/ui/views.d.ts +36 -9
- package/dist/ui/views.js +58 -28
- package/package.json +3 -3
- package/src/migrations/0106_setup_guides_v2.sql +10 -0
- package/src/migrations/0107_extension_client_builds.sql +50 -0
|
@@ -6,6 +6,11 @@ import { ApiError } from "../errors.js";
|
|
|
6
6
|
export interface ThemeRef {
|
|
7
7
|
/** The `platform_themes` id (what settings and picks store). */
|
|
8
8
|
id: string;
|
|
9
|
+
/**
|
|
10
|
+
* `"<plugin>/<theme file id>"`: the name the web's `theme=` state token and `useTheme()` carry
|
|
11
|
+
* (client extensions spec §7.3). Stable across versions, unlike `id`.
|
|
12
|
+
*/
|
|
13
|
+
key: string;
|
|
9
14
|
/** Its current version. */
|
|
10
15
|
version: string;
|
|
11
16
|
name: LocalizedName;
|
|
@@ -19,7 +24,7 @@ export interface ThemeRef {
|
|
|
19
24
|
wordmark: string | null;
|
|
20
25
|
faviconUrl: string | null;
|
|
21
26
|
} | null;
|
|
22
|
-
/** `/api/v1/themes/<id>/<version>/theme.css`. */
|
|
27
|
+
/** `/api/v1/themes/<id>/<version>/r<THEME_CSS_REVISION>/theme.css`. */
|
|
23
28
|
cssUrl: string;
|
|
24
29
|
}
|
|
25
30
|
/** The four colors a theme picker draws per scheme. */
|
|
@@ -34,24 +39,40 @@ export type ListedTheme = ThemeRef & {
|
|
|
34
39
|
swatches: Partial<Record<ThemeScheme, ThemeSwatch>>;
|
|
35
40
|
};
|
|
36
41
|
/**
|
|
37
|
-
* The
|
|
42
|
+
* The render revision of theme stylesheets, a segment of the stylesheet URL (`r<N>/theme.css`).
|
|
43
|
+
* The stylesheet is cached as immutable for a year, keyed by its URL: bump this whenever
|
|
44
|
+
* `renderThemeCss`'s output format changes, so no browser keeps an older format under a URL
|
|
45
|
+
* that now means the new one. The delivery route serves only the current revision (404
|
|
46
|
+
* `theme.not_found` otherwise). History: (none, `<version>/theme.css`) plain rules; 2: one
|
|
47
|
+
* `@layer theme { … }` block (client extensions spec §7.2).
|
|
48
|
+
*/
|
|
49
|
+
export declare const THEME_CSS_REVISION = 2;
|
|
50
|
+
/**
|
|
51
|
+
* The delivery URL of a theme version (spec §8): its stylesheet (with the render revision,
|
|
52
|
+
* {@link THEME_CSS_REVISION}), or with `asset` (a path inside the
|
|
38
53
|
* plugin as the theme file names it, e.g. `./fonts/MiSans.woff2`) that asset, as one path segment
|
|
39
54
|
* (`assets/fonts%2FMiSans.woff2`; the route decodes it back to the stored path `fonts/MiSans.woff2`).
|
|
40
55
|
* The one builder of these URLs: the listing, the stylesheet and the asset routes all use it.
|
|
41
56
|
*/
|
|
42
57
|
export declare function themeUrl(id: string, version: string, asset?: string): string;
|
|
43
|
-
/**
|
|
44
|
-
|
|
58
|
+
/**
|
|
59
|
+
* The {@link ThemeRef} of version `version` of theme `id` of plugin `plugin` (null only for a row
|
|
60
|
+
* without provenance: the key is then the file id alone).
|
|
61
|
+
*/
|
|
62
|
+
export declare function themeRefOf(id: string, version: string, def: ParsedTheme, plugin: string | null): ThemeRef;
|
|
45
63
|
/**
|
|
46
64
|
* The swatches of each scheme the theme defines: its overrides merged onto the current built-in
|
|
47
65
|
* values (a stored definition holds only the theme file's own values, themes spec §6).
|
|
48
66
|
*/
|
|
49
67
|
export declare function themeSwatches(def: ParsedTheme): Partial<Record<ThemeScheme, ThemeSwatch>>;
|
|
50
|
-
/**
|
|
51
|
-
export
|
|
68
|
+
/** A live theme's current definition and its plugin (null: no provenance). */
|
|
69
|
+
export interface ThemeDefinition {
|
|
52
70
|
version: string;
|
|
53
71
|
definition: ParsedTheme;
|
|
54
|
-
|
|
72
|
+
plugin: string | null;
|
|
73
|
+
}
|
|
74
|
+
/** The current definitions of live themes `ids`, by id. */
|
|
75
|
+
export declare function themeDefinitions(db: KernelSchemaDb, ids: readonly string[]): Promise<Map<string, ThemeDefinition>>;
|
|
55
76
|
/** The live themes on in the org, sorted by id. */
|
|
56
77
|
export declare function themesOnIn(db: KernelSchemaDb, orgId: string): Promise<ComponentRow[]>;
|
|
57
78
|
/** The live theme `id` if it can be the platform default, else null. */
|
|
@@ -9,7 +9,17 @@ import { packagePath } from "../packages/tgz.js";
|
|
|
9
9
|
/** The schemes in the order they are listed. */
|
|
10
10
|
const SCHEMES = ["dark", "light"];
|
|
11
11
|
/**
|
|
12
|
-
* The
|
|
12
|
+
* The render revision of theme stylesheets, a segment of the stylesheet URL (`r<N>/theme.css`).
|
|
13
|
+
* The stylesheet is cached as immutable for a year, keyed by its URL: bump this whenever
|
|
14
|
+
* `renderThemeCss`'s output format changes, so no browser keeps an older format under a URL
|
|
15
|
+
* that now means the new one. The delivery route serves only the current revision (404
|
|
16
|
+
* `theme.not_found` otherwise). History: (none, `<version>/theme.css`) plain rules; 2: one
|
|
17
|
+
* `@layer theme { … }` block (client extensions spec §7.2).
|
|
18
|
+
*/
|
|
19
|
+
export const THEME_CSS_REVISION = 2;
|
|
20
|
+
/**
|
|
21
|
+
* The delivery URL of a theme version (spec §8): its stylesheet (with the render revision,
|
|
22
|
+
* {@link THEME_CSS_REVISION}), or with `asset` (a path inside the
|
|
13
23
|
* plugin as the theme file names it, e.g. `./fonts/MiSans.woff2`) that asset, as one path segment
|
|
14
24
|
* (`assets/fonts%2FMiSans.woff2`; the route decodes it back to the stored path `fonts/MiSans.woff2`).
|
|
15
25
|
* The one builder of these URLs: the listing, the stylesheet and the asset routes all use it.
|
|
@@ -17,14 +27,18 @@ const SCHEMES = ["dark", "light"];
|
|
|
17
27
|
export function themeUrl(id, version, asset) {
|
|
18
28
|
const base = `/api/v1/themes/${encodeURIComponent(id)}/${encodeURIComponent(version)}`;
|
|
19
29
|
return asset === undefined
|
|
20
|
-
? `${base}/theme.css`
|
|
30
|
+
? `${base}/r${THEME_CSS_REVISION}/theme.css`
|
|
21
31
|
: `${base}/assets/${encodeURIComponent(packagePath(asset))}`;
|
|
22
32
|
}
|
|
23
|
-
/**
|
|
24
|
-
|
|
33
|
+
/**
|
|
34
|
+
* The {@link ThemeRef} of version `version` of theme `id` of plugin `plugin` (null only for a row
|
|
35
|
+
* without provenance: the key is then the file id alone).
|
|
36
|
+
*/
|
|
37
|
+
export function themeRefOf(id, version, def, plugin) {
|
|
25
38
|
const url = (ref) => (ref === undefined ? null : themeUrl(id, version, ref));
|
|
26
39
|
return {
|
|
27
40
|
id,
|
|
41
|
+
key: plugin === null ? def.id : `${plugin}/${def.id}`,
|
|
28
42
|
version,
|
|
29
43
|
name: def.name,
|
|
30
44
|
schemes: SCHEMES.filter((s) => def.schemes[s] !== undefined),
|
|
@@ -62,12 +76,16 @@ export async function themeDefinitions(db, ids) {
|
|
|
62
76
|
const rows = await db
|
|
63
77
|
.selectFrom("platform_themes as p")
|
|
64
78
|
.innerJoin("platform_theme_versions as v", (j) => j.onRef("v.theme_id", "=", "p.id").onRef("v.version", "=", "p.current_version"))
|
|
65
|
-
.select(["p.id", "p.current_version", "v.definition"])
|
|
79
|
+
.select(["p.id", "p.current_version", "p.plugin_name", "v.definition"])
|
|
66
80
|
.where("p.id", "in", [...ids])
|
|
67
81
|
.where("p.deleted_at", "is", null)
|
|
68
82
|
.execute();
|
|
69
83
|
for (const r of rows) {
|
|
70
|
-
out.set(r.id, {
|
|
84
|
+
out.set(r.id, {
|
|
85
|
+
version: String(r.current_version),
|
|
86
|
+
definition: r.definition,
|
|
87
|
+
plugin: r.plugin_name,
|
|
88
|
+
});
|
|
71
89
|
}
|
|
72
90
|
return out;
|
|
73
91
|
}
|
|
@@ -10,8 +10,13 @@ import { ThemesService } from "./themes.service.js";
|
|
|
10
10
|
export declare class ThemeDeliveryController {
|
|
11
11
|
private readonly delivery;
|
|
12
12
|
constructor(delivery: ThemeDeliveryService);
|
|
13
|
-
/**
|
|
14
|
-
|
|
13
|
+
/**
|
|
14
|
+
* The version's stylesheet: scheme overrides, `@font-face` rules and font stacks, in the render
|
|
15
|
+
* revision `revision` (`r<THEME_CSS_REVISION>`; only the current one is served). The URL without
|
|
16
|
+
* a revision (before revision 2) has no route: 404, so a browser's cached unlayered copy is never
|
|
17
|
+
* answered with the new format under an immutable cache.
|
|
18
|
+
*/
|
|
19
|
+
css(id: string, version: string, revision: string, reply: FastifyReply): Promise<void>;
|
|
15
20
|
/**
|
|
16
21
|
* A font (`font/woff2`) or logo (`image/svg+xml`, sanitized at import, served under a CSP
|
|
17
22
|
* sandbox; `image/png`) the version names; `file` is its plugin path as one segment
|
|
@@ -23,9 +23,14 @@ let ThemeDeliveryController = class ThemeDeliveryController {
|
|
|
23
23
|
constructor(delivery) {
|
|
24
24
|
this.delivery = delivery;
|
|
25
25
|
}
|
|
26
|
-
/**
|
|
27
|
-
|
|
28
|
-
|
|
26
|
+
/**
|
|
27
|
+
* The version's stylesheet: scheme overrides, `@font-face` rules and font stacks, in the render
|
|
28
|
+
* revision `revision` (`r<THEME_CSS_REVISION>`; only the current one is served). The URL without
|
|
29
|
+
* a revision (before revision 2) has no route: 404, so a browser's cached unlayered copy is never
|
|
30
|
+
* answered with the new format under an immutable cache.
|
|
31
|
+
*/
|
|
32
|
+
async css(id, version, revision, reply) {
|
|
33
|
+
const css = await this.delivery.stylesheet(id, version, revision);
|
|
29
34
|
await reply
|
|
30
35
|
.header("content-type", "text/css; charset=utf-8")
|
|
31
36
|
.header("x-content-type-options", "nosniff")
|
|
@@ -51,10 +56,11 @@ let ThemeDeliveryController = class ThemeDeliveryController {
|
|
|
51
56
|
};
|
|
52
57
|
__decorate([
|
|
53
58
|
Public(),
|
|
54
|
-
Get("theme.css"),
|
|
59
|
+
Get(":revision/theme.css"),
|
|
55
60
|
__param(0, Param("id")),
|
|
56
61
|
__param(1, Param("version")),
|
|
57
|
-
__param(2,
|
|
62
|
+
__param(2, Param("revision")),
|
|
63
|
+
__param(3, Res())
|
|
58
64
|
], ThemeDeliveryController.prototype, "css", null);
|
|
59
65
|
__decorate([
|
|
60
66
|
Public(),
|
|
@@ -32,7 +32,7 @@ let ThemesService = class ThemesService {
|
|
|
32
32
|
if (!d)
|
|
33
33
|
continue;
|
|
34
34
|
out.push({
|
|
35
|
-
...themeRefOf(c.id, d.version, d.definition),
|
|
35
|
+
...themeRefOf(c.id, d.version, d.definition, d.plugin),
|
|
36
36
|
swatches: themeSwatches(d.definition),
|
|
37
37
|
});
|
|
38
38
|
}
|
|
@@ -53,7 +53,7 @@ let ThemesService = class ThemesService {
|
|
|
53
53
|
if (id === null || (await eligiblePlatformDefault(this.db.db, id)) === null)
|
|
54
54
|
return null;
|
|
55
55
|
const d = (await themeDefinitions(this.db.db, [id])).get(id);
|
|
56
|
-
return d ? themeRefOf(id, d.version, d.definition) : null;
|
|
56
|
+
return d ? themeRefOf(id, d.version, d.definition, d.plugin) : null;
|
|
57
57
|
}
|
|
58
58
|
/**
|
|
59
59
|
* The listing, the viewer's resolved theme and the org's setting. Separate reads, not one
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
+
import type { ConnectionGuideState } from "../extensions/plugin-guides.js";
|
|
2
3
|
import { PluginIcons } from "../extensions/plugin-icons.js";
|
|
3
4
|
import { type Principal } from "../identity/principal.js";
|
|
4
5
|
import { type OrgContext } from "../orgs/org.guard.js";
|
|
5
6
|
import { ConnectorAccountPolicies, type MemberToolPolicyView } from "./connector-account-policies.js";
|
|
6
|
-
import { CredentialValue, MemberToolPoliciesBody } from "./connectors.schemas.js";
|
|
7
|
+
import { ConnectorGuideAckSchema, CredentialValue, MemberToolPoliciesBody } from "./connectors.schemas.js";
|
|
7
8
|
import { type ConnectorDetail, type ConnectorPreview, type ConnectorSummary, ConnectorsService, type ToolIssue } from "./connectors.service.js";
|
|
8
9
|
declare const CreateBody: z.ZodObject<{
|
|
9
10
|
name: z.ZodString;
|
|
@@ -169,6 +170,14 @@ declare const UpdateBody: z.ZodObject<{
|
|
|
169
170
|
}, z.core.$strict>>>;
|
|
170
171
|
allowUnannotated: z.ZodOptional<z.ZodBoolean>;
|
|
171
172
|
}, z.core.$strict>;
|
|
173
|
+
/**
|
|
174
|
+
* A connector as the org's pages show it: with its plugin's icon (`icon`, see `withIcons`), its
|
|
175
|
+
* template's setup guide and the drift since it was set up (`setupGuide`, `guideDrift`; spec
|
|
176
|
+
* 2026-11-09-setup-guides-v2-design.md §9.3, §11).
|
|
177
|
+
*/
|
|
178
|
+
export type ConnectorPage<T extends ConnectorSummary = ConnectorDetail> = T & ConnectionGuideState & {
|
|
179
|
+
icon: string | null;
|
|
180
|
+
};
|
|
172
181
|
/**
|
|
173
182
|
* 连接器(`/api/v1/orgs/:orgId/connectors`):读与个人凭据用 `connectors.use`(访客起),安装 / 修改 / 删除 / 刷新 /
|
|
174
183
|
* 组织凭据用 `connectors.manage`(管理员起);两者都仅限用户主体。
|
|
@@ -185,10 +194,11 @@ export declare class ConnectorsController {
|
|
|
185
194
|
*/
|
|
186
195
|
private withIcons;
|
|
187
196
|
list(org: OrgContext): Promise<{
|
|
188
|
-
connectors:
|
|
189
|
-
icon: string | null;
|
|
190
|
-
})[];
|
|
197
|
+
connectors: ConnectorPage<ConnectorSummary>[];
|
|
191
198
|
}>;
|
|
199
|
+
/** Adds the icon and the setup guide fields to connector views (one lookup each for all). */
|
|
200
|
+
private withGuides;
|
|
201
|
+
private withGuide;
|
|
192
202
|
/**
|
|
193
203
|
* The connector kinds the org can create connectors of, each with the plugin providing it (the
|
|
194
204
|
* install wizard offers only these; an empty list means no connector extension is enabled).
|
|
@@ -200,7 +210,7 @@ export declare class ConnectorsController {
|
|
|
200
210
|
}[];
|
|
201
211
|
}>;
|
|
202
212
|
create(principal: Principal, org: OrgContext, body: z.infer<typeof CreateBody>): Promise<{
|
|
203
|
-
connector:
|
|
213
|
+
connector: ConnectorPage;
|
|
204
214
|
issues: ToolIssue[];
|
|
205
215
|
}>;
|
|
206
216
|
/**
|
|
@@ -209,16 +219,14 @@ export declare class ConnectorsController {
|
|
|
209
219
|
*/
|
|
210
220
|
preview(principal: Principal, org: OrgContext, body: z.infer<typeof PreviewBody>): Promise<ConnectorPreview>;
|
|
211
221
|
get(principal: Principal, org: OrgContext, connectorId: string): Promise<{
|
|
212
|
-
connector:
|
|
213
|
-
icon: string | null;
|
|
214
|
-
};
|
|
222
|
+
connector: ConnectorPage;
|
|
215
223
|
}>;
|
|
216
224
|
update(principal: Principal, org: OrgContext, connectorId: string, body: z.infer<typeof UpdateBody>): Promise<{
|
|
217
|
-
connector:
|
|
225
|
+
connector: ConnectorPage;
|
|
218
226
|
}>;
|
|
219
227
|
remove(principal: Principal, org: OrgContext, connectorId: string): Promise<void>;
|
|
220
228
|
refresh(principal: Principal, org: OrgContext, connectorId: string): Promise<{
|
|
221
|
-
connector:
|
|
229
|
+
connector: ConnectorPage;
|
|
222
230
|
issues: ToolIssue[];
|
|
223
231
|
changes: {
|
|
224
232
|
added: string[];
|
|
@@ -228,7 +236,16 @@ export declare class ConnectorsController {
|
|
|
228
236
|
}>;
|
|
229
237
|
/** 复核待复核的工具(漂移检测,spec 7.3):approve 采用新定义并启用,reject 停用并保留旧定义。 */
|
|
230
238
|
review(principal: Principal, org: OrgContext, connectorId: string, name: string, body: z.infer<typeof ReviewBody>): Promise<{
|
|
231
|
-
connector:
|
|
239
|
+
connector: ConnectorPage;
|
|
240
|
+
}>;
|
|
241
|
+
/**
|
|
242
|
+
* 「我已更新应用」: the org admin updated the third-party app to the connector template's current
|
|
243
|
+
* setup guide (`{ hash }` of the guide shown) -> 200 `{ connector }`; 409
|
|
244
|
+
* `connector.guide_changed` when the guide moved on since, 404 `catalog.guide_not_found` for a
|
|
245
|
+
* connector without a template guide.
|
|
246
|
+
*/
|
|
247
|
+
ackGuide(principal: Principal, org: OrgContext, connectorId: string, body: z.infer<typeof ConnectorGuideAckSchema>): Promise<{
|
|
248
|
+
connector: ConnectorPage;
|
|
232
249
|
}>;
|
|
233
250
|
setOrgCredential(principal: Principal, org: OrgContext, connectorId: string, body: z.infer<typeof CredentialValue>): Promise<void>;
|
|
234
251
|
setMyCredential(principal: Principal, org: OrgContext, connectorId: string, body: z.infer<typeof CredentialValue>): Promise<void>;
|
|
@@ -17,7 +17,7 @@ import { ZBody } from "../infra/validation/zod-params.js";
|
|
|
17
17
|
import { CurrentOrg, OrgAction } from "../orgs/org.guard.js";
|
|
18
18
|
import { can } from "../orgs/roles.js";
|
|
19
19
|
import { ConnectorAccountPolicies, } from "./connector-account-policies.js";
|
|
20
|
-
import { CredentialValue, MemberToolPoliciesBody } from "./connectors.schemas.js";
|
|
20
|
+
import { ConnectorGuideAckSchema, CredentialValue, MemberToolPoliciesBody, } from "./connectors.schemas.js";
|
|
21
21
|
import { ConnectorsService, } from "./connectors.service.js";
|
|
22
22
|
import { publicEndpoint } from "./endpoint.js";
|
|
23
23
|
/**
|
|
@@ -88,6 +88,8 @@ class ConnectorCredentialBody extends createZodDto(CredentialValue, "ConnectorCr
|
|
|
88
88
|
}
|
|
89
89
|
class MemberToolPoliciesDto extends createZodDto(MemberToolPoliciesBody, "MemberToolPoliciesBody") {
|
|
90
90
|
}
|
|
91
|
+
class ConnectorGuideAckBody extends createZodDto(ConnectorGuideAckSchema, "ConnectorGuideAckBody") {
|
|
92
|
+
}
|
|
91
93
|
/**
|
|
92
94
|
* 连接器(`/api/v1/orgs/:orgId/connectors`):读与个人凭据用 `connectors.use`(访客起),安装 / 修改 / 删除 / 刷新 /
|
|
93
95
|
* 组织凭据用 `connectors.manage`(管理员起);两者都仅限用户主体。
|
|
@@ -115,8 +117,22 @@ let ConnectorsController = class ConnectorsController {
|
|
|
115
117
|
}));
|
|
116
118
|
}
|
|
117
119
|
async list(org) {
|
|
118
|
-
const
|
|
119
|
-
return { connectors: await this.
|
|
120
|
+
const list = (await this.connectors.list(org.orgId)).map((a) => forViewer(org, a));
|
|
121
|
+
return { connectors: await this.withGuides(org, list) };
|
|
122
|
+
}
|
|
123
|
+
/** Adds the icon and the setup guide fields to connector views (one lookup each for all). */
|
|
124
|
+
async withGuides(org, views) {
|
|
125
|
+
const [withIcons, states] = await Promise.all([
|
|
126
|
+
this.withIcons(org, views),
|
|
127
|
+
this.connectors.guideStates(org, views.map((v) => v.id)),
|
|
128
|
+
]);
|
|
129
|
+
return withIcons.map((v) => ({
|
|
130
|
+
...v,
|
|
131
|
+
...(states.get(v.id) ?? { setupGuide: null, guideDrift: null }),
|
|
132
|
+
}));
|
|
133
|
+
}
|
|
134
|
+
async withGuide(org, view) {
|
|
135
|
+
return (await this.withGuides(org, [view]))[0];
|
|
120
136
|
}
|
|
121
137
|
/**
|
|
122
138
|
* The connector kinds the org can create connectors of, each with the plugin providing it (the
|
|
@@ -127,7 +143,8 @@ let ConnectorsController = class ConnectorsController {
|
|
|
127
143
|
}
|
|
128
144
|
async create(principal, org, body) {
|
|
129
145
|
const p = requireUser(principal);
|
|
130
|
-
|
|
146
|
+
const r = await this.connectors.create(org.orgId, p.account.id, body, patTokenId(p));
|
|
147
|
+
return { ...r, connector: await this.withGuide(org, r.connector) };
|
|
131
148
|
}
|
|
132
149
|
/**
|
|
133
150
|
* 预览(安装向导):读端点描述(认证提示、操作清单)并在能认证时列出工具,什么都不写。
|
|
@@ -139,15 +156,13 @@ let ConnectorsController = class ConnectorsController {
|
|
|
139
156
|
}
|
|
140
157
|
async get(principal, org, connectorId) {
|
|
141
158
|
const p = requireUser(principal);
|
|
142
|
-
const
|
|
143
|
-
|
|
144
|
-
return { connector: connector };
|
|
159
|
+
const view = forViewer(org, await this.connectors.get(org.orgId, connectorId, p.account.id));
|
|
160
|
+
return { connector: await this.withGuide(org, view) };
|
|
145
161
|
}
|
|
146
162
|
async update(principal, org, connectorId, body) {
|
|
147
163
|
const p = requireUser(principal);
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
};
|
|
164
|
+
const view = await this.connectors.update(org.orgId, connectorId, p.account.id, body, patTokenId(p));
|
|
165
|
+
return { connector: await this.withGuide(org, view) };
|
|
151
166
|
}
|
|
152
167
|
async remove(principal, org, connectorId) {
|
|
153
168
|
const p = requireUser(principal);
|
|
@@ -155,14 +170,25 @@ let ConnectorsController = class ConnectorsController {
|
|
|
155
170
|
}
|
|
156
171
|
async refresh(principal, org, connectorId) {
|
|
157
172
|
const p = requireUser(principal);
|
|
158
|
-
|
|
173
|
+
const r = await this.connectors.refresh(org.orgId, connectorId, p.account.id, patTokenId(p));
|
|
174
|
+
return { ...r, connector: await this.withGuide(org, r.connector) };
|
|
159
175
|
}
|
|
160
176
|
/** 复核待复核的工具(漂移检测,spec 7.3):approve 采用新定义并启用,reject 停用并保留旧定义。 */
|
|
161
177
|
async review(principal, org, connectorId, name, body) {
|
|
162
178
|
const p = requireUser(principal);
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
179
|
+
const view = await this.connectors.reviewTool(org.orgId, connectorId, name, body.action, p.account.id, patTokenId(p));
|
|
180
|
+
return { connector: await this.withGuide(org, view) };
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* 「我已更新应用」: the org admin updated the third-party app to the connector template's current
|
|
184
|
+
* setup guide (`{ hash }` of the guide shown) -> 200 `{ connector }`; 409
|
|
185
|
+
* `connector.guide_changed` when the guide moved on since, 404 `catalog.guide_not_found` for a
|
|
186
|
+
* connector without a template guide.
|
|
187
|
+
*/
|
|
188
|
+
async ackGuide(principal, org, connectorId, body) {
|
|
189
|
+
const p = requireUser(principal);
|
|
190
|
+
const view = await this.connectors.ackGuide({ orgId: org.orgId, role: org.role }, connectorId, body.hash, p.account.id, patTokenId(p));
|
|
191
|
+
return { connector: await this.withGuide(org, view) };
|
|
166
192
|
}
|
|
167
193
|
async setOrgCredential(principal, org, connectorId, body) {
|
|
168
194
|
const p = requireUser(principal);
|
|
@@ -263,6 +289,15 @@ __decorate([
|
|
|
263
289
|
__param(3, Param("name")),
|
|
264
290
|
__param(4, ZBody(ConnectorToolReviewBody))
|
|
265
291
|
], ConnectorsController.prototype, "review", null);
|
|
292
|
+
__decorate([
|
|
293
|
+
Post(":connectorId/guide-ack"),
|
|
294
|
+
HttpCode(200),
|
|
295
|
+
OrgAction("connectors.manage"),
|
|
296
|
+
__param(0, CurrentPrincipal()),
|
|
297
|
+
__param(1, CurrentOrg()),
|
|
298
|
+
__param(2, Param("connectorId")),
|
|
299
|
+
__param(3, ZBody(ConnectorGuideAckBody))
|
|
300
|
+
], ConnectorsController.prototype, "ackGuide", null);
|
|
266
301
|
__decorate([
|
|
267
302
|
Put(":connectorId/credentials/org"),
|
|
268
303
|
HttpCode(204),
|
|
@@ -5,6 +5,10 @@ import type { RouteSchemas } from "../platform/route-schemas.js";
|
|
|
5
5
|
* echoed); this only bounds its size.
|
|
6
6
|
*/
|
|
7
7
|
export declare const CredentialValue: z.ZodRecord<z.ZodString, z.ZodUnknown>;
|
|
8
|
+
/** 「我已更新应用」: the admin drift hash of the connector template guide the person was shown. */
|
|
9
|
+
export declare const ConnectorGuideAckSchema: z.ZodObject<{
|
|
10
|
+
hash: z.ZodString;
|
|
11
|
+
}, z.core.$strict>;
|
|
8
12
|
/** `:connectorId` as the routes take it (`@Param`). */
|
|
9
13
|
export declare const ConnectorPathParams: z.ZodObject<{
|
|
10
14
|
connectorId: z.ZodString;
|
|
@@ -6,6 +6,10 @@ import { z } from "zod";
|
|
|
6
6
|
export const CredentialValue = z
|
|
7
7
|
.record(z.string().max(100), z.unknown())
|
|
8
8
|
.refine((v) => JSON.stringify(v).length <= 16_384, { message: "Credential is too large" });
|
|
9
|
+
/** 「我已更新应用」: the admin drift hash of the connector template guide the person was shown. */
|
|
10
|
+
export const ConnectorGuideAckSchema = z
|
|
11
|
+
.object({ hash: z.string().regex(/^[0-9a-f]{64}$/) })
|
|
12
|
+
.strict();
|
|
9
13
|
/** `:connectorId` as the routes take it (`@Param`). */
|
|
10
14
|
export const ConnectorPathParams = z.object({ connectorId: z.string().min(1) });
|
|
11
15
|
export const CONNECTOR_ROUTES = [
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import { type AuthHint, type ConnectorOperation, type ToolAnnotations } from "@capybara-hq/plugin-sdk";
|
|
2
|
+
import { type ConnectionGuideState, PluginGuides } from "../extensions/plugin-guides.js";
|
|
2
3
|
import { ExtensionRegistry } from "../extensions/registry.js";
|
|
3
4
|
import { KernelConfigService } from "../infra/config/kernel-config.service.js";
|
|
4
5
|
import { KernelDb } from "../infra/database/kernel-db.js";
|
|
5
6
|
import { TransactionHost } from "../infra/database/transaction-host.js";
|
|
6
7
|
import { OrgLock } from "../orgs/org-lock.js";
|
|
8
|
+
import type { OrgRole } from "../orgs/roles.js";
|
|
7
9
|
import { type ConnectorAuth, type CredentialOwner, Credentials, type StaticSecret } from "./credentials.js";
|
|
8
10
|
import { type DriftChanges, ToolDrift, type ToolIssue } from "./drift.js";
|
|
9
11
|
import { type OAuthClientRecord, type OAuthTokenRecord } from "./oauth/tokens.js";
|
|
@@ -212,11 +214,15 @@ export declare class ConnectorsService {
|
|
|
212
214
|
/** The org row lock (orgs/org-lock.ts): joins the caller's transaction. */
|
|
213
215
|
private readonly orgLock;
|
|
214
216
|
readonly txHost: TransactionHost;
|
|
217
|
+
/** Setup guides of connector templates (spec 2026-11-09-setup-guides-v2-design.md §11). */
|
|
218
|
+
private readonly guides;
|
|
215
219
|
constructor(kdb: KernelDb, registry: ToolRegistry,
|
|
216
220
|
/** Extension manifests (a slug may not take an extension's name). */
|
|
217
221
|
extensions: Pick<ExtensionRegistry, "manifests">, credentials: Credentials, config: Pick<KernelConfigService, "get">, drift: ToolDrift,
|
|
218
222
|
/** The org row lock (orgs/org-lock.ts): joins the caller's transaction. */
|
|
219
|
-
orgLock: Pick<OrgLock, "lockAndAuthorize">, txHost: TransactionHost
|
|
223
|
+
orgLock: Pick<OrgLock, "lockAndAuthorize">, txHost: TransactionHost,
|
|
224
|
+
/** Setup guides of connector templates (spec 2026-11-09-setup-guides-v2-design.md §11). */
|
|
225
|
+
guides: Pick<PluginGuides, "templateAck" | "connectorStates">);
|
|
220
226
|
/** The kernel-schema handle of the current context (its transaction, else the pool). */
|
|
221
227
|
private get db();
|
|
222
228
|
/**
|
|
@@ -305,7 +311,29 @@ export declare class ConnectorsService {
|
|
|
305
311
|
private row;
|
|
306
312
|
private summary;
|
|
307
313
|
private detail;
|
|
308
|
-
/**
|
|
314
|
+
/**
|
|
315
|
+
* The setup guide fields of the org's connectors (`setupGuide`, `guideDrift`; spec
|
|
316
|
+
* 2026-11-09-setup-guides-v2-design.md §9.3, §11): resolved through each connector's platform
|
|
317
|
+
* template to its item's guide for that template; custom connectors get nulls.
|
|
318
|
+
*/
|
|
319
|
+
guideStates(org: {
|
|
320
|
+
orgId: string;
|
|
321
|
+
role: OrgRole;
|
|
322
|
+
}, connectorIds: string[]): Promise<Map<string, ConnectionGuideState>>;
|
|
323
|
+
/**
|
|
324
|
+
* 「我已更新应用」 for a connector made from a template: records the template's current guide as
|
|
325
|
+
* the one it is set up with. `hash` is the admin drift hash of the guide the person was shown;
|
|
326
|
+
* when the plugin moved on since, 409 `connector.guide_changed` (show the new guide first). A
|
|
327
|
+
* connector without a template guide, or whose item the org's catalog does not show -> 404
|
|
328
|
+
* `catalog.guide_not_found`.
|
|
329
|
+
*/
|
|
330
|
+
ackGuide(org: {
|
|
331
|
+
orgId: string;
|
|
332
|
+
role: OrgRole;
|
|
333
|
+
}, connectorId: string, hash: string, actorAccountId: string, actorTokenId?: string): Promise<ConnectorDetail>;
|
|
334
|
+
/** The row and audit of {@link ackGuide}, in one transaction under the org lock. */
|
|
335
|
+
private ackIn;
|
|
336
|
+
/** The org's connectors that are not deleted (by creation time). */
|
|
309
337
|
list(orgId: string): Promise<ConnectorSummary[]>;
|
|
310
338
|
get(orgId: string, connectorId: string, viewerAccountId: string): Promise<ConnectorDetail>;
|
|
311
339
|
/** 改名、启停连接器、工具启停与默认策略、批量把无注解工具改为 allow(`connectors.manage`)。 */
|
|
@@ -15,6 +15,7 @@ import { z } from "zod";
|
|
|
15
15
|
import { connectorTemplateState } from "../catalog/component-state.js";
|
|
16
16
|
import { assertItemInstallable } from "../catalog/request-rules.js";
|
|
17
17
|
import { ApiError } from "../errors.js";
|
|
18
|
+
import { PluginGuides, } from "../extensions/plugin-guides.js";
|
|
18
19
|
import { ExtensionRegistry } from "../extensions/registry.js";
|
|
19
20
|
import { audit } from "../identity/audit.js";
|
|
20
21
|
import { assertCustomAllowed } from "../identity/settings.js";
|
|
@@ -93,11 +94,14 @@ let ConnectorsService = class ConnectorsService {
|
|
|
93
94
|
drift;
|
|
94
95
|
orgLock;
|
|
95
96
|
txHost;
|
|
97
|
+
guides;
|
|
96
98
|
constructor(kdb, registry,
|
|
97
99
|
/** Extension manifests (a slug may not take an extension's name). */
|
|
98
100
|
extensions, credentials, config, drift,
|
|
99
101
|
/** The org row lock (orgs/org-lock.ts): joins the caller's transaction. */
|
|
100
|
-
orgLock, txHost
|
|
102
|
+
orgLock, txHost,
|
|
103
|
+
/** Setup guides of connector templates (spec 2026-11-09-setup-guides-v2-design.md §11). */
|
|
104
|
+
guides) {
|
|
101
105
|
this.kdb = kdb;
|
|
102
106
|
this.registry = registry;
|
|
103
107
|
this.extensions = extensions;
|
|
@@ -106,6 +110,7 @@ let ConnectorsService = class ConnectorsService {
|
|
|
106
110
|
this.drift = drift;
|
|
107
111
|
this.orgLock = orgLock;
|
|
108
112
|
this.txHost = txHost;
|
|
113
|
+
this.guides = guides;
|
|
109
114
|
}
|
|
110
115
|
/** The kernel-schema handle of the current context (its transaction, else the pool). */
|
|
111
116
|
get db() {
|
|
@@ -366,6 +371,11 @@ let ConnectorsService = class ConnectorsService {
|
|
|
366
371
|
return this.credentials.injectionFor(id, auth, secret);
|
|
367
372
|
}, { orgId, slug });
|
|
368
373
|
}
|
|
374
|
+
// the guide the wizard and 补充配置 showed: the template's current one (spec §11)
|
|
375
|
+
const tpl = input.template;
|
|
376
|
+
const guideAck = tpl?.templateId !== undefined
|
|
377
|
+
? await this.guides.templateAck(tpl.itemId, tpl.templateId)
|
|
378
|
+
: null;
|
|
369
379
|
return this.createIn(orgId, actorAccountId, input, actorTokenId, {
|
|
370
380
|
id,
|
|
371
381
|
slug,
|
|
@@ -374,6 +384,7 @@ let ConnectorsService = class ConnectorsService {
|
|
|
374
384
|
secret,
|
|
375
385
|
oauth,
|
|
376
386
|
listed,
|
|
387
|
+
guideAck,
|
|
377
388
|
});
|
|
378
389
|
}
|
|
379
390
|
/** The org-locked, re-checked insert of {@link create} (the outbound checks ran before). */
|
|
@@ -434,6 +445,8 @@ let ConnectorsService = class ConnectorsService {
|
|
|
434
445
|
catalog_item_id: input.template?.itemId ?? null,
|
|
435
446
|
catalog_version: input.template?.version ?? null,
|
|
436
447
|
platform_template_id: platformTemplate?.id ?? null,
|
|
448
|
+
// linked to its template only through the platform component (drift is judged there)
|
|
449
|
+
guide_ack: platformTemplate !== undefined && c.guideAck !== null ? JSON.stringify(c.guideAck) : null,
|
|
437
450
|
// 授权码连接器安装时没有快照:模板的默认策略留到首个快照时应用
|
|
438
451
|
template_tool_policies: input.template?.defaultToolPolicies
|
|
439
452
|
? JSON.stringify(input.template.defaultToolPolicies)
|
|
@@ -826,7 +839,85 @@ let ConnectorsService = class ConnectorsService {
|
|
|
826
839
|
credential: { ownership: auth.type === "none" ? null : auth.ownership, status },
|
|
827
840
|
};
|
|
828
841
|
}
|
|
829
|
-
/**
|
|
842
|
+
/**
|
|
843
|
+
* The setup guide fields of the org's connectors (`setupGuide`, `guideDrift`; spec
|
|
844
|
+
* 2026-11-09-setup-guides-v2-design.md §9.3, §11): resolved through each connector's platform
|
|
845
|
+
* template to its item's guide for that template; custom connectors get nulls.
|
|
846
|
+
*/
|
|
847
|
+
async guideStates(org, connectorIds) {
|
|
848
|
+
if (connectorIds.length === 0)
|
|
849
|
+
return new Map();
|
|
850
|
+
const rows = await this.db
|
|
851
|
+
.selectFrom("connectors as c")
|
|
852
|
+
.leftJoin("platform_connector_templates as t", "t.id", "c.platform_template_id")
|
|
853
|
+
.select(["c.id", "c.guide_ack", "t.catalog_item_id", "t.template_id"])
|
|
854
|
+
.where("c.org_id", "=", org.orgId)
|
|
855
|
+
.where("c.id", "in", [...new Set(connectorIds)])
|
|
856
|
+
.where("c.deleted_at", "is", null)
|
|
857
|
+
.execute();
|
|
858
|
+
return this.guides.connectorStates(rows.map((r) => ({
|
|
859
|
+
id: r.id,
|
|
860
|
+
itemId: r.catalog_item_id ?? null,
|
|
861
|
+
templateId: r.template_id ?? null,
|
|
862
|
+
guideAck: r.guide_ack ?? null,
|
|
863
|
+
})), org);
|
|
864
|
+
}
|
|
865
|
+
/**
|
|
866
|
+
* 「我已更新应用」 for a connector made from a template: records the template's current guide as
|
|
867
|
+
* the one it is set up with. `hash` is the admin drift hash of the guide the person was shown;
|
|
868
|
+
* when the plugin moved on since, 409 `connector.guide_changed` (show the new guide first). A
|
|
869
|
+
* connector without a template guide, or whose item the org's catalog does not show -> 404
|
|
870
|
+
* `catalog.guide_not_found`.
|
|
871
|
+
*/
|
|
872
|
+
async ackGuide(org, connectorId, hash, actorAccountId, actorTokenId) {
|
|
873
|
+
const { orgId } = org;
|
|
874
|
+
const r = await this.db
|
|
875
|
+
.selectFrom("connectors as c")
|
|
876
|
+
.leftJoin("platform_connector_templates as t", "t.id", "c.platform_template_id")
|
|
877
|
+
.select(["c.id", "t.catalog_item_id", "t.template_id"])
|
|
878
|
+
.where("c.id", "=", connectorId)
|
|
879
|
+
.where("c.org_id", "=", orgId)
|
|
880
|
+
.where("c.deleted_at", "is", null)
|
|
881
|
+
.executeTakeFirst();
|
|
882
|
+
if (!r)
|
|
883
|
+
throw notFound();
|
|
884
|
+
const ack = r.catalog_item_id && r.template_id
|
|
885
|
+
? // the guide read's visibility: an item the org's catalog does not show has no guide
|
|
886
|
+
await this.guides.templateAck(r.catalog_item_id, r.template_id, org)
|
|
887
|
+
: null;
|
|
888
|
+
if (ack === null) {
|
|
889
|
+
throw new ApiError("catalog.guide_not_found", "The connector has no template setup guide");
|
|
890
|
+
}
|
|
891
|
+
if (ack.hash !== hash) {
|
|
892
|
+
throw new ApiError("connector.guide_changed", "The connector template's setup guide changed since it was shown; review the new guide first");
|
|
893
|
+
}
|
|
894
|
+
return this.ackIn(orgId, connectorId, ack, actorAccountId, actorTokenId);
|
|
895
|
+
}
|
|
896
|
+
/** The row and audit of {@link ackGuide}, in one transaction under the org lock. */
|
|
897
|
+
async ackIn(orgId, connectorId, ack, actorAccountId, actorTokenId) {
|
|
898
|
+
const t = this.db;
|
|
899
|
+
await this.orgLock.lockAndAuthorize(orgId, actorAccountId, "connectors.manage");
|
|
900
|
+
const updated = await t
|
|
901
|
+
.updateTable("connectors")
|
|
902
|
+
.set({ guide_ack: JSON.stringify(ack), updated_at: sql `now()` })
|
|
903
|
+
.where("id", "=", connectorId)
|
|
904
|
+
.where("org_id", "=", orgId)
|
|
905
|
+
.where("deleted_at", "is", null)
|
|
906
|
+
.returning("id")
|
|
907
|
+
.executeTakeFirst();
|
|
908
|
+
if (!updated)
|
|
909
|
+
throw notFound();
|
|
910
|
+
await audit(t, {
|
|
911
|
+
actorAccountId,
|
|
912
|
+
...(actorTokenId !== undefined && { actorTokenId }),
|
|
913
|
+
orgId,
|
|
914
|
+
action: "connector.guide_confirmed",
|
|
915
|
+
target: connectorId,
|
|
916
|
+
detail: { guideId: ack.guideId, version: ack.version },
|
|
917
|
+
});
|
|
918
|
+
return this.detail(t, orgId, connectorId, actorAccountId);
|
|
919
|
+
}
|
|
920
|
+
/** The org's connectors that are not deleted (by creation time). */
|
|
830
921
|
async list(orgId) {
|
|
831
922
|
const rows = await this.db
|
|
832
923
|
.selectFrom("connectors")
|
|
@@ -1236,6 +1327,9 @@ __decorate([
|
|
|
1236
1327
|
__decorate([
|
|
1237
1328
|
Transactional({ propagation: "mandatory" })
|
|
1238
1329
|
], ConnectorsService.prototype, "applyTemplateIn", null);
|
|
1330
|
+
__decorate([
|
|
1331
|
+
Transactional()
|
|
1332
|
+
], ConnectorsService.prototype, "ackIn", null);
|
|
1239
1333
|
__decorate([
|
|
1240
1334
|
Transactional()
|
|
1241
1335
|
], ConnectorsService.prototype, "update", null);
|
|
@@ -1269,6 +1363,7 @@ ConnectorsService = __decorate([
|
|
|
1269
1363
|
__param(4, Inject(KernelConfigService)),
|
|
1270
1364
|
__param(5, Inject(ToolDrift)),
|
|
1271
1365
|
__param(6, Inject(OrgLock)),
|
|
1272
|
-
__param(7, Inject(TransactionHost))
|
|
1366
|
+
__param(7, Inject(TransactionHost)),
|
|
1367
|
+
__param(8, Inject(PluginGuides))
|
|
1273
1368
|
], ConnectorsService);
|
|
1274
1369
|
export { ConnectorsService };
|