2026-09-02 17:38:50 +02:00
|
|
|
PRAGMA foreign_keys = ON;
|
2026-09-02 17:45:57 +02:00
|
|
|
|
|
|
|
|
CREATE TABLE IF NOT EXISTS organizations (
|
|
|
|
|
id TEXT PRIMARY KEY,
|
|
|
|
|
name TEXT NOT NULL,
|
|
|
|
|
created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
CREATE TABLE IF NOT EXISTS users (
|
|
|
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
|
|
|
organization_id TEXT NOT NULL REFERENCES organizations(id),
|
|
|
|
|
email TEXT NOT NULL UNIQUE,
|
|
|
|
|
password_hash TEXT NOT NULL,
|
|
|
|
|
password_salt TEXT NOT NULL,
|
|
|
|
|
role TEXT NOT NULL CHECK(role IN ('viewer','owner','admin','researcher')),
|
|
|
|
|
created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP
|
|
|
|
|
);
|
|
|
|
|
CREATE INDEX IF NOT EXISTS idx_users_org ON users(organization_id);
|
|
|
|
|
|
|
|
|
|
CREATE TABLE IF NOT EXISTS sessions (
|
|
|
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
|
|
|
user_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
|
|
|
|
token_hash TEXT NOT NULL UNIQUE,
|
|
|
|
|
expires_at TEXT NOT NULL,
|
|
|
|
|
created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP
|
|
|
|
|
);
|
|
|
|
|
CREATE INDEX IF NOT EXISTS idx_sessions_token ON sessions(token_hash);
|
|
|
|
|
|
|
|
|
|
CREATE TABLE IF NOT EXISTS audit_log (
|
|
|
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
|
|
|
organization_id TEXT REFERENCES organizations(id),
|
|
|
|
|
user_id INTEGER REFERENCES users(id) ON DELETE SET NULL,
|
|
|
|
|
action TEXT NOT NULL,
|
|
|
|
|
details TEXT NOT NULL DEFAULT '',
|
|
|
|
|
created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP
|
|
|
|
|
);
|
|
|
|
|
CREATE INDEX IF NOT EXISTS idx_audit_org ON audit_log(organization_id);
|
|
|
|
|
|
2026-09-02 17:38:50 +02:00
|
|
|
CREATE TABLE IF NOT EXISTS businesses (
|
|
|
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
|
|
|
organization_id TEXT NOT NULL,
|
|
|
|
|
name TEXT NOT NULL,
|
|
|
|
|
website TEXT NOT NULL DEFAULT '',
|
|
|
|
|
website_domain TEXT NOT NULL DEFAULT '',
|
|
|
|
|
email TEXT NOT NULL DEFAULT '',
|
|
|
|
|
phone TEXT NOT NULL DEFAULT '',
|
|
|
|
|
description TEXT NOT NULL DEFAULT '',
|
|
|
|
|
score INTEGER NOT NULL DEFAULT 0,
|
|
|
|
|
score_version TEXT NOT NULL DEFAULT 'mvp-1',
|
|
|
|
|
score_factors TEXT NOT NULL DEFAULT '[]',
|
|
|
|
|
website_class TEXT NOT NULL DEFAULT 'missing',
|
|
|
|
|
created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP
|
|
|
|
|
);
|
|
|
|
|
CREATE INDEX IF NOT EXISTS idx_businesses_org ON businesses(organization_id);
|
|
|
|
|
CREATE UNIQUE INDEX IF NOT EXISTS uq_business_domain ON businesses(organization_id, website_domain) WHERE website_domain <> '';
|
|
|
|
|
CREATE UNIQUE INDEX IF NOT EXISTS uq_business_email ON businesses(organization_id, email) WHERE email <> '';
|
|
|
|
|
CREATE UNIQUE INDEX IF NOT EXISTS uq_business_phone ON businesses(organization_id, phone) WHERE phone <> '';
|
2026-09-02 17:45:57 +02:00
|
|
|
|
2026-09-02 17:38:50 +02:00
|
|
|
CREATE TABLE IF NOT EXISTS suppressions (
|
|
|
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
|
|
|
organization_id TEXT NOT NULL,
|
|
|
|
|
kind TEXT NOT NULL CHECK(kind IN ('email','domain','phone')),
|
|
|
|
|
value TEXT NOT NULL,
|
|
|
|
|
created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
|
|
|
UNIQUE(organization_id, kind, value)
|
|
|
|
|
);
|