@preventive/triage 1.0.0-alpha.20 → 1.0.0-alpha.21
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
CHANGED
package/server-managed/config.ts
CHANGED
|
@@ -24,8 +24,8 @@ export interface ManagedConfig {
|
|
|
24
24
|
// + secret, used by the login flow.
|
|
25
25
|
githubClientId: string
|
|
26
26
|
githubClientSecret: string
|
|
27
|
-
// Preapproved GitHub identity
|
|
28
|
-
//
|
|
27
|
+
// Preapproved GitHub identity promoted on login when it is the only user
|
|
28
|
+
// and its role is No access, including a login after registration.
|
|
29
29
|
initialAdminGithubId: number | null
|
|
30
30
|
// Absolute callback registered with GitHub, e.g.
|
|
31
31
|
// 'https://triage.example.com/api/oauth/github/callback'.
|
|
@@ -307,8 +307,8 @@ export interface UserTeam {
|
|
|
307
307
|
// Backend-agnostic store surface (SQLite + PostgreSQL implementations).
|
|
308
308
|
export interface ManagedDb extends ActivityStore, CommentStore, GithubMetadataStore, WorkspaceShareStore, ImportTriageStore {
|
|
309
309
|
// Upsert the identity; returns the user's opaque id (stable across logins).
|
|
310
|
-
// Initial-admin approval comes only from trusted
|
|
311
|
-
//
|
|
310
|
+
// Initial-admin approval comes only from trusted login configuration. It
|
|
311
|
+
// promotes a matching No access identity only while it is the sole user.
|
|
312
312
|
upsertUser(user: ManagedUser, now: number, initialAdminGithubId?: number | null): Promise<string>
|
|
313
313
|
createSession(session: ManagedSession, now: number): Promise<void>
|
|
314
314
|
sessionWithUser(id: string, now: number): Promise<{ session: ManagedSession; user: StoredUser } | null>
|
|
@@ -447,18 +447,20 @@ const BUNDLE_IN_TEAM_PATH_SQL = REPORT_IN_TEAM_PATH_SQL.replaceAll('r.repo_direc
|
|
|
447
447
|
// destructures — keeps openSqliteManagedDb itself small (one place per query).
|
|
448
448
|
function prepareStatements(db: ManagedSql) {
|
|
449
449
|
return {
|
|
450
|
-
//
|
|
451
|
-
//
|
|
452
|
-
// so concurrent first logins cannot both become admins. Re-login never
|
|
453
|
-
// overwrites approval or revocation through the ON CONFLICT update.
|
|
450
|
+
// Identity refreshes preserve roles. Login can separately bootstrap the
|
|
451
|
+
// configured sole No access user within this same writer transaction.
|
|
454
452
|
upsertUserStmt: db.prepare(
|
|
455
453
|
`INSERT INTO managed_user (id, github_user_id, login, name, avatar_url, role, created_at, updated_at)
|
|
456
|
-
VALUES (?, ?, ?, ?, ?,
|
|
454
|
+
VALUES (?, ?, ?, ?, ?, 'none', ?, ?)
|
|
457
455
|
ON CONFLICT(github_user_id) DO UPDATE SET
|
|
458
456
|
login = excluded.login, name = excluded.name,
|
|
459
457
|
avatar_url = excluded.avatar_url, updated_at = excluded.updated_at`,
|
|
460
458
|
),
|
|
461
|
-
selectUserIdStmt: db.prepare(`SELECT id FROM managed_user WHERE github_user_id = ?`),
|
|
459
|
+
selectUserIdStmt: db.prepare(`SELECT id, role FROM managed_user WHERE github_user_id = ?`),
|
|
460
|
+
promoteInitialAdminStmt: db.prepare(
|
|
461
|
+
`UPDATE managed_user SET role = 'admin' WHERE id = ? AND role = 'none'
|
|
462
|
+
AND NOT EXISTS(SELECT 1 FROM managed_user WHERE id <> ?)`,
|
|
463
|
+
),
|
|
462
464
|
selectGithubIdStmt: db.prepare(`SELECT github_user_id AS githubId FROM managed_user WHERE id = ?`),
|
|
463
465
|
insertSessionStmt: db.prepare(
|
|
464
466
|
`INSERT INTO managed_session (id, user_id, csrf_token, created_at, expires_at) VALUES (?, ?, ?, ?, ?)`,
|
|
@@ -1261,7 +1263,7 @@ export function createManagedMethods(db: ManagedSql, options: ManagedDbOptions =
|
|
|
1261
1263
|
const stmts = prepareStatements(db)
|
|
1262
1264
|
const triage = triageMethods(stmts, options.triageHistoryLimit ?? 0)
|
|
1263
1265
|
const {
|
|
1264
|
-
upsertUserStmt, selectUserIdStmt, selectGithubIdStmt, insertSessionStmt, selectSessionStmt, selectUsersStmt,
|
|
1266
|
+
upsertUserStmt, selectUserIdStmt, promoteInitialAdminStmt, selectGithubIdStmt, insertSessionStmt, selectSessionStmt, selectUsersStmt,
|
|
1265
1267
|
touchUserSeenStmt, updateRoleStmt, updateTokensStmt, selectTokensStmt, deleteSessionStmt, deleteExpiredStmt,
|
|
1266
1268
|
} = stmts
|
|
1267
1269
|
|
|
@@ -1269,10 +1271,14 @@ export function createManagedMethods(db: ManagedSql, options: ManagedDbOptions =
|
|
|
1269
1271
|
async upsertUser(user, now, initialAdminGithubId = null) {
|
|
1270
1272
|
// New row → a fresh id; ON CONFLICT(github_user_id) keeps an existing
|
|
1271
1273
|
// user's id (DO UPDATE leaves it untouched), so re-read to return it.
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1274
|
+
await upsertUserStmt.run(randomUUID(), user.githubUserId, user.login, user.name, user.avatarUrl, now, now)
|
|
1275
|
+
const row = (await selectUserIdStmt.get(user.githubUserId)) as { id: string; role: Role }
|
|
1276
|
+
// Avoid the population check unless this login matches the configured
|
|
1277
|
+
// identity and still needs approval. NOT EXISTS stops at any other user.
|
|
1278
|
+
if (initialAdminGithubId != null && Number.isSafeInteger(initialAdminGithubId) && initialAdminGithubId > 0
|
|
1279
|
+
&& user.githubUserId === initialAdminGithubId && row.role === 'none') {
|
|
1280
|
+
await promoteInitialAdminStmt.run(row.id, row.id)
|
|
1281
|
+
}
|
|
1276
1282
|
return row.id
|
|
1277
1283
|
},
|
|
1278
1284
|
async createSession(session, now) {
|