Files
sozial/www/api/database/schema.sql
Madgerm 937a32ad06 Phase 1: Foundation - Database schema, core PHP structure, JWT auth, and User CRUD
Implemented:
- Database schema (SQLite) with tables: users, profiles, posts, swipes, matches, chats, chat_messages, events, notifications, relationships, post_likes, post_comments, refresh_tokens, media
- Core PHP structure following skeleton pattern (www/api/)
- JWT Authentication (register, login, refresh, logout)
- User profile CRUD (show, update, delete, search)
- User sub-resources (posts, friends, followers, following)
- Minimal Firebase JWT implementation for PHP without Composer
2026-04-29 06:49:15 +02:00

250 lines
7.9 KiB
SQL

-- Sozial Platform Database Schema
-- SQLite
-- Users table
CREATE TABLE IF NOT EXISTS users (
id TEXT PRIMARY KEY,
email TEXT UNIQUE NOT NULL,
password_hash TEXT NOT NULL,
username TEXT UNIQUE NOT NULL,
display_name TEXT,
bio TEXT,
avatar_url TEXT,
photos TEXT, -- JSON array
gender TEXT,
interested_in TEXT, -- JSON array
age INTEGER,
location TEXT, -- JSON object {city, lat, lng}
interests TEXT, -- JSON array
is_online INTEGER DEFAULT 0,
last_seen INTEGER,
settings TEXT, -- JSON object
created_at INTEGER NOT NULL,
updated_at INTEGER NOT NULL
);
-- Profiles table (extended profile data)
CREATE TABLE IF NOT EXISTS profiles (
id TEXT PRIMARY KEY,
user_id TEXT NOT NULL UNIQUE,
first_name TEXT,
last_name TEXT,
birthday TEXT,
city TEXT,
country TEXT,
latitude REAL,
longitude REAL,
height_cm INTEGER,
body_type TEXT,
education TEXT,
occupation TEXT,
relationship_status TEXT,
looking_for TEXT,
about_me TEXT,
created_at INTEGER NOT NULL,
updated_at INTEGER NOT NULL,
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
);
-- Posts table
CREATE TABLE IF NOT EXISTS posts (
id TEXT PRIMARY KEY,
user_id TEXT NOT NULL,
type TEXT NOT NULL DEFAULT 'text', -- text|image|checkin
content TEXT,
media_urls TEXT, -- JSON array
location TEXT, -- JSON object {name, lat, lng}
likes_count INTEGER DEFAULT 0,
comments_count INTEGER DEFAULT 0,
shares_count INTEGER DEFAULT 0,
created_at INTEGER NOT NULL,
updated_at INTEGER NOT NULL,
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
);
-- Post likes table
CREATE TABLE IF NOT EXISTS post_likes (
id TEXT PRIMARY KEY,
post_id TEXT NOT NULL,
user_id TEXT NOT NULL,
created_at INTEGER NOT NULL,
FOREIGN KEY (post_id) REFERENCES posts(id) ON DELETE CASCADE,
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,
UNIQUE(post_id, user_id)
);
-- Post comments table
CREATE TABLE IF NOT EXISTS post_comments (
id TEXT PRIMARY KEY,
post_id TEXT NOT NULL,
user_id TEXT NOT NULL,
parent_id TEXT,
content TEXT NOT NULL,
created_at INTEGER NOT NULL,
updated_at INTEGER NOT NULL,
FOREIGN KEY (post_id) REFERENCES posts(id) ON DELETE CASCADE,
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,
FOREIGN KEY (parent_id) REFERENCES post_comments(id) ON DELETE CASCADE
);
-- Relationships table (follows and friends)
CREATE TABLE IF NOT EXISTS relationships (
id TEXT PRIMARY KEY,
follower_id TEXT NOT NULL,
following_id TEXT NOT NULL,
type TEXT NOT NULL DEFAULT 'follow', -- follow|friend
status TEXT DEFAULT 'active', -- active|blocked|pending
created_at INTEGER NOT NULL,
updated_at INTEGER NOT NULL,
FOREIGN KEY (follower_id) REFERENCES users(id) ON DELETE CASCADE,
FOREIGN KEY (following_id) REFERENCES users(id) ON DELETE CASCADE,
UNIQUE(follower_id, following_id)
);
-- Swipes table (for discovery feature)
CREATE TABLE IF NOT EXISTS swipes (
id TEXT PRIMARY KEY,
user_id TEXT NOT NULL,
target_user_id TEXT NOT NULL,
direction TEXT NOT NULL, -- like|pass|super_like
created_at INTEGER NOT NULL,
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,
FOREIGN KEY (target_user_id) REFERENCES users(id) ON DELETE CASCADE,
UNIQUE(user_id, target_user_id)
);
-- Matches table
CREATE TABLE IF NOT EXISTS matches (
id TEXT PRIMARY KEY,
user1_id TEXT NOT NULL,
user2_id TEXT NOT NULL,
created_at INTEGER NOT NULL,
FOREIGN KEY (user1_id) REFERENCES users(id) ON DELETE CASCADE,
FOREIGN KEY (user2_id) REFERENCES users(id) ON DELETE CASCADE,
UNIQUE(user1_id, user2_id)
);
-- Chats (conversations) table
CREATE TABLE IF NOT EXISTS chats (
id TEXT PRIMARY KEY,
type TEXT NOT NULL DEFAULT 'direct', -- direct|group
name TEXT,
avatar_url TEXT,
created_by TEXT,
created_at INTEGER NOT NULL,
updated_at INTEGER NOT NULL,
FOREIGN KEY (created_by) REFERENCES users(id) ON DELETE SET NULL
);
-- Chat participants table
CREATE TABLE IF NOT EXISTS chat_participants (
id TEXT PRIMARY KEY,
chat_id TEXT NOT NULL,
user_id TEXT NOT NULL,
joined_at INTEGER NOT NULL,
last_read_at INTEGER,
is_admin INTEGER DEFAULT 0,
FOREIGN KEY (chat_id) REFERENCES chats(id) ON DELETE CASCADE,
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,
UNIQUE(chat_id, user_id)
);
-- Chat messages table
CREATE TABLE IF NOT EXISTS chat_messages (
id TEXT PRIMARY KEY,
chat_id TEXT NOT NULL,
sender_id TEXT NOT NULL,
content TEXT NOT NULL,
type TEXT DEFAULT 'text', -- text|image|system
media_url TEXT,
read_at INTEGER,
created_at INTEGER NOT NULL,
updated_at INTEGER NOT NULL,
FOREIGN KEY (chat_id) REFERENCES chats(id) ON DELETE CASCADE,
FOREIGN KEY (sender_id) REFERENCES users(id) ON DELETE CASCADE
);
-- Events table
CREATE TABLE IF NOT EXISTS events (
id TEXT PRIMARY KEY,
creator_id TEXT NOT NULL,
title TEXT NOT NULL,
description TEXT,
event_type TEXT DEFAULT 'meetup', -- meetup|party|date|other
location_name TEXT,
location_address TEXT,
latitude REAL,
longitude REAL,
start_date INTEGER,
end_date INTEGER,
max_participants INTEGER,
is_public INTEGER DEFAULT 1,
created_at INTEGER NOT NULL,
updated_at INTEGER NOT NULL,
FOREIGN KEY (creator_id) REFERENCES users(id) ON DELETE CASCADE
);
-- Event participants table
CREATE TABLE IF NOT EXISTS event_participants (
id TEXT PRIMARY KEY,
event_id TEXT NOT NULL,
user_id TEXT NOT NULL,
status TEXT DEFAULT 'going', -- going|maybe|not_going
created_at INTEGER NOT NULL,
FOREIGN KEY (event_id) REFERENCES events(id) ON DELETE CASCADE,
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,
UNIQUE(event_id, user_id)
);
-- Notifications table
CREATE TABLE IF NOT EXISTS notifications (
id TEXT PRIMARY KEY,
user_id TEXT NOT NULL,
type TEXT NOT NULL, -- friend_request|friend_accepted|post_liked|post_commented|message|match|system
title TEXT NOT NULL,
body TEXT,
data TEXT, -- JSON object with additional data
is_read INTEGER DEFAULT 0,
created_at INTEGER NOT NULL,
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
);
-- Media uploads table
CREATE TABLE IF NOT EXISTS media (
id TEXT PRIMARY KEY,
user_id TEXT NOT NULL,
filename TEXT NOT NULL,
original_name TEXT,
mime_type TEXT,
size INTEGER,
url TEXT NOT NULL,
created_at INTEGER NOT NULL,
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
);
-- Refresh tokens table for JWT
CREATE TABLE IF NOT EXISTS refresh_tokens (
id TEXT PRIMARY KEY,
user_id TEXT NOT NULL,
token_hash TEXT NOT NULL,
expires_at INTEGER NOT NULL,
created_at INTEGER NOT NULL,
revoked_at INTEGER,
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
);
-- Indexes for better performance
CREATE INDEX IF NOT EXISTS idx_users_email ON users(email);
CREATE INDEX IF NOT EXISTS idx_users_username ON users(username);
CREATE INDEX IF NOT EXISTS idx_posts_user_id ON posts(user_id);
CREATE INDEX IF NOT EXISTS idx_posts_created_at ON posts(created_at);
CREATE INDEX IF NOT EXISTS idx_relationships_follower ON relationships(follower_id);
CREATE INDEX IF NOT EXISTS idx_relationships_following ON relationships(following_id);
CREATE INDEX IF NOT EXISTS idx_chat_messages_chat_id ON chat_messages(chat_id);
CREATE INDEX IF NOT EXISTS idx_chat_messages_created_at ON chat_messages(created_at);
CREATE INDEX IF NOT EXISTS idx_notifications_user_id ON notifications(user_id);
CREATE INDEX IF NOT EXISTS idx_notifications_created_at ON notifications(created_at);
CREATE INDEX IF NOT EXISTS idx_swipes_user_id ON swipes(user_id);
CREATE INDEX IF NOT EXISTS idx_matches_user1 ON matches(user1_id);
CREATE INDEX IF NOT EXISTS idx_matches_user2 ON matches(user2_id);