Initial commit — Performance West telecom compliance platform

Includes: API (Express/TypeScript), Astro site, Python workers,
document generators, FCC compliance tools, Canada CRTC formation,
Ansible infrastructure, and deployment scripts.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
justin 2026-04-27 06:54:22 -05:00
commit f8cd37ac8c
1823 changed files with 145167 additions and 0 deletions

View file

@ -0,0 +1,481 @@
/**
* Corporation Status Check API
*
* GET /api/v1/corp/status?name=Acme+LLC&state=WY
* Search entity_cache for corporation status (ACTIVE, DELINQUENT, etc.)
* Returns status + years behind + cost to remediate.
*
* GET /api/v1/corp/search?q=Acme&state=WY
* Fuzzy search entity_cache by name. Returns up to 10 matches.
*
* Used by:
* - Standalone Corporation Status Check tool (/tools/corporation-check)
* - FCC Compliance Check (adds corporation_status check)
*/
import { Router, type Request, type Response } from "express";
import { pool } from "../db.js";
const router = Router();
// ── RA pricing by state ──────────────────────────────────────────────────────
const RA_PRICE_CENTS: Record<string, number> = {
WY: 5000, // $50/yr Wyoming
};
const RA_DEFAULT_CENTS = 9900; // $99/yr all other states
// Our flat markup per annual report or reinstatement filing
const FILING_MARKUP_CENTS = 2500; // $25
// ── Helpers ──────────────────────────────────────────────────────────────────
interface CorpStatusResult {
found: boolean;
entity_name: string | null;
entity_number: string | null;
entity_type: string | null;
status: string | null;
formation_date: string | null;
dissolution_date: string | null;
registered_agent: string | null;
principal_address: string | null;
state: string | null;
// Computed fields
years_behind: number;
annual_report_fee_cents: number;
annual_report_frequency: string | null;
cost_per_year_cents: number;
total_catchup_cents: number;
ra_price_cents: number;
total_with_ra_cents: number;
breakdown: string;
}
/**
* Look up corporation status from entity_cache and compute remediation cost.
* Exported so fcc-lookup.ts can call it directly.
*/
export async function lookupCorpStatus(
entityName: string,
stateCode: string,
): Promise<CorpStatusResult | null> {
if (!entityName || !stateCode) return null;
const state = stateCode.toUpperCase().trim();
const name = entityName.trim();
// Try exact match first, then fuzzy (trigram)
let row: Record<string, unknown> | null = null;
try {
const exact = await pool.query(
`SELECT entity_name, entity_number, entity_type, status,
formation_date, dissolution_date, registered_agent, state
FROM entity_cache
WHERE state = $1 AND LOWER(entity_name) = LOWER($2)
LIMIT 1`,
[state, name],
);
if (exact.rows.length > 0) {
row = exact.rows[0] as Record<string, unknown>;
} else {
// Fuzzy match — require very high similarity (0.8+) to avoid false positives
// like "GTDIAL DATA SOLUTIONS" matching "SPATIAL DATA SOLUTIONS" (0.68)
const fuzzy = await pool.query(
`SELECT entity_name, entity_number, entity_type, status,
formation_date, dissolution_date, registered_agent, state,
similarity(entity_name, $2) AS sim
FROM entity_cache
WHERE state = $1 AND similarity(entity_name, $2) > 0.8
ORDER BY sim DESC
LIMIT 1`,
[state, name],
);
if (fuzzy.rows.length > 0) {
row = fuzzy.rows[0] as Record<string, unknown>;
}
}
} catch (err: any) {
// pg_trgm extension may not be available, or entity_cache empty
if (err?.code !== "42P01") {
console.warn("[corp-status] entity_cache query failed:", err?.message);
}
return null;
}
if (!row) {
// Live Playwright fallback for states without bulk data (WY, DE, etc.)
// Calls the workers /entity-status endpoint which uses the state adapter
const WORKER_URL = process.env.WORKER_URL || "http://workers:8090";
console.log(`[corp-status] Cache miss for "${name}" in ${state} — trying live search via workers`);
try {
const liveRes = await fetch(`${WORKER_URL}/entity-status`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ entity_name: name, state_code: state }),
signal: AbortSignal.timeout(45000),
});
const liveData = await liveRes.json() as Record<string, unknown>;
console.log(`[corp-status] Live search result:`, JSON.stringify(liveData).slice(0, 200));
if (liveRes.ok && liveData.found && liveData.entity_name) {
row = {
entity_name: liveData.entity_name,
entity_number: liveData.entity_number || null,
entity_type: liveData.entity_type || null,
status: liveData.status || "UNKNOWN",
formation_date: liveData.formation_date || null,
dissolution_date: null,
registered_agent: liveData.registered_agent || null,
state,
};
}
} catch {
// Worker unavailable or timeout — skip this state
}
}
if (!row) return null;
const status = (row.status as string) || "UNKNOWN";
const formationDate = row.formation_date
? new Date(row.formation_date as string).toISOString().slice(0, 10)
: null;
// Look up annual report obligation for this state
let annualFee = 0;
let frequency = "annual";
let dueMonth: number | null = null;
let isAnniversary = false;
try {
const obl = await pool.query(
`SELECT fee_cents, frequency, due_month, is_anniversary
FROM state_compliance_obligations
WHERE state_code = $1 AND obligation_type = 'annual_report'
LIMIT 1`,
[state],
);
if (obl.rows.length > 0) {
const o = obl.rows[0] as Record<string, unknown>;
annualFee = (o.fee_cents as number) || 0;
frequency = (o.frequency as string) || "annual";
dueMonth = (o.due_month as number) || null;
isAnniversary = (o.is_anniversary as boolean) || false;
}
} catch {
// state_compliance_obligations table may not exist
}
// Calculate years behind
let yearsBehind = 0;
if (status !== "ACTIVE" && formationDate) {
const formed = new Date(formationDate);
const now = new Date();
const yearsExisted = now.getFullYear() - formed.getFullYear();
if (frequency === "biennial") {
yearsBehind = Math.max(1, Math.floor(yearsExisted / 2));
} else if (frequency === "annual") {
// Conservative estimate: if delinquent, assume at least 1 year behind
// Most states revoke after 2-3 years of non-filing
if (status === "DELINQUENT" || status === "SUSPENDED") {
yearsBehind = Math.max(1, Math.min(3, yearsExisted - 1));
} else if (status === "DISSOLVED" || status === "INACTIVE") {
yearsBehind = Math.max(1, Math.min(5, yearsExisted - 1));
}
}
}
// Cost calculations
const costPerYear = annualFee + FILING_MARKUP_CENTS;
const totalCatchup = yearsBehind * costPerYear;
const raPrice = RA_PRICE_CENTS[state] || RA_DEFAULT_CENTS;
const totalWithRA = totalCatchup + raPrice;
// Build human-readable breakdown
let breakdown = "";
if (yearsBehind > 0) {
const stateFeeStr = `$${(annualFee / 100).toFixed(0)}`;
const markupStr = `$${(FILING_MARKUP_CENTS / 100).toFixed(0)}`;
const perYearStr = `$${(costPerYear / 100).toFixed(0)}`;
const totalStr = `$${(totalCatchup / 100).toFixed(0)}`;
const raStr = `$${(raPrice / 100).toFixed(0)}`;
const grandStr = `$${(totalWithRA / 100).toFixed(0)}`;
if (yearsBehind === 1) {
breakdown = `Annual report: ${stateFeeStr} state fee + ${markupStr} filing = ${perYearStr}. `;
} else {
breakdown = `Annual reports (${yearsBehind} years): ${yearsBehind} × (${stateFeeStr} + ${markupStr}) = ${totalStr}. `;
}
breakdown += `Registered agent: ${raStr}/yr. Total: ${grandStr}.`;
}
return {
found: true,
entity_name: (row.entity_name as string) || null,
entity_number: (row.entity_number as string) || null,
entity_type: (row.entity_type as string) || null,
status,
formation_date: formationDate,
dissolution_date: row.dissolution_date
? new Date(row.dissolution_date as string).toISOString().slice(0, 10)
: null,
registered_agent: (row.registered_agent as string) || null,
principal_address: (row.principal_address as string) || null,
state,
years_behind: yearsBehind,
annual_report_fee_cents: annualFee,
annual_report_frequency: frequency,
cost_per_year_cents: costPerYear,
total_catchup_cents: totalCatchup,
ra_price_cents: raPrice,
total_with_ra_cents: totalWithRA,
breakdown,
};
}
// ── GET /api/v1/corp/search ──────────────────────────────────────────────────
router.get("/api/v1/corp/search", async (req: Request, res: Response) => {
const q = (req.query.q as string || "").trim();
const state = (req.query.state as string || "").toUpperCase().trim();
if (!q || q.length < 2) {
res.status(400).json({ error: "q parameter required (min 2 chars)" });
return;
}
if (!state || state.length !== 2) {
res.status(400).json({ error: "state parameter required (2-letter code)" });
return;
}
try {
const { rows } = await pool.query(
`SELECT entity_name, entity_number, entity_type, status, formation_date,
similarity(entity_name, $2) AS sim
FROM entity_cache
WHERE state = $1 AND entity_name % $2
ORDER BY sim DESC
LIMIT 10`,
[state, q],
);
res.json({
state,
query: q,
count: rows.length,
results: rows.map((r: any) => ({
entity_name: r.entity_name,
entity_number: r.entity_number,
entity_type: r.entity_type,
status: r.status,
formation_date: r.formation_date
? new Date(r.formation_date).toISOString().slice(0, 10)
: null,
})),
});
} catch (err: any) {
if (err?.code === "42P01") {
res.json({ state, query: q, count: 0, results: [], note: "Entity database not yet populated for this state." });
} else {
console.error("[corp/search] Error:", err);
res.status(500).json({ error: "Search failed" });
}
}
});
// ── GET /api/v1/corp/status ──────────────────────────────────────────────────
router.get("/api/v1/corp/status", async (req: Request, res: Response) => {
const name = (req.query.name as string || "").trim();
const state = (req.query.state as string || "").toUpperCase().trim();
if (!name) {
res.status(400).json({ error: "name parameter required" });
return;
}
if (!state || state.length !== 2) {
res.status(400).json({ error: "state parameter required (2-letter code)" });
return;
}
const result = await lookupCorpStatus(name, state);
if (!result) {
res.json({
found: false,
state,
searched_name: name,
note: "Entity not found in our database. Try the standalone state search or check directly with the Secretary of State.",
});
return;
}
res.json(result);
});
// ── GET /api/v1/corp/states ──────────────────────────────────────────────────
// Returns list of states with annual report info for the UI dropdown
router.get("/api/v1/corp/states", async (_req: Request, res: Response) => {
try {
const { rows } = await pool.query(`
SELECT s.state_code, s.fee_cents, s.frequency, s.due_description, s.is_anniversary,
(SELECT count(*) FROM entity_cache WHERE state = s.state_code) AS entity_count
FROM state_compliance_obligations s
WHERE s.obligation_type = 'annual_report'
ORDER BY s.state_code
`);
res.json({
states: rows.map((r: any) => ({
code: r.state_code,
annual_report_fee_cents: r.fee_cents,
frequency: r.frequency,
due: r.due_description,
is_anniversary: r.is_anniversary,
entities_cached: parseInt(r.entity_count, 10),
ra_price_cents: RA_PRICE_CENTS[r.state_code] || RA_DEFAULT_CENTS,
})),
});
} catch (err) {
console.error("[corp/states] Error:", err);
res.status(500).json({ error: "Failed to load state data" });
}
});
// ── POST /api/v1/corp/foreign-qual-check ────────────────────────────────────
//
// Bulk check: given an entity name, home state, and list of states served,
// check entity_cache for foreign qualification status in each state.
// Used by the 499-A intake wizard after the JurisdictionStep.
router.post("/api/v1/corp/foreign-qual-check", async (req: Request, res: Response) => {
try {
const { entity_name, home_state, states } = req.body as {
entity_name?: string;
home_state?: string;
states?: string[];
};
if (!entity_name || !states || !Array.isArray(states) || states.length === 0) {
res.status(400).json({ error: "entity_name and states[] required" });
return;
}
const homeState = (home_state || "").toUpperCase().trim();
const name = entity_name.trim();
// Check each state (skip home state — they're already formed there)
// Use entity_cache only (no live Playwright fallback — too slow for bulk)
const results: Array<{
state_code: string;
has_data: boolean;
found: boolean;
status: string | null;
entity_name_found: string | null;
needs_foreign_qual: boolean;
reason: string;
}> = [];
// Check which states have data in entity_cache so we can distinguish
// "not found" from "no data for this state"
let statesWithData: Set<string>;
try {
const dataCheck = await pool.query(
`SELECT DISTINCT state FROM entity_cache WHERE state = ANY($1)`,
[states.map(s => s.toUpperCase().trim())],
);
statesWithData = new Set(dataCheck.rows.map((r: any) => r.state));
} catch {
statesWithData = new Set();
}
for (const stateCode of states) {
const state = stateCode.toUpperCase().trim();
if (state === homeState) continue; // Skip home state
let found = false;
let status: string | null = null;
let foundName: string | null = null;
const hasData = statesWithData.has(state);
if (hasData) {
try {
// Exact match
const exact = await pool.query(
`SELECT entity_name, status FROM entity_cache
WHERE state = $1 AND LOWER(entity_name) = LOWER($2) LIMIT 1`,
[state, name],
);
if (exact.rows.length > 0) {
found = true;
status = (exact.rows[0] as any).status;
foundName = (exact.rows[0] as any).entity_name;
} else {
// Fuzzy match
const fuzzy = await pool.query(
`SELECT entity_name, status, similarity(entity_name, $2) AS sim
FROM entity_cache
WHERE state = $1 AND similarity(entity_name, $2) > 0.8
ORDER BY sim DESC LIMIT 1`,
[state, name],
);
if (fuzzy.rows.length > 0) {
found = true;
status = (fuzzy.rows[0] as any).status;
foundName = (fuzzy.rows[0] as any).entity_name;
}
}
} catch {
// entity_cache or pg_trgm not available — skip
}
}
let needsFQ = false;
let reason = "";
if (!hasData) {
// No entity_cache data for this state — can't determine, skip
reason = "No data available for this state yet";
} else if (!found) {
needsFQ = true;
reason = "No foreign corporation registration found in this state";
} else if (status && ["DISSOLVED", "REVOKED", "CANCELLED", "SUSPENDED", "INACTIVE"].includes(status.toUpperCase())) {
needsFQ = true;
reason = `Registration found but status is ${status} — reinstatement or new filing needed`;
} else if (status && status.toUpperCase() === "DELINQUENT") {
needsFQ = true;
reason = `Registration found but delinquent — annual report or reinstatement needed`;
}
results.push({
state_code: state,
has_data: hasData,
found,
status,
entity_name_found: foundName,
needs_foreign_qual: needsFQ,
reason,
});
}
const missing = results.filter((r) => r.needs_foreign_qual);
res.json({
entity_name: name,
home_state: homeState,
total_states_checked: results.length,
states_missing: missing.length,
results,
foreign_qual_service_fee_cents: 9900, // $99/state for multi-state
});
} catch (err) {
console.error("[corp/foreign-qual-check] Error:", err);
res.status(500).json({ error: "Foreign qualification check failed" });
}
});
export default router;