full backend rewrite.
calling this v0.4.0
This commit is contained in:
@@ -1,49 +0,0 @@
|
||||
-- Add migration script here
|
||||
CREATE TABLE users (
|
||||
id SERIAL PRIMARY KEY,
|
||||
username VARCHAR(50) UNIQUE NOT NULL,
|
||||
password VARCHAR(50) NOT NULL,
|
||||
display_name VARCHAR(50),
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
CREATE TABLE channels (
|
||||
id SERIAL PRIMARY KEY,
|
||||
name VARCHAR(50) NOT NULL,
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
CREATE TABLE messages (
|
||||
id SERIAL PRIMARY KEY,
|
||||
channel_id INTEGER NOT NULL REFERENCES channels(id) ON DELETE CASCADE,
|
||||
user_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
||||
content TEXT NOT NULL,
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
|
||||
is_edited BOOLEAN DEFAULT FALSE
|
||||
);
|
||||
|
||||
create table attachments (
|
||||
id SERIAL PRIMARY KEY,
|
||||
message_id INTEGER NOT NULL REFERENCES messages(id) ON DELETE CASCADE,
|
||||
path TEXT NOT NULL
|
||||
);
|
||||
|
||||
CREATE INDEX idx_messages_channel_id ON messages (channel_id, id DESC);
|
||||
CREATE INDEX idx_new_messages ON messages(created_at DESC);
|
||||
|
||||
-- Create a function to update the updated_at timestamp
|
||||
CREATE OR REPLACE FUNCTION update_updated_at_column()
|
||||
RETURNS TRIGGER AS $$
|
||||
BEGIN
|
||||
NEW.updated_at = CURRENT_TIMESTAMP;
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$ language 'plpgsql';
|
||||
|
||||
-- Create trigger for messages table
|
||||
CREATE TRIGGER update_messages_updated_at
|
||||
BEFORE UPDATE ON messages
|
||||
FOR EACH ROW
|
||||
EXECUTE FUNCTION update_updated_at_column();
|
||||
@@ -1,20 +0,0 @@
|
||||
-- Add migration script here
|
||||
CREATE TABLE sessions (
|
||||
id SERIAL PRIMARY KEY,
|
||||
user_id INTEGER NOT NULL REFERENCES users(id),
|
||||
token TEXT NOT NULL UNIQUE,
|
||||
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
expires_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP + INTERVAL '7 days'
|
||||
);
|
||||
|
||||
CREATE OR REPLACE FUNCTION cleanup_expired_sessions()
|
||||
RETURNS TRIGGER AS $$
|
||||
BEGIN
|
||||
DELETE FROM sessions WHERE expires_at < NOW();
|
||||
RETURN NULL;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
|
||||
CREATE TRIGGER trigger_cleanup_sessions
|
||||
AFTER INSERT ON sessions
|
||||
EXECUTE FUNCTION cleanup_expired_sessions();
|
||||
@@ -1,5 +0,0 @@
|
||||
-- Add migration script here
|
||||
ALTER TABLE users ADD COLUMN email VARCHAR(100);
|
||||
ALTER TABLE users ADD COLUMN twofa_enabled BOOLEAN DEFAULT FALSE;
|
||||
ALTER TABLE users ADD COLUMN totp_secret VARCHAR(64);
|
||||
ALTER TABLE users ADD COLUMN updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP;
|
||||
@@ -1,2 +0,0 @@
|
||||
-- Add migration script here
|
||||
ALTER TABLE users ALTER COLUMN twofa_enabled SET NOT NULL;
|
||||
@@ -1,18 +0,0 @@
|
||||
-- Add migration script here
|
||||
CREATE TABLE access_codes (
|
||||
-- identifiers
|
||||
id SERIAL PRIMARY KEY,
|
||||
creator_id INTEGER NOT NULL REFERENCES users(id),
|
||||
|
||||
-- code data
|
||||
code VARCHAR(255) NOT NULL,
|
||||
name VARCHAR(255) NOT NULL,
|
||||
|
||||
-- uses
|
||||
uses INTEGER NOT NULL DEFAULT 0,
|
||||
max_uses INTEGER NOT NULL DEFAULT 1,
|
||||
|
||||
-- time data
|
||||
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
expires_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP + INTERVAL '1 day'
|
||||
);
|
||||
@@ -1,10 +0,0 @@
|
||||
-- Add migration script here
|
||||
ALTER TABLE access_codes
|
||||
ALTER COLUMN created_at
|
||||
TYPE TIMESTAMP WITH TIME ZONE
|
||||
USING created_at AT TIME ZONE 'UTC';
|
||||
|
||||
ALTER TABLE access_codes
|
||||
ALTER COLUMN expires_at
|
||||
TYPE TIMESTAMP WITH TIME ZONE
|
||||
USING expires_at AT TIME ZONE 'UTC';
|
||||
@@ -1,6 +0,0 @@
|
||||
-- Add migration script here
|
||||
TRUNCATE TABLE users CASCADE;
|
||||
|
||||
ALTER TABLE users DROP COLUMN password;
|
||||
ALTER TABLE users ADD COLUMN pass_hash VARCHAR(255) NOT NULL;
|
||||
ALTER TABLE users ADD CONSTRAINT users_username_unique UNIQUE (username);
|
||||
@@ -1,13 +0,0 @@
|
||||
-- Add migration script here
|
||||
CREATE TYPE status AS ENUM ('pending', 'accepted', 'blocked');
|
||||
|
||||
CREATE TABLE relationships (
|
||||
id SERIAL PRIMARY KEY,
|
||||
from_user INTEGER NOT NULL REFERENCES users(id),
|
||||
to_user INTEGER NOT NULL REFERENCES users(id),
|
||||
status status NOT NULL DEFAULT 'pending',
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
CONSTRAINT no_self_relationship CHECK (from_user != to_user),
|
||||
CONSTRAINT unique_relationship UNIQUE (from_user, to_user)
|
||||
);
|
||||
@@ -0,0 +1,183 @@
|
||||
-- Add migration script here
|
||||
-- Add migration script here
|
||||
CREATE TYPE user_role AS ENUM ('user', 'admin');
|
||||
CREATE TYPE totp_status AS ENUM ('disabled', 'pending', 'enabled');
|
||||
|
||||
CREATE TABLE users (
|
||||
id BIGSERIAL PRIMARY KEY NOT NULL,
|
||||
role user_role NOT NULL DEFAULT 'user',
|
||||
|
||||
-- profile
|
||||
nickname VARCHAR(255),
|
||||
|
||||
-- basic auth
|
||||
username VARCHAR(255) UNIQUE NOT NULL,
|
||||
passhash VARCHAR(255) NOT NULL,
|
||||
|
||||
-- email
|
||||
email VARCHAR(255),
|
||||
email_verified BOOLEAN DEFAULT FALSE,
|
||||
|
||||
-- 2fa
|
||||
totp_secret VARCHAR(255),
|
||||
totp_status totp_status NOT NULL DEFAULT 'disabled',
|
||||
|
||||
-- update tracking
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
deleted_at TIMESTAMPTZ
|
||||
);
|
||||
|
||||
CREATE TABLE access_tokens (
|
||||
id BIGSERIAL PRIMARY KEY NOT NULL,
|
||||
creator_id BIGINT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
||||
|
||||
code VARCHAR(255) NOT NULL,
|
||||
name VARCHAR(255) NOT NULL,
|
||||
|
||||
uses INTEGER NOT NULL DEFAULT 0,
|
||||
max_uses INTEGER NOT NULL DEFAULT 1,
|
||||
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
expires_at TIMESTAMPTZ NOT NULL DEFAULT NOW() + INTERVAL '24 hours',
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE TABLE refresh_tokens (
|
||||
id BIGSERIAL PRIMARY KEY NOT NULL,
|
||||
user_id BIGINT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
||||
token_hash VARCHAR(255) NOT NULL,
|
||||
|
||||
revoked BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
expires_at TIMESTAMPTZ NOT NULL DEFAULT NOW() + INTERVAL '7 days'
|
||||
);
|
||||
|
||||
|
||||
CREATE TABLE spaces (
|
||||
id BIGSERIAL PRIMARY KEY NOT NULL,
|
||||
name VARCHAR(255) NOT NULL,
|
||||
description TEXT,
|
||||
|
||||
owner_id BIGINT NOT NULL REFERENCES users(id),
|
||||
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE TABLE channels (
|
||||
id BIGSERIAL PRIMARY KEY NOT NULL,
|
||||
name VARCHAR(255) NOT NULL,
|
||||
description TEXT,
|
||||
|
||||
space_id BIGINT NOT NULL REFERENCES spaces(id) ON DELETE CASCADE,
|
||||
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
|
||||
CREATE TABLE space_members (
|
||||
space_id BIGINT NOT NULL REFERENCES spaces(id) ON DELETE CASCADE,
|
||||
user_id BIGINT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
||||
|
||||
joined_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
role user_role DEFAULT 'user',
|
||||
|
||||
PRIMARY KEY (space_id, user_id)
|
||||
);
|
||||
|
||||
CREATE TABLE messages (
|
||||
id BIGSERIAL PRIMARY KEY NOT NULL,
|
||||
channel_id BIGINT NOT NULL REFERENCES channels(id) ON DELETE CASCADE,
|
||||
user_id BIGINT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
||||
content TEXT NOT NULL,
|
||||
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
is_edited BOOLEAN DEFAULT FALSE
|
||||
);
|
||||
|
||||
CREATE TABLE attachments (
|
||||
id BIGSERIAL PRIMARY KEY NOT NULL,
|
||||
message_id BIGINT NOT NULL REFERENCES messages(id) ON DELETE CASCADE,
|
||||
filename VARCHAR(255) NOT NULL,
|
||||
content_type VARCHAR(100) NOT NULL, -- mime type e.g. image/png, video/mp4
|
||||
size_bytes BIGINT NOT NULL,
|
||||
url TEXT NOT NULL, -- path to file on your CDN/storage
|
||||
width INTEGER, -- null for non-image/video
|
||||
height INTEGER, -- null for non-image/video
|
||||
duration_ms INTEGER, -- null for non-audio/video
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE TYPE relationship_status AS ENUM ('pending', 'accepted', 'blocked');
|
||||
|
||||
CREATE TABLE relationships (
|
||||
id BIGSERIAL PRIMARY KEY NOT NULL,
|
||||
|
||||
from_user BIGINT NOT NULL REFERENCES users(id),
|
||||
to_user BIGINT NOT NULL REFERENCES users(id),
|
||||
status relationship_status NOT NULL DEFAULT 'pending',
|
||||
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
CONSTRAINT no_self_relationship CHECK (from_user != to_user),
|
||||
CONSTRAINT unique_relationship UNIQUE (from_user, to_user)
|
||||
);
|
||||
|
||||
CREATE INDEX idx_messages_channel_id ON messages(channel_id, created_at DESC);
|
||||
CREATE INDEX idx_messages_user_id ON messages(user_id);
|
||||
CREATE INDEX idx_attachments_message ON attachments(message_id);
|
||||
CREATE INDEX idx_channels_space_id ON channels(space_id);
|
||||
CREATE INDEX idx_space_members_user ON space_members(user_id);
|
||||
CREATE INDEX idx_refresh_tokens_hash ON refresh_tokens(token_hash);
|
||||
CREATE INDEX idx_relationships_from ON relationships(from_user, to_user);
|
||||
CREATE INDEX idx_relationships_to ON relationships(to_user);
|
||||
CREATE INDEX idx_access_tokens_code ON access_tokens(code);
|
||||
|
||||
CREATE OR REPLACE FUNCTION update_updated_at()
|
||||
RETURNS TRIGGER AS $$
|
||||
BEGIN
|
||||
NEW.updated_at = NOW();
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
|
||||
CREATE TRIGGER users_updated_at
|
||||
BEFORE UPDATE ON users
|
||||
FOR EACH ROW EXECUTE FUNCTION update_updated_at();
|
||||
|
||||
CREATE TRIGGER spaces_updated_at
|
||||
BEFORE UPDATE ON spaces
|
||||
FOR EACH ROW EXECUTE FUNCTION update_updated_at();
|
||||
|
||||
CREATE TRIGGER channels_updated_at
|
||||
BEFORE UPDATE ON channels
|
||||
FOR EACH ROW EXECUTE FUNCTION update_updated_at();
|
||||
|
||||
CREATE TRIGGER messages_updated_at
|
||||
BEFORE UPDATE ON messages
|
||||
FOR EACH ROW EXECUTE FUNCTION update_updated_at();
|
||||
|
||||
CREATE TRIGGER relationships_updated_at
|
||||
BEFORE UPDATE ON relationships
|
||||
FOR EACH ROW EXECUTE FUNCTION update_updated_at();
|
||||
|
||||
CREATE TRIGGER access_tokens_updated_at
|
||||
BEFORE UPDATE ON access_tokens
|
||||
FOR EACH ROW EXECUTE FUNCTION update_updated_at();
|
||||
|
||||
CREATE OR REPLACE FUNCTION add_owner_to_space()
|
||||
RETURNS TRIGGER AS $$
|
||||
BEGIN
|
||||
INSERT INTO space_members (space_id, user_id, role, joined_at)
|
||||
VALUES (NEW.id, NEW.owner_id, 'admin', NOW());
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
|
||||
CREATE TRIGGER space_owner_becomes_member
|
||||
AFTER INSERT ON spaces
|
||||
FOR EACH ROW EXECUTE FUNCTION add_owner_to_space();
|
||||
Reference in New Issue
Block a user