PostgreSQL 16 Prototype based on ER diagram - bounswe/bounswe2025group2 GitHub Wiki

Based on PostgreSQL 16

-- Database schema generated from ER model
-- PostgreSQL 16
-- Save as db_schema_pg.sql and run:
--   psql -U <user> -d <db_name> -f db_schema_pg.sql

-- Enable uuid generation helper
CREATE EXTENSION IF NOT EXISTS pgcrypto;

-- ********************
-- custom types
-- ********************
CREATE TYPE user_role AS ENUM ('regular','mentor','coach','admin');

-- ********************
-- core user tables
-- ********************
CREATE TABLE app_user (
    user_id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    username VARCHAR(100) NOT NULL UNIQUE,
    email VARCHAR(255)  NOT NULL UNIQUE,
    password_hash VARCHAR(255) NOT NULL,
    role user_role DEFAULT 'regular',
    created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP
);

CREATE TABLE profile (
    user_id UUID PRIMARY KEY,
    bio TEXT,
    profile_picture VARCHAR(255),
    CONSTRAINT fk_profile_user
        FOREIGN KEY (user_id) REFERENCES app_user(user_id)
        ON DELETE CASCADE ON UPDATE CASCADE
);

CREATE TABLE badge (
    badge_id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    user_id UUID NOT NULL,
    name VARCHAR(100) NOT NULL,
    description TEXT,
    icon VARCHAR(255),
    source VARCHAR(255),
    CONSTRAINT fk_badge_user
        FOREIGN KEY (user_id) REFERENCES app_user(user_id)
        ON DELETE CASCADE
);

CREATE INDEX idx_badge_user ON badge(user_id);

CREATE TABLE goal (
    goal_id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    user_id UUID NOT NULL,
    type VARCHAR(100),
    metric VARCHAR(100),
    target_value DOUBLE PRECISION,
    current_value DOUBLE PRECISION,
    status VARCHAR(50),
    CONSTRAINT fk_goal_user
        FOREIGN KEY (user_id) REFERENCES app_user(user_id)
        ON DELETE CASCADE
);
CREATE INDEX idx_goal_user ON goal(user_id);

-- ********************
-- challenges & leaderboard
-- ********************
CREATE TABLE challenge (
    challenge_id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    name VARCHAR(255) NOT NULL,
    type VARCHAR(100),
    start_date DATE,
    end_date DATE,
    status VARCHAR(50)
);

CREATE TABLE user_challenge (
    user_id UUID,
    challenge_id UUID,
    joined_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
    progress DOUBLE PRECISION,
    PRIMARY KEY (user_id, challenge_id),
    CONSTRAINT fk_uc_user
        FOREIGN KEY (user_id) REFERENCES app_user(user_id)
        ON DELETE CASCADE,
    CONSTRAINT fk_uc_challenge
        FOREIGN KEY (challenge_id) REFERENCES challenge(challenge_id)
        ON DELETE CASCADE
);

CREATE TABLE leaderboard (
    challenge_id UUID PRIMARY KEY,
    last_updated TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
    CONSTRAINT fk_lb_challenge
        FOREIGN KEY (challenge_id) REFERENCES challenge(challenge_id)
        ON DELETE CASCADE
);

CREATE TABLE leaderboard_entry (
    challenge_id UUID,
    user_id UUID,
    rank INT,
    points DOUBLE PRECISION,
    PRIMARY KEY (challenge_id, user_id),
    CONSTRAINT fk_lbe_challenge
        FOREIGN KEY (challenge_id) REFERENCES challenge(challenge_id)
        ON DELETE CASCADE,
    CONSTRAINT fk_lbe_user
        FOREIGN KEY (user_id) REFERENCES app_user(user_id)
        ON DELETE CASCADE
);

-- ********************
-- messaging
-- ********************
CREATE TABLE chat (
    chat_id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP
);

CREATE TABLE chat_participant (
    chat_id UUID,
    user_id UUID,
    added_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
    PRIMARY KEY (chat_id, user_id),
    CONSTRAINT fk_cp_chat
        FOREIGN KEY (chat_id) REFERENCES chat(chat_id)
        ON DELETE CASCADE,
    CONSTRAINT fk_cp_user
        FOREIGN KEY (user_id) REFERENCES app_user(user_id)
        ON DELETE CASCADE
);

CREATE TABLE message (
    message_id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    chat_id UUID NOT NULL,
    sender_id UUID NOT NULL,
    content TEXT NOT NULL,
    sent_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
    CONSTRAINT fk_msg_chat
        FOREIGN KEY (chat_id) REFERENCES chat(chat_id)
        ON DELETE CASCADE,
    CONSTRAINT fk_msg_sender
        FOREIGN KEY (sender_id) REFERENCES app_user(user_id)
        ON DELETE CASCADE
);

CREATE INDEX idx_msg_chat ON message(chat_id);
CREATE INDEX idx_msg_sender ON message(sender_id);

-- ********************
-- forum
-- ********************
CREATE TABLE forum (
    forum_id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    name VARCHAR(255) NOT NULL
);

CREATE TABLE thread (
    thread_id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    forum_id UUID NOT NULL,
    creator_id UUID NOT NULL,
    title VARCHAR(255) NOT NULL,
    created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
    CONSTRAINT fk_thread_forum
        FOREIGN KEY (forum_id) REFERENCES forum(forum_id)
        ON DELETE CASCADE,
    CONSTRAINT fk_thread_creator
        FOREIGN KEY (creator_id) REFERENCES app_user(user_id)
        ON DELETE CASCADE
);
CREATE INDEX idx_thread_forum ON thread(forum_id);

CREATE TABLE post (
    post_id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    thread_id UUID NOT NULL,
    author_id UUID NOT NULL,
    parent_post_id UUID,
    content TEXT NOT NULL,
    created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
    upvotes INT DEFAULT 0,
    downvotes INT DEFAULT 0,
    CONSTRAINT fk_post_thread
        FOREIGN KEY (thread_id) REFERENCES thread(thread_id)
        ON DELETE CASCADE,
    CONSTRAINT fk_post_author
        FOREIGN KEY (author_id) REFERENCES app_user(user_id)
        ON DELETE CASCADE,
    CONSTRAINT fk_post_parent
        FOREIGN KEY (parent_post_id) REFERENCES post(post_id)
        ON DELETE CASCADE
);
CREATE INDEX idx_post_thread ON post(thread_id);

-- ********************
-- mentoring & coaching
-- ********************
CREATE TABLE mentor (
    user_id UUID PRIMARY KEY,
    CONSTRAINT fk_mentor_user
        FOREIGN KEY (user_id) REFERENCES app_user(user_id)
        ON DELETE CASCADE
);

CREATE TABLE coach (
    user_id UUID PRIMARY KEY,
    verification_data TEXT,
    CONSTRAINT fk_coach_user
        FOREIGN KEY (user_id) REFERENCES app_user(user_id)
        ON DELETE CASCADE
);

CREATE TABLE mentor_mentee (
    mentor_id UUID,
    mentee_id UUID,
    started_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
    ended_at TIMESTAMPTZ,
    PRIMARY KEY (mentor_id, mentee_id),
    CONSTRAINT fk_mm_mentor
        FOREIGN KEY (mentor_id) REFERENCES mentor(user_id)
        ON DELETE CASCADE,
    CONSTRAINT fk_mm_mentee
        FOREIGN KEY (mentee_id) REFERENCES app_user(user_id)
        ON DELETE CASCADE
);

CREATE TABLE coach_mentee (
    coach_id UUID,
    mentee_id UUID,
    started_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
    PRIMARY KEY (coach_id, mentee_id),
    CONSTRAINT fk_cm_coach
        FOREIGN KEY (coach_id) REFERENCES coach(user_id)
        ON DELETE CASCADE,
    CONSTRAINT fk_cm_mentee
        FOREIGN KEY (mentee_id) REFERENCES app_user(user_id)
        ON DELETE CASCADE
);

CREATE TABLE feedback (
    feedback_id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    sender_id UUID NOT NULL,
    recipient_id UUID NOT NULL,
    content TEXT NOT NULL,
    is_read BOOLEAN DEFAULT FALSE,
    sent_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
    CONSTRAINT fk_fb_sender
        FOREIGN KEY (sender_id) REFERENCES app_user(user_id)
        ON DELETE CASCADE,
    CONSTRAINT fk_fb_recipient
        FOREIGN KEY (recipient_id) REFERENCES app_user(user_id)
        ON DELETE CASCADE
);
CREATE INDEX idx_fb_recipient ON feedback(recipient_id);

-- ********************
-- notifications & mail
-- ********************
CREATE TABLE notification (
    notification_id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    recipient_id UUID NOT NULL,
    message TEXT NOT NULL,
    is_read BOOLEAN DEFAULT FALSE,
    created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
    CONSTRAINT fk_notif_recipient
        FOREIGN KEY (recipient_id) REFERENCES app_user(user_id)
        ON DELETE CASCADE
);
CREATE INDEX idx_notif_recipient ON notification(recipient_id);

CREATE TABLE mail (
    mail_id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    recipient_id UUID NOT NULL,
    subject VARCHAR(255) NOT NULL,
    content TEXT NOT NULL,
    sent_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
    is_sent BOOLEAN DEFAULT FALSE,
    CONSTRAINT fk_mail_recipient
        FOREIGN KEY (recipient_id) REFERENCES app_user(user_id)
        ON DELETE CASCADE
);
CREATE INDEX idx_mail_recipient ON mail(recipient_id);
⚠️ **GitHub.com Fallback** ⚠️