@byline/cli 4.19.0 → 5.1.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/lib/copied-script-drift.test.d.ts +2 -0
- package/dist/lib/copied-script-drift.test.d.ts.map +1 -0
- package/dist/lib/copied-script-drift.test.js +37 -0
- package/dist/lib/copied-script-drift.test.js.map +1 -0
- package/dist/phases/wire/server-ts.d.ts.map +1 -1
- package/dist/phases/wire/server-ts.js +72 -17
- package/dist/phases/wire/server-ts.js.map +1 -1
- package/dist/phases/wire/server-ts.test.d.ts +2 -0
- package/dist/phases/wire/server-ts.test.d.ts.map +1 -0
- package/dist/phases/wire/server-ts.test.js +37 -0
- package/dist/phases/wire/server-ts.test.js.map +1 -0
- package/dist/phases/wire/start-ts.d.ts +7 -0
- package/dist/phases/wire/start-ts.d.ts.map +1 -1
- package/dist/phases/wire/start-ts.js +76 -10
- package/dist/phases/wire/start-ts.js.map +1 -1
- package/dist/phases/wire/start-ts.test.d.ts +9 -0
- package/dist/phases/wire/start-ts.test.d.ts.map +1 -0
- package/dist/phases/wire/start-ts.test.js +50 -0
- package/dist/phases/wire/start-ts.test.js.map +1 -0
- package/dist/templates/byline-examples/scripts/import-docs.ts +13 -19
- package/dist/templates/byline-examples/scripts/lib/walk-document-status.ts +21 -0
- package/dist/templates/byline-examples/scripts/regenerate-media-operation.ts +17 -69
- package/dist/templates/byline-examples/scripts/regenerate-media.ts +20 -23
- package/dist/templates/byline-examples/seeds/site-settings.ts +10 -7
- package/dist/templates/dialects/mysql/byline/server.config.ts +15 -1
- package/dist/templates/dialects/mysql/byline-examples/server.config.ts +15 -1
- package/dist/templates/dialects/postgres/byline/server.config.ts +15 -1
- package/dist/templates/dialects/postgres/byline-examples/scripts/re-anchor.ts +15 -2
- package/dist/templates/dialects/postgres/byline-examples/server.config.ts +15 -1
- package/dist/templates/migrations/mysql/{0000_clean_magdalene.sql → 0000_kind_exodus.sql} +30 -2
- package/dist/templates/migrations/mysql/meta/_journal.json +2 -2
- package/dist/templates/migrations/postgres/{0000_quiet_johnny_blaze.sql → 0000_lively_talisman.sql} +28 -2
- package/dist/templates/migrations/postgres/meta/_journal.json +2 -2
- package/package.json +8 -8
|
@@ -6,14 +6,10 @@
|
|
|
6
6
|
* storage.
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
-
import type {
|
|
10
|
-
import {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
type CollectionDefinition,
|
|
14
|
-
type IDbAdapter,
|
|
15
|
-
type StoredFileValue,
|
|
16
|
-
} from '@byline/core'
|
|
9
|
+
import type { StoredFileValue } from '@byline/core'
|
|
10
|
+
import { getDocumentHookCommittedDetails } from '@byline/core/services'
|
|
11
|
+
|
|
12
|
+
export { replaceDocumentFieldsPreservingStatus as replaceMediaVersionPreservingStatus } from '@byline/core/services'
|
|
17
13
|
|
|
18
14
|
export function storedFilePaths(value: StoredFileValue): Set<string> {
|
|
19
15
|
return new Set([
|
|
@@ -47,66 +43,18 @@ export function assertCompleteVariantSet(
|
|
|
47
43
|
}
|
|
48
44
|
}
|
|
49
45
|
|
|
50
|
-
/**
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
* ordinary application writes. Scripts are the supported escape hatch for
|
|
64
|
-
* migrations that must preserve metadata while still using the lifecycle for
|
|
65
|
-
* the content update itself.
|
|
66
|
-
*/
|
|
67
|
-
export async function replaceMediaVersionPreservingStatus(params: {
|
|
68
|
-
db: IDbAdapter
|
|
69
|
-
definition: CollectionDefinition
|
|
70
|
-
collectionId: string
|
|
71
|
-
handle: Pick<CollectionHandle, 'update'>
|
|
72
|
-
documentId: string
|
|
73
|
-
fields: Record<string, any>
|
|
74
|
-
targetStatus: string
|
|
75
|
-
}): Promise<{ documentVersionId: string }> {
|
|
76
|
-
const statuses = getWorkflowStatuses(params.definition).map((status) => status.name)
|
|
77
|
-
if (!statuses.includes(params.targetStatus)) {
|
|
78
|
-
throw new Error(
|
|
79
|
-
`regenerate-media: status '${params.targetStatus}' is not declared on collection ` +
|
|
80
|
-
`'${params.definition.path}' (declared: ${statuses.join(', ')}).`
|
|
81
|
-
)
|
|
46
|
+
/** Only rejected document writes may discard freshly prepared storage objects. */
|
|
47
|
+
export function mediaFailureOutcome(
|
|
48
|
+
error: unknown,
|
|
49
|
+
freshPaths: ReadonlySet<string>,
|
|
50
|
+
oldPaths: ReadonlySet<string>
|
|
51
|
+
):
|
|
52
|
+
| { committed: true; revision: number; cleanupPaths: Set<string> }
|
|
53
|
+
| { committed: false; cleanupPaths: Set<string> } {
|
|
54
|
+
const committed = getDocumentHookCommittedDetails(error)
|
|
55
|
+
if (committed) return { committed: true, revision: committed.revision, cleanupPaths: new Set() }
|
|
56
|
+
return {
|
|
57
|
+
committed: false,
|
|
58
|
+
cleanupPaths: new Set([...freshPaths].filter((path) => !oldPaths.has(path))),
|
|
82
59
|
}
|
|
83
|
-
|
|
84
|
-
const defaultStatus = getDefaultStatus(params.definition)
|
|
85
|
-
return params.db.withTransaction(async () => {
|
|
86
|
-
const result = await params.handle.update(params.documentId, params.fields)
|
|
87
|
-
if (params.targetStatus === defaultStatus) return result
|
|
88
|
-
|
|
89
|
-
await params.db.commands.documents.setDocumentStatus({
|
|
90
|
-
document_version_id: result.documentVersionId,
|
|
91
|
-
status: params.targetStatus,
|
|
92
|
-
})
|
|
93
|
-
if (params.targetStatus === 'published') {
|
|
94
|
-
await params.db.commands.documents.archivePublishedVersions({
|
|
95
|
-
document_id: params.documentId,
|
|
96
|
-
excludeVersionId: result.documentVersionId,
|
|
97
|
-
})
|
|
98
|
-
}
|
|
99
|
-
await params.db.commands.audit.append({
|
|
100
|
-
documentId: params.documentId,
|
|
101
|
-
collectionId: params.collectionId,
|
|
102
|
-
actorId: null,
|
|
103
|
-
actorRealm: 'system',
|
|
104
|
-
action: 'document.status.changed',
|
|
105
|
-
field: 'status',
|
|
106
|
-
before: defaultStatus,
|
|
107
|
-
after: params.targetStatus,
|
|
108
|
-
})
|
|
109
|
-
|
|
110
|
-
return result
|
|
111
|
-
})
|
|
112
60
|
}
|
|
@@ -61,17 +61,14 @@ import path from 'node:path'
|
|
|
61
61
|
|
|
62
62
|
import { createSuperAdminContext } from '@byline/auth'
|
|
63
63
|
import { createBylineClient } from '@byline/client'
|
|
64
|
-
import {
|
|
65
|
-
type FieldUploadContext,
|
|
66
|
-
getCollectionDefinition,
|
|
67
|
-
getServerConfig,
|
|
68
|
-
} from '@byline/core'
|
|
64
|
+
import { type FieldUploadContext, getCollectionDefinition, getServerConfig } from '@byline/core'
|
|
69
65
|
import { extractImageMeta, generateImageVariants, isBypassMimeType } from '@byline/core/image'
|
|
70
66
|
import { uploadField as coreUploadField } from '@byline/core/services'
|
|
71
|
-
|
|
72
67
|
import type { MediaFields } from '@byline/generated-types'
|
|
68
|
+
|
|
73
69
|
import {
|
|
74
70
|
assertCompleteVariantSet,
|
|
71
|
+
mediaFailureOutcome,
|
|
75
72
|
replaceMediaVersionPreservingStatus,
|
|
76
73
|
storedFilePaths,
|
|
77
74
|
} from './regenerate-media-operation.js'
|
|
@@ -120,14 +117,15 @@ async function run(): Promise<void> {
|
|
|
120
117
|
id: string
|
|
121
118
|
path: string
|
|
122
119
|
status: string
|
|
120
|
+
revision: number
|
|
123
121
|
fields: MediaFields
|
|
124
122
|
}[] = []
|
|
125
123
|
const pageSize = 100
|
|
126
124
|
for (let page = 1; ; page++) {
|
|
127
|
-
const result = await handle.
|
|
125
|
+
const result = await handle.findForEdit({
|
|
126
|
+
locale: 'all',
|
|
128
127
|
page,
|
|
129
128
|
pageSize,
|
|
130
|
-
status: 'any',
|
|
131
129
|
_bypassBeforeRead: true,
|
|
132
130
|
})
|
|
133
131
|
for (const d of result.docs) {
|
|
@@ -135,6 +133,7 @@ async function run(): Promise<void> {
|
|
|
135
133
|
id: d.id,
|
|
136
134
|
path: d.path,
|
|
137
135
|
status: d.status,
|
|
136
|
+
revision: d.revision,
|
|
138
137
|
fields: d.fields,
|
|
139
138
|
})
|
|
140
139
|
}
|
|
@@ -228,15 +227,10 @@ async function run(): Promise<void> {
|
|
|
228
227
|
}
|
|
229
228
|
|
|
230
229
|
const nextFields = { ...doc.fields, [FIELD_NAME]: uploadResult.storedFile }
|
|
231
|
-
await replaceMediaVersionPreservingStatus(
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
handle,
|
|
236
|
-
documentId: doc.id,
|
|
237
|
-
fields: nextFields,
|
|
238
|
-
targetStatus: doc.status,
|
|
239
|
-
})
|
|
230
|
+
await replaceMediaVersionPreservingStatus(
|
|
231
|
+
{ ...baseUploadCtx, requestContext },
|
|
232
|
+
{ documentId: doc.id, data: nextFields, expectedRevision: doc.revision }
|
|
233
|
+
)
|
|
240
234
|
|
|
241
235
|
const variantSummary =
|
|
242
236
|
uploadResult.storedFile.variants?.map((v) => `${v.name}:${v.format ?? '?'}`).join(', ') ??
|
|
@@ -246,11 +240,11 @@ async function run(): Promise<void> {
|
|
|
246
240
|
)
|
|
247
241
|
processed += 1
|
|
248
242
|
} catch (err) {
|
|
249
|
-
//
|
|
250
|
-
//
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
const cleanupErrors = await deletePathsBestEffort(storage,
|
|
243
|
+
// A post-commit hook failure must retain the files referenced by the saved version.
|
|
244
|
+
// Rejected writes discard only fresh paths, preserving any reused original path.
|
|
245
|
+
const outcome = mediaFailureOutcome(err, freshPaths, oldPaths)
|
|
246
|
+
if (outcome.committed) processed += 1
|
|
247
|
+
const cleanupErrors = await deletePathsBestEffort(storage, outcome.cleanupPaths)
|
|
254
248
|
const failure = asDocumentError(doc.id, doc.path, err)
|
|
255
249
|
failures.push(
|
|
256
250
|
cleanupErrors.length === 0
|
|
@@ -260,7 +254,10 @@ async function run(): Promise<void> {
|
|
|
260
254
|
`regenerate-media: operation and rollback cleanup failed for '${doc.path}'`
|
|
261
255
|
)
|
|
262
256
|
)
|
|
263
|
-
console.error(
|
|
257
|
+
console.error(
|
|
258
|
+
` ! ${outcome.committed ? `saved revision ${outcome.revision}; hook failed` : 'fail'} ${doc.id} (${doc.path}):`,
|
|
259
|
+
err
|
|
260
|
+
)
|
|
264
261
|
}
|
|
265
262
|
}
|
|
266
263
|
|
|
@@ -11,14 +11,17 @@ import { getSystemBylineClient } from '@byline/client/server'
|
|
|
11
11
|
/** Seed the singleton slot once, without overwriting later editorial changes. */
|
|
12
12
|
export async function seedSiteSettings(): Promise<'seeded' | 'unchanged'> {
|
|
13
13
|
const settings = getSystemBylineClient().singleton('site-settings')
|
|
14
|
-
const existing = await settings.
|
|
15
|
-
if (existing
|
|
14
|
+
const existing = await settings.getForEdit()
|
|
15
|
+
if (existing?.state !== 'empty') return 'unchanged'
|
|
16
16
|
|
|
17
|
-
await settings.update(
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
17
|
+
await settings.update(
|
|
18
|
+
{
|
|
19
|
+
siteName: 'Example site',
|
|
20
|
+
siteDescription:
|
|
21
|
+
'A concise description of this site for search results and social media previews.',
|
|
22
|
+
},
|
|
23
|
+
{ expectedState: 'empty' }
|
|
24
|
+
)
|
|
22
25
|
console.log('Seeded site settings')
|
|
23
26
|
return 'seeded'
|
|
24
27
|
}
|
|
@@ -14,7 +14,9 @@
|
|
|
14
14
|
*/
|
|
15
15
|
|
|
16
16
|
import { type AdminStore, registerAdminAbilities } from '@byline/admin'
|
|
17
|
-
import { JwtSessionProvider } from '@byline/admin/auth'
|
|
17
|
+
import { createPasswordSignInLimiter, JwtSessionProvider } from '@byline/admin/auth'
|
|
18
|
+
import { getRequestIP } from '@tanstack/react-start/server'
|
|
19
|
+
import { createClientIpResolver } from '@byline/host-tanstack-start/integrations/client-ip'
|
|
18
20
|
import { getAdminBylineClient } from '@byline/client/server'
|
|
19
21
|
import { type BylineCore, initBylineCore } from '@byline/core'
|
|
20
22
|
import { mysqlAdapter } from '@byline/db-mysql'
|
|
@@ -67,6 +69,8 @@ async function buildBylineCore(): Promise<BylineCore<AdminStore>> {
|
|
|
67
69
|
)
|
|
68
70
|
}
|
|
69
71
|
|
|
72
|
+
const signInLimiter = createPasswordSignInLimiter(adminStore.signInRateLimits, signingSecret)
|
|
73
|
+
|
|
70
74
|
const sessionProvider = new JwtSessionProvider({
|
|
71
75
|
store: adminStore,
|
|
72
76
|
signingSecret,
|
|
@@ -86,6 +90,16 @@ async function buildBylineCore(): Promise<BylineCore<AdminStore>> {
|
|
|
86
90
|
baseUrl: '/uploads',
|
|
87
91
|
}),
|
|
88
92
|
sessionProvider,
|
|
93
|
+
recurringTasks: [signInLimiter.cleanupTask],
|
|
94
|
+
passwordSignIn: {
|
|
95
|
+
limiter: signInLimiter,
|
|
96
|
+
resolveClientIp: createClientIpResolver({
|
|
97
|
+
// Opt in only when the proxy overwrites this header and direct access is blocked.
|
|
98
|
+
trustedProxyHeader: process.env.BYLINE_TRUSTED_CLIENT_IP_HEADER || undefined,
|
|
99
|
+
resolvePeerIp: () => getRequestIP() ??
|
|
100
|
+
(process.env.NODE_ENV === 'development' ? '127.0.0.1' : null),
|
|
101
|
+
}),
|
|
102
|
+
},
|
|
89
103
|
fields: {
|
|
90
104
|
richText: {
|
|
91
105
|
embed: lexicalEditorEmbedServer({ getClient: getAdminBylineClient }),
|
|
@@ -11,7 +11,9 @@
|
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
13
|
import { type AdminStore, registerAdminAbilities } from '@byline/admin'
|
|
14
|
-
import { JwtSessionProvider } from '@byline/admin/auth'
|
|
14
|
+
import { createPasswordSignInLimiter, JwtSessionProvider } from '@byline/admin/auth'
|
|
15
|
+
import { getRequestIP } from '@tanstack/react-start/server'
|
|
16
|
+
import { createClientIpResolver } from '@byline/host-tanstack-start/integrations/client-ip'
|
|
15
17
|
import { getAdminBylineClient } from '@byline/client/server'
|
|
16
18
|
import { type BylineCore, initBylineCore } from '@byline/core'
|
|
17
19
|
import { mysqlAdapter } from '@byline/db-mysql'
|
|
@@ -75,6 +77,8 @@ async function buildBylineCore(): Promise<BylineCore<AdminStore>> {
|
|
|
75
77
|
)
|
|
76
78
|
}
|
|
77
79
|
|
|
80
|
+
const signInLimiter = createPasswordSignInLimiter(adminStore.signInRateLimits, signingSecret)
|
|
81
|
+
|
|
78
82
|
const sessionProvider = new JwtSessionProvider({
|
|
79
83
|
store: adminStore,
|
|
80
84
|
signingSecret,
|
|
@@ -94,6 +98,16 @@ async function buildBylineCore(): Promise<BylineCore<AdminStore>> {
|
|
|
94
98
|
baseUrl: '/uploads',
|
|
95
99
|
}),
|
|
96
100
|
sessionProvider,
|
|
101
|
+
recurringTasks: [signInLimiter.cleanupTask],
|
|
102
|
+
passwordSignIn: {
|
|
103
|
+
limiter: signInLimiter,
|
|
104
|
+
resolveClientIp: createClientIpResolver({
|
|
105
|
+
// Opt in only when the proxy overwrites this header and direct access is blocked.
|
|
106
|
+
trustedProxyHeader: process.env.BYLINE_TRUSTED_CLIENT_IP_HEADER || undefined,
|
|
107
|
+
resolvePeerIp: () => getRequestIP() ??
|
|
108
|
+
(process.env.NODE_ENV === 'development' ? '127.0.0.1' : null),
|
|
109
|
+
}),
|
|
110
|
+
},
|
|
97
111
|
fields: {
|
|
98
112
|
richText: {
|
|
99
113
|
embed: lexicalEditorEmbedServer({ getClient: getAdminBylineClient }),
|
|
@@ -18,7 +18,9 @@
|
|
|
18
18
|
*/
|
|
19
19
|
|
|
20
20
|
import { type AdminStore, registerAdminAbilities } from '@byline/admin'
|
|
21
|
-
import { JwtSessionProvider } from '@byline/admin/auth'
|
|
21
|
+
import { createPasswordSignInLimiter, JwtSessionProvider } from '@byline/admin/auth'
|
|
22
|
+
import { getRequestIP } from '@tanstack/react-start/server'
|
|
23
|
+
import { createClientIpResolver } from '@byline/host-tanstack-start/integrations/client-ip'
|
|
22
24
|
import { type BylineCore, initBylineCore } from '@byline/core'
|
|
23
25
|
import { pgAdapter } from '@byline/db-postgres'
|
|
24
26
|
import { createAdminStore } from '@byline/db-postgres/admin'
|
|
@@ -82,6 +84,8 @@ async function buildBylineCore(): Promise<BylineCore<AdminStore>> {
|
|
|
82
84
|
)
|
|
83
85
|
}
|
|
84
86
|
|
|
87
|
+
const signInLimiter = createPasswordSignInLimiter(adminStore.signInRateLimits, signingSecret)
|
|
88
|
+
|
|
85
89
|
const sessionProvider = new JwtSessionProvider({
|
|
86
90
|
store: adminStore,
|
|
87
91
|
signingSecret,
|
|
@@ -136,6 +140,16 @@ async function buildBylineCore(): Promise<BylineCore<AdminStore>> {
|
|
|
136
140
|
// cacheControl: 'public, max-age=31536000, immutable',
|
|
137
141
|
// }),
|
|
138
142
|
sessionProvider,
|
|
143
|
+
recurringTasks: [signInLimiter.cleanupTask],
|
|
144
|
+
passwordSignIn: {
|
|
145
|
+
limiter: signInLimiter,
|
|
146
|
+
resolveClientIp: createClientIpResolver({
|
|
147
|
+
// Opt in only when the proxy overwrites this header and direct access is blocked.
|
|
148
|
+
trustedProxyHeader: process.env.BYLINE_TRUSTED_CLIENT_IP_HEADER || undefined,
|
|
149
|
+
resolvePeerIp: () => getRequestIP() ??
|
|
150
|
+
(process.env.NODE_ENV === 'development' ? '127.0.0.1' : null),
|
|
151
|
+
}),
|
|
152
|
+
},
|
|
139
153
|
fields: {
|
|
140
154
|
richText: {
|
|
141
155
|
embed: lexicalEditorEmbedServer({ getClient: getAdminBylineClient }),
|
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
* listed — that list is the outstanding-translation backlog. Re-run as
|
|
17
17
|
* translation progresses; it is idempotent (each doc is its own transaction).
|
|
18
18
|
*
|
|
19
|
+
* From the application directory containing byline/:
|
|
19
20
|
* pnpm tsx byline/scripts/re-anchor.ts --to fr
|
|
20
21
|
* pnpm tsx byline/scripts/re-anchor.ts --to fr --collection pages
|
|
21
22
|
* pnpm tsx byline/scripts/re-anchor.ts --to fr --dry-run
|
|
@@ -28,7 +29,7 @@ import '../server.config.js'
|
|
|
28
29
|
|
|
29
30
|
import { parseArgs } from 'node:util'
|
|
30
31
|
|
|
31
|
-
import { getBylineCore, getServerConfig } from '@byline/core'
|
|
32
|
+
import { documentRevisionFromDatabase, getBylineCore, getServerConfig } from '@byline/core'
|
|
32
33
|
import type { PgAdapter } from '@byline/db-postgres'
|
|
33
34
|
|
|
34
35
|
const USAGE =
|
|
@@ -74,7 +75,19 @@ async function run() {
|
|
|
74
75
|
`${dryRun ? '[dry-run] ' : ''}re-anchoring ${scope} → content source locale '${targetLocale}'…`
|
|
75
76
|
)
|
|
76
77
|
|
|
77
|
-
|
|
78
|
+
// Capture each precondition before starting the batch; never refresh and retry a stale target.
|
|
79
|
+
const observed = await db.pool.query<{ document_id: string; revision: string }>(
|
|
80
|
+
`SELECT DISTINCT d.id AS document_id, d.revision
|
|
81
|
+
FROM byline_documents d JOIN byline_document_versions v ON v.document_id = d.id
|
|
82
|
+
WHERE v.is_deleted = false AND ($1::uuid IS NULL OR v.collection_id = $1::uuid)
|
|
83
|
+
ORDER BY d.id`,
|
|
84
|
+
[collectionId ?? null]
|
|
85
|
+
)
|
|
86
|
+
const targets = observed.rows.map((row) => ({
|
|
87
|
+
documentId: row.document_id,
|
|
88
|
+
expectedRevision: documentRevisionFromDatabase(row.revision),
|
|
89
|
+
}))
|
|
90
|
+
const report = await db.reAnchorDocuments({ targets, targetLocale, collectionId, dryRun })
|
|
78
91
|
|
|
79
92
|
console.log(
|
|
80
93
|
`${dryRun ? '[dry-run] would re-anchor' : '✓ re-anchored'} ${report.reanchored}/${report.total} document(s) → '${targetLocale}'`
|
|
@@ -15,7 +15,9 @@
|
|
|
15
15
|
*/
|
|
16
16
|
|
|
17
17
|
import { type AdminStore, registerAdminAbilities } from '@byline/admin'
|
|
18
|
-
import { JwtSessionProvider } from '@byline/admin/auth'
|
|
18
|
+
import { createPasswordSignInLimiter, JwtSessionProvider } from '@byline/admin/auth'
|
|
19
|
+
import { getRequestIP } from '@tanstack/react-start/server'
|
|
20
|
+
import { createClientIpResolver } from '@byline/host-tanstack-start/integrations/client-ip'
|
|
19
21
|
import { type BylineCore, initBylineCore } from '@byline/core'
|
|
20
22
|
import { pgAdapter } from '@byline/db-postgres'
|
|
21
23
|
import { createAdminStore } from '@byline/db-postgres/admin'
|
|
@@ -130,6 +132,8 @@ async function buildBylineCore(): Promise<BylineCore<AdminStore>> {
|
|
|
130
132
|
)
|
|
131
133
|
}
|
|
132
134
|
|
|
135
|
+
const signInLimiter = createPasswordSignInLimiter(adminStore.signInRateLimits, signingSecret)
|
|
136
|
+
|
|
133
137
|
const sessionProvider = new JwtSessionProvider({
|
|
134
138
|
store: adminStore,
|
|
135
139
|
signingSecret,
|
|
@@ -190,6 +194,16 @@ async function buildBylineCore(): Promise<BylineCore<AdminStore>> {
|
|
|
190
194
|
// cacheControl: 'public, max-age=31536000, immutable',
|
|
191
195
|
// }),
|
|
192
196
|
sessionProvider,
|
|
197
|
+
recurringTasks: [signInLimiter.cleanupTask],
|
|
198
|
+
passwordSignIn: {
|
|
199
|
+
limiter: signInLimiter,
|
|
200
|
+
resolveClientIp: createClientIpResolver({
|
|
201
|
+
// Opt in only when the proxy overwrites this header and direct access is blocked.
|
|
202
|
+
trustedProxyHeader: process.env.BYLINE_TRUSTED_CLIENT_IP_HEADER || undefined,
|
|
203
|
+
resolvePeerIp: () => getRequestIP() ??
|
|
204
|
+
(process.env.NODE_ENV === 'development' ? '127.0.0.1' : null),
|
|
205
|
+
}),
|
|
206
|
+
},
|
|
193
207
|
fields: {
|
|
194
208
|
// Server-side richtext adapter — refreshes embedded relation
|
|
195
209
|
// envelopes (link `{ title, path }`, inline-image `{ title, altText,
|
|
@@ -1,3 +1,12 @@
|
|
|
1
|
+
CREATE TABLE `byline_admin_login_sessions` (
|
|
2
|
+
`id` char(36) CHARACTER SET ascii COLLATE ascii_bin NOT NULL,
|
|
3
|
+
`admin_user_id` char(36) CHARACTER SET ascii COLLATE ascii_bin NOT NULL,
|
|
4
|
+
`session_version` int NOT NULL,
|
|
5
|
+
`expires_at` datetime(6) NOT NULL,
|
|
6
|
+
`revoked_at` datetime(6),
|
|
7
|
+
CONSTRAINT `byline_admin_login_sessions_id` PRIMARY KEY(`id`)
|
|
8
|
+
);
|
|
9
|
+
--> statement-breakpoint
|
|
1
10
|
CREATE TABLE `byline_admin_permissions` (
|
|
2
11
|
`id` char(36) CHARACTER SET ascii COLLATE ascii_bin NOT NULL,
|
|
3
12
|
`vid` int NOT NULL DEFAULT 1,
|
|
@@ -12,7 +21,9 @@ CREATE TABLE `byline_admin_permissions` (
|
|
|
12
21
|
CREATE TABLE `byline_admin_refresh_tokens` (
|
|
13
22
|
`id` char(36) CHARACTER SET ascii COLLATE ascii_bin NOT NULL,
|
|
14
23
|
`admin_user_id` char(36) CHARACTER SET ascii COLLATE ascii_bin NOT NULL,
|
|
24
|
+
`sid` char(36) CHARACTER SET ascii COLLATE ascii_bin,
|
|
15
25
|
`token_hash` varchar(64) NOT NULL,
|
|
26
|
+
`session_version` int NOT NULL DEFAULT -1,
|
|
16
27
|
`issued_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6),
|
|
17
28
|
`expires_at` datetime(6) NOT NULL,
|
|
18
29
|
`revoked_at` datetime(6),
|
|
@@ -46,6 +57,13 @@ CREATE TABLE `byline_admin_roles` (
|
|
|
46
57
|
CONSTRAINT `byline_admin_roles_machine_name_unique` UNIQUE(`machine_name`)
|
|
47
58
|
);
|
|
48
59
|
--> statement-breakpoint
|
|
60
|
+
CREATE TABLE `byline_admin_sign_in_rate_limits` (
|
|
61
|
+
`key` varchar(64) NOT NULL,
|
|
62
|
+
`attempts` int NOT NULL,
|
|
63
|
+
`expires_at` datetime(3) NOT NULL,
|
|
64
|
+
CONSTRAINT `byline_admin_sign_in_rate_limits_key` PRIMARY KEY(`key`)
|
|
65
|
+
);
|
|
66
|
+
--> statement-breakpoint
|
|
49
67
|
CREATE TABLE `byline_admin_user_preferences` (
|
|
50
68
|
`user_id` char(36) CHARACTER SET ascii COLLATE ascii_bin NOT NULL,
|
|
51
69
|
`scope` varchar(255) NOT NULL,
|
|
@@ -57,6 +75,7 @@ CREATE TABLE `byline_admin_user_preferences` (
|
|
|
57
75
|
--> statement-breakpoint
|
|
58
76
|
CREATE TABLE `byline_admin_users` (
|
|
59
77
|
`id` char(36) CHARACTER SET ascii COLLATE ascii_bin NOT NULL,
|
|
78
|
+
`session_version` int NOT NULL DEFAULT 0,
|
|
60
79
|
`vid` int NOT NULL DEFAULT 1,
|
|
61
80
|
`given_name` varchar(100),
|
|
62
81
|
`family_name` varchar(100),
|
|
@@ -173,6 +192,7 @@ CREATE TABLE `byline_document_publish_schedules` (
|
|
|
173
192
|
`document_id` char(36) CHARACTER SET ascii COLLATE ascii_bin NOT NULL,
|
|
174
193
|
`collection_id` char(36) CHARACTER SET ascii COLLATE ascii_bin NOT NULL,
|
|
175
194
|
`target_version_id` char(36) CHARACTER SET ascii COLLATE ascii_bin NOT NULL,
|
|
195
|
+
`authorized_revision` bigint,
|
|
176
196
|
`publish_at` datetime(6) NOT NULL,
|
|
177
197
|
`state` varchar(32) NOT NULL DEFAULT 'armed',
|
|
178
198
|
`suspended_at` datetime(6),
|
|
@@ -189,8 +209,9 @@ CREATE TABLE `byline_document_publish_schedules` (
|
|
|
189
209
|
`attempt_count` int NOT NULL DEFAULT 0,
|
|
190
210
|
`last_error` text,
|
|
191
211
|
CONSTRAINT `byline_document_publish_schedules_document_id` PRIMARY KEY(`document_id`),
|
|
212
|
+
CONSTRAINT `check_publish_schedules_authorized_revision` CHECK(`byline_document_publish_schedules`.`authorized_revision` IS NULL OR `byline_document_publish_schedules`.`authorized_revision` BETWEEN 1 AND 9007199254740991),
|
|
192
213
|
CONSTRAINT `check_publish_schedules_state` CHECK(`byline_document_publish_schedules`.`state` IN ('armed', 'needs_reconfirm')),
|
|
193
|
-
CONSTRAINT `check_publish_schedules_suspended_reason` CHECK(`byline_document_publish_schedules`.`suspended_reason` IS NULL OR `byline_document_publish_schedules`.`suspended_reason`
|
|
214
|
+
CONSTRAINT `check_publish_schedules_suspended_reason` CHECK(`byline_document_publish_schedules`.`suspended_reason` IS NULL OR `byline_document_publish_schedules`.`suspended_reason` IN ('content_edited', 'document_metadata_changed', 'upgrade_invalidated'))
|
|
194
215
|
);
|
|
195
216
|
--> statement-breakpoint
|
|
196
217
|
CREATE TABLE `byline_document_relationships` (
|
|
@@ -229,10 +250,12 @@ CREATE TABLE `byline_documents` (
|
|
|
229
250
|
`collection_id` char(36) CHARACTER SET ascii COLLATE ascii_bin NOT NULL,
|
|
230
251
|
`order_key` varchar(128) CHARACTER SET ascii COLLATE ascii_bin,
|
|
231
252
|
`source_locale` varchar(10) NOT NULL,
|
|
253
|
+
`revision` bigint NOT NULL,
|
|
232
254
|
`created_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6),
|
|
233
255
|
`updated_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6),
|
|
234
256
|
CONSTRAINT `byline_documents_id` PRIMARY KEY(`id`),
|
|
235
|
-
CONSTRAINT `uq_documents_collection_id_id` UNIQUE(`collection_id`,`id`)
|
|
257
|
+
CONSTRAINT `uq_documents_collection_id_id` UNIQUE(`collection_id`,`id`),
|
|
258
|
+
CONSTRAINT `check_documents_revision` CHECK(`byline_documents`.`revision` BETWEEN 1 AND 9007199254740991)
|
|
236
259
|
);
|
|
237
260
|
--> statement-breakpoint
|
|
238
261
|
CREATE TABLE `byline_store_file` (
|
|
@@ -373,7 +396,9 @@ CREATE TABLE `byline_store_text` (
|
|
|
373
396
|
CONSTRAINT `unique_text_field` UNIQUE(`document_version_id`,`field_path`,`locale`)
|
|
374
397
|
);
|
|
375
398
|
--> statement-breakpoint
|
|
399
|
+
ALTER TABLE `byline_admin_login_sessions` ADD CONSTRAINT `fk_admin_login_sessions_user` FOREIGN KEY (`admin_user_id`) REFERENCES `byline_admin_users`(`id`) ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
|
376
400
|
ALTER TABLE `byline_admin_permissions` ADD CONSTRAINT `fk_admin_permissions_admin_role_id` FOREIGN KEY (`admin_role_id`) REFERENCES `byline_admin_roles`(`id`) ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
|
401
|
+
ALTER TABLE `byline_admin_refresh_tokens` ADD CONSTRAINT `fk_admin_refresh_tokens_sid` FOREIGN KEY (`sid`) REFERENCES `byline_admin_login_sessions`(`id`) ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
|
377
402
|
ALTER TABLE `byline_admin_refresh_tokens` ADD CONSTRAINT `fk_admin_refresh_tokens_admin_user_id` FOREIGN KEY (`admin_user_id`) REFERENCES `byline_admin_users`(`id`) ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
|
378
403
|
ALTER TABLE `byline_admin_role_admin_user` ADD CONSTRAINT `fk_admin_role_admin_user_admin_role_id` FOREIGN KEY (`admin_role_id`) REFERENCES `byline_admin_roles`(`id`) ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
|
379
404
|
ALTER TABLE `byline_admin_role_admin_user` ADD CONSTRAINT `fk_admin_role_admin_user_admin_user_id` FOREIGN KEY (`admin_user_id`) REFERENCES `byline_admin_users`(`id`) ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
|
@@ -410,11 +435,14 @@ ALTER TABLE `byline_store_relation` ADD CONSTRAINT `fk_store_relation_target_col
|
|
|
410
435
|
ALTER TABLE `byline_singleton_documents` ADD CONSTRAINT `fk_singleton_documents_document` FOREIGN KEY (`collection_id`,`document_id`) REFERENCES `byline_documents`(`collection_id`,`id`) ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
|
411
436
|
ALTER TABLE `byline_store_text` ADD CONSTRAINT `fk_store_text_document_version_id` FOREIGN KEY (`document_version_id`) REFERENCES `byline_document_versions`(`id`) ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
|
412
437
|
ALTER TABLE `byline_store_text` ADD CONSTRAINT `fk_store_text_collection_id` FOREIGN KEY (`collection_id`) REFERENCES `byline_collections`(`id`) ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
|
438
|
+
CREATE INDEX `idx_admin_login_sessions_user` ON `byline_admin_login_sessions` (`admin_user_id`);--> statement-breakpoint
|
|
413
439
|
CREATE INDEX `idx_byline_admin_permissions_role` ON `byline_admin_permissions` (`admin_role_id`);--> statement-breakpoint
|
|
440
|
+
CREATE INDEX `idx_admin_refresh_tokens_sid` ON `byline_admin_refresh_tokens` (`sid`);--> statement-breakpoint
|
|
414
441
|
CREATE INDEX `idx_byline_admin_refresh_tokens_user` ON `byline_admin_refresh_tokens` (`admin_user_id`);--> statement-breakpoint
|
|
415
442
|
CREATE INDEX `idx_byline_admin_refresh_tokens_token_hash` ON `byline_admin_refresh_tokens` (`token_hash`);--> statement-breakpoint
|
|
416
443
|
CREATE INDEX `idx_byline_admin_role_admin_user_user` ON `byline_admin_role_admin_user` (`admin_user_id`);--> statement-breakpoint
|
|
417
444
|
CREATE INDEX `idx_byline_admin_roles_machine_name` ON `byline_admin_roles` (`machine_name`);--> statement-breakpoint
|
|
445
|
+
CREATE INDEX `idx_admin_sign_in_rate_limits_expiry` ON `byline_admin_sign_in_rate_limits` (`expires_at`);--> statement-breakpoint
|
|
418
446
|
CREATE INDEX `idx_byline_admin_users_email` ON `byline_admin_users` (`email`);--> statement-breakpoint
|
|
419
447
|
CREATE INDEX `idx_audit_log_document_id` ON `byline_audit_log` (`document_id`,`id`);--> statement-breakpoint
|
|
420
448
|
CREATE INDEX `idx_audit_log_actor_id` ON `byline_audit_log` (`actor_id`,`id`);--> statement-breakpoint
|
package/dist/templates/migrations/postgres/{0000_quiet_johnny_blaze.sql → 0000_lively_talisman.sql}
RENAMED
|
@@ -1,3 +1,11 @@
|
|
|
1
|
+
CREATE TABLE "byline_admin_login_sessions" (
|
|
2
|
+
"id" uuid PRIMARY KEY NOT NULL,
|
|
3
|
+
"admin_user_id" uuid NOT NULL,
|
|
4
|
+
"session_version" integer NOT NULL,
|
|
5
|
+
"expires_at" timestamp (6) with time zone NOT NULL,
|
|
6
|
+
"revoked_at" timestamp (6) with time zone
|
|
7
|
+
);
|
|
8
|
+
--> statement-breakpoint
|
|
1
9
|
CREATE TABLE "byline_admin_permissions" (
|
|
2
10
|
"id" uuid PRIMARY KEY NOT NULL,
|
|
3
11
|
"vid" integer DEFAULT 1 NOT NULL,
|
|
@@ -11,7 +19,9 @@ CREATE TABLE "byline_admin_permissions" (
|
|
|
11
19
|
CREATE TABLE "byline_admin_refresh_tokens" (
|
|
12
20
|
"id" uuid PRIMARY KEY NOT NULL,
|
|
13
21
|
"admin_user_id" uuid NOT NULL,
|
|
22
|
+
"sid" uuid,
|
|
14
23
|
"token_hash" varchar(64) NOT NULL,
|
|
24
|
+
"session_version" integer DEFAULT -1 NOT NULL,
|
|
15
25
|
"issued_at" timestamp (6) with time zone DEFAULT now() NOT NULL,
|
|
16
26
|
"expires_at" timestamp (6) with time zone NOT NULL,
|
|
17
27
|
"revoked_at" timestamp (6) with time zone,
|
|
@@ -43,6 +53,12 @@ CREATE TABLE "byline_admin_roles" (
|
|
|
43
53
|
CONSTRAINT "byline_admin_roles_machine_name_unique" UNIQUE("machine_name")
|
|
44
54
|
);
|
|
45
55
|
--> statement-breakpoint
|
|
56
|
+
CREATE TABLE "byline_admin_sign_in_rate_limits" (
|
|
57
|
+
"key" varchar(64) PRIMARY KEY NOT NULL,
|
|
58
|
+
"attempts" integer NOT NULL,
|
|
59
|
+
"expires_at" timestamp (3) with time zone NOT NULL
|
|
60
|
+
);
|
|
61
|
+
--> statement-breakpoint
|
|
46
62
|
CREATE TABLE "byline_admin_user_preferences" (
|
|
47
63
|
"user_id" uuid NOT NULL,
|
|
48
64
|
"scope" varchar(255) NOT NULL,
|
|
@@ -54,6 +70,7 @@ CREATE TABLE "byline_admin_user_preferences" (
|
|
|
54
70
|
--> statement-breakpoint
|
|
55
71
|
CREATE TABLE "byline_admin_users" (
|
|
56
72
|
"id" uuid PRIMARY KEY NOT NULL,
|
|
73
|
+
"session_version" integer DEFAULT 0 NOT NULL,
|
|
57
74
|
"vid" integer DEFAULT 1 NOT NULL,
|
|
58
75
|
"given_name" varchar(100),
|
|
59
76
|
"family_name" varchar(100),
|
|
@@ -163,6 +180,7 @@ CREATE TABLE "byline_document_publish_schedules" (
|
|
|
163
180
|
"document_id" uuid PRIMARY KEY NOT NULL,
|
|
164
181
|
"collection_id" uuid NOT NULL,
|
|
165
182
|
"target_version_id" uuid NOT NULL,
|
|
183
|
+
"authorized_revision" bigint,
|
|
166
184
|
"publish_at" timestamp (6) with time zone NOT NULL,
|
|
167
185
|
"state" varchar(32) DEFAULT 'armed' NOT NULL,
|
|
168
186
|
"suspended_at" timestamp (6) with time zone,
|
|
@@ -178,8 +196,9 @@ CREATE TABLE "byline_document_publish_schedules" (
|
|
|
178
196
|
"next_attempt_at" timestamp (6) with time zone NOT NULL,
|
|
179
197
|
"attempt_count" integer DEFAULT 0 NOT NULL,
|
|
180
198
|
"last_error" text,
|
|
199
|
+
CONSTRAINT "check_publish_schedules_authorized_revision" CHECK ("byline_document_publish_schedules"."authorized_revision" IS NULL OR "byline_document_publish_schedules"."authorized_revision" BETWEEN 1 AND 9007199254740991),
|
|
181
200
|
CONSTRAINT "check_document_publish_schedules_state" CHECK ("byline_document_publish_schedules"."state" IN ('armed', 'needs_reconfirm')),
|
|
182
|
-
CONSTRAINT "check_document_publish_schedules_suspended_reason" CHECK ("byline_document_publish_schedules"."suspended_reason" IS NULL OR "byline_document_publish_schedules"."suspended_reason"
|
|
201
|
+
CONSTRAINT "check_document_publish_schedules_suspended_reason" CHECK ("byline_document_publish_schedules"."suspended_reason" IS NULL OR "byline_document_publish_schedules"."suspended_reason" IN ('content_edited', 'document_metadata_changed', 'upgrade_invalidated'))
|
|
183
202
|
);
|
|
184
203
|
--> statement-breakpoint
|
|
185
204
|
CREATE TABLE "byline_document_relationships" (
|
|
@@ -217,9 +236,11 @@ CREATE TABLE "byline_documents" (
|
|
|
217
236
|
"collection_id" uuid NOT NULL,
|
|
218
237
|
"order_key" varchar(128) COLLATE "C",
|
|
219
238
|
"source_locale" varchar(10) NOT NULL,
|
|
239
|
+
"revision" bigint NOT NULL,
|
|
220
240
|
"created_at" timestamp (6) with time zone DEFAULT now() NOT NULL,
|
|
221
241
|
"updated_at" timestamp (6) with time zone DEFAULT now() NOT NULL,
|
|
222
|
-
CONSTRAINT "uq_documents_collection_id_id" UNIQUE("collection_id","id")
|
|
242
|
+
CONSTRAINT "uq_documents_collection_id_id" UNIQUE("collection_id","id"),
|
|
243
|
+
CONSTRAINT "check_documents_revision" CHECK ("byline_documents"."revision" BETWEEN 1 AND 9007199254740991)
|
|
223
244
|
);
|
|
224
245
|
--> statement-breakpoint
|
|
225
246
|
CREATE TABLE "byline_store_file" (
|
|
@@ -352,8 +373,10 @@ CREATE TABLE "byline_store_text" (
|
|
|
352
373
|
CONSTRAINT "unique_text_field" UNIQUE("document_version_id","field_path","locale")
|
|
353
374
|
);
|
|
354
375
|
--> statement-breakpoint
|
|
376
|
+
ALTER TABLE "byline_admin_login_sessions" ADD CONSTRAINT "byline_admin_login_sessions_admin_user_id_byline_admin_users_id_fk" FOREIGN KEY ("admin_user_id") REFERENCES "public"."byline_admin_users"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
|
355
377
|
ALTER TABLE "byline_admin_permissions" ADD CONSTRAINT "byline_admin_permissions_admin_role_id_byline_admin_roles_id_fk" FOREIGN KEY ("admin_role_id") REFERENCES "public"."byline_admin_roles"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
|
356
378
|
ALTER TABLE "byline_admin_refresh_tokens" ADD CONSTRAINT "byline_admin_refresh_tokens_admin_user_id_byline_admin_users_id_fk" FOREIGN KEY ("admin_user_id") REFERENCES "public"."byline_admin_users"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
|
379
|
+
ALTER TABLE "byline_admin_refresh_tokens" ADD CONSTRAINT "byline_admin_refresh_tokens_sid_byline_admin_login_sessions_id_fk" FOREIGN KEY ("sid") REFERENCES "public"."byline_admin_login_sessions"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
|
357
380
|
ALTER TABLE "byline_admin_role_admin_user" ADD CONSTRAINT "byline_admin_role_admin_user_admin_role_id_byline_admin_roles_id_fk" FOREIGN KEY ("admin_role_id") REFERENCES "public"."byline_admin_roles"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
|
358
381
|
ALTER TABLE "byline_admin_role_admin_user" ADD CONSTRAINT "byline_admin_role_admin_user_admin_user_id_byline_admin_users_id_fk" FOREIGN KEY ("admin_user_id") REFERENCES "public"."byline_admin_users"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
|
359
382
|
ALTER TABLE "byline_admin_user_preferences" ADD CONSTRAINT "byline_admin_user_preferences_user_id_byline_admin_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."byline_admin_users"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
|
@@ -389,11 +412,14 @@ ALTER TABLE "byline_store_relation" ADD CONSTRAINT "byline_store_relation_target
|
|
|
389
412
|
ALTER TABLE "byline_singleton_documents" ADD CONSTRAINT "fk_singleton_documents_document" FOREIGN KEY ("collection_id","document_id") REFERENCES "public"."byline_documents"("collection_id","id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
|
390
413
|
ALTER TABLE "byline_store_text" ADD CONSTRAINT "byline_store_text_document_version_id_byline_document_versions_id_fk" FOREIGN KEY ("document_version_id") REFERENCES "public"."byline_document_versions"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
|
391
414
|
ALTER TABLE "byline_store_text" ADD CONSTRAINT "byline_store_text_collection_id_byline_collections_id_fk" FOREIGN KEY ("collection_id") REFERENCES "public"."byline_collections"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
|
415
|
+
CREATE INDEX "idx_admin_login_sessions_user" ON "byline_admin_login_sessions" USING btree ("admin_user_id");--> statement-breakpoint
|
|
392
416
|
CREATE INDEX "idx_byline_admin_permissions_role" ON "byline_admin_permissions" USING btree ("admin_role_id");--> statement-breakpoint
|
|
417
|
+
CREATE INDEX "idx_admin_refresh_tokens_sid" ON "byline_admin_refresh_tokens" USING btree ("sid");--> statement-breakpoint
|
|
393
418
|
CREATE INDEX "idx_byline_admin_refresh_tokens_user" ON "byline_admin_refresh_tokens" USING btree ("admin_user_id");--> statement-breakpoint
|
|
394
419
|
CREATE INDEX "idx_byline_admin_refresh_tokens_token_hash" ON "byline_admin_refresh_tokens" USING btree ("token_hash");--> statement-breakpoint
|
|
395
420
|
CREATE INDEX "idx_byline_admin_role_admin_user_user" ON "byline_admin_role_admin_user" USING btree ("admin_user_id");--> statement-breakpoint
|
|
396
421
|
CREATE INDEX "idx_byline_admin_roles_machine_name" ON "byline_admin_roles" USING btree ("machine_name");--> statement-breakpoint
|
|
422
|
+
CREATE INDEX "idx_admin_sign_in_rate_limits_expiry" ON "byline_admin_sign_in_rate_limits" USING btree ("expires_at");--> statement-breakpoint
|
|
397
423
|
CREATE INDEX "idx_byline_admin_users_email" ON "byline_admin_users" USING btree ("email");--> statement-breakpoint
|
|
398
424
|
CREATE INDEX "idx_audit_log_document_id" ON "byline_audit_log" USING btree ("document_id","id");--> statement-breakpoint
|
|
399
425
|
CREATE INDEX "idx_audit_log_actor_id" ON "byline_audit_log" USING btree ("actor_id","id");--> statement-breakpoint
|