@cat-factory/node-server 0.238.2 → 0.240.0
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/config.d.ts.map +1 -1
- package/dist/config.js +2 -0
- package/dist/config.js.map +1 -1
- package/dist/container-core-deps.d.ts.map +1 -1
- package/dist/container-core-deps.js +30 -0
- package/dist/container-core-deps.js.map +1 -1
- package/dist/db/schema.d.ts +42 -5
- package/dist/db/schema.d.ts.map +1 -1
- package/dist/db/schema.js +14 -2
- package/dist/db/schema.js.map +1 -1
- package/dist/db/tables/identity.d.ts +142 -0
- package/dist/db/tables/identity.d.ts.map +1 -1
- package/dist/db/tables/identity.js +15 -1
- package/dist/db/tables/identity.js.map +1 -1
- package/dist/directoryWebhooks.d.ts +3 -0
- package/dist/directoryWebhooks.d.ts.map +1 -0
- package/dist/directoryWebhooks.js +20 -0
- package/dist/directoryWebhooks.js.map +1 -0
- package/dist/repositories/directoryFeed.d.ts +11 -8
- package/dist/repositories/directoryFeed.d.ts.map +1 -1
- package/dist/repositories/directoryFeed.js +23 -42
- package/dist/repositories/directoryFeed.js.map +1 -1
- package/dist/repositories/directoryRepository.d.ts +25 -0
- package/dist/repositories/directoryRepository.d.ts.map +1 -0
- package/dist/repositories/directoryRepository.js +263 -0
- package/dist/repositories/directoryRepository.js.map +1 -0
- package/dist/repositories/directoryWebhookRepository.d.ts +16 -0
- package/dist/repositories/directoryWebhookRepository.d.ts.map +1 -0
- package/dist/repositories/directoryWebhookRepository.js +116 -0
- package/dist/repositories/directoryWebhookRepository.js.map +1 -0
- package/dist/repositories/drizzle/accounts.d.ts +4 -1
- package/dist/repositories/drizzle/accounts.d.ts.map +1 -1
- package/dist/repositories/drizzle/accounts.js +22 -4
- package/dist/repositories/drizzle/accounts.js.map +1 -1
- package/dist/repositories/drizzle.d.ts +2 -0
- package/dist/repositories/drizzle.d.ts.map +1 -1
- package/dist/repositories/drizzle.js +2 -0
- package/dist/repositories/drizzle.js.map +1 -1
- package/dist/repositories/publicApiKey.d.ts +6 -3
- package/dist/repositories/publicApiKey.d.ts.map +1 -1
- package/dist/repositories/publicApiKey.js +66 -28
- package/dist/repositories/publicApiKey.js.map +1 -1
- package/dist/retention.d.ts +3 -1
- package/dist/retention.d.ts.map +1 -1
- package/dist/retention.js +1 -0
- package/dist/retention.js.map +1 -1
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +6 -0
- package/dist/server.js.map +1 -1
- package/drizzle/20261009143721_public_api_key_workspaces/migration.sql +11 -0
- package/drizzle/20261009143721_public_api_key_workspaces/snapshot.json +23844 -0
- package/drizzle/20261009143724_public_api_key_drop_workspace/migration.sql +2 -0
- package/drizzle/20261009143724_public_api_key_drop_workspace/snapshot.json +23810 -0
- package/drizzle/20261010113115_directory_webhooks/migration.sql +12 -0
- package/drizzle/20261010113115_directory_webhooks/snapshot.json +23944 -0
- package/package.json +21 -21
|
@@ -1,7 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
import { directoryChanges } from '../db/schema.js';
|
|
4
|
-
// The append half of the directory change feed (docs/initiatives/directory-sync.md), shared by
|
|
1
|
+
import { sql } from 'drizzle-orm';
|
|
2
|
+
// The append half of the directory change feed (backend/docs/adr/0067-directory-sync.md), shared by
|
|
5
3
|
// every repository that writes a directory entity. Mirror of the Cloudflare facade's
|
|
6
4
|
// `directoryFeed.ts`; the conformance suite `defineDirectoryFeedSuite` holds the two to one
|
|
7
5
|
// behaviour.
|
|
@@ -11,6 +9,16 @@ import { directoryChanges } from '../db/schema.js';
|
|
|
11
9
|
// write, and one that reads the rows it creates must run AFTER, both inside the same transaction.
|
|
12
10
|
/** Advisory-lock class for the feed; the second key is the account. Distinct from every other class. */
|
|
13
11
|
const DIRECTORY_FEED_LOCK_CLASS = 0x6469_7263;
|
|
12
|
+
/** Advisory-lock class for re-linking one login identity; the second key is `provider:subject`. */
|
|
13
|
+
const IDENTITY_LINK_LOCK_CLASS = 0x6964_6c6b;
|
|
14
|
+
/**
|
|
15
|
+
* Serialize the links of one login identity. Taken before the feed lock, it keeps the owner that
|
|
16
|
+
* `identityOwnerSource` reads at lock time the owner it reads again when appending, so two re-links
|
|
17
|
+
* of the same subject cannot lock one set of accounts and then append to another.
|
|
18
|
+
*/
|
|
19
|
+
export async function lockIdentityLink(tx, provider, subject) {
|
|
20
|
+
await tx.execute(sql `SELECT pg_advisory_xact_lock(${IDENTITY_LINK_LOCK_CLASS}, hashtext(${`${provider}:${subject}`}))`);
|
|
21
|
+
}
|
|
14
22
|
/**
|
|
15
23
|
* Take the feed's per-account advisory lock for every account the source names. It is held until
|
|
16
24
|
* the enclosing transaction ends, so a second appender for the same account cannot compute its
|
|
@@ -67,6 +75,17 @@ export function userSource(userId) {
|
|
|
67
75
|
user_id AS entity_id
|
|
68
76
|
FROM memberships WHERE user_id = ${text(userId)}`;
|
|
69
77
|
}
|
|
78
|
+
/**
|
|
79
|
+
* The user a login identity belongs to now, once per account, unless that is `exceptUserId`.
|
|
80
|
+
* Appended before a re-link so the user losing the account is recorded while the row still names them.
|
|
81
|
+
*/
|
|
82
|
+
export function identityOwnerSource(provider, subject, exceptUserId) {
|
|
83
|
+
return sql `SELECT m.account_id, CAST('user' AS TEXT) AS entity_type, CAST(NULL AS TEXT) AS workspace_id,
|
|
84
|
+
m.user_id AS entity_id
|
|
85
|
+
FROM memberships m JOIN user_identities i ON i.user_id = m.user_id
|
|
86
|
+
WHERE i.provider = ${text(provider)} AND i.subject = ${text(subject)}
|
|
87
|
+
AND i.user_id <> ${text(exceptUserId)}`;
|
|
88
|
+
}
|
|
70
89
|
/** An account membership, plus the member's `user` entity whose visibility it decides. */
|
|
71
90
|
export function accountMembershipSource(accountId, userId) {
|
|
72
91
|
return sql `SELECT ${text(accountId)} AS account_id, CAST('account_membership' AS TEXT) AS entity_type,
|
|
@@ -115,42 +134,4 @@ export function reposSource(workspaceId, githubIds) {
|
|
|
115
134
|
WHERE r.workspace_id = ${text(workspaceId)}
|
|
116
135
|
AND r.github_id IN (${sql.join(githubIds.map((id) => sql `${id}`), sql `, `)})`;
|
|
117
136
|
}
|
|
118
|
-
function rowToChange(row) {
|
|
119
|
-
// The vocabulary is append-only, so an unknown value is a row this build cannot interpret, not
|
|
120
|
-
// one to skip: a reader that dropped it would advance its cursor past a change it never served.
|
|
121
|
-
if (!isDirectoryEntityType(row.entity_type)) {
|
|
122
|
-
throw new Error(`Unknown directory entity type '${row.entity_type}' at seq ${row.seq}`);
|
|
123
|
-
}
|
|
124
|
-
return {
|
|
125
|
-
accountId: row.account_id,
|
|
126
|
-
seq: row.seq,
|
|
127
|
-
entityType: row.entity_type,
|
|
128
|
-
workspaceId: row.workspace_id,
|
|
129
|
-
entityId: row.entity_id,
|
|
130
|
-
at: row.at,
|
|
131
|
-
};
|
|
132
|
-
}
|
|
133
|
-
/** Postgres read side of the directory change feed. */
|
|
134
|
-
export class DrizzleDirectoryChangeRepository {
|
|
135
|
-
db;
|
|
136
|
-
constructor(db) {
|
|
137
|
-
this.db = db;
|
|
138
|
-
}
|
|
139
|
-
async listAfter(accountId, afterSeq, limit) {
|
|
140
|
-
const rows = await this.db
|
|
141
|
-
.select()
|
|
142
|
-
.from(directoryChanges)
|
|
143
|
-
.where(and(eq(directoryChanges.account_id, accountId), gt(directoryChanges.seq, afterSeq)))
|
|
144
|
-
.orderBy(asc(directoryChanges.seq))
|
|
145
|
-
.limit(limit);
|
|
146
|
-
return rows.map(rowToChange);
|
|
147
|
-
}
|
|
148
|
-
async headSeq(accountId) {
|
|
149
|
-
const [row] = await this.db
|
|
150
|
-
.select({ head: max(directoryChanges.seq) })
|
|
151
|
-
.from(directoryChanges)
|
|
152
|
-
.where(eq(directoryChanges.account_id, accountId));
|
|
153
|
-
return row?.head ?? 0;
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
137
|
//# sourceMappingURL=directoryFeed.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"directoryFeed.js","sourceRoot":"","sources":["../../src/repositories/directoryFeed.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"directoryFeed.js","sourceRoot":"","sources":["../../src/repositories/directoryFeed.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { type DirectoryAccountMembership, type DirectoryRepo, type DirectoryUser, type DirectoryWorkspace, type DirectoryWorkspaceMembership } from '@cat-factory/contracts';
|
|
2
|
+
import { type DirectoryChangeRecord, type DirectoryRepoKey, type DirectoryRepository, type WorkspaceMembershipKey } from '@cat-factory/kernel';
|
|
3
|
+
import type { DrizzleDb } from '../db/client.js';
|
|
4
|
+
export declare class DrizzleDirectoryRepository implements DirectoryRepository {
|
|
5
|
+
private readonly db;
|
|
6
|
+
constructor(db: DrizzleDb);
|
|
7
|
+
listChanges(accountId: string, afterSeq: number, upToSeq: number, limit: number, workspaceIds: string[] | null): Promise<DirectoryChangeRecord[]>;
|
|
8
|
+
headSeq(accountId: string): Promise<number>;
|
|
9
|
+
headSeqs(accountIds: string[]): Promise<Map<string, number>>;
|
|
10
|
+
oldestSeq(accountId: string): Promise<number | null>;
|
|
11
|
+
pruneChanges(before: number): Promise<number>;
|
|
12
|
+
listWorkspaces(accountId: string, after: string | null, limit: number, workspaceIds: string[] | null): Promise<DirectoryWorkspace[]>;
|
|
13
|
+
listUsers(accountId: string, after: string | null, limit: number): Promise<DirectoryUser[]>;
|
|
14
|
+
/** One read for the host-account identities of these users, whatever their number. */
|
|
15
|
+
private hostIdentitiesOf;
|
|
16
|
+
listAccountMemberships(accountId: string, after: string | null, limit: number): Promise<DirectoryAccountMembership[]>;
|
|
17
|
+
listWorkspaceMemberships(accountId: string, after: WorkspaceMembershipKey | null, limit: number, workspaceIds: string[] | null): Promise<DirectoryWorkspaceMembership[]>;
|
|
18
|
+
listRepos(accountId: string, after: DirectoryRepoKey | null, limit: number, workspaceIds: string[] | null): Promise<DirectoryRepo[]>;
|
|
19
|
+
getWorkspaces(accountId: string, ids: string[]): Promise<DirectoryWorkspace[]>;
|
|
20
|
+
getUsers(accountId: string, ids: string[]): Promise<DirectoryUser[]>;
|
|
21
|
+
getAccountMemberships(accountId: string, userIds: string[]): Promise<DirectoryAccountMembership[]>;
|
|
22
|
+
getWorkspaceMemberships(accountId: string, keys: WorkspaceMembershipKey[]): Promise<DirectoryWorkspaceMembership[]>;
|
|
23
|
+
getRepos(accountId: string, keys: DirectoryRepoKey[]): Promise<DirectoryRepo[]>;
|
|
24
|
+
}
|
|
25
|
+
//# sourceMappingURL=directoryRepository.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"directoryRepository.d.ts","sourceRoot":"","sources":["../../src/repositories/directoryRepository.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
import {} from '@cat-factory/contracts';
|
|
2
|
+
import { DIRECTORY_ACCOUNT_PROVIDERS, directoryChangeFromRow, WORKSPACE_DIRECTORY_ENTITY_TYPES, withDirectoryAccounts, } from '@cat-factory/kernel';
|
|
3
|
+
import { and, asc, eq, gt, inArray, isNull, lte, max, min, or, sql } from 'drizzle-orm';
|
|
4
|
+
import { directoryChanges, githubRepos, memberships, userIdentities, users, workspaceMembers, workspaces, } from '../db/schema.js';
|
|
5
|
+
import { parseRoles } from './drizzle/accounts.js';
|
|
6
|
+
// The directory read side over Postgres (backend/docs/adr/0067-directory-sync.md). Mirror of the
|
|
7
|
+
// Cloudflare facade's `D1DirectoryRepository`; `defineDirectoryFeedSuite` and
|
|
8
|
+
// `defineDirectoryReadSuite` hold the two to one behaviour. The feed rows themselves are appended
|
|
9
|
+
// by the writing repositories through `directoryFeed.ts`.
|
|
10
|
+
const workspaceColumns = {
|
|
11
|
+
id: workspaces.id,
|
|
12
|
+
name: workspaces.name,
|
|
13
|
+
description: workspaces.description,
|
|
14
|
+
accessMode: workspaces.access_mode,
|
|
15
|
+
};
|
|
16
|
+
function toWorkspace(row) {
|
|
17
|
+
return {
|
|
18
|
+
id: row.id,
|
|
19
|
+
name: row.name,
|
|
20
|
+
description: row.description,
|
|
21
|
+
accessMode: row.accessMode === 'restricted' ? 'restricted' : 'account',
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
const userColumns = {
|
|
25
|
+
id: users.id,
|
|
26
|
+
name: users.name,
|
|
27
|
+
email: users.email,
|
|
28
|
+
avatarUrl: users.avatar_url,
|
|
29
|
+
};
|
|
30
|
+
const accountMembershipColumns = {
|
|
31
|
+
userId: memberships.user_id,
|
|
32
|
+
roles: memberships.roles,
|
|
33
|
+
createdAt: memberships.created_at,
|
|
34
|
+
};
|
|
35
|
+
function toAccountMembership(row) {
|
|
36
|
+
return { userId: row.userId, roles: parseRoles(row.roles), createdAt: row.createdAt };
|
|
37
|
+
}
|
|
38
|
+
const workspaceMembershipColumns = {
|
|
39
|
+
workspaceId: workspaceMembers.workspace_id,
|
|
40
|
+
userId: workspaceMembers.user_id,
|
|
41
|
+
role: workspaceMembers.role,
|
|
42
|
+
createdAt: workspaceMembers.created_at,
|
|
43
|
+
};
|
|
44
|
+
function toWorkspaceMembership(row) {
|
|
45
|
+
return {
|
|
46
|
+
workspaceId: row.workspaceId,
|
|
47
|
+
userId: row.userId,
|
|
48
|
+
role: row.role === 'admin' || row.role === 'member' ? row.role : 'viewer',
|
|
49
|
+
createdAt: row.createdAt,
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
const repoColumns = {
|
|
53
|
+
workspaceId: githubRepos.workspace_id,
|
|
54
|
+
repoId: githubRepos.github_id,
|
|
55
|
+
provider: githubRepos.provider,
|
|
56
|
+
owner: githubRepos.owner,
|
|
57
|
+
name: githubRepos.name,
|
|
58
|
+
defaultBranch: githubRepos.default_branch,
|
|
59
|
+
private: githubRepos.private,
|
|
60
|
+
isMonorepo: githubRepos.is_monorepo,
|
|
61
|
+
};
|
|
62
|
+
function toRepo(row) {
|
|
63
|
+
return {
|
|
64
|
+
workspaceId: row.workspaceId,
|
|
65
|
+
repoId: row.repoId,
|
|
66
|
+
provider: row.provider === 'gitlab' ? 'gitlab' : 'github',
|
|
67
|
+
owner: row.owner,
|
|
68
|
+
name: row.name,
|
|
69
|
+
defaultBranch: row.defaultBranch,
|
|
70
|
+
private: row.private === 1,
|
|
71
|
+
monorepo: row.isMonorepo === 1,
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
/** A workspace reach as a predicate on `column`, or none for an unrestricted key. */
|
|
75
|
+
function reach(column, workspaceIds) {
|
|
76
|
+
if (workspaceIds === null)
|
|
77
|
+
return undefined;
|
|
78
|
+
if (workspaceIds.length === 0)
|
|
79
|
+
return sql `false`;
|
|
80
|
+
return inArray(column, workspaceIds);
|
|
81
|
+
}
|
|
82
|
+
export class DrizzleDirectoryRepository {
|
|
83
|
+
db;
|
|
84
|
+
constructor(db) {
|
|
85
|
+
this.db = db;
|
|
86
|
+
}
|
|
87
|
+
async listChanges(accountId, afterSeq, upToSeq, limit, workspaceIds) {
|
|
88
|
+
const rows = await this.db
|
|
89
|
+
.select()
|
|
90
|
+
.from(directoryChanges)
|
|
91
|
+
.where(and(eq(directoryChanges.account_id, accountId), gt(directoryChanges.seq, afterSeq), lte(directoryChanges.seq, upToSeq),
|
|
92
|
+
// A restricted key reads its workspaces' changes plus the deletion of ANY workspace that
|
|
93
|
+
// no longer exists: deleting a board also drops the key's grant on it, so without the
|
|
94
|
+
// second arm the key would never learn that a board it mirrored is gone.
|
|
95
|
+
workspaceIds === null
|
|
96
|
+
? undefined
|
|
97
|
+
: or(and(inArray(directoryChanges.entity_type, [...WORKSPACE_DIRECTORY_ENTITY_TYPES]), reach(directoryChanges.workspace_id, workspaceIds)), and(eq(directoryChanges.entity_type, 'workspace'), sql `NOT EXISTS (SELECT 1 FROM ${workspaces} WHERE ${workspaces.id} = ${directoryChanges.entity_id})`))))
|
|
98
|
+
.orderBy(asc(directoryChanges.seq))
|
|
99
|
+
.limit(limit);
|
|
100
|
+
return rows.map(directoryChangeFromRow);
|
|
101
|
+
}
|
|
102
|
+
async headSeq(accountId) {
|
|
103
|
+
const [row] = await this.db
|
|
104
|
+
.select({ head: max(directoryChanges.seq) })
|
|
105
|
+
.from(directoryChanges)
|
|
106
|
+
.where(eq(directoryChanges.account_id, accountId));
|
|
107
|
+
return row?.head ?? 0;
|
|
108
|
+
}
|
|
109
|
+
async headSeqs(accountIds) {
|
|
110
|
+
const heads = new Map(accountIds.map((id) => [id, 0]));
|
|
111
|
+
if (accountIds.length === 0)
|
|
112
|
+
return heads;
|
|
113
|
+
const rows = await this.db
|
|
114
|
+
.select({ accountId: directoryChanges.account_id, head: max(directoryChanges.seq) })
|
|
115
|
+
.from(directoryChanges)
|
|
116
|
+
.where(inArray(directoryChanges.account_id, accountIds))
|
|
117
|
+
.groupBy(directoryChanges.account_id);
|
|
118
|
+
for (const row of rows)
|
|
119
|
+
heads.set(row.accountId, row.head ?? 0);
|
|
120
|
+
return heads;
|
|
121
|
+
}
|
|
122
|
+
async oldestSeq(accountId) {
|
|
123
|
+
const [row] = await this.db
|
|
124
|
+
.select({ oldest: min(directoryChanges.seq) })
|
|
125
|
+
.from(directoryChanges)
|
|
126
|
+
.where(eq(directoryChanges.account_id, accountId));
|
|
127
|
+
return row?.oldest ?? null;
|
|
128
|
+
}
|
|
129
|
+
async pruneChanges(before) {
|
|
130
|
+
// A `seq` PREFIX per account, up to its newest row older than the cutoff. `at` is not monotonic
|
|
131
|
+
// in `seq` (a writer stamps it before taking the feed's advisory lock), so deleting by `at`
|
|
132
|
+
// alone could remove a row while keeping an older-`seq` one, leaving a hole behind the oldest
|
|
133
|
+
// row that the reader's expiry check cannot see.
|
|
134
|
+
const result = await this.db.execute(sql `
|
|
135
|
+
WITH cut AS (SELECT account_id, MAX(seq) AS upto FROM directory_changes
|
|
136
|
+
WHERE at < ${before} GROUP BY account_id)
|
|
137
|
+
DELETE FROM directory_changes c USING cut
|
|
138
|
+
WHERE c.account_id = cut.account_id
|
|
139
|
+
AND c.seq <= cut.upto
|
|
140
|
+
AND c.seq < (SELECT MAX(d.seq) FROM directory_changes d
|
|
141
|
+
WHERE d.account_id = c.account_id)`);
|
|
142
|
+
return result.rowCount ?? 0;
|
|
143
|
+
}
|
|
144
|
+
async listWorkspaces(accountId, after, limit, workspaceIds) {
|
|
145
|
+
const rows = await this.db
|
|
146
|
+
.select(workspaceColumns)
|
|
147
|
+
.from(workspaces)
|
|
148
|
+
.where(and(eq(workspaces.account_id, accountId), gt(workspaces.id, after ?? ''), reach(workspaces.id, workspaceIds)))
|
|
149
|
+
.orderBy(asc(workspaces.id))
|
|
150
|
+
.limit(limit);
|
|
151
|
+
return rows.map(toWorkspace);
|
|
152
|
+
}
|
|
153
|
+
async listUsers(accountId, after, limit) {
|
|
154
|
+
const rows = await this.db
|
|
155
|
+
.select(userColumns)
|
|
156
|
+
.from(users)
|
|
157
|
+
.innerJoin(memberships, eq(memberships.user_id, users.id))
|
|
158
|
+
.where(and(eq(memberships.account_id, accountId), gt(users.id, after ?? '')))
|
|
159
|
+
.orderBy(asc(users.id))
|
|
160
|
+
.limit(limit);
|
|
161
|
+
return withDirectoryAccounts(rows, await this.hostIdentitiesOf(rows.map((row) => row.id)));
|
|
162
|
+
}
|
|
163
|
+
/** One read for the host-account identities of these users, whatever their number. */
|
|
164
|
+
async hostIdentitiesOf(userIds) {
|
|
165
|
+
if (userIds.length === 0)
|
|
166
|
+
return [];
|
|
167
|
+
return this.db
|
|
168
|
+
.select({
|
|
169
|
+
user_id: userIdentities.user_id,
|
|
170
|
+
provider: userIdentities.provider,
|
|
171
|
+
subject: userIdentities.subject,
|
|
172
|
+
metadata: userIdentities.metadata,
|
|
173
|
+
})
|
|
174
|
+
.from(userIdentities)
|
|
175
|
+
.where(and(inArray(userIdentities.user_id, userIds), inArray(userIdentities.provider, [...DIRECTORY_ACCOUNT_PROVIDERS])));
|
|
176
|
+
}
|
|
177
|
+
async listAccountMemberships(accountId, after, limit) {
|
|
178
|
+
const rows = await this.db
|
|
179
|
+
.select(accountMembershipColumns)
|
|
180
|
+
.from(memberships)
|
|
181
|
+
.where(and(eq(memberships.account_id, accountId), gt(memberships.user_id, after ?? '')))
|
|
182
|
+
.orderBy(asc(memberships.user_id))
|
|
183
|
+
.limit(limit);
|
|
184
|
+
return rows.map(toAccountMembership);
|
|
185
|
+
}
|
|
186
|
+
async listWorkspaceMemberships(accountId, after, limit, workspaceIds) {
|
|
187
|
+
const rows = await this.db
|
|
188
|
+
.select(workspaceMembershipColumns)
|
|
189
|
+
.from(workspaceMembers)
|
|
190
|
+
.innerJoin(workspaces, eq(workspaces.id, workspaceMembers.workspace_id))
|
|
191
|
+
.where(and(eq(workspaces.account_id, accountId), after === null
|
|
192
|
+
? undefined
|
|
193
|
+
: or(gt(workspaceMembers.workspace_id, after.workspaceId), and(eq(workspaceMembers.workspace_id, after.workspaceId), gt(workspaceMembers.user_id, after.userId))), reach(workspaceMembers.workspace_id, workspaceIds)))
|
|
194
|
+
.orderBy(asc(workspaceMembers.workspace_id), asc(workspaceMembers.user_id))
|
|
195
|
+
.limit(limit);
|
|
196
|
+
return rows.map(toWorkspaceMembership);
|
|
197
|
+
}
|
|
198
|
+
async listRepos(accountId, after, limit, workspaceIds) {
|
|
199
|
+
const rows = await this.db
|
|
200
|
+
.select(repoColumns)
|
|
201
|
+
.from(githubRepos)
|
|
202
|
+
.innerJoin(workspaces, eq(workspaces.id, githubRepos.workspace_id))
|
|
203
|
+
.where(and(eq(workspaces.account_id, accountId), isNull(githubRepos.deleted_at), after === null
|
|
204
|
+
? undefined
|
|
205
|
+
: or(gt(githubRepos.workspace_id, after.workspaceId), and(eq(githubRepos.workspace_id, after.workspaceId), gt(githubRepos.github_id, after.repoId))), reach(githubRepos.workspace_id, workspaceIds)))
|
|
206
|
+
.orderBy(asc(githubRepos.workspace_id), asc(githubRepos.github_id))
|
|
207
|
+
.limit(limit);
|
|
208
|
+
return rows.map(toRepo);
|
|
209
|
+
}
|
|
210
|
+
async getWorkspaces(accountId, ids) {
|
|
211
|
+
if (ids.length === 0)
|
|
212
|
+
return [];
|
|
213
|
+
const rows = await this.db
|
|
214
|
+
.select(workspaceColumns)
|
|
215
|
+
.from(workspaces)
|
|
216
|
+
.where(and(eq(workspaces.account_id, accountId), inArray(workspaces.id, ids)));
|
|
217
|
+
return rows.map(toWorkspace);
|
|
218
|
+
}
|
|
219
|
+
async getUsers(accountId, ids) {
|
|
220
|
+
if (ids.length === 0)
|
|
221
|
+
return [];
|
|
222
|
+
// The ids are known up front, so the identities are read beside the users rather than after.
|
|
223
|
+
const [rows, identities] = await Promise.all([
|
|
224
|
+
this.db
|
|
225
|
+
.select(userColumns)
|
|
226
|
+
.from(users)
|
|
227
|
+
.innerJoin(memberships, eq(memberships.user_id, users.id))
|
|
228
|
+
.where(and(eq(memberships.account_id, accountId), inArray(users.id, ids))),
|
|
229
|
+
this.hostIdentitiesOf(ids),
|
|
230
|
+
]);
|
|
231
|
+
return withDirectoryAccounts(rows, identities);
|
|
232
|
+
}
|
|
233
|
+
async getAccountMemberships(accountId, userIds) {
|
|
234
|
+
if (userIds.length === 0)
|
|
235
|
+
return [];
|
|
236
|
+
const rows = await this.db
|
|
237
|
+
.select(accountMembershipColumns)
|
|
238
|
+
.from(memberships)
|
|
239
|
+
.where(and(eq(memberships.account_id, accountId), inArray(memberships.user_id, userIds)));
|
|
240
|
+
return rows.map(toAccountMembership);
|
|
241
|
+
}
|
|
242
|
+
async getWorkspaceMemberships(accountId, keys) {
|
|
243
|
+
if (keys.length === 0)
|
|
244
|
+
return [];
|
|
245
|
+
const rows = await this.db
|
|
246
|
+
.select(workspaceMembershipColumns)
|
|
247
|
+
.from(workspaceMembers)
|
|
248
|
+
.innerJoin(workspaces, eq(workspaces.id, workspaceMembers.workspace_id))
|
|
249
|
+
.where(and(eq(workspaces.account_id, accountId), or(...keys.map((k) => and(eq(workspaceMembers.workspace_id, k.workspaceId), eq(workspaceMembers.user_id, k.userId))))));
|
|
250
|
+
return rows.map(toWorkspaceMembership);
|
|
251
|
+
}
|
|
252
|
+
async getRepos(accountId, keys) {
|
|
253
|
+
if (keys.length === 0)
|
|
254
|
+
return [];
|
|
255
|
+
const rows = await this.db
|
|
256
|
+
.select(repoColumns)
|
|
257
|
+
.from(githubRepos)
|
|
258
|
+
.innerJoin(workspaces, eq(workspaces.id, githubRepos.workspace_id))
|
|
259
|
+
.where(and(eq(workspaces.account_id, accountId), isNull(githubRepos.deleted_at), or(...keys.map((k) => and(eq(githubRepos.workspace_id, k.workspaceId), eq(githubRepos.github_id, k.repoId))))));
|
|
260
|
+
return rows.map(toRepo);
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
//# sourceMappingURL=directoryRepository.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"directoryRepository.js","sourceRoot":"","sources":["../../src/repositories/directoryRepository.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { DirectoryWebhookRecord, DirectoryWebhookRepository } from '@cat-factory/kernel';
|
|
2
|
+
import type { DrizzleDb } from '../db/client.js';
|
|
3
|
+
export declare class DrizzleDirectoryWebhookRepository implements DirectoryWebhookRepository {
|
|
4
|
+
private readonly db;
|
|
5
|
+
constructor(db: DrizzleDb);
|
|
6
|
+
list(accountId: string): Promise<DirectoryWebhookRecord[]>;
|
|
7
|
+
get(accountId: string, id: string): Promise<DirectoryWebhookRecord | null>;
|
|
8
|
+
put(record: DirectoryWebhookRecord, limit: number): Promise<'stored' | 'limit_reached'>;
|
|
9
|
+
delete(accountId: string, id: string): Promise<void>;
|
|
10
|
+
listEnabled(): Promise<DirectoryWebhookRecord[]>;
|
|
11
|
+
claim(accountId: string, id: string, token: string, now: number, leaseUntil: number): Promise<number | null>;
|
|
12
|
+
complete(accountId: string, id: string, token: string, toSeq: number): Promise<boolean>;
|
|
13
|
+
release(accountId: string, id: string, token: string): Promise<void>;
|
|
14
|
+
private heldBy;
|
|
15
|
+
}
|
|
16
|
+
//# sourceMappingURL=directoryWebhookRepository.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"directoryWebhookRepository.d.ts","sourceRoot":"","sources":["../../src/repositories/directoryWebhookRepository.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,sBAAsB,EAAE,0BAA0B,EAAE,MAAM,qBAAqB,CAAA;AAE7F,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAA;AAuBhD,qBAAa,iCAAkC,YAAW,0BAA0B;IACtE,OAAO,CAAC,QAAQ,CAAC,EAAE;IAA/B,YAA6B,EAAE,EAAE,SAAS,EAAI;IAExC,IAAI,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,sBAAsB,EAAE,CAAC,CAO/D;IAEK,GAAG,CAAC,SAAS,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,sBAAsB,GAAG,IAAI,CAAC,CAM/E;IAEK,GAAG,CAAC,MAAM,EAAE,sBAAsB,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,GAAG,eAAe,CAAC,CA4C5F;IAEK,MAAM,CAAC,SAAS,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAIzD;IAEK,WAAW,IAAI,OAAO,CAAC,sBAAsB,EAAE,CAAC,CAOrD;IAEK,KAAK,CACT,SAAS,EAAE,MAAM,EACjB,EAAE,EAAE,MAAM,EACV,KAAK,EAAE,MAAM,EACb,GAAG,EAAE,MAAM,EACX,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAcxB;IAEK,QAAQ,CAAC,SAAS,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAO5F;IAEK,OAAO,CAAC,SAAS,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAKzE;IAED,OAAO,CAAC,MAAM;CAOf"}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import { and, asc, count, eq, isNull, lte, or, sql } from 'drizzle-orm';
|
|
2
|
+
import { directoryWebhooks } from '../db/schema.js';
|
|
3
|
+
// Directory webhook endpoints over Postgres. Mirror of the Cloudflare facade's
|
|
4
|
+
// `D1DirectoryWebhookRepository`; `defineDirectoryWebhookSuite` holds the two to one behaviour.
|
|
5
|
+
/** Advisory-lock class for the per-account endpoint cap; the second key is the account. */
|
|
6
|
+
const DIRECTORY_WEBHOOK_CAP_LOCK_CLASS = 0x6477_6863;
|
|
7
|
+
function toRecord(row) {
|
|
8
|
+
return {
|
|
9
|
+
accountId: row.account_id,
|
|
10
|
+
id: row.id,
|
|
11
|
+
url: row.url,
|
|
12
|
+
enabled: row.enabled === 1,
|
|
13
|
+
secretSealed: row.secret_sealed,
|
|
14
|
+
deliveredSeq: row.delivered_seq,
|
|
15
|
+
updatedAt: row.updated_at,
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
export class DrizzleDirectoryWebhookRepository {
|
|
19
|
+
db;
|
|
20
|
+
constructor(db) {
|
|
21
|
+
this.db = db;
|
|
22
|
+
}
|
|
23
|
+
async list(accountId) {
|
|
24
|
+
const rows = await this.db
|
|
25
|
+
.select()
|
|
26
|
+
.from(directoryWebhooks)
|
|
27
|
+
.where(eq(directoryWebhooks.account_id, accountId))
|
|
28
|
+
.orderBy(asc(directoryWebhooks.id));
|
|
29
|
+
return rows.map(toRecord);
|
|
30
|
+
}
|
|
31
|
+
async get(accountId, id) {
|
|
32
|
+
const [row] = await this.db
|
|
33
|
+
.select()
|
|
34
|
+
.from(directoryWebhooks)
|
|
35
|
+
.where(and(eq(directoryWebhooks.account_id, accountId), eq(directoryWebhooks.id, id)));
|
|
36
|
+
return row ? toRecord(row) : null;
|
|
37
|
+
}
|
|
38
|
+
async put(record, limit) {
|
|
39
|
+
// At READ COMMITTED there is no row yet to lock, so two registrations counting nine would both
|
|
40
|
+
// insert. Writes for one account are serialized on a transaction-scoped advisory lock instead,
|
|
41
|
+
// the same shape the notification webhook cap uses.
|
|
42
|
+
return await this.db.transaction(async (tx) => {
|
|
43
|
+
await tx.execute(sql `SELECT pg_advisory_xact_lock(${DIRECTORY_WEBHOOK_CAP_LOCK_CLASS}, hashtext(${record.accountId}))`);
|
|
44
|
+
const [existing] = await tx
|
|
45
|
+
.select({ id: directoryWebhooks.id })
|
|
46
|
+
.from(directoryWebhooks)
|
|
47
|
+
.where(and(eq(directoryWebhooks.account_id, record.accountId), eq(directoryWebhooks.id, record.id)));
|
|
48
|
+
if (!existing) {
|
|
49
|
+
const [counted] = await tx
|
|
50
|
+
.select({ total: count() })
|
|
51
|
+
.from(directoryWebhooks)
|
|
52
|
+
.where(eq(directoryWebhooks.account_id, record.accountId));
|
|
53
|
+
if ((counted?.total ?? 0) >= limit)
|
|
54
|
+
return 'limit_reached';
|
|
55
|
+
}
|
|
56
|
+
const editable = {
|
|
57
|
+
url: record.url,
|
|
58
|
+
enabled: record.enabled ? 1 : 0,
|
|
59
|
+
secret_sealed: record.secretSealed,
|
|
60
|
+
updated_at: record.updatedAt,
|
|
61
|
+
};
|
|
62
|
+
await tx
|
|
63
|
+
.insert(directoryWebhooks)
|
|
64
|
+
.values({
|
|
65
|
+
account_id: record.accountId,
|
|
66
|
+
id: record.id,
|
|
67
|
+
delivered_seq: record.deliveredSeq,
|
|
68
|
+
...editable,
|
|
69
|
+
})
|
|
70
|
+
.onConflictDoUpdate({
|
|
71
|
+
target: [directoryWebhooks.account_id, directoryWebhooks.id],
|
|
72
|
+
set: editable,
|
|
73
|
+
});
|
|
74
|
+
return 'stored';
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
async delete(accountId, id) {
|
|
78
|
+
await this.db
|
|
79
|
+
.delete(directoryWebhooks)
|
|
80
|
+
.where(and(eq(directoryWebhooks.account_id, accountId), eq(directoryWebhooks.id, id)));
|
|
81
|
+
}
|
|
82
|
+
async listEnabled() {
|
|
83
|
+
const rows = await this.db
|
|
84
|
+
.select()
|
|
85
|
+
.from(directoryWebhooks)
|
|
86
|
+
.where(eq(directoryWebhooks.enabled, 1))
|
|
87
|
+
.orderBy(asc(directoryWebhooks.account_id), asc(directoryWebhooks.id));
|
|
88
|
+
return rows.map(toRecord);
|
|
89
|
+
}
|
|
90
|
+
async claim(accountId, id, token, now, leaseUntil) {
|
|
91
|
+
const [row] = await this.db
|
|
92
|
+
.update(directoryWebhooks)
|
|
93
|
+
.set({ lease_token: token, lease_until: leaseUntil })
|
|
94
|
+
.where(and(eq(directoryWebhooks.account_id, accountId), eq(directoryWebhooks.id, id), eq(directoryWebhooks.enabled, 1), or(isNull(directoryWebhooks.lease_until), lte(directoryWebhooks.lease_until, now))))
|
|
95
|
+
.returning({ deliveredSeq: directoryWebhooks.delivered_seq });
|
|
96
|
+
return row ? row.deliveredSeq : null;
|
|
97
|
+
}
|
|
98
|
+
async complete(accountId, id, token, toSeq) {
|
|
99
|
+
const moved = await this.db
|
|
100
|
+
.update(directoryWebhooks)
|
|
101
|
+
.set({ delivered_seq: toSeq, lease_token: null, lease_until: null })
|
|
102
|
+
.where(this.heldBy(accountId, id, token))
|
|
103
|
+
.returning({ id: directoryWebhooks.id });
|
|
104
|
+
return moved.length > 0;
|
|
105
|
+
}
|
|
106
|
+
async release(accountId, id, token) {
|
|
107
|
+
await this.db
|
|
108
|
+
.update(directoryWebhooks)
|
|
109
|
+
.set({ lease_token: null, lease_until: null })
|
|
110
|
+
.where(this.heldBy(accountId, id, token));
|
|
111
|
+
}
|
|
112
|
+
heldBy(accountId, id, token) {
|
|
113
|
+
return and(eq(directoryWebhooks.account_id, accountId), eq(directoryWebhooks.id, id), eq(directoryWebhooks.lease_token, token));
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
//# sourceMappingURL=directoryWebhookRepository.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"directoryWebhookRepository.js","sourceRoot":"","sources":["../../src/repositories/directoryWebhookRepository.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { AccountInvitationRecord, AccountInvitationRepository, AccountRecord, AccountRepository, AccountSettingsPatch, AuditEventPage, AuditEventRecord, AuditEventRepository, AuthAttemptRecord, AuthAttemptRepository, EmailConnectionRecord, EmailConnectionRepository, IdentityProvider, MachineNodeMint, MachineNodeMintOutcome, MachineNodeRecord, MachineNodeRepository, Membership, MembershipRepository, PasswordResetTokenRecord, PasswordResetTokenRepository, PasswordResetTokenStatus, UserIdentityRecord, UserRecord, UserRepository } from '@cat-factory/kernel';
|
|
1
|
+
import type { AccountInvitationRecord, AccountInvitationRepository, AccountRecord, AccountRepository, AccountRole, AccountSettingsPatch, AuditEventPage, AuditEventRecord, AuditEventRepository, AuthAttemptRecord, AuthAttemptRepository, EmailConnectionRecord, EmailConnectionRepository, IdentityProvider, MachineNodeMint, MachineNodeMintOutcome, MachineNodeRecord, MachineNodeRepository, Membership, MembershipRepository, PasswordResetTokenRecord, PasswordResetTokenRepository, PasswordResetTokenStatus, UserIdentityRecord, UserRecord, UserRepository } from '@cat-factory/kernel';
|
|
2
2
|
import type { DrizzleDb } from '../../db/client.js';
|
|
3
3
|
export declare class DrizzleAccountRepository implements AccountRepository {
|
|
4
4
|
private readonly db;
|
|
@@ -11,6 +11,8 @@ export declare class DrizzleAccountRepository implements AccountRepository {
|
|
|
11
11
|
updateSettings(id: string, patch: AccountSettingsPatch): Promise<void>;
|
|
12
12
|
findPersonalByUser(userId: string): Promise<AccountRecord | null>;
|
|
13
13
|
}
|
|
14
|
+
/** Parse the CSV `roles` column into a non-empty role set (defaults to developer). */
|
|
15
|
+
export declare function parseRoles(csv: string | null): AccountRole[];
|
|
14
16
|
export declare class DrizzleMembershipRepository implements MembershipRepository {
|
|
15
17
|
private readonly db;
|
|
16
18
|
constructor(db: DrizzleDb);
|
|
@@ -31,6 +33,7 @@ export declare class DrizzleUserRepository implements UserRepository {
|
|
|
31
33
|
listByIds(ids: string[]): Promise<UserRecord[]>;
|
|
32
34
|
getIdentity(provider: IdentityProvider, subject: string): Promise<UserIdentityRecord | null>;
|
|
33
35
|
linkIdentity(identity: UserIdentityRecord): Promise<void>;
|
|
36
|
+
private upsertIdentity;
|
|
34
37
|
listIdentities(userId: string): Promise<UserIdentityRecord[]>;
|
|
35
38
|
sessionGeneration(userId: string): Promise<number | null>;
|
|
36
39
|
bumpSessionGeneration(userId: string): Promise<number>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"accounts.d.ts","sourceRoot":"","sources":["../../../src/repositories/drizzle/accounts.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EACV,uBAAuB,EACvB,2BAA2B,EAC3B,aAAa,EACb,iBAAiB,
|
|
1
|
+
{"version":3,"file":"accounts.d.ts","sourceRoot":"","sources":["../../../src/repositories/drizzle/accounts.ts"],"names":[],"mappings":"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"}
|