@igstack/app-catalog-backend-core 0.3.1-alpha-20260403020019 → 0.3.1-alpha-20260405015231
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/db/syncAppCatalog.d.mts +11 -1
- package/dist/db/syncAppCatalog.d.mts.map +1 -1
- package/dist/db/syncAppCatalog.mjs +54 -2
- package/dist/db/syncAppCatalog.mjs.map +1 -1
- package/dist/db/tableSyncMagazine.d.mts +18 -0
- package/dist/db/tableSyncMagazine.d.mts.map +1 -1
- package/dist/db/tableSyncMagazine.mjs +18 -0
- package/dist/db/tableSyncMagazine.mjs.map +1 -1
- package/dist/generated/prisma/client.mjs.map +1 -1
- package/dist/generated/prisma/commonInputTypes.d.mts +50 -50
- package/dist/generated/prisma/commonInputTypes.d.mts.map +1 -1
- package/dist/generated/prisma/enums.d.mts +2 -0
- package/dist/generated/prisma/enums.d.mts.map +1 -1
- package/dist/generated/prisma/internal/class.d.mts +48 -0
- package/dist/generated/prisma/internal/class.d.mts.map +1 -1
- package/dist/generated/prisma/internal/class.mjs +4 -4
- package/dist/generated/prisma/internal/class.mjs.map +1 -1
- package/dist/generated/prisma/internal/prismaNamespace.d.mts +362 -12
- package/dist/generated/prisma/internal/prismaNamespace.d.mts.map +1 -1
- package/dist/generated/prisma/models/DbAppForCatalog.d.mts +198 -3
- package/dist/generated/prisma/models/DbAppForCatalog.d.mts.map +1 -1
- package/dist/generated/prisma/models/DbAsset.d.mts +2 -8
- package/dist/generated/prisma/models/DbAsset.d.mts.map +1 -1
- package/dist/generated/prisma/models/DbGroup.d.mts +1124 -0
- package/dist/generated/prisma/models/DbGroup.d.mts.map +1 -0
- package/dist/generated/prisma/models/DbGroupMembership.d.mts +1199 -0
- package/dist/generated/prisma/models/DbGroupMembership.d.mts.map +1 -0
- package/dist/generated/prisma/models/DbPerson.d.mts +1155 -0
- package/dist/generated/prisma/models/DbPerson.d.mts.map +1 -0
- package/dist/generated/prisma/models/DbSubResource.d.mts +1468 -0
- package/dist/generated/prisma/models/DbSubResource.d.mts.map +1 -0
- package/dist/generated/prisma/models/SourceReference.d.mts +1 -1
- package/dist/generated/prisma/models.d.mts +6 -2
- package/dist/index.d.mts +5 -3
- package/dist/middleware/featureRegistry.mjs +5 -1
- package/dist/middleware/featureRegistry.mjs.map +1 -1
- package/dist/modules/appCatalog/checkLinks.d.mts.map +1 -1
- package/dist/modules/appCatalog/checkLinks.mjs +31 -2
- package/dist/modules/appCatalog/checkLinks.mjs.map +1 -1
- package/dist/modules/appCatalog/service.mjs +52 -5
- package/dist/modules/appCatalog/service.mjs.map +1 -1
- package/dist/modules/auth/registerAuthRoutes.mjs +5 -1
- package/dist/modules/auth/registerAuthRoutes.mjs.map +1 -1
- package/dist/modules/lighthouseKeeper/tools.d.mts +1 -1
- package/dist/modules/lighthouseKeeper/tools.mjs +1 -1
- package/dist/modules/lighthouseKeeper/tools.mjs.map +1 -1
- package/dist/types/common/appCatalogTypes.d.mts +19 -1
- package/dist/types/common/appCatalogTypes.d.mts.map +1 -1
- package/dist/types/common/approvalMethodTypes.d.mts +52 -36
- package/dist/types/common/approvalMethodTypes.d.mts.map +1 -1
- package/dist/types/common/personGroupTypes.d.mts +22 -0
- package/dist/types/common/personGroupTypes.d.mts.map +1 -0
- package/dist/types/common/subResourceTypes.d.mts +24 -0
- package/dist/types/common/subResourceTypes.d.mts.map +1 -0
- package/package.json +3 -3
- package/prisma/schema.prisma +59 -0
- package/src/db/syncAppCatalog.ts +73 -1
- package/src/db/tableSyncMagazine.ts +18 -0
- package/src/generated/prisma/browser.ts +20 -0
- package/src/generated/prisma/client.ts +20 -0
- package/src/generated/prisma/commonInputTypes.ts +54 -54
- package/src/generated/prisma/enums.ts +3 -1
- package/src/generated/prisma/internal/class.ts +44 -4
- package/src/generated/prisma/internal/prismaNamespace.ts +373 -15
- package/src/generated/prisma/internal/prismaNamespaceBrowser.ts +58 -0
- package/src/generated/prisma/models/DbAppForCatalog.ts +208 -1
- package/src/generated/prisma/models/DbAsset.ts +0 -8
- package/src/generated/prisma/models/DbGroup.ts +1324 -0
- package/src/generated/prisma/models/DbGroupMembership.ts +1432 -0
- package/src/generated/prisma/models/DbPerson.ts +1356 -0
- package/src/generated/prisma/models/DbSubResource.ts +1692 -0
- package/src/generated/prisma/models.ts +4 -0
- package/src/middleware/featureRegistry.ts +4 -1
- package/src/modules/appCatalog/checkLinks.ts +58 -5
- package/src/modules/appCatalog/service.ts +80 -6
- package/src/modules/auth/registerAuthRoutes.ts +4 -1
- package/src/modules/lighthouseKeeper/tools.ts +1 -1
- package/src/prisma-json-types.d.ts +6 -0
- package/src/types/common/appCatalogTypes.ts +23 -0
- package/src/types/common/approvalMethodTypes.ts +59 -57
- package/src/types/common/personGroupTypes.ts +20 -0
- package/src/types/common/subResourceTypes.ts +20 -0
- package/src/types/index.ts +2 -0
|
@@ -12,8 +12,12 @@ export type * from './models/user'
|
|
|
12
12
|
export type * from './models/session'
|
|
13
13
|
export type * from './models/account'
|
|
14
14
|
export type * from './models/verification'
|
|
15
|
+
export type * from './models/DbPerson'
|
|
16
|
+
export type * from './models/DbGroup'
|
|
17
|
+
export type * from './models/DbGroupMembership'
|
|
15
18
|
export type * from './models/DbApprovalMethod'
|
|
16
19
|
export type * from './models/DbAppForCatalog'
|
|
20
|
+
export type * from './models/DbSubResource'
|
|
17
21
|
export type * from './models/DbAppTagDefinition'
|
|
18
22
|
export type * from './models/DbAsset'
|
|
19
23
|
export type * from './models/Source'
|
|
@@ -74,7 +74,10 @@ const FEATURES: FeatureRegistration[] = [
|
|
|
74
74
|
}
|
|
75
75
|
res.json({ ...session, isAdmin: admin })
|
|
76
76
|
} else {
|
|
77
|
-
|
|
77
|
+
// Return 200 with null session instead of 401 to avoid
|
|
78
|
+
// browser "Failed to load resource" console errors.
|
|
79
|
+
// The frontend checks for session.user to determine auth state.
|
|
80
|
+
res.json({ session: null, user: null, isAuthenticated: false })
|
|
78
81
|
}
|
|
79
82
|
} catch (error) {
|
|
80
83
|
console.error('[Auth Session Error]', error)
|
|
@@ -20,6 +20,59 @@ async function sleep(ms: number): Promise<void> {
|
|
|
20
20
|
return new Promise((resolve) => setTimeout(resolve, ms))
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
+
function classifyNetworkError(err: unknown): string {
|
|
24
|
+
// Node's fetch wraps the real error in error.cause
|
|
25
|
+
const cause =
|
|
26
|
+
err instanceof Error && 'cause' in err && err.cause instanceof Error
|
|
27
|
+
? err.cause
|
|
28
|
+
: null
|
|
29
|
+
|
|
30
|
+
const hasCode = (obj: unknown): obj is { code: string } =>
|
|
31
|
+
obj != null && typeof obj === 'object' && 'code' in obj
|
|
32
|
+
|
|
33
|
+
const code =
|
|
34
|
+
(hasCode(cause) ? cause.code : null) ?? (hasCode(err) ? err.code : null)
|
|
35
|
+
const causeMsg = cause?.message ?? ''
|
|
36
|
+
const topMsg = err instanceof Error ? err.message : String(err)
|
|
37
|
+
|
|
38
|
+
if (code) {
|
|
39
|
+
switch (code) {
|
|
40
|
+
case 'ENOTFOUND':
|
|
41
|
+
case 'EAI_AGAIN':
|
|
42
|
+
return `DNS resolution failed (${code})`
|
|
43
|
+
case 'ECONNREFUSED':
|
|
44
|
+
return `Connection refused (${code})`
|
|
45
|
+
case 'ETIMEDOUT':
|
|
46
|
+
case 'ECONNRESET':
|
|
47
|
+
return `Connection timeout (${code})`
|
|
48
|
+
case 'ENETUNREACH':
|
|
49
|
+
case 'EHOSTUNREACH':
|
|
50
|
+
return `Network unreachable (${code})`
|
|
51
|
+
case 'UNABLE_TO_VERIFY_LEAF_SIGNATURE':
|
|
52
|
+
return `SSL certificate error: unable to verify certificate (${code})`
|
|
53
|
+
case 'CERT_HAS_EXPIRED':
|
|
54
|
+
return `SSL certificate error: certificate expired (${code})`
|
|
55
|
+
case 'ERR_TLS_CERT_ALTNAME_INVALID':
|
|
56
|
+
return `SSL certificate error: hostname mismatch (${code})`
|
|
57
|
+
case 'DEPTH_ZERO_SELF_SIGNED_CERT':
|
|
58
|
+
case 'SELF_SIGNED_CERT_IN_CHAIN':
|
|
59
|
+
return `SSL certificate error: self-signed certificate (${code})`
|
|
60
|
+
default:
|
|
61
|
+
if (code.startsWith('ERR_TLS') || code.startsWith('CERT'))
|
|
62
|
+
return `SSL/TLS error (${code}): ${causeMsg || topMsg}`
|
|
63
|
+
return `Network error (${code}): ${causeMsg || topMsg}`
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const msg = causeMsg || topMsg
|
|
68
|
+
if (/certificate/i.test(msg) || /SSL|TLS/i.test(msg))
|
|
69
|
+
return `SSL/TLS error: ${msg}`
|
|
70
|
+
if (/abort|timeout/i.test(msg)) return `Request timeout: ${msg}`
|
|
71
|
+
if (/dns|resolve/i.test(msg)) return `DNS error: ${msg}`
|
|
72
|
+
|
|
73
|
+
return causeMsg || topMsg
|
|
74
|
+
}
|
|
75
|
+
|
|
23
76
|
async function checkUrlWithRetry(
|
|
24
77
|
url: string,
|
|
25
78
|
options: {
|
|
@@ -38,15 +91,15 @@ async function checkUrlWithRetry(
|
|
|
38
91
|
})
|
|
39
92
|
return { status: response.status }
|
|
40
93
|
} catch (error: unknown) {
|
|
41
|
-
const errorMessage =
|
|
42
|
-
error instanceof Error ? error.message : String(error)
|
|
94
|
+
const errorMessage = classifyNetworkError(error)
|
|
43
95
|
lastError = errorMessage
|
|
44
96
|
|
|
45
|
-
// Check if it's a retryable error (timeout or
|
|
97
|
+
// Check if it's a retryable error (timeout or transient network issues)
|
|
46
98
|
const isRetryable =
|
|
47
|
-
errorMessage.includes('aborted') ||
|
|
48
99
|
errorMessage.includes('timeout') ||
|
|
49
|
-
errorMessage.includes('
|
|
100
|
+
errorMessage.includes('ECONNRESET') ||
|
|
101
|
+
errorMessage.includes('ETIMEDOUT') ||
|
|
102
|
+
errorMessage.includes('EAI_AGAIN')
|
|
50
103
|
|
|
51
104
|
if (isRetryable && attempt < options.maxRetries) {
|
|
52
105
|
// Exponential backoff: 1s, 2s, 4s
|
|
@@ -9,9 +9,10 @@ import type {
|
|
|
9
9
|
} from '../../types/common/appCatalogTypes'
|
|
10
10
|
import type {
|
|
11
11
|
CustomConfig,
|
|
12
|
-
PersonTeamConfig,
|
|
13
12
|
ServiceConfig,
|
|
14
13
|
} from '../../types/common/approvalMethodTypes'
|
|
14
|
+
import type { Group, Person } from '../../types/common/personGroupTypes'
|
|
15
|
+
import type { SubResource } from '../../types/common/subResourceTypes'
|
|
15
16
|
import { omit } from 'radashi'
|
|
16
17
|
import { parseSourceSlug } from '../../utils/parseSourceSlug'
|
|
17
18
|
|
|
@@ -96,17 +97,34 @@ export async function getApprovalMethodsFromPrisma(): Promise<
|
|
|
96
97
|
case 'service':
|
|
97
98
|
return {
|
|
98
99
|
...baseFields,
|
|
99
|
-
type: 'service',
|
|
100
|
+
type: 'service' as const,
|
|
100
101
|
config: config as ServiceConfig,
|
|
101
102
|
}
|
|
103
|
+
case 'custom':
|
|
104
|
+
return {
|
|
105
|
+
...baseFields,
|
|
106
|
+
type: 'custom' as const,
|
|
107
|
+
config: config as CustomConfig,
|
|
108
|
+
}
|
|
109
|
+
case 'noAccessRequired':
|
|
110
|
+
return {
|
|
111
|
+
...baseFields,
|
|
112
|
+
type: 'noAccessRequired' as const,
|
|
113
|
+
config: config as CustomConfig,
|
|
114
|
+
}
|
|
115
|
+
case 'unknown':
|
|
116
|
+
return {
|
|
117
|
+
...baseFields,
|
|
118
|
+
type: 'unknown' as const,
|
|
119
|
+
config: config as CustomConfig,
|
|
120
|
+
}
|
|
102
121
|
case 'personTeam':
|
|
122
|
+
// Legacy: map personTeam to custom
|
|
103
123
|
return {
|
|
104
124
|
...baseFields,
|
|
105
|
-
type: '
|
|
106
|
-
config: config as
|
|
125
|
+
type: 'custom' as const,
|
|
126
|
+
config: config as CustomConfig,
|
|
107
127
|
}
|
|
108
|
-
case 'custom':
|
|
109
|
-
return { ...baseFields, type: 'custom', config: config as CustomConfig }
|
|
110
128
|
}
|
|
111
129
|
})
|
|
112
130
|
}
|
|
@@ -138,6 +156,10 @@ function rowToAppForCatalog(row: AppRowWithSourceRefs): AppForCatalog {
|
|
|
138
156
|
const urlIssues = (row.urlIssues as unknown as string[] | null)?.length
|
|
139
157
|
? (row.urlIssues as unknown as string[])
|
|
140
158
|
: undefined
|
|
159
|
+
const tiers =
|
|
160
|
+
row.tiers == null
|
|
161
|
+
? undefined
|
|
162
|
+
: (row.tiers as unknown as AppForCatalog['tiers'])
|
|
141
163
|
|
|
142
164
|
return {
|
|
143
165
|
id: row.id,
|
|
@@ -157,6 +179,7 @@ function rowToAppForCatalog(row: AppRowWithSourceRefs): AppForCatalog {
|
|
|
157
179
|
deprecated,
|
|
158
180
|
aiPrompt,
|
|
159
181
|
urlIssues,
|
|
182
|
+
tiers,
|
|
160
183
|
}
|
|
161
184
|
}
|
|
162
185
|
|
|
@@ -265,6 +288,54 @@ export function deriveCategories(apps: AppForCatalog[]): AppCategory[] {
|
|
|
265
288
|
return categories
|
|
266
289
|
}
|
|
267
290
|
|
|
291
|
+
export async function getPersonsFromPrisma(): Promise<Person[]> {
|
|
292
|
+
const prisma = getDbClient()
|
|
293
|
+
const rows = await prisma.dbPerson.findMany()
|
|
294
|
+
return rows.map((row) => ({
|
|
295
|
+
slug: row.slug,
|
|
296
|
+
firstName: row.firstName,
|
|
297
|
+
lastName: row.lastName,
|
|
298
|
+
email: row.email ?? undefined,
|
|
299
|
+
}))
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
export async function getGroupsFromPrisma(): Promise<Group[]> {
|
|
303
|
+
const prisma = getDbClient()
|
|
304
|
+
const rows = await prisma.dbGroup.findMany({
|
|
305
|
+
include: { memberships: true },
|
|
306
|
+
})
|
|
307
|
+
return rows.map((row) => ({
|
|
308
|
+
slug: row.slug,
|
|
309
|
+
displayName: row.displayName ?? undefined,
|
|
310
|
+
email: row.email ?? undefined,
|
|
311
|
+
memberSlugs: row.memberships.map((m) => m.personSlug),
|
|
312
|
+
}))
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
export async function getSubResourcesFromPrisma(): Promise<SubResource[]> {
|
|
316
|
+
const prisma = getDbClient()
|
|
317
|
+
const rows = await prisma.dbSubResource.findMany()
|
|
318
|
+
return rows.map((row) => ({
|
|
319
|
+
slug: row.slug,
|
|
320
|
+
displayName: row.displayName,
|
|
321
|
+
description: row.description ?? undefined,
|
|
322
|
+
appSlug: row.appSlug,
|
|
323
|
+
familySlug: row.familySlug ?? undefined,
|
|
324
|
+
tierSlug: row.tierSlug ?? undefined,
|
|
325
|
+
aliases: row.aliases as unknown as string[],
|
|
326
|
+
ownerPersonSlug: row.ownerPersonSlug ?? undefined,
|
|
327
|
+
accessMaintainerGroupSlugs:
|
|
328
|
+
row.accessMaintainerGroupSlugs as unknown as string[],
|
|
329
|
+
accessRequest: row.accessRequest
|
|
330
|
+
? (row.accessRequest as unknown as SubResource['accessRequest'])
|
|
331
|
+
: undefined,
|
|
332
|
+
accessComments: row.accessComments ?? undefined,
|
|
333
|
+
extra: row.extra
|
|
334
|
+
? (row.extra as unknown as Record<string, unknown>)
|
|
335
|
+
: undefined,
|
|
336
|
+
}))
|
|
337
|
+
}
|
|
338
|
+
|
|
268
339
|
export async function getAppCatalogData(
|
|
269
340
|
getAppsOptional?: () => Promise<AppForCatalog[]>,
|
|
270
341
|
): Promise<AppCatalogData> {
|
|
@@ -276,5 +347,8 @@ export async function getAppCatalogData(
|
|
|
276
347
|
apps,
|
|
277
348
|
tagsDefinitions: await getGroupingTagDefinitionsFromPrisma(),
|
|
278
349
|
approvalMethods: await getApprovalMethodsFromPrisma(),
|
|
350
|
+
persons: await getPersonsFromPrisma(),
|
|
351
|
+
groups: await getGroupsFromPrisma(),
|
|
352
|
+
subResources: await getSubResourcesFromPrisma(),
|
|
279
353
|
}
|
|
280
354
|
}
|
|
@@ -38,7 +38,10 @@ export function registerAuthRoutes(
|
|
|
38
38
|
}
|
|
39
39
|
res.json({ ...session, isAdmin: admin })
|
|
40
40
|
} else {
|
|
41
|
-
|
|
41
|
+
// Return 200 with null session instead of 401 to avoid
|
|
42
|
+
// browser "Failed to load resource" console errors.
|
|
43
|
+
// The frontend checks for session.user to determine auth state.
|
|
44
|
+
res.json({ session: null, user: null, isAuthenticated: false })
|
|
42
45
|
}
|
|
43
46
|
} catch (error) {
|
|
44
47
|
console.error('[Auth Session Error]', error)
|
|
@@ -112,7 +112,7 @@ Return the updated card as a structured JSON object matching the card format:
|
|
|
112
112
|
- appUrl: Main application URL
|
|
113
113
|
- links: Array of {url, title} objects for related links
|
|
114
114
|
- sources: Array of source URLs (documentation, Confluence pages, etc.)
|
|
115
|
-
- accessRequest: Object with
|
|
115
|
+
- accessRequest: Object with approvalMethodSlug, comments, requestPrompt, postApprovalInstructions, roles, approverPersonSlugs, urls
|
|
116
116
|
- notes: Additional notes
|
|
117
117
|
- deprecated: Optional deprecation info with type, replacementSlug, comment
|
|
118
118
|
|
|
@@ -28,6 +28,12 @@ declare global {
|
|
|
28
28
|
|
|
29
29
|
// AppRole used within accessRequest
|
|
30
30
|
type AppRole = import('./types/index').AppRole
|
|
31
|
+
|
|
32
|
+
// DbAppForCatalog.tiers - Tier variants (prod/dev)
|
|
33
|
+
type AppTierVariant = import('./types/index').AppTierVariant
|
|
34
|
+
|
|
35
|
+
// DbSubResource.extra - Arbitrary extra data
|
|
36
|
+
type SubResourceExtra = Record<string, unknown>
|
|
31
37
|
}
|
|
32
38
|
}
|
|
33
39
|
|
|
@@ -7,6 +7,24 @@
|
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
9
|
import type { AppAccessRequest, ApprovalMethod } from './approvalMethodTypes'
|
|
10
|
+
import type { Group, Person } from './personGroupTypes'
|
|
11
|
+
import type { SubResource } from './subResourceTypes'
|
|
12
|
+
|
|
13
|
+
// ============================================================================
|
|
14
|
+
// APP TIER VARIANT
|
|
15
|
+
// ============================================================================
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* A tier variant of an app (e.g., prod/dev environments).
|
|
19
|
+
* Each tier can have its own URL and access process.
|
|
20
|
+
*/
|
|
21
|
+
export interface AppTierVariant {
|
|
22
|
+
tierSlug: string
|
|
23
|
+
displayName?: string
|
|
24
|
+
description?: string
|
|
25
|
+
appUrl?: string
|
|
26
|
+
accessRequest?: AppAccessRequest
|
|
27
|
+
}
|
|
10
28
|
|
|
11
29
|
// ============================================================================
|
|
12
30
|
// APP CATALOG TYPES
|
|
@@ -53,6 +71,8 @@ export interface AppForCatalog {
|
|
|
53
71
|
aiPrompt?: string
|
|
54
72
|
/** URL health issues detected by automated scanning */
|
|
55
73
|
urlIssues?: string[]
|
|
74
|
+
/** Optional tier variants (e.g., prod/dev) with per-tier URLs and access */
|
|
75
|
+
tiers?: AppTierVariant[]
|
|
56
76
|
}
|
|
57
77
|
|
|
58
78
|
// Derived catalog data returned by backend
|
|
@@ -98,5 +118,8 @@ export interface AppCatalogData {
|
|
|
98
118
|
apps: AppForCatalog[]
|
|
99
119
|
tagsDefinitions: GroupingTagDefinition[]
|
|
100
120
|
approvalMethods: AppApprovalMethod[]
|
|
121
|
+
persons: Person[]
|
|
122
|
+
groups: Group[]
|
|
123
|
+
subResources: SubResource[]
|
|
101
124
|
versions?: AppVersionInfo
|
|
102
125
|
}
|
|
@@ -2,22 +2,19 @@
|
|
|
2
2
|
* Approval Method Types
|
|
3
3
|
*
|
|
4
4
|
* Global approval method templates that apps can link to.
|
|
5
|
-
* Each method has a type (service,
|
|
5
|
+
* Each method has a type (service, custom, noAccessRequired, unknown) with type-specific config.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
// ============================================================================
|
|
9
9
|
// APPROVAL METHOD TYPES (Global Templates)
|
|
10
10
|
// ============================================================================
|
|
11
11
|
|
|
12
|
-
export type ApprovalMethodType =
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
displayName: string
|
|
19
|
-
contact: string // email, slack handle, etc.
|
|
20
|
-
}
|
|
12
|
+
export type ApprovalMethodType =
|
|
13
|
+
| 'service'
|
|
14
|
+
| 'personTeam'
|
|
15
|
+
| 'custom'
|
|
16
|
+
| 'noAccessRequired'
|
|
17
|
+
| 'unknown'
|
|
21
18
|
|
|
22
19
|
/**
|
|
23
20
|
* Service type config - for bots, ticketing systems, self-service portals
|
|
@@ -28,10 +25,11 @@ export interface ServiceConfig {
|
|
|
28
25
|
}
|
|
29
26
|
|
|
30
27
|
/**
|
|
31
|
-
*
|
|
28
|
+
* PersonTeam type config - for person/group-based approval
|
|
32
29
|
*/
|
|
33
30
|
export interface PersonTeamConfig {
|
|
34
|
-
|
|
31
|
+
personSlugs?: string[] // Person references
|
|
32
|
+
groupSlugs?: string[] // Group references
|
|
35
33
|
}
|
|
36
34
|
|
|
37
35
|
/**
|
|
@@ -52,19 +50,15 @@ export type ApprovalMethodConfig =
|
|
|
52
50
|
/**
|
|
53
51
|
* Approval Method - stored in DbApprovalMethod
|
|
54
52
|
*/
|
|
55
|
-
// export interface ApprovalMethod {
|
|
56
|
-
// slug: string
|
|
57
|
-
// type: ApprovalMethodType
|
|
58
|
-
// displayName: string
|
|
59
|
-
// config?: ApprovalMethodConfig
|
|
60
|
-
// createdAt?: Date
|
|
61
|
-
// updatedAt?: Date
|
|
62
|
-
// }
|
|
63
|
-
|
|
64
53
|
export type ApprovalMethod = {
|
|
65
54
|
slug: string
|
|
55
|
+
/**
|
|
56
|
+
* Displayed approval method
|
|
57
|
+
*/
|
|
66
58
|
displayName: string
|
|
67
|
-
/**
|
|
59
|
+
/**
|
|
60
|
+
* Optionally - older name of approval method if there were migration in organization.
|
|
61
|
+
*/
|
|
68
62
|
deprecatedAliases?: string[]
|
|
69
63
|
createdAt?: Date
|
|
70
64
|
updatedAt?: Date
|
|
@@ -81,6 +75,14 @@ export type ApprovalMethod = {
|
|
|
81
75
|
type: 'custom'
|
|
82
76
|
config: CustomConfig
|
|
83
77
|
}
|
|
78
|
+
| {
|
|
79
|
+
type: 'noAccessRequired'
|
|
80
|
+
config: CustomConfig
|
|
81
|
+
}
|
|
82
|
+
| {
|
|
83
|
+
type: 'unknown'
|
|
84
|
+
config: CustomConfig
|
|
85
|
+
}
|
|
84
86
|
)
|
|
85
87
|
|
|
86
88
|
// ============================================================================
|
|
@@ -91,19 +93,20 @@ export type ApprovalMethod = {
|
|
|
91
93
|
* Role that can be requested for an app
|
|
92
94
|
*/
|
|
93
95
|
export interface AppRole {
|
|
96
|
+
/**
|
|
97
|
+
* User-friendly role name.
|
|
98
|
+
*/
|
|
94
99
|
displayName: string
|
|
100
|
+
/**
|
|
101
|
+
* Description of role.
|
|
102
|
+
*/
|
|
95
103
|
description?: string
|
|
104
|
+
/**
|
|
105
|
+
* Notes for admins/approvers (Not for requestores)
|
|
106
|
+
*/
|
|
96
107
|
adminNotes?: string
|
|
97
108
|
}
|
|
98
109
|
|
|
99
|
-
/**
|
|
100
|
-
* Approver contact (person who approves, may differ from reach-out contact)
|
|
101
|
-
*/
|
|
102
|
-
export interface ApproverContact {
|
|
103
|
-
displayName: string
|
|
104
|
-
contact?: string
|
|
105
|
-
}
|
|
106
|
-
|
|
107
110
|
/**
|
|
108
111
|
* URL link with optional label
|
|
109
112
|
*/
|
|
@@ -113,39 +116,38 @@ export interface ApprovalUrl {
|
|
|
113
116
|
}
|
|
114
117
|
|
|
115
118
|
/**
|
|
116
|
-
*
|
|
117
|
-
* All comment/text-like strings are markdown
|
|
119
|
+
* Used to store ONLY instructions related to access request
|
|
118
120
|
*/
|
|
119
121
|
export interface AppAccessRequest {
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
122
|
+
/**
|
|
123
|
+
* Method of asking for access.
|
|
124
|
+
*/
|
|
125
|
+
approvalMethodSlug: string // FK to DbApprovalMethod
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Additional comments, if no other fields are fit.
|
|
129
|
+
*/
|
|
123
130
|
comments?: string
|
|
131
|
+
/**
|
|
132
|
+
* A template to put into a request ask.
|
|
133
|
+
*/
|
|
124
134
|
requestPrompt?: string
|
|
135
|
+
/**
|
|
136
|
+
* Recommended steps post approvel to get access to specific resources.
|
|
137
|
+
*/
|
|
125
138
|
postApprovalInstructions?: string
|
|
126
139
|
|
|
127
|
-
|
|
140
|
+
/**
|
|
141
|
+
* Available roles for given resource.
|
|
142
|
+
*/
|
|
128
143
|
roles?: AppRole[]
|
|
129
|
-
approvers?: ApproverContact[]
|
|
130
|
-
urls?: ApprovalUrl[]
|
|
131
|
-
|
|
132
|
-
// Type-specific (Person/Team only)
|
|
133
|
-
whoToReachOut?: string // markdown
|
|
134
|
-
}
|
|
135
144
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
export interface UpdateApprovalMethodInput {
|
|
147
|
-
id: string
|
|
148
|
-
type?: ApprovalMethodType
|
|
149
|
-
displayName?: string
|
|
150
|
-
config?: ApprovalMethodConfig
|
|
145
|
+
/**
|
|
146
|
+
* Individuals that will approve request within given approval method. No need to reach them directly unless specified.
|
|
147
|
+
*/
|
|
148
|
+
approverPersonSlugs?: string[] // slugs referencing Person entities
|
|
149
|
+
/**
|
|
150
|
+
* Additional instructions to get approvals
|
|
151
|
+
*/
|
|
152
|
+
urls?: ApprovalUrl[]
|
|
151
153
|
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Person & Group Types
|
|
3
|
+
*
|
|
4
|
+
* Persons represent individuals (employees, contacts).
|
|
5
|
+
* Groups represent named sets of persons (teams, distribution lists).
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
export interface Person {
|
|
9
|
+
slug: string
|
|
10
|
+
firstName: string
|
|
11
|
+
lastName: string
|
|
12
|
+
email?: string
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export interface Group {
|
|
16
|
+
slug: string
|
|
17
|
+
displayName?: string
|
|
18
|
+
email?: string
|
|
19
|
+
memberSlugs: string[]
|
|
20
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { AppAccessRequest } from './approvalMethodTypes'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* A sub-resource within an app.
|
|
5
|
+
* Example: an AWS account under the "AWS Console" app.
|
|
6
|
+
*/
|
|
7
|
+
export interface SubResource {
|
|
8
|
+
slug: string
|
|
9
|
+
displayName: string
|
|
10
|
+
description?: string
|
|
11
|
+
appSlug: string
|
|
12
|
+
familySlug?: string
|
|
13
|
+
tierSlug?: string
|
|
14
|
+
aliases: string[]
|
|
15
|
+
ownerPersonSlug?: string
|
|
16
|
+
accessMaintainerGroupSlugs: string[]
|
|
17
|
+
accessRequest?: AppAccessRequest
|
|
18
|
+
accessComments?: string
|
|
19
|
+
extra?: Record<string, unknown>
|
|
20
|
+
}
|
package/src/types/index.ts
CHANGED
|
@@ -3,6 +3,8 @@ export * from './common/sharedTypes.js'
|
|
|
3
3
|
export * from './common/resourceTypes.js'
|
|
4
4
|
export * from './common/appCatalogTypes.js'
|
|
5
5
|
export * from './common/approvalMethodTypes.js'
|
|
6
|
+
export * from './common/personGroupTypes.js'
|
|
7
|
+
export * from './common/subResourceTypes.js'
|
|
6
8
|
export * from './common/env/envTypes.js'
|
|
7
9
|
export * from './common/app/appTypes.js'
|
|
8
10
|
export * from './common/app/ui/appUiTypes.js'
|