feat(auth): Story 1.5 — atomare, idempotente Migration Alt-Daten → Admin
This commit is contained in:
parent
c994dbf3b5
commit
07f1bccff0
2 changed files with 69 additions and 1 deletions
|
|
@ -117,9 +117,38 @@ function seedAdmin(adminEmail) {
|
|||
return Number(existing.id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Migriert die (globale) Single-User-Datenbasis auf Mehrbenutzer, indem alle
|
||||
* Alt-Zeilen dem Admin zugeordnet werden. Atomar (eine Transaktion), idempotent
|
||||
* (Guard über die documents.user_id-Spalte). Muss NACH seedAdmin laufen.
|
||||
* @returns {boolean} true wenn migriert, false wenn bereits migriert (No-Op)
|
||||
*/
|
||||
function migrateToMultiUser(adminUserId) {
|
||||
const cols = db.prepare("PRAGMA table_info('documents')").all().map(c => c.name);
|
||||
if (cols.includes('user_id')) return false; // schon migriert
|
||||
const tx = db.transaction(() => {
|
||||
db.exec(`CREATE TABLE documents_v2 (
|
||||
user_id INTEGER NOT NULL,
|
||||
key TEXT NOT NULL,
|
||||
value TEXT NOT NULL,
|
||||
updated_at TEXT NOT NULL,
|
||||
PRIMARY KEY (user_id, key)
|
||||
)`);
|
||||
db.prepare('INSERT INTO documents_v2 (user_id, key, value, updated_at) SELECT ?, key, value, updated_at FROM documents')
|
||||
.run(adminUserId);
|
||||
db.exec('DROP TABLE documents');
|
||||
db.exec('ALTER TABLE documents_v2 RENAME TO documents');
|
||||
const hcols = db.prepare("PRAGMA table_info('history')").all().map(c => c.name);
|
||||
if (!hcols.includes('user_id')) db.exec('ALTER TABLE history ADD COLUMN user_id INTEGER');
|
||||
db.prepare('UPDATE history SET user_id = ? WHERE user_id IS NULL').run(adminUserId);
|
||||
});
|
||||
tx();
|
||||
return true;
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
normalizeEmail, hashToken, createLoginToken, consumeLoginToken,
|
||||
createSession, validateSession, deleteSession, deleteUserSessions,
|
||||
seedAdmin,
|
||||
seedAdmin, migrateToMultiUser,
|
||||
TOKEN_TTL_MIN, SESSION_TTL_DAYS, SESSION_IDLE_HOURS,
|
||||
};
|
||||
|
|
|
|||
39
server/migrate.test.js
Normal file
39
server/migrate.test.js
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
const { test } = require('node:test');
|
||||
const assert = require('node:assert');
|
||||
const fs = require('fs');
|
||||
const os = require('os');
|
||||
const path = require('path');
|
||||
|
||||
process.env.DATA_DIR = fs.mkdtempSync(path.join(os.tmpdir(), 'dp-migrate-'));
|
||||
const { db } = require('./db');
|
||||
const { migrateToMultiUser, seedAdmin } = require('./auth');
|
||||
|
||||
test('Migration ordnet Alt-Daten dem Admin zu, setzt neues PK-Schema, ist idempotent', () => {
|
||||
// Alt-Schema (Single-User) erzwingen — unabhängig von der aktuellen db.js-Definition
|
||||
db.exec("DROP TABLE IF EXISTS documents; CREATE TABLE documents (key TEXT PRIMARY KEY, value TEXT NOT NULL, updated_at TEXT NOT NULL)");
|
||||
db.exec("DROP TABLE IF EXISTS history; CREATE TABLE history (id INTEGER PRIMARY KEY AUTOINCREMENT, key TEXT NOT NULL, value TEXT NOT NULL, replaced_at TEXT NOT NULL)");
|
||||
const now = new Date().toISOString();
|
||||
db.prepare('INSERT INTO documents (key,value,updated_at) VALUES (?,?,?)').run('employees', '["Max"]', now);
|
||||
db.prepare('INSERT INTO documents (key,value,updated_at) VALUES (?,?,?)').run('duties', '{}', now);
|
||||
db.prepare('INSERT INTO history (key,value,replaced_at) VALUES (?,?,?)').run('employees', '[]', now);
|
||||
|
||||
const adminId = seedAdmin('admin@x.de');
|
||||
assert.strictEqual(migrateToMultiUser(adminId), true);
|
||||
|
||||
const cols = db.prepare("PRAGMA table_info('documents')").all().map(c => c.name);
|
||||
assert.ok(cols.includes('user_id'), 'documents hat user_id');
|
||||
|
||||
const rows = db.prepare('SELECT user_id, key FROM documents ORDER BY key').all();
|
||||
assert.deepStrictEqual(rows, [
|
||||
{ user_id: adminId, key: 'duties' },
|
||||
{ user_id: adminId, key: 'employees' },
|
||||
]);
|
||||
assert.strictEqual(db.prepare('SELECT user_id FROM history').get().user_id, adminId);
|
||||
|
||||
// PK ist (user_id,key): dieselbe Kombi doppelt schlägt fehl
|
||||
assert.throws(() => db.prepare('INSERT INTO documents (user_id,key,value,updated_at) VALUES (?,?,?,?)')
|
||||
.run(adminId, 'duties', '{}', now));
|
||||
|
||||
// Idempotent
|
||||
assert.strictEqual(migrateToMultiUser(adminId), false);
|
||||
});
|
||||
Loading…
Add table
Add a link
Reference in a new issue