← back to Abrams Discovery Viewer
server.js
221 lines
// Abrams v. Pabo discovery response viewer.
// Left pane: PDF (FIN or RPD for Steven or Natalia). Right pane: question rows
// with Steven + Natalia response fields. Auto-saves to data/responses.json.
// Export buttons produce CSV per side or combined.
const express = require('express');
const helmet = require('helmet');
const fs = require('fs');
const path = require('path');
const ROOT = __dirname;
const DATA_FILE = path.join(ROOT, 'data', 'responses.json');
const PORT = parseInt(process.env.PORT, 10) || 9870;
const app = express();
// Security headers via helmet (added 2026-05-04 overnight YOLO loop)
app.use(helmet({ contentSecurityPolicy: false }));
// ----- Basic Auth -----
// LAN-exposed but PII-bearing — every request must present credentials.
// Override via env or the .auth-config.json file (gitignored).
const AUTH_FILE = path.join(ROOT, '.auth-config.json');
let AUTH_USER = process.env.AUTH_USER || 'steve';
let AUTH_PASS = process.env.AUTH_PASS || '';
if (!AUTH_PASS && fs.existsSync(AUTH_FILE)) {
try {
const c = JSON.parse(fs.readFileSync(AUTH_FILE, 'utf-8'));
AUTH_USER = c.user || AUTH_USER;
AUTH_PASS = c.pass || AUTH_PASS;
} catch(e) {}
}
if (!AUTH_PASS) {
// First-run bootstrap — generate a random pass and write it to .auth-config.json
AUTH_PASS = require('crypto').randomBytes(8).toString('base64url');
fs.writeFileSync(AUTH_FILE, JSON.stringify({ user: AUTH_USER, pass: AUTH_PASS }, null, 2), 'utf-8');
console.log(`[auth] Generated credentials → ${AUTH_FILE}`);
console.log(`[auth] user: ${AUTH_USER}`);
console.log(`[auth] pass: ${AUTH_PASS}`);
}
app.use((req, res, next) => {
const hdr = req.headers.authorization || '';
if (hdr.startsWith('Basic ')) {
const decoded = Buffer.from(hdr.slice(6), 'base64').toString('utf-8');
const idx = decoded.indexOf(':');
if (idx >= 0) {
const u = decoded.slice(0, idx);
const p = decoded.slice(idx + 1);
if (u === AUTH_USER && p === AUTH_PASS) return next();
}
}
res.set('WWW-Authenticate', 'Basic realm="Abrams v. Pabo Discovery", charset="UTF-8"');
res.status(401).send('Authentication required.');
});
app.use(express.json({ limit: '5mb' }));
// Refuse to serve snapshot/backup files (*.bak, *.bak.*, *.pre-*) from any static
// root. Defense-in-depth so a stray editor backup or pre-deploy snapshot can never
// leak case PII via a guessable URL. (2026-05-19, fleet-refactor-sweep)
app.use((req, res, next) => {
if (/\.(bak|pre)(\.|$)|\.pre-/i.test(req.path)) {
return res.status(404).type('text').send('Not found.');
}
next();
});
app.use(express.static(path.join(ROOT, 'public')));
app.use('/pdfs', express.static(path.join(ROOT, 'pdfs')));
// ----- Question bank (FIN sections relevant to PI motor vehicle + RPD 1-17) -----
const FIN_QUESTIONS = [
['1.1', 'Identity of Persons Answering: Name, ADDRESS, telephone, and relationship of each PERSON who prepared or assisted in preparing these responses.'],
['2.1', 'State (a) your name; (b) every name you have used in the past; (c) the dates you used each name.'],
['2.2', 'State the date and place of your birth.'],
['2.3', "At the time of the INCIDENT, did you have a driver's license? If so: (a) issuing entity; (b) license number and type; (c) date of issuance; (d) all restrictions."],
['2.4', 'At the time of the INCIDENT, did you have any other permit/license to operate a motor vehicle? Same subparts as 2.3.'],
['2.5', 'State (a) present residence ADDRESS; (b) residence ADDRESSES for the past five years; (c) the dates at each.'],
['2.6', 'Employment: (a) present employer/self-employment name/ADDRESS/phone; (b) every employer or self-employment name/ADDRESS/dates/title/work for five years before the INCIDENT until today.'],
['2.7', 'Education: (a) each school from high school onward; (b) dates attended; (c) highest grade completed; (d) degrees received.'],
['2.8', 'Have you ever been convicted of a felony? If so, for each: (a) city/state; (b) date; (c) offense; (d) court and case number.'],
['2.9', 'Can you speak English with ease? If not, what language and dialect do you normally use?'],
['2.10', 'Can you read and write English with ease? If not, what language and dialect do you normally use?'],
['2.11', 'At the time of the INCIDENT were you acting as agent/employee for any PERSON? If so: (a) name/ADDRESS/phone; (b) duties.'],
['2.12', 'Did you or any other person have any physical, emotional, or mental disability/condition that may have contributed to the INCIDENT? If so: (a) name/ADDRESS/phone; (b) condition; (c) how it contributed.'],
['2.13', 'Within 24 hours before the INCIDENT did you or any person involved use alcohol, marijuana, or any drug/medication (Rx or not)? For each: (a)–(g) per the form.'],
['4.1', 'Was any insurance policy in effect under which you were/might be insured for damages from the INCIDENT? For each policy: (a) coverage; (b) insurer name/ADDRESS; (c) named insured name/ADDRESS/phone; (d) policy number; (e) limits; (f) reservation/coverage dispute; (g) custodian.'],
['4.2', 'Are you self-insured under any statute for damages arising from the INCIDENT? If so, specify the statute.'],
['6.1', 'Do you attribute any physical, mental, or emotional injuries to the INCIDENT? (If "no," skip 6.2–6.7.)'],
['6.2', 'Identify each injury you attribute to the INCIDENT and the area of your body affected.'],
['6.3', 'Do you still have any complaints attributed to the INCIDENT? For each: (a) description; (b) subsiding/same/worsening; (c) frequency and duration.'],
['6.4', 'Did you receive any consultation/examination/treatment from a HEALTH CARE PROVIDER (excluding experts)? For each: (a) name/ADDRESS/phone; (b) type of service; (c) dates; (d) charges to date.'],
['6.5', 'Have you taken any medication (Rx or not) due to injuries attributed to the INCIDENT? For each: (a) name; (b) prescriber; (c) date prescribed/furnished; (d) start/stop dates; (e) cost to date.'],
['6.6', 'Any other medical services necessitated by the injuries (e.g., ambulance, nursing, prosthetics)? For each: (a) nature; (b) date; (c) cost; (d) provider name/ADDRESS/phone.'],
['6.7', 'Has any HEALTH CARE PROVIDER advised future or additional treatment? For each injury: (a) provider name/ADDRESS; (b) complaints; (c) nature, duration, estimated cost.'],
['7.1', 'Do you attribute any vehicle/property damage to the INCIDENT? For each item: (a) describe; (b) damage nature/location; (c) amount claimed and how calculated; (d) if sold: seller, date, price.'],
['7.2', 'Has a written estimate or evaluation been made for any 7.1 property? For each: (a) preparer/date; (b) custodian; (c) amount.'],
['7.3', 'Has any 7.1 property been repaired? For each: (a) date; (b) description; (c) cost; (d) repairer; (e) who paid.'],
['8.1', 'Do you attribute any loss of income or earning capacity to the INCIDENT? (If "no," skip 8.2–8.8.)'],
['8.2', 'State (a) nature of your work; (b) job title at time of INCIDENT; (c) date employment began.'],
['8.3', 'State the last date before the INCIDENT that you worked for compensation.'],
['8.4', 'State your monthly income at the time of the INCIDENT and how it was calculated.'],
['8.5', 'State the date you returned to work at each place of employment following the INCIDENT.'],
['8.6', 'State the dates you did not work and lost income as a result of the INCIDENT.'],
['8.7', 'State the total income you have lost to date as a result of the INCIDENT and how it was calculated.'],
['8.8', 'Will you lose income in the future as a result of the INCIDENT? If so: (a) facts; (b) estimate; (c) time unable to work; (d) calculation method.'],
['9.1', 'Any other damages attributed to the INCIDENT? For each: (a) nature; (b) date; (c) amount; (d) name/ADDRESS/phone of obligee.'],
['9.2', 'Do any DOCUMENTS support 9.1? Describe each and identify custodian.'],
['10.1', 'Before the INCIDENT, did you have complaints/injuries to the same body parts now claimed? For each: (a) description; (b) start/end dates; (c) provider name/ADDRESS/phone.'],
['10.2', 'List all physical, mental, and emotional disabilities you had immediately before the INCIDENT.'],
['10.3', 'After the INCIDENT, did you sustain injuries of the kind now claimed? For each: (a) date/place; (b) other persons; (c) nature; (d) provider; (e) treatment nature/duration.'],
['11.1', 'Except this action, in the past 10 years have you filed an action or made a written claim for personal injury compensation? For each: (a)–(f) per the form.'],
['11.2', "In the past 10 years have you made a written claim/demand for workers' comp benefits? For each: (a)–(g) per the form."],
['12.1', 'Name/ADDRESS/phone of each individual: (a) who witnessed the INCIDENT or events immediately before/after; (b) made statements at the scene; (c) heard statements at the scene; (d) anyone with knowledge.'],
['12.2', 'Have you or anyone acting on your behalf interviewed any individual about the INCIDENT? For each: (a) interviewee; (b) date; (c) interviewer.'],
['12.3', 'Any written/recorded statement obtained from any individual about the INCIDENT? For each: (a) source; (b) obtainer; (c) date; (d) custodian.'],
['12.4', 'Any photographs, films, or videotapes depicting place/object/individual concerning the INCIDENT or plaintiff\'s injuries? (a) number; (b) subjects; (c) dates; (d) photographer; (e) custodian.'],
['12.5', 'Any diagram, reproduction, or model of any place/thing concerning the INCIDENT? For each: (a) type; (b) subject; (c) custodian.'],
['12.6', 'Was a report made by any PERSON concerning the INCIDENT? (a) author; (b) date/type; (c) for whom; (d) custodian.'],
['12.7', 'Have you or anyone acting on your behalf inspected the scene? For each: (a) inspector; (b) date.'],
['13.1', 'Have you or anyone acting on your behalf conducted surveillance of any party? For each: (a)–(d) per the form.'],
['13.2', 'Has a written report been prepared on the surveillance? For each: (a) title; (b) date; (c) author; (d) custodian.'],
['14.1', 'Do you contend any PERSON involved violated any statute/ordinance/regulation that was a legal cause of the INCIDENT? Identify name/ADDRESS/phone and the law violated.'],
['14.2', 'Was any PERSON cited or charged with a violation as a result of the INCIDENT? For each: (a)–(d) per the form.'],
['15.1', 'Identify each denial/affirmative defense in your pleadings; for each: (a) facts; (b) persons with knowledge; (c) DOCUMENTS and custodians.'],
['17.1', 'Is each response to the request for admissions an unqualified admission? If not, for each non-admission: (a)–(d) per the form.'],
['20.1', 'State the date, time, and place of the INCIDENT (closest street ADDRESS or intersection).'],
['20.2', 'For each vehicle involved: (a) year/make/model/license; (b) driver; (c) other occupants; (d) registered owner; (e) lessee; (f) other owner/lien holder; (g) who gave permission to drive.'],
['20.3', 'ADDRESS/location where your trip began and destination.'],
['20.4', 'Describe your route and the location of each non-routine stop.'],
['20.5', 'Street, lane, and direction of travel of each vehicle for the 500 feet before the INCIDENT.'],
['20.6', 'Did the INCIDENT occur at an intersection? If so, describe all traffic control devices/signals/signs.'],
['20.7', 'Was a traffic signal facing you at the time? (a)–(d) per the form.'],
['20.8', 'How the INCIDENT occurred: speed/direction/location of each vehicle (a) just before, (b) at the time, (c) just after.'],
['20.9', 'Information that a malfunction/defect in a vehicle caused the INCIDENT? (a)–(d) per the form.'],
['20.10', 'Information that a malfunction/defect contributed to the injuries? (a)–(d) per the form.'],
['20.11', 'Name/ADDRESS/phone of each owner/possessor of each vehicle since the INCIDENT.'],
];
const RPD_QUESTIONS = [
[1, 'All DOCUMENTS that set forth or establish all HEALTH CARE obtained by YOU as a result of the SUBJECT INCIDENT.'],
[2, 'All DOCUMENTS that set forth or establish all HEALTH CARE COSTS incurred by YOU (charged, paid, accepted as payment in full).'],
[3, 'All DOCUMENTS that set forth or establish YOUR past, present and future loss of earnings or income claimed.'],
[4, 'All written or electronically recorded statements by any person who saw/heard events immediately prior to, during or after the SUBJECT INCIDENT.'],
[5, 'All PHOTOGRAPHS/FILMS/TAPES depicting bodily injuries sustained by YOU.'],
[6, 'All PHOTOGRAPHS/FILMS/TAPES depicting damage to any vehicle.'],
[7, 'All PHOTOGRAPHS/FILMS/TAPES depicting the SUBJECT INCIDENT.'],
[8, 'All DOCUMENTS YOU obtained from any police agency regarding the SUBJECT INCIDENT.'],
[9, 'All EXPLANATIONS OF BENEFITS for HEALTH CARE COSTS (Howell v. Hamilton Meats evaluation).'],
[10, 'All correspondence between YOU and any defendants regarding the SUBJECT INCIDENT.'],
[11, 'All diaries or journal entries mentioning the SUBJECT INCIDENT or YOUR claimed injuries.'],
[12, 'All DOCUMENTS YOU prepared, or contributed to preparing, that reference the SUBJECT INCIDENT.'],
[13, 'All DOCUMENTS that identify any limitation on YOUR ability to perform work as a result of the SUBJECT INCIDENT.'],
[14, 'All DOCUMENTS identified in YOUR response to Form Interrogatories, Set One.'],
[15, 'All PHOTOGRAPHS/FILMS/TAPES depicting the scene of the SUBJECT INCIDENT.'],
[16, 'A copy of YOUR health insurance card.'],
[17, 'A copy of YOUR Medicare card.'],
];
function buildBank() {
// Each row gets a stable id keyed by source + plaintiff + item.
const rows = [];
for (const plaintiff of ['Steven Marcus Abrams', 'Natalia Debra Dultz']) {
const pCode = plaintiff.startsWith('Steven') ? 'abrams' : 'dultz';
for (const [num, q] of FIN_QUESTIONS) {
rows.push({ id: `${pCode}-fin-${num}`, plaintiff, source: 'FIN', item: num, question: q, pdf: `${pCode}-fin.pdf` });
}
for (const [num, q] of RPD_QUESTIONS) {
rows.push({ id: `${pCode}-rpd-${num}`, plaintiff, source: 'RPD', item: String(num), question: q, pdf: `${pCode}-rpd.pdf` });
}
}
return rows;
}
const BANK = buildBank();
function loadResponses() {
try { return JSON.parse(fs.readFileSync(DATA_FILE, 'utf-8')); } catch(e) { return {}; }
}
function saveResponses(data) {
fs.mkdirSync(path.dirname(DATA_FILE), { recursive: true });
fs.writeFileSync(DATA_FILE, JSON.stringify(data, null, 2), 'utf-8');
}
app.get('/api/bank', (req, res) => res.json(BANK));
app.get('/api/responses', (req, res) => res.json(loadResponses()));
app.post('/api/responses', (req, res) => {
const { id, response, status, documents, notes } = req.body || {};
if (!id) return res.status(400).json({ error: 'missing id' });
const data = loadResponses();
data[id] = {
...(data[id] || {}),
...(response !== undefined ? { response } : {}),
...(status !== undefined ? { status } : {}),
...(documents !== undefined ? { documents } : {}),
...(notes !== undefined ? { notes } : {}),
updated: new Date().toISOString(),
};
saveResponses(data);
res.json({ ok: true, row: data[id] });
});
app.get('/api/csv', (req, res) => {
const data = loadResponses();
const esc = s => /[",\n]/.test(String(s||'')) ? '"' + String(s).replace(/"/g,'""') + '"' : String(s||'');
const lines = [['Plaintiff','Source','Set','Item','Question','Response','Documents','Status','Notes','Updated'].join(',')];
for (const r of BANK) {
const v = data[r.id] || {};
lines.push([r.plaintiff, r.source, 'One', r.item, r.question, v.response || '', v.documents || '', v.status || '', v.notes || '', v.updated || ''].map(esc).join(','));
}
res.set('Content-Type', 'text/csv; charset=utf-8');
res.set('Content-Disposition', 'attachment; filename="abrams-pabo-discovery.csv"');
res.send(lines.join('\n'));
});
// Bind via env (default 0.0.0.0 for LAN access). Note: this server holds
// discovery responses (PII, legal case data) — anyone on your WiFi can reach
// it. If that ever feels wrong, set BIND=127.0.0.1.
const BIND = process.env.BIND || '0.0.0.0';
app.listen(PORT, BIND, () => {
console.log(`Abrams discovery viewer on http://${BIND}:${PORT}`);
});