@stacksjs/database 0.70.366 → 0.70.367
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/auth-tables.js +10 -10
- package/dist/notification-tables.js +6 -6
- package/dist/rbac-tables.js +10 -10
- package/dist/sql-helpers.d.ts +1 -0
- package/dist/sql-helpers.js +1 -1
- package/dist/trait-tables.js +9 -9
- package/package.json +10 -10
package/dist/auth-tables.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import process from"node:process";import{randomBytes}from"node:crypto";import{log}from"@stacksjs/logging";import{env as envVars}from"@stacksjs/env";import{db}from"./utils";import{sqlHelpers}from"./sql-helpers";import{indexSqlForDialect}from"./dialect";function getDbDriver(){return envVars.DB_CONNECTION||"sqlite"}export function usersEmailVerifiedAtSql(sql){return`ALTER TABLE users ADD COLUMN email_verified_at ${sql.nullableTimestamp}`}export function usersPasswordChangedAtSql(sql){return`ALTER TABLE users ADD COLUMN password_changed_at ${sql.nullableTimestamp}`}export function usersTwoFactorColumnsSql(sql){return["ALTER TABLE users ADD COLUMN two_factor_secret VARCHAR(255)",`ALTER TABLE users ADD COLUMN two_factor_enabled BOOLEAN NOT NULL DEFAULT ${sql.boolFalse}`,"ALTER TABLE users ADD COLUMN two_factor_last_used_step BIGINT"]}export function usersStripeIdSql(){return"ALTER TABLE users ADD COLUMN stripe_id VARCHAR(255)"}export function oauthAccessTokenDeviceColumnsSql(){return["ALTER TABLE oauth_access_tokens ADD COLUMN user_agent VARCHAR(255)","ALTER TABLE oauth_access_tokens ADD COLUMN ip_address VARCHAR(45)"]}export function passwordResetsExpiresAtSql(){return["ALTER TABLE password_resets ADD COLUMN expires_at TIMESTAMP"]}export async function ensureUsersAuthColumns(sql,options={}){const alters=[usersEmailVerifiedAtSql(sql),usersPasswordChangedAtSql(sql),...usersTwoFactorColumnsSql(sql),usersStripeIdSql()];for(const alterSql of alters)try{await db.unsafe(alterSql).execute()}catch{if(options.verbose)log.debug(`[auth-tables] Skipped (already applied or users missing): ${alterSql}`)}try{await db.unsafe(indexSqlForDialect("CREATE UNIQUE INDEX IF NOT EXISTS idx_users_stripe_id ON users(stripe_id)",getDbDriver())).execute()}catch{if(options.verbose)log.debug("[auth-tables] Skipped users.stripe_id unique index (already applied or users missing)")}}export async function migrateAuthTables(options={}){const dbDriver=getDbDriver(),sql=sqlHelpers(dbDriver),{isPostgres,boolTrue,now,pkColumn,nullableTimestamp,datetime}=sql;if(options.verbose)log.info(`Creating auth tables for ${dbDriver}...`);try{if(options.verbose)log.info("Creating oauth_clients table...");await db.unsafe(`
|
|
1
|
+
import process from"node:process";import{randomBytes}from"node:crypto";import{log}from"@stacksjs/logging";import{env as envVars}from"@stacksjs/env";import{db}from"./utils";import{sqlHelpers}from"./sql-helpers";import{indexSqlForDialect}from"./dialect";function getDbDriver(){return envVars.DB_CONNECTION||"sqlite"}export function usersEmailVerifiedAtSql(sql){return`ALTER TABLE users ADD COLUMN email_verified_at ${sql.nullableTimestamp}`}export function usersPasswordChangedAtSql(sql){return`ALTER TABLE users ADD COLUMN password_changed_at ${sql.nullableTimestamp}`}export function usersTwoFactorColumnsSql(sql){return["ALTER TABLE users ADD COLUMN two_factor_secret VARCHAR(255)",`ALTER TABLE users ADD COLUMN two_factor_enabled BOOLEAN NOT NULL DEFAULT ${sql.boolFalse}`,"ALTER TABLE users ADD COLUMN two_factor_last_used_step BIGINT"]}export function usersStripeIdSql(){return"ALTER TABLE users ADD COLUMN stripe_id VARCHAR(255)"}export function oauthAccessTokenDeviceColumnsSql(){return["ALTER TABLE oauth_access_tokens ADD COLUMN user_agent VARCHAR(255)","ALTER TABLE oauth_access_tokens ADD COLUMN ip_address VARCHAR(45)"]}export function passwordResetsExpiresAtSql(){return["ALTER TABLE password_resets ADD COLUMN expires_at TIMESTAMP"]}export async function ensureUsersAuthColumns(sql,options={}){const alters=[usersEmailVerifiedAtSql(sql),usersPasswordChangedAtSql(sql),...usersTwoFactorColumnsSql(sql),usersStripeIdSql()];for(const alterSql of alters)try{await db.unsafe(alterSql).execute()}catch{if(options.verbose)log.debug(`[auth-tables] Skipped (already applied or users missing): ${alterSql}`)}try{await db.unsafe(indexSqlForDialect("CREATE UNIQUE INDEX IF NOT EXISTS idx_users_stripe_id ON users(stripe_id)",getDbDriver())).execute()}catch{if(options.verbose)log.debug("[auth-tables] Skipped users.stripe_id unique index (already applied or users missing)")}}export async function migrateAuthTables(options={}){const dbDriver=getDbDriver(),sql=sqlHelpers(dbDriver),{isPostgres,boolTrue,now,pkColumn,nullableTimestamp,datetime,utcNow}=sql;if(options.verbose)log.info(`Creating auth tables for ${dbDriver}...`);try{if(options.verbose)log.info("Creating oauth_clients table...");await db.unsafe(`
|
|
2
2
|
CREATE TABLE IF NOT EXISTS oauth_clients (
|
|
3
3
|
${pkColumn},
|
|
4
4
|
name VARCHAR(255) NOT NULL,
|
|
@@ -8,7 +8,7 @@ import process from"node:process";import{randomBytes}from"node:crypto";import{lo
|
|
|
8
8
|
personal_access_client BOOLEAN NOT NULL DEFAULT ${sql.boolFalse},
|
|
9
9
|
password_client BOOLEAN NOT NULL DEFAULT ${sql.boolFalse},
|
|
10
10
|
revoked BOOLEAN NOT NULL DEFAULT ${sql.boolFalse},
|
|
11
|
-
created_at ${datetime} DEFAULT
|
|
11
|
+
created_at ${datetime} DEFAULT ${utcNow},
|
|
12
12
|
updated_at ${nullableTimestamp}
|
|
13
13
|
)
|
|
14
14
|
`).execute();if(options.verbose)log.info("Creating oauth_access_tokens table...");await db.unsafe(`
|
|
@@ -26,7 +26,7 @@ import process from"node:process";import{randomBytes}from"node:crypto";import{lo
|
|
|
26
26
|
-- a token minted by a script, which has neither.
|
|
27
27
|
user_agent VARCHAR(255),
|
|
28
28
|
ip_address VARCHAR(45),
|
|
29
|
-
created_at ${datetime} DEFAULT
|
|
29
|
+
created_at ${datetime} DEFAULT ${utcNow},
|
|
30
30
|
updated_at ${nullableTimestamp}
|
|
31
31
|
)
|
|
32
32
|
`).execute();await createTokenIndex("idx_oauth_access_tokens_token","oauth_access_tokens","token");for(const alterSql of oauthAccessTokenDeviceColumnsSql())try{await db.unsafe(alterSql).execute()}catch{if(options.verbose)log.debug(`[auth-tables] Skipped (already applied): ${alterSql}`)}if(options.verbose)log.info("Creating oauth_refresh_tokens table...");await db.unsafe(`
|
|
@@ -36,7 +36,7 @@ import process from"node:process";import{randomBytes}from"node:crypto";import{lo
|
|
|
36
36
|
token TEXT NOT NULL,
|
|
37
37
|
revoked BOOLEAN NOT NULL DEFAULT ${sql.boolFalse},
|
|
38
38
|
expires_at ${nullableTimestamp},
|
|
39
|
-
created_at ${datetime} DEFAULT
|
|
39
|
+
created_at ${datetime} DEFAULT ${utcNow}
|
|
40
40
|
)
|
|
41
41
|
`).execute();await createTokenIndex("idx_oauth_refresh_tokens_token","oauth_refresh_tokens","token");if(options.verbose)log.info("Creating password_resets table...");await db.unsafe(`
|
|
42
42
|
CREATE TABLE IF NOT EXISTS password_resets (
|
|
@@ -48,7 +48,7 @@ import process from"node:process";import{randomBytes}from"node:crypto";import{lo
|
|
|
48
48
|
-- still falls back to clock arithmetic against created_at, because
|
|
49
49
|
-- rows written before the column existed have to keep verifying.
|
|
50
50
|
expires_at ${nullableTimestamp},
|
|
51
|
-
created_at ${datetime} DEFAULT
|
|
51
|
+
created_at ${datetime} DEFAULT ${utcNow}
|
|
52
52
|
)
|
|
53
53
|
`).execute();for(const alterSql of passwordResetsExpiresAtSql())try{await db.unsafe(alterSql).execute()}catch{if(options.verbose)log.debug(`[auth-tables] Skipped (already applied): ${alterSql}`)}try{await db.unsafe(indexSqlForDialect("CREATE INDEX IF NOT EXISTS idx_password_resets_email ON password_resets(email)",dbDriver)).execute()}catch{}if(options.verbose)log.info("Creating email_verifications table...");await db.unsafe(`
|
|
54
54
|
CREATE TABLE IF NOT EXISTS email_verifications (
|
|
@@ -56,7 +56,7 @@ import process from"node:process";import{randomBytes}from"node:crypto";import{lo
|
|
|
56
56
|
user_id INTEGER NOT NULL,
|
|
57
57
|
token VARCHAR(255) NOT NULL,
|
|
58
58
|
expires_at ${datetime} NOT NULL,
|
|
59
|
-
created_at ${datetime} DEFAULT
|
|
59
|
+
created_at ${datetime} DEFAULT ${utcNow}
|
|
60
60
|
)
|
|
61
61
|
`).execute();try{await db.unsafe(indexSqlForDialect("CREATE INDEX IF NOT EXISTS idx_email_verifications_user_id ON email_verifications(user_id)",dbDriver)).execute()}catch{}if(options.verbose)log.info("Creating passkeys table...");await db.unsafe(`
|
|
62
62
|
CREATE TABLE IF NOT EXISTS passkeys (
|
|
@@ -70,7 +70,7 @@ import process from"node:process";import{randomBytes}from"node:crypto";import{lo
|
|
|
70
70
|
backup_eligible BOOLEAN NOT NULL DEFAULT ${sql.boolFalse},
|
|
71
71
|
backup_status BOOLEAN NOT NULL DEFAULT ${sql.boolFalse},
|
|
72
72
|
transports TEXT,
|
|
73
|
-
created_at ${datetime} DEFAULT
|
|
73
|
+
created_at ${datetime} DEFAULT ${utcNow},
|
|
74
74
|
last_used_at ${nullableTimestamp}
|
|
75
75
|
)
|
|
76
76
|
`).execute();try{await db.unsafe(indexSqlForDialect("CREATE INDEX IF NOT EXISTS idx_passkeys_user_id ON passkeys(user_id)",dbDriver)).execute()}catch{}if(options.verbose)log.info("Creating webauthn_challenges table...");await db.unsafe(`
|
|
@@ -80,21 +80,21 @@ import process from"node:process";import{randomBytes}from"node:crypto";import{lo
|
|
|
80
80
|
challenge TEXT NOT NULL,
|
|
81
81
|
purpose VARCHAR(20) NOT NULL,
|
|
82
82
|
expires_at ${datetime} NOT NULL,
|
|
83
|
-
created_at ${datetime} DEFAULT
|
|
83
|
+
created_at ${datetime} DEFAULT ${utcNow}
|
|
84
84
|
)
|
|
85
85
|
`).execute();try{await db.unsafe(indexSqlForDialect("CREATE UNIQUE INDEX IF NOT EXISTS idx_webauthn_challenges_user_purpose ON webauthn_challenges(user_id, purpose)",dbDriver)).execute()}catch{}if(options.verbose)log.info("Creating two_factor_challenges table...");await db.unsafe(`
|
|
86
86
|
CREATE TABLE IF NOT EXISTS two_factor_challenges (
|
|
87
87
|
id VARCHAR(255) PRIMARY KEY,
|
|
88
88
|
user_id INTEGER NOT NULL,
|
|
89
89
|
expires_at ${datetime} NOT NULL,
|
|
90
|
-
created_at ${datetime} DEFAULT
|
|
90
|
+
created_at ${datetime} DEFAULT ${utcNow}
|
|
91
91
|
)
|
|
92
92
|
`).execute();try{await db.unsafe(indexSqlForDialect("CREATE INDEX IF NOT EXISTS idx_two_factor_challenges_user_id ON two_factor_challenges(user_id)",dbDriver)).execute()}catch{}if(options.verbose)log.info("Creating two_factor_pending_secrets table...");await db.unsafe(`
|
|
93
93
|
CREATE TABLE IF NOT EXISTS two_factor_pending_secrets (
|
|
94
94
|
user_id INTEGER PRIMARY KEY,
|
|
95
95
|
secret VARCHAR(255) NOT NULL,
|
|
96
96
|
expires_at ${datetime} NOT NULL,
|
|
97
|
-
created_at ${datetime} DEFAULT
|
|
97
|
+
created_at ${datetime} DEFAULT ${utcNow}
|
|
98
98
|
)
|
|
99
99
|
`).execute();if(options.verbose)log.info("Ensuring users auth columns exist (users may not exist yet on a fresh install \u2014 see ensureUsersAuthColumns)...");await ensureUsersAuthColumns(sql,options);if(options.verbose)log.info("Ensuring personal access client exists...");if((await db.unsafe(`
|
|
100
100
|
SELECT id FROM oauth_clients WHERE personal_access_client = ${boolTrue} LIMIT 1
|
|
@@ -1,22 +1,22 @@
|
|
|
1
|
-
import{log}from"@stacksjs/logging";import{env as envVars}from"@stacksjs/env";import{db}from"./utils";import{sqlHelpers}from"./sql-helpers";import{indexSqlForDialect,isDuplicateIndexError}from"./dialect";function getDbDriver(){return process.env.DB_CONNECTION||envVars.DB_CONNECTION||"sqlite"}export function notificationsTableSql(sql){const{bigPkColumn,bigInteger,nullableTimestamp,datetime}=sql;return`CREATE TABLE IF NOT EXISTS notifications (
|
|
1
|
+
import{log}from"@stacksjs/logging";import{env as envVars}from"@stacksjs/env";import{db}from"./utils";import{sqlHelpers}from"./sql-helpers";import{indexSqlForDialect,isDuplicateIndexError}from"./dialect";function getDbDriver(){return process.env.DB_CONNECTION||envVars.DB_CONNECTION||"sqlite"}export function notificationsTableSql(sql){const{bigPkColumn,bigInteger,nullableTimestamp,datetime,utcNow}=sql;return`CREATE TABLE IF NOT EXISTS notifications (
|
|
2
2
|
${bigPkColumn},
|
|
3
3
|
user_id ${bigInteger},
|
|
4
4
|
type VARCHAR(255) NOT NULL,
|
|
5
5
|
data TEXT NOT NULL,
|
|
6
6
|
read_at ${nullableTimestamp},
|
|
7
7
|
uuid VARCHAR(255),
|
|
8
|
-
created_at ${datetime} DEFAULT
|
|
8
|
+
created_at ${datetime} DEFAULT ${utcNow},
|
|
9
9
|
updated_at ${nullableTimestamp}
|
|
10
|
-
)`}export function notificationPreferencesTableSql(sql){const{bigPkColumn,bigInteger,boolTrue,nullableTimestamp,datetime}=sql;return`CREATE TABLE IF NOT EXISTS notification_preferences (
|
|
10
|
+
)`}export function notificationPreferencesTableSql(sql){const{bigPkColumn,bigInteger,boolTrue,nullableTimestamp,datetime,utcNow}=sql;return`CREATE TABLE IF NOT EXISTS notification_preferences (
|
|
11
11
|
${bigPkColumn},
|
|
12
12
|
user_id ${bigInteger} NOT NULL,
|
|
13
13
|
channel VARCHAR(50) NOT NULL,
|
|
14
14
|
enabled BOOLEAN NOT NULL DEFAULT ${boolTrue},
|
|
15
15
|
category VARCHAR(255),
|
|
16
|
-
created_at ${datetime} DEFAULT
|
|
16
|
+
created_at ${datetime} DEFAULT ${utcNow},
|
|
17
17
|
updated_at ${nullableTimestamp},
|
|
18
18
|
UNIQUE (user_id, channel, category)
|
|
19
|
-
)`}export function notificationDeliveriesTableSql(sql){const{bigPkColumn,nullableTimestamp,datetime}=sql;return`CREATE TABLE IF NOT EXISTS notification_deliveries (
|
|
19
|
+
)`}export function notificationDeliveriesTableSql(sql){const{bigPkColumn,nullableTimestamp,datetime,utcNow}=sql;return`CREATE TABLE IF NOT EXISTS notification_deliveries (
|
|
20
20
|
${bigPkColumn},
|
|
21
21
|
user_id INTEGER,
|
|
22
22
|
channel VARCHAR(50) NOT NULL,
|
|
@@ -27,6 +27,6 @@ import{log}from"@stacksjs/logging";import{env as envVars}from"@stacksjs/env";imp
|
|
|
27
27
|
error TEXT,
|
|
28
28
|
metadata TEXT,
|
|
29
29
|
sent_at ${nullableTimestamp},
|
|
30
|
-
created_at ${datetime} DEFAULT
|
|
30
|
+
created_at ${datetime} DEFAULT ${utcNow},
|
|
31
31
|
updated_at ${nullableTimestamp}
|
|
32
32
|
)`}async function createIndex(statement,dialect){try{await db.unsafe(indexSqlForDialect(statement,dialect)).execute()}catch(error){if(!isDuplicateIndexError(error))throw error}}const REQUIRED_COLUMNS={notifications:["id","user_id","type","data","read_at"],notification_preferences:["id","user_id","channel","enabled","category"],notification_deliveries:["id","user_id","channel","recipient","body","status"]};export const notificationTableNames=["notifications","notification_preferences","notification_deliveries"];export function notificationTablesMissingCreateStatements(sql){return notificationTableNames.filter((table)=>{const escaped=table.replace(/[.*+?^${}()|[\]\\]/g,"\\$&");return!new RegExp(`\\bCREATE\\s+TABLE\\s+(?:IF\\s+NOT\\s+EXISTS\\s+)?["\\x60\\[]?${escaped}["\\x60\\]]?\\s*\\(`,"i").test(sql)})}async function warnOnShapeMismatch(table){const required=REQUIRED_COLUMNS[table];if(!required)return;try{const rows=await db.unsafe(`SELECT * FROM ${table} WHERE 1 = 0`).execute(),known=Array.isArray(rows)&&rows.length>0?Object.keys(rows[0]):null,missing=[];for(const column of required){if(known){if(!known.includes(column))missing.push(column);continue}try{await db.unsafe(`SELECT ${column} FROM ${table} WHERE 1 = 0`).execute()}catch{missing.push(column)}}if(missing.length>0)log.warn(`[notifications] "${table}" already exists without ${missing.join(", ")}. CREATE TABLE IF NOT EXISTS left it untouched, so the notification driver will fail on those columns. Something else owns this table - either rename it, or stop using the framework's notification tables.`)}catch{}}export async function migrateNotificationTables(options={}){const dbDriver=getDbDriver(),sql=sqlHelpers(dbDriver),tables=new Set(options.tables??notificationTableNames);if(options.verbose)log.info(`Creating notification tables for ${dbDriver}...`);try{if(tables.has("notifications")){if(options.verbose)log.info("Creating notifications table...");await warnOnShapeMismatch("notifications");await db.unsafe(notificationsTableSql(sql)).execute();await createIndex("CREATE INDEX IF NOT EXISTS idx_notifications_user ON notifications (user_id)",dbDriver)}if(tables.has("notification_preferences")){if(options.verbose)log.info("Creating notification_preferences table...");await warnOnShapeMismatch("notification_preferences");await db.unsafe(notificationPreferencesTableSql(sql)).execute();await createIndex("CREATE INDEX IF NOT EXISTS idx_notification_preferences_user ON notification_preferences (user_id)",dbDriver)}if(tables.has("notification_deliveries")){if(options.verbose)log.info("Creating notification deliveries table...");await warnOnShapeMismatch("notification_deliveries");await db.unsafe(notificationDeliveriesTableSql(sql)).execute();await createIndex("CREATE INDEX IF NOT EXISTS idx_notification_deliveries_channel ON notification_deliveries (channel)",dbDriver);await createIndex("CREATE INDEX IF NOT EXISTS idx_notification_deliveries_status ON notification_deliveries (status)",dbDriver)}if(options.verbose)log.success("Notification tables created");return{success:!0}}catch(error){const message=error instanceof Error?error.message:String(error);log.error(`Failed to create notification tables: ${message}`);return{success:!1,error:message}}}export async function ensureNotificationForeignKeys(options={}){if(getDbDriver()==="sqlite")return;for(const table of["notifications","notification_deliveries"]){const statements=[`ALTER TABLE ${table} DROP CONSTRAINT IF EXISTS "${table}_user_id_fk"`,`ALTER TABLE ${table} DROP CONSTRAINT IF EXISTS "${table}_user_id_fkey"`,`ALTER TABLE ${table} ADD CONSTRAINT "${table}_user_id_fk" FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE`];for(const statement of statements)try{await db.unsafe(statement).execute()}catch(error){if(options.verbose)log.debug(`[notification-tables] Skipped: ${statement} (${error instanceof Error?error.message:String(error)})`)}}}
|
package/dist/rbac-tables.js
CHANGED
|
@@ -1,32 +1,32 @@
|
|
|
1
|
-
import{log}from"@stacksjs/logging";import{env as envVars}from"@stacksjs/env";import{db}from"./utils";import{sqlHelpers}from"./sql-helpers";function getDbDriver(){return envVars.DB_CONNECTION||"sqlite"}export function rolesTableSql(sql){const{pkColumn,nullableTimestamp,datetime}=sql;return`CREATE TABLE IF NOT EXISTS roles (
|
|
1
|
+
import{log}from"@stacksjs/logging";import{env as envVars}from"@stacksjs/env";import{db}from"./utils";import{sqlHelpers}from"./sql-helpers";function getDbDriver(){return envVars.DB_CONNECTION||"sqlite"}export function rolesTableSql(sql){const{pkColumn,nullableTimestamp,datetime,utcNow}=sql;return`CREATE TABLE IF NOT EXISTS roles (
|
|
2
2
|
${pkColumn},
|
|
3
3
|
name VARCHAR(255) NOT NULL,
|
|
4
4
|
guard_name VARCHAR(255) NOT NULL DEFAULT 'web',
|
|
5
5
|
description TEXT,
|
|
6
|
-
created_at ${datetime} DEFAULT
|
|
6
|
+
created_at ${datetime} DEFAULT ${utcNow},
|
|
7
7
|
updated_at ${nullableTimestamp},
|
|
8
8
|
UNIQUE (name, guard_name)
|
|
9
|
-
)`}export function permissionsTableSql(sql){const{pkColumn,nullableTimestamp,datetime}=sql;return`CREATE TABLE IF NOT EXISTS permissions (
|
|
9
|
+
)`}export function permissionsTableSql(sql){const{pkColumn,nullableTimestamp,datetime,utcNow}=sql;return`CREATE TABLE IF NOT EXISTS permissions (
|
|
10
10
|
${pkColumn},
|
|
11
11
|
name VARCHAR(255) NOT NULL,
|
|
12
12
|
guard_name VARCHAR(255) NOT NULL DEFAULT 'web',
|
|
13
13
|
description TEXT,
|
|
14
|
-
created_at ${datetime} DEFAULT
|
|
14
|
+
created_at ${datetime} DEFAULT ${utcNow},
|
|
15
15
|
updated_at ${nullableTimestamp},
|
|
16
16
|
UNIQUE (name, guard_name)
|
|
17
|
-
)`}export function userRolesTableSql(sql){const{datetime}=sql;return`CREATE TABLE IF NOT EXISTS user_roles (
|
|
17
|
+
)`}export function userRolesTableSql(sql){const{datetime,utcNow}=sql;return`CREATE TABLE IF NOT EXISTS user_roles (
|
|
18
18
|
user_id INTEGER NOT NULL,
|
|
19
19
|
role_id INTEGER NOT NULL,
|
|
20
|
-
created_at ${datetime} DEFAULT
|
|
20
|
+
created_at ${datetime} DEFAULT ${utcNow},
|
|
21
21
|
PRIMARY KEY (user_id, role_id)
|
|
22
|
-
)`}export function userPermissionsTableSql(sql){const{datetime}=sql;return`CREATE TABLE IF NOT EXISTS user_permissions (
|
|
22
|
+
)`}export function userPermissionsTableSql(sql){const{datetime,utcNow}=sql;return`CREATE TABLE IF NOT EXISTS user_permissions (
|
|
23
23
|
user_id INTEGER NOT NULL,
|
|
24
24
|
permission_id INTEGER NOT NULL,
|
|
25
|
-
created_at ${datetime} DEFAULT
|
|
25
|
+
created_at ${datetime} DEFAULT ${utcNow},
|
|
26
26
|
PRIMARY KEY (user_id, permission_id)
|
|
27
|
-
)`}export function rolePermissionsTableSql(sql){const{datetime}=sql;return`CREATE TABLE IF NOT EXISTS role_permissions (
|
|
27
|
+
)`}export function rolePermissionsTableSql(sql){const{datetime,utcNow}=sql;return`CREATE TABLE IF NOT EXISTS role_permissions (
|
|
28
28
|
role_id INTEGER NOT NULL,
|
|
29
29
|
permission_id INTEGER NOT NULL,
|
|
30
|
-
created_at ${datetime} DEFAULT
|
|
30
|
+
created_at ${datetime} DEFAULT ${utcNow},
|
|
31
31
|
PRIMARY KEY (role_id, permission_id)
|
|
32
32
|
)`}export async function migrateRbacTables(options={}){const dbDriver=getDbDriver(),sql=sqlHelpers(dbDriver);if(options.verbose)log.info(`Creating RBAC tables for ${dbDriver}...`);try{if(options.verbose)log.info("Creating roles table...");await db.unsafe(rolesTableSql(sql)).execute();if(options.verbose)log.info("Creating permissions table...");await db.unsafe(permissionsTableSql(sql)).execute();if(options.verbose)log.info("Creating user_roles pivot...");await db.unsafe(userRolesTableSql(sql)).execute();if(options.verbose)log.info("Creating user_permissions pivot...");await db.unsafe(userPermissionsTableSql(sql)).execute();if(options.verbose)log.info("Creating role_permissions pivot...");await db.unsafe(rolePermissionsTableSql(sql)).execute();if(options.verbose)log.success("RBAC tables created");return{success:!0}}catch(error){const message=error instanceof Error?error.message:String(error);log.error(`Failed to create RBAC tables: ${message}`);return{success:!1,error:message}}}
|
package/dist/sql-helpers.d.ts
CHANGED
package/dist/sql-helpers.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{dialectCapabilities}from"./dialect";export function sqlDateTime(value=new Date){return value.toISOString().slice(0,-1)}export function sqlDateTimeLiteral(value=new Date){return`'${sqlDateTime(value)}'`}export function parseSqlDateTime(value){if(value===null||value===void 0)return null;if(value instanceof Date)return Number.isNaN(value.getTime())?null:value;if(typeof value==="number")return Number.isNaN(value)?null:new Date(value);if(typeof value!=="string")return null;const trimmed=value.trim();if(!trimmed)return null;let normalized=trimmed.replace(" ","T");if(!/(?:Z|[+-]\d{2}:?\d{2})$/i.test(normalized))normalized+="Z";const parsed=new Date(normalized);return Number.isNaN(parsed.getTime())?null:parsed}export function sqlHelpers(driver){const caps=dialectCapabilities(driver),isPostgres=caps.wire==="postgres",isMysql=caps.wire==="mysql",isSqlite=caps.wire==="sqlite";return{driver,isPostgres,isMysql,isSqlite,now:isPostgres||isMysql?"NOW()":"datetime('now')",boolTrue:isPostgres?"true":"1",boolFalse:isPostgres?"false":"0",autoIncrement:isPostgres?"SERIAL":"INTEGER",bigInteger:isSqlite?"INTEGER":"BIGINT",primaryKey:!caps.supportsAutoIncrement?"PRIMARY KEY":isPostgres?"PRIMARY KEY":isMysql?"PRIMARY KEY AUTO_INCREMENT":"PRIMARY KEY AUTOINCREMENT",pkColumn:!caps.supportsAutoIncrement?"id BIGINT NOT NULL PRIMARY KEY":isPostgres?"id SERIAL PRIMARY KEY":isMysql?"id INTEGER PRIMARY KEY AUTO_INCREMENT":"id INTEGER PRIMARY KEY AUTOINCREMENT",bigPkColumn:!caps.supportsAutoIncrement?"id BIGINT NOT NULL PRIMARY KEY":isPostgres?"id BIGSERIAL PRIMARY KEY":isMysql?"id BIGINT PRIMARY KEY AUTO_INCREMENT":"id INTEGER PRIMARY KEY AUTOINCREMENT",datetime:isMysql?"DATETIME":"TIMESTAMP",nullableTimestamp:isMysql?"DATETIME NULL":"TIMESTAMP",param(index){return isPostgres?`$${index}`:"?"},params(...values){if(isPostgres)return{sql:values.map((_,i)=>`$${i+1}`).join(", "),values};return{sql:values.map(()=>"?").join(", "),values}}}}
|
|
1
|
+
import{dialectCapabilities}from"./dialect";export function sqlDateTime(value=new Date){return value.toISOString().slice(0,-1)}export function sqlDateTimeLiteral(value=new Date){return`'${sqlDateTime(value)}'`}export function parseSqlDateTime(value){if(value===null||value===void 0)return null;if(value instanceof Date)return Number.isNaN(value.getTime())?null:value;if(typeof value==="number")return Number.isNaN(value)?null:new Date(value);if(typeof value!=="string")return null;const trimmed=value.trim();if(!trimmed)return null;let normalized=trimmed.replace(" ","T");if(!/(?:Z|[+-]\d{2}:?\d{2})$/i.test(normalized))normalized+="Z";const parsed=new Date(normalized);return Number.isNaN(parsed.getTime())?null:parsed}export function sqlHelpers(driver){const caps=dialectCapabilities(driver),isPostgres=caps.wire==="postgres",isMysql=caps.wire==="mysql",isSqlite=caps.wire==="sqlite";return{driver,isPostgres,isMysql,isSqlite,now:isPostgres||isMysql?"NOW()":"datetime('now')",utcNow:isPostgres?"(now() AT TIME ZONE 'utc')":isMysql?"UTC_TIMESTAMP":"CURRENT_TIMESTAMP",boolTrue:isPostgres?"true":"1",boolFalse:isPostgres?"false":"0",autoIncrement:isPostgres?"SERIAL":"INTEGER",bigInteger:isSqlite?"INTEGER":"BIGINT",primaryKey:!caps.supportsAutoIncrement?"PRIMARY KEY":isPostgres?"PRIMARY KEY":isMysql?"PRIMARY KEY AUTO_INCREMENT":"PRIMARY KEY AUTOINCREMENT",pkColumn:!caps.supportsAutoIncrement?"id BIGINT NOT NULL PRIMARY KEY":isPostgres?"id SERIAL PRIMARY KEY":isMysql?"id INTEGER PRIMARY KEY AUTO_INCREMENT":"id INTEGER PRIMARY KEY AUTOINCREMENT",bigPkColumn:!caps.supportsAutoIncrement?"id BIGINT NOT NULL PRIMARY KEY":isPostgres?"id BIGSERIAL PRIMARY KEY":isMysql?"id BIGINT PRIMARY KEY AUTO_INCREMENT":"id INTEGER PRIMARY KEY AUTOINCREMENT",datetime:isMysql?"DATETIME":"TIMESTAMP",nullableTimestamp:isMysql?"DATETIME NULL":"TIMESTAMP",param(index){return isPostgres?`$${index}`:"?"},params(...values){if(isPostgres)return{sql:values.map((_,i)=>`$${i+1}`).join(", "),values};return{sql:values.map(()=>"?").join(", "),values}}}}
|
package/dist/trait-tables.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{log}from"@stacksjs/logging";import{env as envVars}from"@stacksjs/env";import{db}from"./utils";import{sqlHelpers}from"./sql-helpers";import{indexSqlForDialect,isDuplicateColumnError,isDuplicateIndexError}from"./dialect";export{indexSqlForDialect}from"./dialect";function getDbDriver(){return process.env.DB_CONNECTION||envVars.DB_CONNECTION||"sqlite"}function createdAt(sql){return`created_at ${sql.datetime}`}function updatedAt(sql){return`updated_at ${sql.nullableTimestamp}`}export function traitTableNames(){return["commentables","taggables","categorizables","commentable_upvotes","taggable_models","categorizable_models"]}export const UNSCOPED_OWNER_ID=0;export function commentablesTableSql(sql){const{pkColumn,boolTrue}=sql;return`CREATE TABLE IF NOT EXISTS commentables (
|
|
1
|
+
import{log}from"@stacksjs/logging";import{env as envVars}from"@stacksjs/env";import{db}from"./utils";import{sqlHelpers}from"./sql-helpers";import{indexSqlForDialect,isDuplicateColumnError,isDuplicateIndexError}from"./dialect";export{indexSqlForDialect}from"./dialect";function getDbDriver(){return process.env.DB_CONNECTION||envVars.DB_CONNECTION||"sqlite"}function createdAt(sql){return`created_at ${sql.datetime}`}function updatedAt(sql){return`updated_at ${sql.nullableTimestamp}`}export function traitTableNames(){return["commentables","taggables","categorizables","commentable_upvotes","taggable_models","categorizable_models"]}export const UNSCOPED_OWNER_ID=0;export function commentablesTableSql(sql){const{pkColumn,boolTrue,utcNow}=sql;return`CREATE TABLE IF NOT EXISTS commentables (
|
|
2
2
|
${pkColumn},
|
|
3
3
|
title VARCHAR(255) NOT NULL,
|
|
4
4
|
body TEXT NOT NULL,
|
|
@@ -11,7 +11,7 @@ import{log}from"@stacksjs/logging";import{env as envVars}from"@stacksjs/env";imp
|
|
|
11
11
|
is_active BOOLEAN NOT NULL DEFAULT ${boolTrue},
|
|
12
12
|
${createdAt(sql)},
|
|
13
13
|
${updatedAt(sql)}
|
|
14
|
-
)`}export function taggablesTableSql(sql){const{pkColumn,boolTrue}=sql;return`CREATE TABLE IF NOT EXISTS taggables (
|
|
14
|
+
)`}export function taggablesTableSql(sql){const{pkColumn,boolTrue,utcNow}=sql;return`CREATE TABLE IF NOT EXISTS taggables (
|
|
15
15
|
${pkColumn},
|
|
16
16
|
name VARCHAR(255) NOT NULL,
|
|
17
17
|
slug VARCHAR(255) NOT NULL,
|
|
@@ -21,7 +21,7 @@ import{log}from"@stacksjs/logging";import{env as envVars}from"@stacksjs/env";imp
|
|
|
21
21
|
taggable_type VARCHAR(255) NOT NULL,
|
|
22
22
|
${createdAt(sql)},
|
|
23
23
|
${updatedAt(sql)}
|
|
24
|
-
)`}export function categorizablesTableSql(sql){const{pkColumn,boolTrue}=sql;return`CREATE TABLE IF NOT EXISTS categorizables (
|
|
24
|
+
)`}export function categorizablesTableSql(sql){const{pkColumn,boolTrue,utcNow}=sql;return`CREATE TABLE IF NOT EXISTS categorizables (
|
|
25
25
|
${pkColumn},
|
|
26
26
|
name VARCHAR(255) NOT NULL,
|
|
27
27
|
slug VARCHAR(255) NOT NULL,
|
|
@@ -31,28 +31,28 @@ import{log}from"@stacksjs/logging";import{env as envVars}from"@stacksjs/env";imp
|
|
|
31
31
|
categorizable_type VARCHAR(255) NOT NULL,
|
|
32
32
|
${createdAt(sql)},
|
|
33
33
|
${updatedAt(sql)}
|
|
34
|
-
)`}export function taggableModelsTableSql(sql){const{bigPkColumn,bigInteger}=sql;return`CREATE TABLE IF NOT EXISTS taggable_models (
|
|
34
|
+
)`}export function taggableModelsTableSql(sql){const{bigPkColumn,bigInteger,utcNow}=sql;return`CREATE TABLE IF NOT EXISTS taggable_models (
|
|
35
35
|
${bigPkColumn},
|
|
36
36
|
tag_id ${bigInteger} NOT NULL,
|
|
37
37
|
taggable_id ${bigInteger} NOT NULL,
|
|
38
38
|
taggable_type VARCHAR(255) NOT NULL DEFAULT 'posts',
|
|
39
|
-
created_at ${sql.datetime} NOT NULL DEFAULT
|
|
39
|
+
created_at ${sql.datetime} NOT NULL DEFAULT ${utcNow},
|
|
40
40
|
${updatedAt(sql)}
|
|
41
|
-
)`}export function categorizableModelsTableSql(sql){const{bigPkColumn,bigInteger}=sql;return`CREATE TABLE IF NOT EXISTS categorizable_models (
|
|
41
|
+
)`}export function categorizableModelsTableSql(sql){const{bigPkColumn,bigInteger,utcNow}=sql;return`CREATE TABLE IF NOT EXISTS categorizable_models (
|
|
42
42
|
${bigPkColumn},
|
|
43
43
|
category_id ${bigInteger} NOT NULL,
|
|
44
44
|
categorizable_id ${bigInteger} NOT NULL,
|
|
45
45
|
categorizable_type VARCHAR(255) NOT NULL DEFAULT 'posts',
|
|
46
|
-
created_at ${sql.datetime} NOT NULL DEFAULT
|
|
46
|
+
created_at ${sql.datetime} NOT NULL DEFAULT ${utcNow},
|
|
47
47
|
${updatedAt(sql)}
|
|
48
|
-
)`}export function likesTableSql(sql,table,foreignKey){const{pkColumn}=sql;return`CREATE TABLE IF NOT EXISTS ${table} (
|
|
48
|
+
)`}export function likesTableSql(sql,table,foreignKey){const{pkColumn,utcNow}=sql;return`CREATE TABLE IF NOT EXISTS ${table} (
|
|
49
49
|
${pkColumn},
|
|
50
50
|
${foreignKey} INTEGER NOT NULL,
|
|
51
51
|
user_id INTEGER NOT NULL,
|
|
52
52
|
${createdAt(sql)},
|
|
53
53
|
${updatedAt(sql)},
|
|
54
54
|
UNIQUE (${foreignKey}, user_id)
|
|
55
|
-
)`}export function commentableUpvotesTableSql(sql){const{pkColumn}=sql;return`CREATE TABLE IF NOT EXISTS commentable_upvotes (
|
|
55
|
+
)`}export function commentableUpvotesTableSql(sql){const{pkColumn,utcNow}=sql;return`CREATE TABLE IF NOT EXISTS commentable_upvotes (
|
|
56
56
|
${pkColumn},
|
|
57
57
|
user_id INTEGER,
|
|
58
58
|
upvoteable_id INTEGER NOT NULL,
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "@stacksjs/database",
|
|
3
3
|
"type": "module",
|
|
4
4
|
"sideEffects": false,
|
|
5
|
-
"version": "0.70.
|
|
5
|
+
"version": "0.70.367",
|
|
6
6
|
"description": "The Stacks database integration.",
|
|
7
7
|
"author": "Chris Breuer",
|
|
8
8
|
"contributors": [
|
|
@@ -65,15 +65,15 @@
|
|
|
65
65
|
"dynamodb-tooling": "^0.3.2"
|
|
66
66
|
},
|
|
67
67
|
"devDependencies": {
|
|
68
|
-
"@stacksjs/cli": "0.70.
|
|
69
|
-
"@stacksjs/config": "0.70.
|
|
70
|
-
"@stacksjs/logging": "0.70.
|
|
71
|
-
"@stacksjs/router": "0.70.
|
|
68
|
+
"@stacksjs/cli": "0.70.367",
|
|
69
|
+
"@stacksjs/config": "0.70.367",
|
|
70
|
+
"@stacksjs/logging": "0.70.367",
|
|
71
|
+
"@stacksjs/router": "0.70.367",
|
|
72
72
|
"better-dx": "^0.2.17",
|
|
73
|
-
"@stacksjs/path": "0.70.
|
|
74
|
-
"@stacksjs/query-builder": "0.70.
|
|
75
|
-
"@stacksjs/storage": "0.70.
|
|
76
|
-
"@stacksjs/strings": "0.70.
|
|
77
|
-
"@stacksjs/utils": "0.70.
|
|
73
|
+
"@stacksjs/path": "0.70.367",
|
|
74
|
+
"@stacksjs/query-builder": "0.70.367",
|
|
75
|
+
"@stacksjs/storage": "0.70.367",
|
|
76
|
+
"@stacksjs/strings": "0.70.367",
|
|
77
|
+
"@stacksjs/utils": "0.70.367"
|
|
78
78
|
}
|
|
79
79
|
}
|