Checkpoint SHD dashboard design system
This commit is contained in:
@@ -0,0 +1,24 @@
|
||||
import { connect } from '@tursodatabase/database';
|
||||
import { mkdir, readFile } from 'node:fs/promises';
|
||||
import { dirname, resolve } from 'node:path';
|
||||
import process from 'node:process';
|
||||
|
||||
const databasePath = resolve(process.env.TDR_DATABASE_PATH || './data/shd-build.db');
|
||||
const schemaPath = resolve('./database/schema.sql');
|
||||
|
||||
await mkdir(dirname(databasePath), { recursive: true });
|
||||
|
||||
const database = await connect(databasePath);
|
||||
const schema = await readFile(schemaPath, 'utf8');
|
||||
|
||||
// The project schema deliberately avoids semicolons inside string literals.
|
||||
const statements = schema
|
||||
.split(';')
|
||||
.map((statement) => statement.trim())
|
||||
.filter(Boolean);
|
||||
|
||||
for (const statement of statements) {
|
||||
await database.exec(statement);
|
||||
}
|
||||
|
||||
console.log(`Applied ${statements.length} schema statements to ${databasePath}`);
|
||||
@@ -0,0 +1,32 @@
|
||||
import { spawnSync } from 'node:child_process';
|
||||
import { resolve } from 'node:path';
|
||||
import process from 'node:process';
|
||||
|
||||
const files = ['.env.development', '.env.production'];
|
||||
const action = process.argv[2];
|
||||
const gitCommand = process.platform === 'win32' ? 'git.exe' : 'git';
|
||||
const dotenvxCli = resolve('node_modules/@dotenvx/dotenvx/src/cli/dotenvx.js');
|
||||
|
||||
function run(command, args) {
|
||||
const result = spawnSync(command, args, { cwd: process.cwd(), stdio: 'inherit' });
|
||||
if (result.error) throw result.error;
|
||||
if (result.status !== 0) process.exit(result.status ?? 1);
|
||||
}
|
||||
|
||||
function dotenvx(command, file) {
|
||||
run(process.execPath, [dotenvxCli, command, '-f', file]);
|
||||
}
|
||||
|
||||
if (!['encrypt', 'decrypt', 'stage'].includes(action)) {
|
||||
console.error('Usage: node scripts/env-files.mjs <encrypt|decrypt|stage>');
|
||||
process.exit(2);
|
||||
}
|
||||
|
||||
for (const file of files) {
|
||||
dotenvx(action === 'decrypt' ? 'decrypt' : 'encrypt', file);
|
||||
}
|
||||
|
||||
if (action === 'stage') {
|
||||
run(gitCommand, ['add', '--', ...files]);
|
||||
console.log('Encrypted and staged environment files.');
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
import { connect } from '@tursodatabase/database';
|
||||
import { argon2id, hash } from 'argon2';
|
||||
import { randomBytes, randomUUID } from 'node:crypto';
|
||||
import { resolve } from 'node:path';
|
||||
import process from 'node:process';
|
||||
|
||||
const databasePath = resolve(process.env.TDR_DATABASE_PATH || './data/shd-build.db');
|
||||
const database = await connect(databasePath);
|
||||
|
||||
const users = [
|
||||
{ username: 'Aewyn', role: 'admin' },
|
||||
{ username: 'Quartermaster', role: 'user' }
|
||||
];
|
||||
|
||||
const findUser = await database.prepare(
|
||||
'SELECT id, username, role FROM users WHERE username = ? COLLATE NOCASE'
|
||||
);
|
||||
const insertUser = await database.prepare(
|
||||
'INSERT INTO users (id, username, password_hash, role, created_at) VALUES (?, ?, ?, ?, ?)'
|
||||
);
|
||||
|
||||
for (const user of users) {
|
||||
const existing = await findUser.get(user.username);
|
||||
|
||||
if (existing) {
|
||||
console.log(`${user.username}: already exists (${existing.role}); left unchanged`);
|
||||
continue;
|
||||
}
|
||||
|
||||
// 120 bits of entropy encoded without punctuation that is awkward to paste.
|
||||
const temporaryPassword = randomBytes(15).toString('base64url');
|
||||
const passwordHash = await hash(temporaryPassword, {
|
||||
type: argon2id,
|
||||
memoryCost: 19_456,
|
||||
timeCost: 2,
|
||||
parallelism: 1
|
||||
});
|
||||
|
||||
await insertUser.run(
|
||||
randomUUID(),
|
||||
user.username,
|
||||
passwordHash,
|
||||
user.role,
|
||||
new Date().toISOString()
|
||||
);
|
||||
|
||||
console.log(`${user.username} (${user.role}) temporary password: ${temporaryPassword}`);
|
||||
}
|
||||
Reference in New Issue
Block a user