chnages
chnages
This commit is contained in:
1 parent
3e22355510
commit
e22a8dc248
71 files changed
+24775
-1
No files matched your search
@@ -0,0 +1,174 @@
|
||||
-- ============================================================
|
||||
-- schema.sql — Database tables for the AI-Solutions website
|
||||
-- ============================================================
|
||||
-- Database: ai_solutions (or reuse the existing auth_db).
|
||||
-- Apply with:
|
||||
-- psql -U postgres -d auth_db -f database/schema.sql
|
||||
-- Then seed sample data + admin with:
|
||||
-- node backend/seed.js
|
||||
-- ============================================================
|
||||
|
||||
-- ============================================================
|
||||
-- AUTH / ADMIN TABLES
|
||||
-- ============================================================
|
||||
-- There are NO public user accounts. The only accounts are
|
||||
-- administrators, created by the seed script (not self-service
|
||||
-- registration). RBAC is therefore unnecessary — every row is
|
||||
-- an admin — but the role column is kept for defense-in-depth.
|
||||
-- ============================================================
|
||||
|
||||
-- Admin accounts
|
||||
CREATE TABLE IF NOT EXISTS users (
|
||||
id SERIAL PRIMARY KEY,
|
||||
username VARCHAR(30) UNIQUE NOT NULL,
|
||||
email VARCHAR(255) UNIQUE NOT NULL,
|
||||
password_hash TEXT NOT NULL,
|
||||
role VARCHAR(10) DEFAULT 'admin' CHECK (role IN ('admin')),
|
||||
failed_attempts INTEGER DEFAULT 0,
|
||||
locked_until TIMESTAMP,
|
||||
email_verified BOOLEAN DEFAULT true, -- admins are pre-verified by the seed script
|
||||
password_changed_at TIMESTAMP DEFAULT NOW(), -- tracks when password was last changed (expires after 90 days)
|
||||
otp_code TEXT, -- stores bcrypt hash of the login OTP
|
||||
otp_expires_at TIMESTAMP,
|
||||
otp_attempts INTEGER DEFAULT 0, -- tracks wrong OTP guesses (max 3)
|
||||
created_at TIMESTAMP DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- Password history — prevents reuse of the last 5 passwords
|
||||
CREATE TABLE IF NOT EXISTS password_history (
|
||||
id SERIAL PRIMARY KEY,
|
||||
user_id INTEGER REFERENCES users(id) ON DELETE CASCADE,
|
||||
password_hash TEXT NOT NULL,
|
||||
created_at TIMESTAMP DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- Sessions — tracks every logged-in admin device
|
||||
CREATE TABLE IF NOT EXISTS sessions (
|
||||
id SERIAL PRIMARY KEY,
|
||||
user_id INTEGER REFERENCES users(id) ON DELETE CASCADE,
|
||||
jti VARCHAR(64) NOT NULL, -- JWT ID for access token
|
||||
refresh_jti VARCHAR(64), -- JWT ID for refresh token
|
||||
ip_address VARCHAR(45),
|
||||
device_info TEXT,
|
||||
created_at TIMESTAMP DEFAULT NOW(),
|
||||
last_used_at TIMESTAMP DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- Activity logs — full audit trail
|
||||
CREATE TABLE IF NOT EXISTS logs (
|
||||
id SERIAL PRIMARY KEY,
|
||||
user_id INTEGER REFERENCES users(id) ON DELETE SET NULL,
|
||||
action VARCHAR(50) NOT NULL,
|
||||
status VARCHAR(10) NOT NULL CHECK (status IN ('success', 'failed', 'warning')),
|
||||
details TEXT,
|
||||
ip_address VARCHAR(45),
|
||||
created_at TIMESTAMP DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- ============================================================
|
||||
-- PUBLIC WEBSITE CONTENT TABLES
|
||||
-- ============================================================
|
||||
|
||||
-- Customer inquiries — submitted via the public "Contact Us" form.
|
||||
-- Captures the 7 CSE fields. No account/password is required to submit.
|
||||
CREATE TABLE IF NOT EXISTS inquiries (
|
||||
id SERIAL PRIMARY KEY,
|
||||
full_name VARCHAR(120) NOT NULL,
|
||||
email VARCHAR(255) NOT NULL,
|
||||
phone VARCHAR(40),
|
||||
company VARCHAR(160),
|
||||
country VARCHAR(80),
|
||||
job_title VARCHAR(120),
|
||||
job_details TEXT NOT NULL,
|
||||
is_read BOOLEAN DEFAULT false,
|
||||
created_at TIMESTAMP DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- Software solutions / services offered to customers
|
||||
CREATE TABLE IF NOT EXISTS solutions (
|
||||
id SERIAL PRIMARY KEY,
|
||||
title VARCHAR(160) NOT NULL,
|
||||
summary VARCHAR(300) NOT NULL,
|
||||
description TEXT,
|
||||
icon VARCHAR(40) DEFAULT 'robot', -- icon key used by the frontend
|
||||
sort_order INTEGER DEFAULT 0,
|
||||
is_active BOOLEAN DEFAULT true,
|
||||
created_at TIMESTAMP DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- Case studies — "highlights of past industry solutions"
|
||||
CREATE TABLE IF NOT EXISTS case_studies (
|
||||
id SERIAL PRIMARY KEY,
|
||||
title VARCHAR(180) NOT NULL,
|
||||
client VARCHAR(160),
|
||||
industry VARCHAR(120),
|
||||
summary VARCHAR(400) NOT NULL,
|
||||
outcome TEXT,
|
||||
image_url TEXT,
|
||||
created_at TIMESTAMP DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- Customer feedback with star ratings
|
||||
CREATE TABLE IF NOT EXISTS feedback (
|
||||
id SERIAL PRIMARY KEY,
|
||||
customer_name VARCHAR(120) NOT NULL,
|
||||
company VARCHAR(160),
|
||||
rating INTEGER NOT NULL CHECK (rating BETWEEN 1 AND 5),
|
||||
comment TEXT NOT NULL,
|
||||
is_published BOOLEAN DEFAULT true,
|
||||
created_at TIMESTAMP DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- Promotional articles
|
||||
CREATE TABLE IF NOT EXISTS articles (
|
||||
id SERIAL PRIMARY KEY,
|
||||
slug VARCHAR(180) UNIQUE NOT NULL,
|
||||
title VARCHAR(200) NOT NULL,
|
||||
excerpt VARCHAR(400),
|
||||
body TEXT NOT NULL,
|
||||
cover_image TEXT,
|
||||
author VARCHAR(120) DEFAULT 'AI-Solutions Team',
|
||||
is_published BOOLEAN DEFAULT true,
|
||||
published_at TIMESTAMP DEFAULT NOW(),
|
||||
created_at TIMESTAMP DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- Promotional + upcoming events
|
||||
CREATE TABLE IF NOT EXISTS events (
|
||||
id SERIAL PRIMARY KEY,
|
||||
title VARCHAR(200) NOT NULL,
|
||||
description TEXT,
|
||||
location VARCHAR(180),
|
||||
event_date DATE,
|
||||
is_upcoming BOOLEAN DEFAULT false,
|
||||
cover_image TEXT,
|
||||
created_at TIMESTAMP DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- Photo gallery — images optionally tied to an event
|
||||
CREATE TABLE IF NOT EXISTS gallery_images (
|
||||
id SERIAL PRIMARY KEY,
|
||||
event_id INTEGER REFERENCES events(id) ON DELETE SET NULL,
|
||||
image_url TEXT NOT NULL,
|
||||
caption VARCHAR(200),
|
||||
created_at TIMESTAMP DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- ============================================================
|
||||
-- INDEXES
|
||||
-- ============================================================
|
||||
CREATE INDEX IF NOT EXISTS idx_sessions_user_id ON sessions(user_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_sessions_jti ON sessions(jti);
|
||||
CREATE INDEX IF NOT EXISTS idx_sessions_refresh_jti ON sessions(refresh_jti);
|
||||
CREATE INDEX IF NOT EXISTS idx_logs_created_at ON logs(created_at DESC);
|
||||
CREATE INDEX IF NOT EXISTS idx_logs_user_id ON logs(user_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_logs_action ON logs(action);
|
||||
CREATE INDEX IF NOT EXISTS idx_password_history_user ON password_history(user_id);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_inquiries_created_at ON inquiries(created_at DESC);
|
||||
CREATE INDEX IF NOT EXISTS idx_solutions_active ON solutions(is_active, sort_order);
|
||||
CREATE INDEX IF NOT EXISTS idx_feedback_published ON feedback(is_published);
|
||||
CREATE INDEX IF NOT EXISTS idx_articles_slug ON articles(slug);
|
||||
CREATE INDEX IF NOT EXISTS idx_articles_published ON articles(is_published, published_at DESC);
|
||||
CREATE INDEX IF NOT EXISTS idx_events_date ON events(event_date DESC);
|
||||
CREATE INDEX IF NOT EXISTS idx_gallery_event ON gallery_images(event_id);
|
||||
Reference in new issue
Block a user