Files
pulse-api/internal/repository/db.go

108 lines
3.8 KiB
Go

package repository
import (
"github.com/jmoiron/sqlx"
_ "github.com/lib/pq"
)
func NewDB(databaseURL string) (*sqlx.DB, error) {
db, err := sqlx.Connect("postgres", databaseURL)
if err != nil {
return nil, err
}
db.SetMaxOpenConns(25)
db.SetMaxIdleConns(5)
return db, nil
}
func RunMigrations(db *sqlx.DB) error {
migrations := []string{
`CREATE TABLE IF NOT EXISTS users (
id SERIAL PRIMARY KEY,
email VARCHAR(255) UNIQUE NOT NULL,
username VARCHAR(100) NOT NULL,
password_hash VARCHAR(255) NOT NULL,
email_verified BOOLEAN DEFAULT FALSE,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)`,
`CREATE TABLE IF NOT EXISTS habits (
id SERIAL PRIMARY KEY,
user_id INTEGER REFERENCES users(id) ON DELETE CASCADE,
name VARCHAR(255) NOT NULL,
description TEXT DEFAULT '',
color VARCHAR(20) DEFAULT '#6366f1',
icon VARCHAR(50) DEFAULT 'check',
frequency VARCHAR(20) DEFAULT 'daily',
target_days INTEGER[] DEFAULT '{1,2,3,4,5,6,0}',
target_count INTEGER DEFAULT 1,
is_archived BOOLEAN DEFAULT FALSE,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)`,
`CREATE TABLE IF NOT EXISTS habit_logs (
id SERIAL PRIMARY KEY,
habit_id INTEGER REFERENCES habits(id) ON DELETE CASCADE,
user_id INTEGER REFERENCES users(id) ON DELETE CASCADE,
date DATE NOT NULL,
count INTEGER DEFAULT 1,
note TEXT DEFAULT '',
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
UNIQUE(habit_id, date)
)`,
`CREATE TABLE IF NOT EXISTS email_tokens (
id SERIAL PRIMARY KEY,
user_id INTEGER REFERENCES users(id) ON DELETE CASCADE,
token VARCHAR(255) UNIQUE NOT NULL,
type VARCHAR(20) NOT NULL,
expires_at TIMESTAMP NOT NULL,
used_at TIMESTAMP,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)`,
`CREATE TABLE IF NOT EXISTS tasks (
id SERIAL PRIMARY KEY,
user_id INTEGER REFERENCES users(id) ON DELETE CASCADE,
title VARCHAR(255) NOT NULL,
description TEXT DEFAULT '',
icon VARCHAR(10) DEFAULT '📋',
color VARCHAR(7) DEFAULT '#6B7280',
due_date DATE,
priority INTEGER DEFAULT 0,
completed_at TIMESTAMP,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)`,
`CREATE INDEX IF NOT EXISTS idx_habits_user_id ON habits(user_id)`,
`CREATE INDEX IF NOT EXISTS idx_habit_logs_habit_id ON habit_logs(habit_id)`,
`CREATE INDEX IF NOT EXISTS idx_habit_logs_date ON habit_logs(date)`,
`CREATE INDEX IF NOT EXISTS idx_habit_logs_user_date ON habit_logs(user_id, date)`,
`CREATE INDEX IF NOT EXISTS idx_email_tokens_token ON email_tokens(token)`,
`CREATE INDEX IF NOT EXISTS idx_email_tokens_user_id ON email_tokens(user_id)`,
`CREATE INDEX IF NOT EXISTS idx_tasks_user_id ON tasks(user_id)`,
`CREATE INDEX IF NOT EXISTS idx_tasks_due_date ON tasks(due_date)`,
`CREATE INDEX IF NOT EXISTS idx_tasks_completed ON tasks(user_id, completed_at)`,
`DO $$
BEGIN
IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name='users' AND column_name='email_verified') THEN
ALTER TABLE users ADD COLUMN email_verified BOOLEAN DEFAULT FALSE;
END IF;
END $$;`,
`ALTER TABLE users ADD COLUMN IF NOT EXISTS telegram_chat_id BIGINT`,
`ALTER TABLE users ADD COLUMN IF NOT EXISTS notifications_enabled BOOLEAN DEFAULT true`,
`ALTER TABLE users ADD COLUMN IF NOT EXISTS timezone VARCHAR(50) DEFAULT 'Europe/Moscow'`,
`ALTER TABLE tasks ADD COLUMN IF NOT EXISTS reminder_time TIME`,
`ALTER TABLE habits ADD COLUMN IF NOT EXISTS reminder_time TIME`,
`CREATE INDEX IF NOT EXISTS idx_users_telegram_chat_id ON users(telegram_chat_id)`,
}
for _, migration := range migrations {
if _, err := db.Exec(migration); err != nil {
return err
}
}
return nil
}