← back to Crankd

server.js

66 lines

'use strict';

require('dotenv').config();
const express = require('express');
const path = require('path');
const { aiDisclosurePreamble, preflight, boolEnv } = require('./lib/compliance');

const PORT = parseInt(process.env.PORT || '9821', 10);
const app = express();

app.use(express.json({ limit: '256kb' }));
app.use(express.urlencoded({ extended: false }));
app.use((_req, res, next) => {
  res.set('Cache-Control', 'no-store');
  next();
});

app.get('/healthz', (_req, res) => {
  res.json({
    ok: true,
    service: 'crankd',
    port: PORT,
    steve_only_mode: boolEnv('COMPLIANCE_STEVE_ONLY_MODE', true),
    opt_in_required: boolEnv('COMPLIANCE_REQUIRE_OPT_IN', true),
    ai_disclosure_required: boolEnv('COMPLIANCE_AI_DISCLOSURE_REQUIRED', true),
  });
});

// 404-guard: never serve snapshot/backup files from the static root
const SNAPSHOT_PATH_RE = /(?:\.bak(?:\.|$)|\.pre-|\/\.pre-)/i;
app.use((req, res, next) => {
  if (SNAPSHOT_PATH_RE.test(req.path)) return res.status(404).type('text/plain').send('not found');
  next();
});

// Public landing
app.use('/', express.static(path.join(__dirname, 'public'), { extensions: ['html'] }));

// --- Compliance pre-flight (dry-run; no Twilio call yet) ---
app.post('/api/preflight', (req, res) => {
  const { recipient, opt_in_record, has_recording_consent, dnc_clean } = req.body || {};
  if (!recipient) return res.status(400).json({ ok: false, error: 'recipient required' });
  const result = preflight({ recipient, opt_in_record, has_recording_consent, dnc_clean });
  res.json({
    ...result,
    preamble: aiDisclosurePreamble(),
  });
});

// --- Place call (HARD-DISABLED in v0.1; flip CRANKD_ENABLE_DIAL=1 only after wiring) ---
app.post('/api/call', (req, res) => {
  if (!/^(1|true)$/i.test(process.env.CRANKD_ENABLE_DIAL || '')) {
    return res.status(503).json({
      ok: false,
      error: 'dialing_disabled_in_v0_1',
      hint: 'Wire opt-in flow + DNC scrub + recipient allowlist first, then set CRANKD_ENABLE_DIAL=1.',
    });
  }
  return res.status(501).json({ ok: false, error: 'not_implemented' });
});

app.listen(PORT, () => {
  console.log(`[crankd] listening on :${PORT}`);
  console.log(`[crankd] steve_only_mode=${boolEnv('COMPLIANCE_STEVE_ONLY_MODE', true)} opt_in=${boolEnv('COMPLIANCE_REQUIRE_OPT_IN', true)}`);
});