49 lines
1.9 KiB
JavaScript
49 lines
1.9 KiB
JavaScript
// Phase 1 — auth schema: users + refresh_tokens (sessions).
|
|
|
|
/** @param {import('node-pg-migrate').MigrationBuilder} pgm */
|
|
exports.up = (pgm) => {
|
|
// Case-insensitive text for the unique username (and non-unique email).
|
|
pgm.createExtension('citext', { ifNotExists: true });
|
|
|
|
pgm.createTable('users', {
|
|
id: { type: 'uuid', primaryKey: true, default: pgm.func('gen_random_uuid()') },
|
|
username: { type: 'citext', notNull: true, unique: true },
|
|
display_name: { type: 'text', notNull: true },
|
|
// Email is optional and NOT unique (contact field only, not a login credential).
|
|
email: { type: 'citext' },
|
|
phone: { type: 'text' },
|
|
avatar_ref: { type: 'text' },
|
|
password_hash: { type: 'text', notNull: true },
|
|
created_at: { type: 'timestamptz', notNull: true, default: pgm.func('now()') },
|
|
updated_at: { type: 'timestamptz', notNull: true, default: pgm.func('now()') },
|
|
last_seen_at: { type: 'timestamptz' },
|
|
});
|
|
|
|
pgm.createTable('refresh_tokens', {
|
|
id: { type: 'uuid', primaryKey: true, default: pgm.func('gen_random_uuid()') },
|
|
user_id: {
|
|
type: 'uuid',
|
|
notNull: true,
|
|
references: 'users',
|
|
onDelete: 'CASCADE',
|
|
},
|
|
// Rotation lineage: reuse of any token in a family revokes the whole family.
|
|
family_id: { type: 'uuid', notNull: true },
|
|
token_hash: { type: 'text', notNull: true, unique: true },
|
|
user_agent: { type: 'text' },
|
|
ip: { type: 'text' },
|
|
created_at: { type: 'timestamptz', notNull: true, default: pgm.func('now()') },
|
|
last_used_at: { type: 'timestamptz', notNull: true, default: pgm.func('now()') },
|
|
expires_at: { type: 'timestamptz', notNull: true },
|
|
revoked_at: { type: 'timestamptz' },
|
|
});
|
|
|
|
pgm.createIndex('refresh_tokens', 'user_id');
|
|
pgm.createIndex('refresh_tokens', 'family_id');
|
|
};
|
|
|
|
/** @param {import('node-pg-migrate').MigrationBuilder} pgm */
|
|
exports.down = (pgm) => {
|
|
pgm.dropTable('refresh_tokens');
|
|
pgm.dropTable('users');
|
|
};
|