mirror of
git://f0xx.org/ac/ac-deploy
synced 2026-07-29 06:17:57 +03:00
cluster0: lab seeds, Gitea mirrors, compose mail, verify tooling
Freeze lab-seeds backend for COMPOSE_SKIP_MONOLITH; fix Gitea mirror scripts for 1.25 API; add secrets.lab.env template, verify-mail-lab, LAB_PUBLIC_ORIGIN, and credentials pointers for mirror token recovery. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,46 @@
|
||||
-- Phase 1: companies, memberships, devices, report scoping.
|
||||
-- Idempotent on fresh DB; existing installs: prefer app auto-migration (Database::ensureRbacSchema).
|
||||
-- Manual run: mysql -u root -p androidcast_crashes < sql/migrations/001_rbac_companies.sql
|
||||
|
||||
USE androidcast_crashes;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS companies (
|
||||
id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
|
||||
slug VARCHAR(64) NOT NULL,
|
||||
name VARCHAR(128) NOT NULL,
|
||||
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
UNIQUE KEY uq_companies_slug (slug)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS company_memberships (
|
||||
id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
|
||||
user_id INT UNSIGNED NOT NULL,
|
||||
company_id INT UNSIGNED NOT NULL,
|
||||
role ENUM('owner', 'admin', 'member') NOT NULL DEFAULT 'member',
|
||||
permissions_json LONGTEXT NULL,
|
||||
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
UNIQUE KEY uq_membership_user_company (user_id, company_id),
|
||||
KEY idx_membership_company (company_id),
|
||||
CONSTRAINT fk_membership_user FOREIGN KEY (user_id) REFERENCES users (id) ON DELETE CASCADE,
|
||||
CONSTRAINT fk_membership_company FOREIGN KEY (company_id) REFERENCES companies (id) ON DELETE CASCADE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS devices (
|
||||
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
|
||||
company_id INT UNSIGNED NOT NULL,
|
||||
external_id VARCHAR(128) NOT NULL,
|
||||
display_name VARCHAR(256) NULL,
|
||||
manufacturer VARCHAR(128) NULL,
|
||||
model VARCHAR(128) NULL,
|
||||
source ENUM('auto', 'manual', 'barcode') NOT NULL DEFAULT 'auto',
|
||||
registered_at_ms BIGINT NOT NULL,
|
||||
last_seen_at_ms BIGINT NOT NULL,
|
||||
meta_json LONGTEXT NULL,
|
||||
UNIQUE KEY uq_devices_company_external (company_id, external_id),
|
||||
KEY idx_devices_company (company_id),
|
||||
CONSTRAINT fk_devices_company FOREIGN KEY (company_id) REFERENCES companies (id) ON DELETE CASCADE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
|
||||
-- reports.company_id / reports.device_id: run 002_reports_company_columns.sql as root (app user cannot ALTER)
|
||||
|
||||
INSERT IGNORE INTO companies (id, slug, name) VALUES (1, 'default', 'Default');
|
||||
@@ -0,0 +1,24 @@
|
||||
-- Add RBAC columns to existing reports table (requires ALTER — run as MySQL/MariaDB root).
|
||||
-- App user androidcast typically has SELECT,INSERT,UPDATE,DELETE only.
|
||||
--
|
||||
-- mysql -u root -p androidcast_crashes < sql/migrations/002_reports_company_columns.sql
|
||||
--
|
||||
-- If 001_rbac_companies.sql was not applied yet, run that first.
|
||||
|
||||
USE androidcast_crashes;
|
||||
|
||||
INSERT IGNORE INTO companies (id, slug, name) VALUES (1, 'default', 'Default');
|
||||
|
||||
ALTER TABLE reports
|
||||
ADD COLUMN IF NOT EXISTS company_id INT UNSIGNED NOT NULL DEFAULT 1 AFTER id,
|
||||
ADD COLUMN IF NOT EXISTS device_id BIGINT UNSIGNED NULL AFTER company_id;
|
||||
|
||||
UPDATE reports SET company_id = 1 WHERE company_id IS NULL OR company_id = 0;
|
||||
|
||||
ALTER TABLE reports ADD INDEX IF NOT EXISTS idx_reports_company (company_id);
|
||||
ALTER TABLE reports ADD INDEX IF NOT EXISTS idx_reports_device (device_id);
|
||||
|
||||
INSERT IGNORE INTO company_memberships (user_id, company_id, role)
|
||||
SELECT u.id, 1,
|
||||
CASE WHEN LOWER(u.role) IN ('root', 'admin', 'platform_admin') THEN 'owner' ELSE 'member' END
|
||||
FROM users u;
|
||||
@@ -0,0 +1,32 @@
|
||||
-- Tickets (tasks / QA / user issues) — same console as crash reports.
|
||||
-- Run: mysql -u root -p androidcast_crashes < sql/migrations/003_tickets.sql
|
||||
|
||||
CREATE TABLE IF NOT EXISTS tickets (
|
||||
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
|
||||
company_id INT UNSIGNED NOT NULL DEFAULT 1,
|
||||
device_id BIGINT UNSIGNED NULL,
|
||||
ticket_id VARCHAR(128) NOT NULL,
|
||||
title VARCHAR(256) NOT NULL,
|
||||
brief TEXT NULL,
|
||||
body LONGTEXT NOT NULL,
|
||||
opened_at_ms BIGINT NOT NULL,
|
||||
created_at_ms BIGINT NOT NULL,
|
||||
updated_at_ms BIGINT NOT NULL,
|
||||
owner_username VARCHAR(64) NOT NULL DEFAULT '',
|
||||
verified_by_username VARCHAR(64) NULL,
|
||||
device_model VARCHAR(128) NULL,
|
||||
app_package VARCHAR(128) NULL,
|
||||
app_version VARCHAR(64) NULL,
|
||||
os_name VARCHAR(64) NULL,
|
||||
os_version VARCHAR(128) NULL,
|
||||
rating TINYINT UNSIGNED NOT NULL DEFAULT 0,
|
||||
tags_json LONGTEXT NOT NULL,
|
||||
payload_json LONGTEXT NOT NULL,
|
||||
UNIQUE KEY uq_tickets_ticket_id (ticket_id),
|
||||
KEY idx_tickets_company (company_id),
|
||||
KEY idx_tickets_opened (opened_at_ms),
|
||||
KEY idx_tickets_updated (updated_at_ms),
|
||||
KEY idx_tickets_owner (owner_username),
|
||||
CONSTRAINT fk_tickets_company FOREIGN KEY (company_id) REFERENCES companies (id),
|
||||
CONSTRAINT fk_tickets_device FOREIGN KEY (device_id) REFERENCES devices (id) ON DELETE SET NULL
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
@@ -0,0 +1,27 @@
|
||||
CREATE TABLE IF NOT EXISTS tickets (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
company_id INTEGER NOT NULL DEFAULT 1,
|
||||
device_id INTEGER NULL,
|
||||
ticket_id TEXT NOT NULL UNIQUE,
|
||||
title TEXT NOT NULL,
|
||||
brief TEXT,
|
||||
body TEXT NOT NULL,
|
||||
opened_at_ms INTEGER NOT NULL,
|
||||
created_at_ms INTEGER NOT NULL,
|
||||
updated_at_ms INTEGER NOT NULL,
|
||||
owner_username TEXT NOT NULL DEFAULT '',
|
||||
verified_by_username TEXT,
|
||||
device_model TEXT,
|
||||
app_package TEXT,
|
||||
app_version TEXT,
|
||||
os_name TEXT,
|
||||
os_version TEXT,
|
||||
rating INTEGER NOT NULL DEFAULT 0,
|
||||
tags_json TEXT NOT NULL DEFAULT '[]',
|
||||
payload_json TEXT NOT NULL,
|
||||
FOREIGN KEY (company_id) REFERENCES companies(id),
|
||||
FOREIGN KEY (device_id) REFERENCES devices(id) ON DELETE SET NULL
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_tickets_company ON tickets(company_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_tickets_opened ON tickets(opened_at_ms DESC);
|
||||
@@ -0,0 +1,18 @@
|
||||
-- Ticket workflow, assignees, comments. Run as MySQL root on existing DBs.
|
||||
-- mysql -u root -p androidcast_crashes < sql/migrations/004_ticket_workflow.sql
|
||||
|
||||
ALTER TABLE tickets
|
||||
ADD COLUMN workflow_state VARCHAR(32) NOT NULL DEFAULT 'triage' AFTER tags_json,
|
||||
ADD COLUMN assignees_json LONGTEXT NOT NULL DEFAULT '[]' AFTER workflow_state;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS ticket_comments (
|
||||
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
|
||||
ticket_id BIGINT UNSIGNED NOT NULL,
|
||||
author_username VARCHAR(64) NOT NULL,
|
||||
body LONGTEXT NOT NULL,
|
||||
created_at_ms BIGINT NOT NULL,
|
||||
updated_at_ms BIGINT NOT NULL,
|
||||
KEY idx_ticket_comments_ticket (ticket_id),
|
||||
KEY idx_ticket_comments_created (created_at_ms),
|
||||
CONSTRAINT fk_ticket_comments_ticket FOREIGN KEY (ticket_id) REFERENCES tickets (id) ON DELETE CASCADE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
@@ -0,0 +1,13 @@
|
||||
ALTER TABLE tickets ADD COLUMN workflow_state TEXT NOT NULL DEFAULT 'triage';
|
||||
ALTER TABLE tickets ADD COLUMN assignees_json TEXT NOT NULL DEFAULT '[]';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS ticket_comments (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
ticket_id INTEGER NOT NULL,
|
||||
author_username TEXT NOT NULL,
|
||||
body TEXT NOT NULL,
|
||||
created_at_ms INTEGER NOT NULL,
|
||||
updated_at_ms INTEGER NOT NULL,
|
||||
FOREIGN KEY (ticket_id) REFERENCES tickets (id) ON DELETE CASCADE
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS idx_ticket_comments_ticket ON ticket_comments (ticket_id);
|
||||
@@ -0,0 +1,20 @@
|
||||
-- Ticket attachments: uploaded files + external links (incl. Google URLs).
|
||||
-- mysql -u root -p androidcast_crashes < sql/migrations/005_ticket_attachments.sql
|
||||
|
||||
CREATE TABLE IF NOT EXISTS ticket_attachments (
|
||||
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
|
||||
ticket_id BIGINT UNSIGNED NOT NULL,
|
||||
kind ENUM('file', 'link') NOT NULL,
|
||||
title VARCHAR(256) NOT NULL DEFAULT '',
|
||||
external_url TEXT NULL,
|
||||
provider VARCHAR(32) NOT NULL DEFAULT 'generic',
|
||||
storage_name VARCHAR(128) NULL,
|
||||
original_name VARCHAR(256) NULL,
|
||||
mime_type VARCHAR(128) NULL,
|
||||
size_bytes BIGINT UNSIGNED NULL,
|
||||
created_by VARCHAR(64) NOT NULL,
|
||||
created_at_ms BIGINT NOT NULL,
|
||||
KEY idx_ticket_attachments_ticket (ticket_id),
|
||||
KEY idx_ticket_attachments_created (created_at_ms),
|
||||
CONSTRAINT fk_ticket_attachments_ticket FOREIGN KEY (ticket_id) REFERENCES tickets (id) ON DELETE CASCADE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
@@ -0,0 +1,16 @@
|
||||
CREATE TABLE IF NOT EXISTS ticket_attachments (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
ticket_id INTEGER NOT NULL,
|
||||
kind TEXT NOT NULL CHECK (kind IN ('file', 'link')),
|
||||
title TEXT NOT NULL DEFAULT '',
|
||||
external_url TEXT NULL,
|
||||
provider TEXT NOT NULL DEFAULT 'generic',
|
||||
storage_name TEXT NULL,
|
||||
original_name TEXT NULL,
|
||||
mime_type TEXT NULL,
|
||||
size_bytes INTEGER NULL,
|
||||
created_by TEXT NOT NULL,
|
||||
created_at_ms INTEGER NOT NULL,
|
||||
FOREIGN KEY (ticket_id) REFERENCES tickets (id) ON DELETE CASCADE
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS idx_ticket_attachments_ticket ON ticket_attachments (ticket_id);
|
||||
@@ -0,0 +1,30 @@
|
||||
-- Graph session metrics (direct MariaDB ingest; no ETL).
|
||||
-- Run as root on existing servers: mysql -u root -p androidcast_crashes < sql/migrations/006_graph_sessions.sql
|
||||
|
||||
CREATE TABLE IF NOT EXISTS graph_sessions (
|
||||
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
|
||||
session_id VARCHAR(96) NOT NULL,
|
||||
company_id INT UNSIGNED NOT NULL DEFAULT 1,
|
||||
user_id INT UNSIGNED NULL,
|
||||
device_id VARCHAR(128) NULL,
|
||||
app_version VARCHAR(64) NULL,
|
||||
sdk_int INT NULL,
|
||||
started_at_ms BIGINT NOT NULL,
|
||||
ended_at_ms BIGINT NOT NULL,
|
||||
duration_s INT NOT NULL,
|
||||
completed TINYINT(1) NOT NULL DEFAULT 1,
|
||||
direction ENUM('sender','receiver') NOT NULL DEFAULT 'sender',
|
||||
transport VARCHAR(24) NOT NULL DEFAULT 'wifi',
|
||||
avg_kbps INT NOT NULL DEFAULT 0,
|
||||
recv_kbps INT NOT NULL DEFAULT 0,
|
||||
packet_loss_pct DECIMAL(6,3) NOT NULL DEFAULT 0.0,
|
||||
ntp_source VARCHAR(32) NOT NULL DEFAULT 'device',
|
||||
ntp_correction_s INT NULL,
|
||||
install_source VARCHAR(32) NOT NULL DEFAULT 'direct',
|
||||
meta_json LONGTEXT NULL,
|
||||
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
UNIQUE KEY uq_graph_session_id (session_id),
|
||||
KEY idx_graph_company_time (company_id, started_at_ms),
|
||||
KEY idx_graph_device (device_id),
|
||||
KEY idx_graph_app_version (app_version)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
@@ -0,0 +1,6 @@
|
||||
-- SQLite graph_sessions indexes (table may already exist via GraphRepository::ensureSchema).
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_graph_company_time ON graph_sessions (company_id, started_at_ms);
|
||||
CREATE INDEX IF NOT EXISTS idx_graph_user_time ON graph_sessions (user_id, started_at_ms);
|
||||
CREATE INDEX IF NOT EXISTS idx_graph_started ON graph_sessions (started_at_ms);
|
||||
CREATE INDEX IF NOT EXISTS idx_graph_device ON graph_sessions (device_id);
|
||||
@@ -0,0 +1,56 @@
|
||||
-- Remote access control plane (WireGuard v1, reverse SSH reserved).
|
||||
-- MariaDB / production: mysql -u root -p androidcast_crashes < sql/migrations/007_remote_access.sql
|
||||
|
||||
CREATE TABLE IF NOT EXISTS remote_access_devices (
|
||||
id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
|
||||
device_id VARCHAR(128) NOT NULL,
|
||||
company_id INT UNSIGNED NOT NULL DEFAULT 1,
|
||||
whitelisted TINYINT(1) NOT NULL DEFAULT 0,
|
||||
opt_in_mode ENUM('none','wireguard','rssh') NOT NULL DEFAULT 'none',
|
||||
last_random VARCHAR(96) NULL,
|
||||
app_version VARCHAR(64) NULL,
|
||||
last_seen_at TIMESTAMP NULL,
|
||||
notes TEXT NULL,
|
||||
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
UNIQUE KEY uq_ra_device (device_id),
|
||||
KEY idx_ra_device_company (company_id),
|
||||
KEY idx_ra_device_whitelist (whitelisted, opt_in_mode)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS remote_access_sessions (
|
||||
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
|
||||
session_id VARCHAR(36) NOT NULL,
|
||||
device_id VARCHAR(128) NOT NULL,
|
||||
company_id INT UNSIGNED NOT NULL DEFAULT 1,
|
||||
operator_user_id INT UNSIGNED NULL,
|
||||
status ENUM('pending','active','inactive','closed','closed_timeout') NOT NULL DEFAULT 'pending',
|
||||
tunnel ENUM('wireguard','ssh_reverse') NOT NULL DEFAULT 'wireguard',
|
||||
endpoint VARCHAR(255) NULL,
|
||||
wg_client_private_key VARCHAR(64) NULL,
|
||||
wg_client_address VARCHAR(64) NULL,
|
||||
wg_server_public_key VARCHAR(64) NULL,
|
||||
wg_peer_allowed_ips VARCHAR(255) NULL,
|
||||
expires_at INT UNSIGNED NULL,
|
||||
last_activity_at TIMESTAMP NULL,
|
||||
close_reason VARCHAR(255) NULL,
|
||||
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
closed_at TIMESTAMP NULL,
|
||||
UNIQUE KEY uq_ra_session (session_id),
|
||||
KEY idx_ra_sess_device (device_id, status),
|
||||
KEY idx_ra_sess_status (status, last_activity_at)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS remote_access_events (
|
||||
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
|
||||
session_id VARCHAR(36) NULL,
|
||||
device_id VARCHAR(128) NOT NULL,
|
||||
actor_user_id INT UNSIGNED NULL,
|
||||
action VARCHAR(64) NOT NULL,
|
||||
reason VARCHAR(255) NULL,
|
||||
meta_json LONGTEXT NULL,
|
||||
client_ip VARCHAR(64) NULL,
|
||||
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
KEY idx_ra_evt_device (device_id, created_at),
|
||||
KEY idx_ra_evt_session (session_id)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
@@ -0,0 +1,50 @@
|
||||
-- SQLite dev mirror for remote access tables.
|
||||
|
||||
CREATE TABLE IF NOT EXISTS remote_access_devices (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
device_id TEXT NOT NULL UNIQUE,
|
||||
company_id INTEGER NOT NULL DEFAULT 1,
|
||||
whitelisted INTEGER NOT NULL DEFAULT 0,
|
||||
opt_in_mode TEXT NOT NULL DEFAULT 'none',
|
||||
last_random TEXT NULL,
|
||||
app_version TEXT NULL,
|
||||
last_seen_at TEXT NULL,
|
||||
notes TEXT NULL,
|
||||
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
||||
updated_at TEXT NOT NULL DEFAULT (datetime('now'))
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS remote_access_sessions (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
session_id TEXT NOT NULL UNIQUE,
|
||||
device_id TEXT NOT NULL,
|
||||
company_id INTEGER NOT NULL DEFAULT 1,
|
||||
operator_user_id INTEGER NULL,
|
||||
status TEXT NOT NULL DEFAULT 'pending',
|
||||
tunnel TEXT NOT NULL DEFAULT 'wireguard',
|
||||
endpoint TEXT NULL,
|
||||
wg_client_private_key TEXT NULL,
|
||||
wg_client_address TEXT NULL,
|
||||
wg_server_public_key TEXT NULL,
|
||||
wg_peer_allowed_ips TEXT NULL,
|
||||
expires_at INTEGER NULL,
|
||||
last_activity_at TEXT NULL,
|
||||
close_reason TEXT NULL,
|
||||
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
||||
closed_at TEXT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS remote_access_events (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
session_id TEXT NULL,
|
||||
device_id TEXT NOT NULL,
|
||||
actor_user_id INTEGER NULL,
|
||||
action TEXT NOT NULL,
|
||||
reason TEXT NULL,
|
||||
meta_json TEXT NULL,
|
||||
client_ip TEXT NULL,
|
||||
created_at TEXT NOT NULL DEFAULT (datetime('now'))
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_ra_evt_device ON remote_access_events(device_id, created_at);
|
||||
CREATE INDEX IF NOT EXISTS idx_ra_sess_device ON remote_access_sessions(device_id, status);
|
||||
@@ -0,0 +1,41 @@
|
||||
-- Email registration + 2FA tables (MariaDB). Run as DB root once; SQLite dev auto-migrates via AuthEmailSchema.
|
||||
|
||||
ALTER TABLE users
|
||||
ADD COLUMN IF NOT EXISTS email_normalized VARCHAR(254) NULL,
|
||||
ADD COLUMN IF NOT EXISTS email_verified_at TIMESTAMP NULL,
|
||||
ADD COLUMN IF NOT EXISTS status ENUM('pending','active','locked','disabled') NOT NULL DEFAULT 'active',
|
||||
ADD COLUMN IF NOT EXISTS recovery_email_normalized VARCHAR(254) NULL;
|
||||
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS uq_users_email_normalized ON users (email_normalized);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS auth_tokens (
|
||||
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
|
||||
user_id INT UNSIGNED NULL,
|
||||
purpose ENUM('verify_email','reset_password','login_magic') NOT NULL,
|
||||
token_hash CHAR(64) NOT NULL,
|
||||
email_normalized VARCHAR(254) NULL,
|
||||
expires_at TIMESTAMP NOT NULL,
|
||||
used_at TIMESTAMP NULL,
|
||||
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
KEY idx_auth_tokens_hash (token_hash),
|
||||
KEY idx_auth_tokens_email (email_normalized)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS auth_factors (
|
||||
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
|
||||
user_id INT UNSIGNED NOT NULL,
|
||||
type ENUM('totp','webauthn','backup_code') NOT NULL,
|
||||
secret_encrypted TEXT NULL,
|
||||
label VARCHAR(64) NULL,
|
||||
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
KEY idx_auth_factors_user (user_id)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS auth_attempts (
|
||||
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
|
||||
ip_hash CHAR(64) NOT NULL,
|
||||
username_hash CHAR(64) NULL,
|
||||
outcome VARCHAR(32) NOT NULL,
|
||||
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
KEY idx_auth_attempts_ip (ip_hash, created_at)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
@@ -0,0 +1,5 @@
|
||||
-- Extra graph_sessions indexes for graphs dashboard (user scope + platform-wide time range).
|
||||
-- Idempotent on MariaDB 10.5+: duplicate index names are ignored manually when re-run.
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_graph_user_time ON graph_sessions (user_id, started_at_ms);
|
||||
CREATE INDEX IF NOT EXISTS idx_graph_started ON graph_sessions (started_at_ms);
|
||||
@@ -0,0 +1,8 @@
|
||||
-- RSSH session credentials (MariaDB). Idempotent ALTERs.
|
||||
ALTER TABLE remote_access_sessions
|
||||
ADD COLUMN IF NOT EXISTS rssh_bastion_host VARCHAR(255) NULL,
|
||||
ADD COLUMN IF NOT EXISTS rssh_bastion_port INT UNSIGNED NULL,
|
||||
ADD COLUMN IF NOT EXISTS rssh_username VARCHAR(64) NULL,
|
||||
ADD COLUMN IF NOT EXISTS rssh_secret VARCHAR(128) NULL,
|
||||
ADD COLUMN IF NOT EXISTS rssh_remote_port INT UNSIGNED NULL,
|
||||
ADD COLUMN IF NOT EXISTS rssh_local_port INT UNSIGNED NULL DEFAULT 8022;
|
||||
@@ -0,0 +1 @@
|
||||
-- RSSH columns for SQLite (applied via Database::ensureSchema).
|
||||
127
sim/cluster0/lab-seeds/backend/sql/schema.mariadb.sql
Normal file
127
sim/cluster0/lab-seeds/backend/sql/schema.mariadb.sql
Normal file
@@ -0,0 +1,127 @@
|
||||
-- Fresh MariaDB install for Android Cast crash reporter.
|
||||
-- Run as root: mysql -u root -p < sql/schema.mariadb.sql
|
||||
-- App user needs: SELECT, INSERT, UPDATE, DELETE on androidcast_crashes.*
|
||||
|
||||
CREATE DATABASE IF NOT EXISTS androidcast_crashes
|
||||
CHARACTER SET utf8mb4
|
||||
COLLATE utf8mb4_unicode_ci;
|
||||
|
||||
USE androidcast_crashes;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS companies (
|
||||
id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
|
||||
slug VARCHAR(64) NOT NULL,
|
||||
name VARCHAR(128) NOT NULL,
|
||||
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
UNIQUE KEY uq_companies_slug (slug)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS users (
|
||||
id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
|
||||
username VARCHAR(64) NOT NULL,
|
||||
password_hash VARCHAR(255) NOT NULL,
|
||||
role ENUM('root', 'platform_admin', 'admin', 'viewer') NOT NULL DEFAULT 'viewer',
|
||||
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
UNIQUE KEY uq_users_username (username)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS company_memberships (
|
||||
id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
|
||||
user_id INT UNSIGNED NOT NULL,
|
||||
company_id INT UNSIGNED NOT NULL,
|
||||
role ENUM('owner', 'admin', 'member') NOT NULL DEFAULT 'member',
|
||||
permissions_json LONGTEXT NULL,
|
||||
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
UNIQUE KEY uq_membership_user_company (user_id, company_id),
|
||||
KEY idx_membership_company (company_id),
|
||||
CONSTRAINT fk_membership_user FOREIGN KEY (user_id) REFERENCES users (id) ON DELETE CASCADE,
|
||||
CONSTRAINT fk_membership_company FOREIGN KEY (company_id) REFERENCES companies (id) ON DELETE CASCADE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS devices (
|
||||
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
|
||||
company_id INT UNSIGNED NOT NULL,
|
||||
external_id VARCHAR(128) NOT NULL,
|
||||
display_name VARCHAR(256) NULL,
|
||||
manufacturer VARCHAR(128) NULL,
|
||||
model VARCHAR(128) NULL,
|
||||
source ENUM('auto', 'manual', 'barcode') NOT NULL DEFAULT 'auto',
|
||||
registered_at_ms BIGINT NOT NULL,
|
||||
last_seen_at_ms BIGINT NOT NULL,
|
||||
meta_json LONGTEXT NULL,
|
||||
UNIQUE KEY uq_devices_company_external (company_id, external_id),
|
||||
KEY idx_devices_company (company_id),
|
||||
CONSTRAINT fk_devices_company FOREIGN KEY (company_id) REFERENCES companies (id) ON DELETE CASCADE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS reports (
|
||||
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
|
||||
company_id INT UNSIGNED NOT NULL DEFAULT 1,
|
||||
device_id BIGINT UNSIGNED NULL,
|
||||
report_id VARCHAR(128) NOT NULL,
|
||||
fingerprint VARCHAR(64) NOT NULL,
|
||||
crash_type VARCHAR(32) NOT NULL,
|
||||
generated_at_ms BIGINT NOT NULL,
|
||||
received_at_ms BIGINT NOT NULL,
|
||||
device_model VARCHAR(128) NULL,
|
||||
app_version VARCHAR(64) NULL,
|
||||
payload_json LONGTEXT NOT NULL,
|
||||
tags_json LONGTEXT NOT NULL,
|
||||
UNIQUE KEY uq_reports_report_id (report_id),
|
||||
KEY idx_reports_company (company_id),
|
||||
KEY idx_reports_device (device_id),
|
||||
KEY idx_reports_generated (generated_at_ms),
|
||||
KEY idx_reports_received (received_at_ms),
|
||||
KEY idx_reports_fingerprint (fingerprint),
|
||||
KEY idx_reports_fp_received (fingerprint, received_at_ms),
|
||||
CONSTRAINT fk_reports_company FOREIGN KEY (company_id) REFERENCES companies (id),
|
||||
CONSTRAINT fk_reports_device FOREIGN KEY (device_id) REFERENCES devices (id) ON DELETE SET NULL
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS report_views (
|
||||
user_id INT UNSIGNED NOT NULL,
|
||||
report_id BIGINT UNSIGNED NOT NULL,
|
||||
viewed_at_ms BIGINT NOT NULL,
|
||||
PRIMARY KEY (user_id, report_id),
|
||||
CONSTRAINT fk_report_views_report
|
||||
FOREIGN KEY (report_id) REFERENCES reports (id) ON DELETE CASCADE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS tickets (
|
||||
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
|
||||
company_id INT UNSIGNED NOT NULL DEFAULT 1,
|
||||
device_id BIGINT UNSIGNED NULL,
|
||||
ticket_id VARCHAR(128) NOT NULL,
|
||||
title VARCHAR(256) NOT NULL,
|
||||
brief TEXT NULL,
|
||||
body LONGTEXT NOT NULL,
|
||||
opened_at_ms BIGINT NOT NULL,
|
||||
created_at_ms BIGINT NOT NULL,
|
||||
updated_at_ms BIGINT NOT NULL,
|
||||
owner_username VARCHAR(64) NOT NULL DEFAULT '',
|
||||
verified_by_username VARCHAR(64) NULL,
|
||||
device_model VARCHAR(128) NULL,
|
||||
app_package VARCHAR(128) NULL,
|
||||
app_version VARCHAR(64) NULL,
|
||||
os_name VARCHAR(64) NULL,
|
||||
os_version VARCHAR(128) NULL,
|
||||
rating TINYINT UNSIGNED NOT NULL DEFAULT 0,
|
||||
tags_json LONGTEXT NOT NULL,
|
||||
payload_json LONGTEXT NOT NULL,
|
||||
UNIQUE KEY uq_tickets_ticket_id (ticket_id),
|
||||
KEY idx_tickets_company (company_id),
|
||||
KEY idx_tickets_opened (opened_at_ms),
|
||||
KEY idx_tickets_updated (updated_at_ms),
|
||||
KEY idx_tickets_owner (owner_username),
|
||||
CONSTRAINT fk_tickets_company FOREIGN KEY (company_id) REFERENCES companies (id),
|
||||
CONSTRAINT fk_tickets_device FOREIGN KEY (device_id) REFERENCES devices (id) ON DELETE SET NULL
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
|
||||
INSERT IGNORE INTO companies (id, slug, name) VALUES (1, 'default', 'Default');
|
||||
|
||||
INSERT IGNORE INTO users (id, username, password_hash, role)
|
||||
VALUES (1, 'admin', '$2y$10$t2JyYCyjBwRxAHfmLwsnG.APoj/in0i6nxpKcQggdl.AkhFJR13o.', 'root');
|
||||
-- default password: admin — change in production
|
||||
|
||||
INSERT IGNORE INTO company_memberships (user_id, company_id, role)
|
||||
VALUES (1, 1, 'owner');
|
||||
113
sim/cluster0/lab-seeds/backend/sql/schema.sqlite.sql
Normal file
113
sim/cluster0/lab-seeds/backend/sql/schema.sqlite.sql
Normal file
@@ -0,0 +1,113 @@
|
||||
PRAGMA foreign_keys = ON;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS companies (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
slug TEXT NOT NULL UNIQUE,
|
||||
name TEXT NOT NULL,
|
||||
created_at TEXT NOT NULL DEFAULT (datetime('now'))
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS users (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
username TEXT NOT NULL UNIQUE,
|
||||
password_hash TEXT NOT NULL,
|
||||
role TEXT NOT NULL DEFAULT 'viewer',
|
||||
created_at TEXT NOT NULL DEFAULT (datetime('now'))
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS company_memberships (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
user_id INTEGER NOT NULL,
|
||||
company_id INTEGER NOT NULL,
|
||||
role TEXT NOT NULL DEFAULT 'member',
|
||||
permissions_json TEXT,
|
||||
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
||||
UNIQUE (user_id, company_id),
|
||||
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,
|
||||
FOREIGN KEY (company_id) REFERENCES companies(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS devices (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
company_id INTEGER NOT NULL,
|
||||
external_id TEXT NOT NULL,
|
||||
display_name TEXT,
|
||||
manufacturer TEXT,
|
||||
model TEXT,
|
||||
source TEXT NOT NULL DEFAULT 'auto',
|
||||
registered_at_ms INTEGER NOT NULL,
|
||||
last_seen_at_ms INTEGER NOT NULL,
|
||||
meta_json TEXT,
|
||||
UNIQUE (company_id, external_id),
|
||||
FOREIGN KEY (company_id) REFERENCES companies(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS reports (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
company_id INTEGER NOT NULL DEFAULT 1,
|
||||
device_id INTEGER,
|
||||
report_id TEXT NOT NULL UNIQUE,
|
||||
fingerprint TEXT NOT NULL,
|
||||
crash_type TEXT NOT NULL,
|
||||
generated_at_ms INTEGER NOT NULL,
|
||||
received_at_ms INTEGER NOT NULL,
|
||||
device_model TEXT,
|
||||
app_version TEXT,
|
||||
payload_json TEXT NOT NULL,
|
||||
tags_json TEXT NOT NULL DEFAULT '[]',
|
||||
FOREIGN KEY (company_id) REFERENCES companies(id),
|
||||
FOREIGN KEY (device_id) REFERENCES devices(id) ON DELETE SET NULL
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS report_views (
|
||||
user_id INTEGER NOT NULL,
|
||||
report_id INTEGER NOT NULL,
|
||||
viewed_at_ms INTEGER NOT NULL,
|
||||
PRIMARY KEY (user_id, report_id),
|
||||
FOREIGN KEY (report_id) REFERENCES reports(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS tickets (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
company_id INTEGER NOT NULL DEFAULT 1,
|
||||
device_id INTEGER NULL,
|
||||
ticket_id TEXT NOT NULL UNIQUE,
|
||||
title TEXT NOT NULL,
|
||||
brief TEXT,
|
||||
body TEXT NOT NULL,
|
||||
opened_at_ms INTEGER NOT NULL,
|
||||
created_at_ms INTEGER NOT NULL,
|
||||
updated_at_ms INTEGER NOT NULL,
|
||||
owner_username TEXT NOT NULL DEFAULT '',
|
||||
verified_by_username TEXT,
|
||||
device_model TEXT,
|
||||
app_package TEXT,
|
||||
app_version TEXT,
|
||||
os_name TEXT,
|
||||
os_version TEXT,
|
||||
rating INTEGER NOT NULL DEFAULT 0,
|
||||
tags_json TEXT NOT NULL DEFAULT '[]',
|
||||
payload_json TEXT NOT NULL,
|
||||
FOREIGN KEY (company_id) REFERENCES companies(id),
|
||||
FOREIGN KEY (device_id) REFERENCES devices(id) ON DELETE SET NULL
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_tickets_company ON tickets(company_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_tickets_opened ON tickets(opened_at_ms DESC);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_reports_company ON reports(company_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_reports_generated ON reports(generated_at_ms DESC);
|
||||
CREATE INDEX IF NOT EXISTS idx_reports_received ON reports(received_at_ms DESC);
|
||||
CREATE INDEX IF NOT EXISTS idx_reports_fingerprint ON reports(fingerprint);
|
||||
CREATE INDEX IF NOT EXISTS idx_reports_company_fingerprint ON reports(company_id, fingerprint);
|
||||
CREATE INDEX IF NOT EXISTS idx_reports_company_received ON reports(company_id, received_at_ms);
|
||||
CREATE INDEX IF NOT EXISTS idx_reports_fp_received ON reports(fingerprint, received_at_ms);
|
||||
|
||||
INSERT OR IGNORE INTO companies (id, slug, name) VALUES (1, 'default', 'Default');
|
||||
|
||||
INSERT OR IGNORE INTO users (id, username, password_hash, role)
|
||||
VALUES (1, 'admin', '$2y$10$t2JyYCyjBwRxAHfmLwsnG.APoj/in0i6nxpKcQggdl.AkhFJR13o.', 'root');
|
||||
-- password: admin
|
||||
|
||||
INSERT OR IGNORE INTO company_memberships (user_id, company_id, role)
|
||||
VALUES (1, 1, 'owner');
|
||||
Reference in New Issue
Block a user