Files
TeamFlow--Trello-Like-/server/db.js
admin 460f83aef8 🌊 TeamFlow — Modern Trello alternative with email integration
- Full-stack: React 18 + Express + SQLite
- Drag-and-drop kanban boards with @hello-pangea/dnd
- Google App Password email integration (SMTP + IMAP)
- Inbound email: create cards by sending emails
- Reply-to-card: email replies become comments
- Admin/user management with role-based access
- Setup wizard: email config → admin creation
- Checklists, time tracking, priorities, labels, due dates
- Real-time notifications with activity feed
- Beautiful HTML email templates
2026-04-03 15:11:27 +00:00

183 lines
5.7 KiB
JavaScript

import Database from 'better-sqlite3';
import { fileURLToPath } from 'url';
import { dirname, join } from 'path';
const __dirname = dirname(fileURLToPath(import.meta.url));
const DB_PATH = join(__dirname, 'data', 'teamflow.db');
import { mkdirSync } from 'fs';
mkdirSync(join(__dirname, 'data'), { recursive: true });
const db = new Database(DB_PATH);
db.pragma('journal_mode = WAL');
db.pragma('foreign_keys = ON');
db.exec(`
CREATE TABLE IF NOT EXISTS app_state (
key TEXT PRIMARY KEY,
value TEXT NOT NULL
);
CREATE TABLE IF NOT EXISTS users (
id INTEGER PRIMARY KEY AUTOINCREMENT,
email TEXT UNIQUE NOT NULL,
password TEXT NOT NULL,
name TEXT NOT NULL,
avatar_color TEXT DEFAULT '#6366f1',
role TEXT DEFAULT 'member',
is_active INTEGER DEFAULT 1,
created_at TEXT DEFAULT (datetime('now'))
);
CREATE TABLE IF NOT EXISTS boards (
id INTEGER PRIMARY KEY AUTOINCREMENT,
title TEXT NOT NULL,
description TEXT DEFAULT '',
background TEXT DEFAULT 'gradient-blue',
is_archived INTEGER DEFAULT 0,
created_by INTEGER REFERENCES users(id),
created_at TEXT DEFAULT (datetime('now'))
);
CREATE TABLE IF NOT EXISTS board_members (
board_id INTEGER REFERENCES boards(id) ON DELETE CASCADE,
user_id INTEGER REFERENCES users(id) ON DELETE CASCADE,
role TEXT DEFAULT 'member',
joined_at TEXT DEFAULT (datetime('now')),
PRIMARY KEY (board_id, user_id)
);
CREATE TABLE IF NOT EXISTS lists (
id INTEGER PRIMARY KEY AUTOINCREMENT,
board_id INTEGER REFERENCES boards(id) ON DELETE CASCADE,
title TEXT NOT NULL,
position REAL NOT NULL,
is_archived INTEGER DEFAULT 0,
created_at TEXT DEFAULT (datetime('now'))
);
CREATE TABLE IF NOT EXISTS cards (
id INTEGER PRIMARY KEY AUTOINCREMENT,
list_id INTEGER REFERENCES lists(id) ON DELETE CASCADE,
title TEXT NOT NULL,
description TEXT DEFAULT '',
position REAL NOT NULL,
due_date TEXT,
priority TEXT DEFAULT 'none',
color TEXT DEFAULT '',
estimated_hours REAL,
time_spent REAL DEFAULT 0,
created_by INTEGER REFERENCES users(id),
assigned_to INTEGER REFERENCES users(id),
created_at TEXT DEFAULT (datetime('now')),
updated_at TEXT DEFAULT (datetime('now'))
);
CREATE TABLE IF NOT EXISTS labels (
id INTEGER PRIMARY KEY AUTOINCREMENT,
board_id INTEGER REFERENCES boards(id) ON DELETE CASCADE,
name TEXT NOT NULL,
color TEXT NOT NULL,
created_at TEXT DEFAULT (datetime('now'))
);
CREATE TABLE IF NOT EXISTS card_labels (
card_id INTEGER REFERENCES cards(id) ON DELETE CASCADE,
label_id INTEGER REFERENCES labels(id) ON DELETE CASCADE,
PRIMARY KEY (card_id, label_id)
);
CREATE TABLE IF NOT EXISTS card_comments (
id INTEGER PRIMARY KEY AUTOINCREMENT,
card_id INTEGER REFERENCES cards(id) ON DELETE CASCADE,
user_id INTEGER REFERENCES users(id),
content TEXT NOT NULL,
is_email_reply INTEGER DEFAULT 0,
created_at TEXT DEFAULT (datetime('now'))
);
CREATE TABLE IF NOT EXISTS card_activity (
id INTEGER PRIMARY KEY AUTOINCREMENT,
card_id INTEGER REFERENCES cards(id) ON DELETE CASCADE,
user_id INTEGER REFERENCES users(id),
action TEXT NOT NULL,
details TEXT DEFAULT '',
created_at TEXT DEFAULT (datetime('now'))
);
CREATE TABLE IF NOT EXISTS email_config (
id INTEGER PRIMARY KEY CHECK (id = 1),
smtp_host TEXT DEFAULT 'smtp.gmail.com',
smtp_port INTEGER DEFAULT 587,
email TEXT NOT NULL,
app_password TEXT NOT NULL,
imap_host TEXT DEFAULT 'imap.gmail.com',
imap_port INTEGER DEFAULT 993,
inbound_enabled INTEGER DEFAULT 0,
inbound_folder TEXT DEFAULT 'INBOX',
board_email_prefix TEXT DEFAULT 'tf-',
configured_at TEXT DEFAULT (datetime('now'))
);
CREATE TABLE IF NOT EXISTS email_log (
id INTEGER PRIMARY KEY AUTOINCREMENT,
from_email TEXT,
to_email TEXT,
subject TEXT,
card_id INTEGER REFERENCES cards(id),
direction TEXT,
status TEXT,
error TEXT DEFAULT '',
created_at TEXT DEFAULT (datetime('now'))
);
CREATE TABLE IF NOT EXISTS email_tokens (
id INTEGER PRIMARY KEY AUTOINCREMENT,
card_id INTEGER REFERENCES cards(id) ON DELETE CASCADE,
user_id INTEGER REFERENCES users(id),
token TEXT UNIQUE NOT NULL,
purpose TEXT DEFAULT 'reply',
expires_at TEXT,
created_at TEXT DEFAULT (datetime('now'))
);
CREATE TABLE IF NOT EXISTS notifications (
id INTEGER PRIMARY KEY AUTOINCREMENT,
user_id INTEGER REFERENCES users(id) ON DELETE CASCADE,
type TEXT NOT NULL,
title TEXT NOT NULL,
message TEXT DEFAULT '',
card_id INTEGER,
board_id INTEGER,
is_read INTEGER DEFAULT 0,
created_at TEXT DEFAULT (datetime('now'))
);
CREATE TABLE IF NOT EXISTS checklists (
id INTEGER PRIMARY KEY AUTOINCREMENT,
card_id INTEGER REFERENCES cards(id) ON DELETE CASCADE,
title TEXT NOT NULL DEFAULT 'Checklist',
position REAL DEFAULT 0,
created_at TEXT DEFAULT (datetime('now'))
);
CREATE TABLE IF NOT EXISTS checklist_items (
id INTEGER PRIMARY KEY AUTOINCREMENT,
checklist_id INTEGER REFERENCES checklists(id) ON DELETE CASCADE,
text TEXT NOT NULL,
is_checked INTEGER DEFAULT 0,
position REAL DEFAULT 0,
created_at TEXT DEFAULT (datetime('now'))
);
CREATE INDEX IF NOT EXISTS idx_cards_list ON cards(list_id);
CREATE INDEX IF NOT EXISTS idx_card_activity_card ON card_activity(card_id);
CREATE INDEX IF NOT EXISTS idx_card_comments_card ON card_comments(card_id);
CREATE INDEX IF NOT EXISTS idx_notifications_user ON notifications(user_id, is_read);
CREATE INDEX IF NOT EXISTS idx_lists_board ON lists(board_id);
CREATE INDEX IF NOT EXISTS idx_email_tokens_token ON email_tokens(token);
`);
export default db;