pediatric-ai-scribe-v3/src/utils/sessions.js
Daniel b5abbb69fc Add node-pg-migrate for versioned schema changes + better mobile UA labels
Infrastructure only — no existing data or tables modified.

  src/db/migrate.js           — programmatic runner, fires at boot after
                                 the existing idempotent initDatabase()
  migrations/1744600000000...  — intentionally empty example, documents
                                 the file shape. Registered in the new
                                 pgmigrations tracking table so it won't
                                 rerun.
  .node-pg-migraterc.json     — CLI config (migrations-dir, utc naming)
  docs/migrations.md          — workflow + conventions
  package.json                — migrate:up/down/new/status npm scripts
                                 (status is a direct pgmigrations query
                                 since node-pg-migrate v7 lacks a status
                                 subcommand)

src/utils/sessions.js:
  - parseUserAgent now recognizes the Capacitor wrapper (UA suffix
    "PedScribe-Android" / "PedScribe-iOS") and labels sessions
    "PedScribe (Android)" instead of "Chrome on Android".

Going forward: schema changes go in /migrations as versioned files
with up() + down(); the inline init in database.js is the implicit
baseline for everything already in production.
2026-04-14 05:06:19 +02:00

42 lines
1.4 KiB
JavaScript

var crypto = require('crypto');
function hashToken(token) {
return crypto.createHash('sha256').update(token).digest('hex');
}
function parseUserAgent(ua) {
if (!ua) return 'Unknown device';
// Native PedScribe app (Capacitor appends "PedScribe-Android" / "PedScribe-iOS")
if (/PedScribe-Android/i.test(ua)) return 'PedScribe (Android)';
if (/PedScribe-iOS/i.test(ua)) return 'PedScribe (iOS)';
if (/Capacitor/i.test(ua)) {
if (/Android/i.test(ua)) return 'PedScribe (Android)';
if (/iPhone|iPad|iOS/i.test(ua)) return 'PedScribe (iOS)';
return 'PedScribe app';
}
var browser = 'Unknown browser';
var os = 'Unknown OS';
if (/Edg\//.test(ua)) browser = 'Edge';
else if (/OPR\/|Opera/.test(ua)) browser = 'Opera';
else if (/Firefox\//.test(ua)) browser = 'Firefox';
else if (/Safari\//.test(ua) && !/Chrome/.test(ua)) browser = 'Safari';
else if (/Chrome\//.test(ua)) browser = 'Chrome';
if (/Windows/.test(ua)) os = 'Windows';
else if (/Mac OS X/.test(ua)) os = 'macOS';
else if (/Android/.test(ua)) os = 'Android';
else if (/iPhone|iPad/.test(ua)) os = 'iOS';
else if (/Linux/.test(ua)) os = 'Linux';
else if (/CrOS/.test(ua)) os = 'ChromeOS';
return browser + ' on ' + os;
}
function generateSessionId() {
return crypto.randomUUID();
}
module.exports = { hashToken, parseUserAgent, generateSessionId };