update cicd
This commit is contained in:
188
script/MIGRATION_GUIDE.md
Normal file
188
script/MIGRATION_GUIDE.md
Normal file
@@ -0,0 +1,188 @@
|
||||
# Player Configs Migration Guide
|
||||
|
||||
## Overview
|
||||
|
||||
Đây là tài liệu hướng dẫn migrate player settings từ bảng `user_preferences` sang bảng `player_configs` mới.
|
||||
|
||||
### Tại sao cần migrate?
|
||||
|
||||
- **user_preferences**: Một hàng mỗi user, không thể có nhiều cấu hình player
|
||||
- **player_configs**: Nhiều hàng mỗi user, hỗ trợ nhiều cấu hình player khác nhau
|
||||
|
||||
### Các bước thực hiện
|
||||
|
||||
## Bước 1: Chạy Migration SQL
|
||||
|
||||
```bash
|
||||
cd /home/dat/projects/stream/stream.api
|
||||
|
||||
# Cách 1: Dùng script tự động (khuyến nghị)
|
||||
./migrate_player_configs.sh
|
||||
|
||||
# Cách 2: Chạy SQL thủ công
|
||||
psql -h 47.84.63.130 -U postgres -d video_db -f full_player_configs_migration.sql
|
||||
```
|
||||
|
||||
### Script sẽ thực hiện:
|
||||
|
||||
1. ✅ Tạo bảng `player_configs` với các columns:
|
||||
- id, user_id, name, description
|
||||
- autoplay, loop, muted, show_controls, pip, airplay, chromecast
|
||||
- is_active, is_default, created_at, updated_at, version
|
||||
|
||||
2. ✅ Migrate dữ liệu từ `user_preferences` sang `player_configs`:
|
||||
- Mỗi user sẽ có 1 config mặc định tên "Default Config"
|
||||
- Description là "Migrated from user_preferences"
|
||||
|
||||
3. ✅ Xóa các columns player khỏi `user_preferences`:
|
||||
- autoplay, loop, muted
|
||||
- show_controls, pip, airplay, chromecast
|
||||
- encrytion_m3u8 (VIP feature không dùng)
|
||||
|
||||
4. ✅ Tạo indexes và triggers
|
||||
|
||||
## Bước 2: Regenerate Go Models
|
||||
|
||||
Sau khi migration xong, chạy:
|
||||
|
||||
```bash
|
||||
go run cmd/gendb/main.go
|
||||
```
|
||||
|
||||
Script này sẽ đọc schema mới từ database và regenerate:
|
||||
- `internal/database/model/user_preferences.gen.go` (không còn player fields)
|
||||
- `internal/database/model/player_configs.gen.go` (mới)
|
||||
- `internal/database/query/*.gen.go`
|
||||
|
||||
## Bước 3: Cập nhật Code
|
||||
|
||||
Sau khi models được regenerate, cập nhật các file sau:
|
||||
|
||||
### 3.1. Cập nhật `internal/api/preferences/service.go`
|
||||
|
||||
```go
|
||||
// Xóa các field player settings khỏi UpdateInput
|
||||
type UpdateInput struct {
|
||||
EmailNotifications *bool
|
||||
PushNotifications *bool
|
||||
MarketingNotifications *bool
|
||||
TelegramNotifications *bool
|
||||
// XÓA: Autoplay, Loop, Muted, ShowControls, Pip, Airplay, Chromecast
|
||||
Language *string
|
||||
Locale *string
|
||||
}
|
||||
|
||||
// Xóa logic update player settings khỏi UpdateUserPreferences
|
||||
```
|
||||
|
||||
### 3.2. Cập nhật `internal/rpc/app/service_account.go`
|
||||
|
||||
```go
|
||||
// UpdatePreferences chỉ update notification settings + language/locale
|
||||
func (s *appServices) UpdatePreferences(...) {
|
||||
// Chỉ update các fields không phải player settings
|
||||
pref, err := preferencesapi.UpdateUserPreferences(ctx, s.db, s.logger, result.UserID, preferencesapi.UpdateInput{
|
||||
EmailNotifications: req.EmailNotifications,
|
||||
PushNotifications: req.PushNotifications,
|
||||
MarketingNotifications: req.MarketingNotifications,
|
||||
TelegramNotifications: req.TelegramNotifications,
|
||||
Language: req.Language,
|
||||
Locale: req.Locale,
|
||||
// XÓA: Autoplay, Loop, Muted, ShowControls, Pip, Airplay, Chromecast
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
### 3.3. Sử dụng PlayerConfigs API cho player settings
|
||||
|
||||
Thay vì dùng `UpdatePreferences` cho player settings, dùng:
|
||||
|
||||
```go
|
||||
// Tạo hoặc cập nhật default player config
|
||||
func (s *appServices) UpdatePlayerSettings(ctx context.Context, req *appv1.UpdatePlayerSettingsRequest) {
|
||||
// Dùng player_configs API đã implement
|
||||
// ListPlayerConfigs, CreatePlayerConfig, UpdatePlayerConfig
|
||||
}
|
||||
```
|
||||
|
||||
## Bước 4: Build và Test
|
||||
|
||||
```bash
|
||||
# Build
|
||||
go build -o bin/api ./cmd/api
|
||||
|
||||
# Test migration
|
||||
# 1. Kiểm tra bảng player_configs
|
||||
psql -h 47.84.63.130 -U postgres -d video_db -c "SELECT COUNT(*) FROM player_configs;"
|
||||
|
||||
# 2. Kiểm tra user_preferences không còn player columns
|
||||
psql -h 47.84.63.130 -U postgres -d video_db -c "\d user_preferences"
|
||||
```
|
||||
|
||||
## Rollback (nếu cần)
|
||||
|
||||
Nếu muốn rollback:
|
||||
|
||||
```sql
|
||||
-- Thêm lại columns vào user_preferences
|
||||
ALTER TABLE user_preferences
|
||||
ADD COLUMN IF NOT EXISTS autoplay BOOLEAN DEFAULT FALSE,
|
||||
ADD COLUMN IF NOT EXISTS loop BOOLEAN DEFAULT FALSE,
|
||||
ADD COLUMN IF NOT EXISTS muted BOOLEAN DEFAULT FALSE,
|
||||
ADD COLUMN IF NOT EXISTS show_controls BOOLEAN DEFAULT TRUE,
|
||||
ADD COLUMN IF NOT EXISTS pip BOOLEAN DEFAULT TRUE,
|
||||
ADD COLUMN IF NOT EXISTS airplay BOOLEAN DEFAULT TRUE,
|
||||
ADD COLUMN IF NOT EXISTS chromecast BOOLEAN DEFAULT TRUE,
|
||||
ADD COLUMN IF NOT EXISTS encrytion_m3u8 BOOLEAN DEFAULT FALSE;
|
||||
|
||||
-- Copy dữ liệu từ player_configs về (cho default config)
|
||||
UPDATE user_preferences up
|
||||
SET autoplay = pc.autoplay,
|
||||
loop = pc.loop,
|
||||
muted = pc.muted,
|
||||
show_controls = pc.show_controls,
|
||||
pip = pc.pip,
|
||||
airplay = pc.airplay,
|
||||
chromecast = pc.chromecast
|
||||
FROM player_configs pc
|
||||
WHERE pc.user_id = up.user_id AND pc.is_default = TRUE;
|
||||
|
||||
-- Xóa bảng player_configs
|
||||
DROP TABLE IF EXISTS player_configs CASCADE;
|
||||
```
|
||||
|
||||
## Files liên quan
|
||||
|
||||
### Migration scripts:
|
||||
- `migrations/001_create_player_configs_table.sql` - Tạo bảng
|
||||
- `migrations/002_migrate_player_settings.sql` - Migrate data
|
||||
- `full_player_configs_migration.sql` - Kết hợp cả 2
|
||||
- `install_player_configs.sql` - Script đơn giản để chạy trực tiếp
|
||||
- `migrate_player_configs.sh` - Shell script tự động hóa
|
||||
|
||||
### Models:
|
||||
- `internal/database/model/player_configs.gen.go` - Model mới
|
||||
- `internal/database/model/user_preferences.gen.go` - Model cũ (sẽ thay đổi)
|
||||
|
||||
### Services:
|
||||
- `internal/rpc/app/service_user_features.go` - Player configs CRUD
|
||||
- `internal/rpc/app/service_admin_finance_catalog.go` - Admin player configs
|
||||
- `internal/api/preferences/service.go` - Legacy preferences (cần update)
|
||||
|
||||
### Frontend:
|
||||
- `stream.ui/src/routes/settings/PlayerConfigs/PlayerConfigs.vue` - UI mới
|
||||
- `stream.ui/src/routes/settings/Settings.vue` - Menu navigation
|
||||
|
||||
## Timeline khuyến nghị
|
||||
|
||||
1. **Tuần 1**: Chạy migration trên staging, test kỹ
|
||||
2. **Tuần 2**: Cập nhật code backend (preferences service)
|
||||
3. **Tuần 3**: Cập nhật frontend nếu cần
|
||||
4. **Tuần 4**: Deploy production
|
||||
|
||||
## Lưu ý
|
||||
|
||||
- ✅ Backup database trước khi chạy migration
|
||||
- ✅ Test trên staging trước khi production
|
||||
- ✅ Migration có transaction, sẽ rollback nếu lỗi
|
||||
- ✅ Dữ liệu user_preferences được giữ nguyên cho notification settings
|
||||
376
script/create_database.sql
Normal file
376
script/create_database.sql
Normal file
@@ -0,0 +1,376 @@
|
||||
-- DROP SCHEMA public;
|
||||
|
||||
CREATE SCHEMA public AUTHORIZATION pg_database_owner;
|
||||
|
||||
COMMENT ON SCHEMA public IS 'standard public schema';
|
||||
-- public.ad_templates definition
|
||||
|
||||
-- Drop table
|
||||
|
||||
-- DROP TABLE public.ad_templates;
|
||||
|
||||
CREATE TABLE public.ad_templates (
|
||||
id uuid NOT NULL,
|
||||
user_id uuid NOT NULL,
|
||||
"name" text NOT NULL,
|
||||
description text NULL,
|
||||
vast_tag_url text NOT NULL,
|
||||
ad_format varchar(50) DEFAULT 'pre-roll'::character varying NOT NULL,
|
||||
duration int8 NULL,
|
||||
is_active bool DEFAULT true NOT NULL,
|
||||
created_at timestamptz NULL,
|
||||
updated_at timestamptz NULL,
|
||||
is_default bool DEFAULT false NOT NULL,
|
||||
"version" int8 DEFAULT 1 NOT NULL,
|
||||
CONSTRAINT ad_templates_pkey PRIMARY KEY (id)
|
||||
);
|
||||
CREATE INDEX idx_ad_templates_user_id ON public.ad_templates USING btree (user_id);
|
||||
|
||||
|
||||
-- public.domains definition
|
||||
|
||||
-- Drop table
|
||||
|
||||
-- DROP TABLE public.domains;
|
||||
|
||||
CREATE TABLE public.domains (
|
||||
id uuid NOT NULL,
|
||||
user_id uuid NOT NULL,
|
||||
"name" text NOT NULL,
|
||||
created_at timestamptz NULL,
|
||||
updated_at timestamptz NULL,
|
||||
"version" int8 DEFAULT 1 NOT NULL,
|
||||
CONSTRAINT domains_pkey PRIMARY KEY (id)
|
||||
);
|
||||
CREATE INDEX idx_domains_user_id ON public.domains USING btree (user_id);
|
||||
|
||||
|
||||
-- public.jobs definition
|
||||
|
||||
-- Drop table
|
||||
|
||||
-- DROP TABLE public.jobs;
|
||||
|
||||
CREATE TABLE public.jobs (
|
||||
id uuid DEFAULT gen_random_uuid() NOT NULL,
|
||||
status text NULL,
|
||||
priority int8 DEFAULT 0 NULL,
|
||||
input_url text NULL,
|
||||
output_url text NULL,
|
||||
total_duration int8 NULL,
|
||||
current_time int8 NULL,
|
||||
progress numeric NULL,
|
||||
agent_id int8 NULL,
|
||||
logs text NULL,
|
||||
config text NULL,
|
||||
cancelled bool DEFAULT false NULL,
|
||||
retry_count int8 DEFAULT 0 NULL,
|
||||
max_retries int8 DEFAULT 3 NULL,
|
||||
created_at timestamptz NULL,
|
||||
updated_at timestamptz NULL,
|
||||
"version" int8 NULL,
|
||||
video_id uuid NULL,
|
||||
user_id uuid NULL,
|
||||
time_limit int8 DEFAULT 3600000 NULL,
|
||||
CONSTRAINT jobs_pkey PRIMARY KEY (id)
|
||||
);
|
||||
CREATE INDEX idx_jobs_priority ON public.jobs USING btree (priority);
|
||||
|
||||
|
||||
-- public.notifications definition
|
||||
|
||||
-- Drop table
|
||||
|
||||
-- DROP TABLE public.notifications;
|
||||
|
||||
CREATE TABLE public.notifications (
|
||||
id uuid NOT NULL,
|
||||
user_id uuid NOT NULL,
|
||||
"type" varchar(50) NOT NULL,
|
||||
title text NOT NULL,
|
||||
message text NOT NULL,
|
||||
metadata text NULL,
|
||||
action_url text NULL,
|
||||
action_label text NULL,
|
||||
is_read bool DEFAULT false NOT NULL,
|
||||
created_at timestamptz NULL,
|
||||
updated_at timestamptz NULL,
|
||||
"version" int8 DEFAULT 1 NOT NULL,
|
||||
CONSTRAINT notifications_pkey PRIMARY KEY (id)
|
||||
);
|
||||
CREATE INDEX idx_notifications_user_id ON public.notifications USING btree (user_id);
|
||||
|
||||
|
||||
-- public."plan" definition
|
||||
|
||||
-- Drop table
|
||||
|
||||
-- DROP TABLE public."plan";
|
||||
|
||||
CREATE TABLE public."plan" (
|
||||
id uuid DEFAULT gen_random_uuid() NOT NULL,
|
||||
"name" text NOT NULL,
|
||||
description text NULL,
|
||||
price numeric(65, 30) NOT NULL,
|
||||
"cycle" varchar(20) NOT NULL,
|
||||
storage_limit int8 NOT NULL,
|
||||
upload_limit int4 NOT NULL,
|
||||
duration_limit int4 NOT NULL,
|
||||
quality_limit text NOT NULL,
|
||||
features _text NULL,
|
||||
is_active bool DEFAULT true NOT NULL,
|
||||
"version" int8 DEFAULT 1 NOT NULL,
|
||||
CONSTRAINT plan_pkey PRIMARY KEY (id)
|
||||
);
|
||||
|
||||
|
||||
-- public.plan_subscriptions definition
|
||||
|
||||
-- Drop table
|
||||
|
||||
-- DROP TABLE public.plan_subscriptions;
|
||||
|
||||
CREATE TABLE public.plan_subscriptions (
|
||||
id uuid NOT NULL,
|
||||
user_id uuid NOT NULL,
|
||||
payment_id uuid NOT NULL,
|
||||
plan_id uuid NOT NULL,
|
||||
term_months int4 NOT NULL,
|
||||
payment_method varchar(20) NOT NULL,
|
||||
wallet_amount numeric(65, 30) NOT NULL,
|
||||
topup_amount numeric(65, 30) NOT NULL,
|
||||
started_at timestamptz NOT NULL,
|
||||
expires_at timestamptz NOT NULL,
|
||||
reminder_7d_sent_at timestamptz NULL,
|
||||
reminder_3d_sent_at timestamptz NULL,
|
||||
reminder_1d_sent_at timestamptz NULL,
|
||||
created_at timestamptz NULL,
|
||||
updated_at timestamptz NULL,
|
||||
"version" int8 DEFAULT 1 NOT NULL,
|
||||
CONSTRAINT plan_subscriptions_pkey PRIMARY KEY (id)
|
||||
);
|
||||
CREATE INDEX idx_plan_subscriptions_expires_at ON public.plan_subscriptions USING btree (expires_at);
|
||||
CREATE INDEX idx_plan_subscriptions_payment_id ON public.plan_subscriptions USING btree (payment_id);
|
||||
CREATE INDEX idx_plan_subscriptions_plan_id ON public.plan_subscriptions USING btree (plan_id);
|
||||
CREATE INDEX idx_plan_subscriptions_user_id ON public.plan_subscriptions USING btree (user_id);
|
||||
|
||||
|
||||
-- public.user_preferences definition
|
||||
|
||||
-- Drop table
|
||||
|
||||
-- DROP TABLE public.user_preferences;
|
||||
|
||||
CREATE TABLE public.user_preferences (
|
||||
user_id uuid NOT NULL,
|
||||
"language" text DEFAULT 'en'::text NOT NULL,
|
||||
locale text DEFAULT 'en'::text NOT NULL,
|
||||
email_notifications bool DEFAULT true NOT NULL,
|
||||
push_notifications bool DEFAULT true NOT NULL,
|
||||
marketing_notifications bool DEFAULT false NOT NULL,
|
||||
telegram_notifications bool DEFAULT false NOT NULL,
|
||||
created_at timestamptz NULL,
|
||||
updated_at timestamptz NULL,
|
||||
"version" int8 DEFAULT 1 NOT NULL,
|
||||
CONSTRAINT user_preferences_pkey PRIMARY KEY (user_id)
|
||||
);
|
||||
|
||||
|
||||
-- public.wallet_transactions definition
|
||||
|
||||
-- Drop table
|
||||
|
||||
-- DROP TABLE public.wallet_transactions;
|
||||
|
||||
CREATE TABLE public.wallet_transactions (
|
||||
id uuid NOT NULL,
|
||||
user_id uuid NOT NULL,
|
||||
"type" varchar(50) NOT NULL,
|
||||
amount numeric(65, 30) NOT NULL,
|
||||
currency text DEFAULT 'USD'::text NOT NULL,
|
||||
note text NULL,
|
||||
created_at timestamptz NULL,
|
||||
updated_at timestamptz NULL,
|
||||
payment_id uuid NULL,
|
||||
plan_id uuid NULL,
|
||||
term_months int4 NULL,
|
||||
"version" int8 DEFAULT 1 NOT NULL,
|
||||
CONSTRAINT wallet_transactions_pkey PRIMARY KEY (id)
|
||||
);
|
||||
CREATE INDEX idx_wallet_transactions_payment_id ON public.wallet_transactions USING btree (payment_id);
|
||||
CREATE INDEX idx_wallet_transactions_plan_id ON public.wallet_transactions USING btree (plan_id);
|
||||
CREATE INDEX idx_wallet_transactions_user_id ON public.wallet_transactions USING btree (user_id);
|
||||
|
||||
|
||||
-- public."user" definition
|
||||
|
||||
-- Drop table
|
||||
|
||||
-- DROP TABLE public."user";
|
||||
|
||||
CREATE TABLE public."user" (
|
||||
id uuid DEFAULT gen_random_uuid() NOT NULL,
|
||||
email text NOT NULL,
|
||||
"password" text NULL,
|
||||
username text NULL,
|
||||
avatar text NULL,
|
||||
"role" varchar(20) DEFAULT 'USER'::character varying NOT NULL,
|
||||
google_id text NULL,
|
||||
storage_used int8 DEFAULT 0 NOT NULL,
|
||||
plan_id uuid NULL,
|
||||
created_at timestamp(3) DEFAULT CURRENT_TIMESTAMP NOT NULL,
|
||||
updated_at timestamp(3) NOT NULL,
|
||||
"version" int8 DEFAULT 1 NOT NULL,
|
||||
telegram_id varchar NULL,
|
||||
referred_by_user_id uuid NULL,
|
||||
referral_eligible bool DEFAULT true NOT NULL,
|
||||
referral_reward_bps int4 NULL,
|
||||
referral_reward_granted_at timestamptz NULL,
|
||||
referral_reward_payment_id uuid NULL,
|
||||
referral_reward_amount numeric(65, 30) NULL,
|
||||
CONSTRAINT user_pkey PRIMARY KEY (id),
|
||||
CONSTRAINT user_plan_id_fkey FOREIGN KEY (plan_id) REFERENCES public."plan"(id) ON DELETE SET NULL ON UPDATE CASCADE,
|
||||
CONSTRAINT user_referred_by_user_id_fkey FOREIGN KEY (referred_by_user_id) REFERENCES public."user"(id) ON DELETE SET NULL
|
||||
);
|
||||
CREATE INDEX idx_user_referred_by_user_id ON public."user" USING btree (referred_by_user_id);
|
||||
CREATE UNIQUE INDEX user_email_key ON public."user" USING btree (email);
|
||||
CREATE UNIQUE INDEX user_google_id_key ON public."user" USING btree (google_id);
|
||||
|
||||
|
||||
-- public.video definition
|
||||
|
||||
-- Drop table
|
||||
|
||||
-- DROP TABLE public.video;
|
||||
|
||||
CREATE TABLE public.video (
|
||||
id uuid DEFAULT gen_random_uuid() NOT NULL,
|
||||
"name" text NOT NULL,
|
||||
title text NOT NULL,
|
||||
description text NULL,
|
||||
url text NOT NULL,
|
||||
thumbnail text NULL,
|
||||
hls_token text NULL,
|
||||
hls_path text NULL,
|
||||
duration int4 NOT NULL,
|
||||
"size" int8 NOT NULL,
|
||||
storage_type varchar(20) DEFAULT 'tiktok_avatar'::character varying NOT NULL,
|
||||
format text NOT NULL,
|
||||
status varchar(20) DEFAULT 'PUBLIC'::character varying NOT NULL,
|
||||
processing_status varchar(20) DEFAULT 'PENDING'::character varying NOT NULL,
|
||||
"views" int4 DEFAULT 0 NOT NULL,
|
||||
user_id uuid NOT NULL,
|
||||
created_at timestamp(3) DEFAULT CURRENT_TIMESTAMP NOT NULL,
|
||||
updated_at timestamp(3) NOT NULL,
|
||||
"version" int8 DEFAULT 1 NOT NULL,
|
||||
ad_id uuid NULL,
|
||||
metadata jsonb NULL,
|
||||
CONSTRAINT video_pkey PRIMARY KEY (id),
|
||||
CONSTRAINT video_user_id_fkey FOREIGN KEY (user_id) REFERENCES public."user"(id) ON DELETE RESTRICT ON UPDATE CASCADE
|
||||
);
|
||||
|
||||
|
||||
-- public.payment definition
|
||||
|
||||
-- Drop table
|
||||
|
||||
-- DROP TABLE public.payment;
|
||||
|
||||
CREATE TABLE public.payment (
|
||||
id uuid DEFAULT gen_random_uuid() NOT NULL,
|
||||
user_id uuid NOT NULL,
|
||||
plan_id uuid NULL,
|
||||
amount numeric(65, 30) NOT NULL,
|
||||
currency text DEFAULT 'USD'::text NOT NULL,
|
||||
status varchar(20) DEFAULT 'PENDING'::character varying NOT NULL,
|
||||
provider varchar(20) DEFAULT 'STRIPE'::character varying NOT NULL,
|
||||
transaction_id text NULL,
|
||||
created_at timestamp(3) DEFAULT CURRENT_TIMESTAMP NOT NULL,
|
||||
updated_at timestamp(3) NOT NULL,
|
||||
"version" int8 DEFAULT 1 NOT NULL,
|
||||
CONSTRAINT payment_pkey PRIMARY KEY (id),
|
||||
CONSTRAINT payment_plan_id_fkey FOREIGN KEY (plan_id) REFERENCES public."plan"(id) ON DELETE SET NULL ON UPDATE CASCADE,
|
||||
CONSTRAINT payment_user_id_fkey FOREIGN KEY (user_id) REFERENCES public."user"(id) ON DELETE RESTRICT ON UPDATE CASCADE
|
||||
);
|
||||
|
||||
|
||||
-- public.player_configs definition
|
||||
|
||||
-- Drop table
|
||||
|
||||
-- DROP TABLE public.player_configs;
|
||||
|
||||
CREATE TABLE public.player_configs (
|
||||
id uuid DEFAULT gen_random_uuid() NOT NULL,
|
||||
user_id uuid NOT NULL,
|
||||
"name" text NOT NULL,
|
||||
description text NULL,
|
||||
autoplay bool DEFAULT false NOT NULL,
|
||||
"loop" bool DEFAULT false NOT NULL,
|
||||
muted bool DEFAULT false NOT NULL,
|
||||
show_controls bool DEFAULT true NOT NULL,
|
||||
pip bool DEFAULT true NOT NULL,
|
||||
airplay bool DEFAULT true NOT NULL,
|
||||
chromecast bool DEFAULT true NOT NULL,
|
||||
is_active bool DEFAULT true NOT NULL,
|
||||
is_default bool DEFAULT false NOT NULL,
|
||||
created_at timestamp(3) DEFAULT CURRENT_TIMESTAMP NOT NULL,
|
||||
updated_at timestamp(3) NOT NULL,
|
||||
"version" int8 DEFAULT 1 NOT NULL,
|
||||
encrytion_m3u8 bool DEFAULT true NOT NULL,
|
||||
logo_url varchar(500) NULL,
|
||||
CONSTRAINT player_configs_pkey PRIMARY KEY (id),
|
||||
CONSTRAINT player_configs_url_check CHECK (((logo_url)::text ~* '^https?://'::text)),
|
||||
CONSTRAINT player_configs_user_id_fkey FOREIGN KEY (user_id) REFERENCES public."user"(id) ON DELETE CASCADE
|
||||
);
|
||||
CREATE INDEX idx_player_configs_is_default ON public.player_configs USING btree (is_default);
|
||||
CREATE UNIQUE INDEX idx_player_configs_one_default_per_user ON public.player_configs USING btree (user_id) WHERE (is_default = true);
|
||||
CREATE INDEX idx_player_configs_user_default ON public.player_configs USING btree (user_id, is_default);
|
||||
CREATE INDEX idx_player_configs_user_id ON public.player_configs USING btree (user_id);
|
||||
|
||||
-- Table Triggers
|
||||
|
||||
create trigger trg_update_player_configs before
|
||||
update
|
||||
on
|
||||
public.player_configs for each row execute function update_player_configs_updated_at();
|
||||
|
||||
|
||||
-- public.popup_ads definition
|
||||
|
||||
-- Drop table
|
||||
|
||||
-- DROP TABLE public.popup_ads;
|
||||
|
||||
CREATE TABLE public.popup_ads (
|
||||
id uuid NOT NULL,
|
||||
user_id uuid NOT NULL,
|
||||
"type" varchar(20) NOT NULL,
|
||||
"label" text NOT NULL,
|
||||
value text NOT NULL,
|
||||
is_active bool DEFAULT true NOT NULL,
|
||||
max_triggers_per_session int4 DEFAULT 3 NOT NULL,
|
||||
created_at timestamptz DEFAULT CURRENT_TIMESTAMP NULL,
|
||||
updated_at timestamptz NULL,
|
||||
"version" int8 DEFAULT 1 NOT NULL,
|
||||
CONSTRAINT popup_ads_pkey PRIMARY KEY (id),
|
||||
CONSTRAINT popup_ads_user_id_fkey FOREIGN KEY (user_id) REFERENCES public."user"(id) ON DELETE CASCADE
|
||||
);
|
||||
CREATE INDEX idx_popup_ads_user_active ON public.popup_ads USING btree (user_id, is_active);
|
||||
CREATE INDEX idx_popup_ads_user_id ON public.popup_ads USING btree (user_id);
|
||||
|
||||
|
||||
|
||||
-- DROP FUNCTION public.update_player_configs_updated_at();
|
||||
|
||||
CREATE OR REPLACE FUNCTION public.update_player_configs_updated_at()
|
||||
RETURNS trigger
|
||||
LANGUAGE plpgsql
|
||||
AS $function$
|
||||
BEGIN
|
||||
NEW.updated_at = CURRENT_TIMESTAMP;
|
||||
NEW.version = OLD.version + 1;
|
||||
RETURN NEW;
|
||||
END;
|
||||
$function$
|
||||
;
|
||||
143
script/full_player_configs_migration.sql
Normal file
143
script/full_player_configs_migration.sql
Normal file
@@ -0,0 +1,143 @@
|
||||
-- Full migration script for player_configs
|
||||
-- This combines all migrations into one file for easy execution
|
||||
-- Run: psql -h 47.84.63.130 -U postgres -d video_db -f full_player_configs_migration.sql
|
||||
|
||||
\echo '=============================================='
|
||||
\echo 'Starting full player_configs migration...'
|
||||
\echo '=============================================='
|
||||
|
||||
BEGIN;
|
||||
|
||||
-- ============================================================
|
||||
-- PART 1: Create player_configs table
|
||||
-- ============================================================
|
||||
|
||||
CREATE TABLE IF NOT EXISTS player_configs (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
user_id UUID NOT NULL REFERENCES "user"(id) ON DELETE CASCADE,
|
||||
name TEXT NOT NULL,
|
||||
description TEXT,
|
||||
autoplay BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
loop BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
muted BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
show_controls BOOLEAN NOT NULL DEFAULT TRUE,
|
||||
pip BOOLEAN NOT NULL DEFAULT TRUE,
|
||||
airplay BOOLEAN NOT NULL DEFAULT TRUE,
|
||||
chromecast BOOLEAN NOT NULL DEFAULT TRUE,
|
||||
is_active BOOLEAN NOT NULL DEFAULT TRUE,
|
||||
is_default BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
created_at TIMESTAMP(3) WITHOUT TIME ZONE NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP(3) WITHOUT TIME ZONE NOT NULL,
|
||||
version BIGINT NOT NULL DEFAULT 1
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_player_configs_user_id ON player_configs(user_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_player_configs_is_default ON player_configs(is_default);
|
||||
CREATE INDEX IF NOT EXISTS idx_player_configs_user_default ON player_configs(user_id, is_default);
|
||||
|
||||
CREATE OR REPLACE FUNCTION update_player_configs_updated_at()
|
||||
RETURNS TRIGGER AS $$
|
||||
BEGIN
|
||||
NEW.updated_at = CURRENT_TIMESTAMP;
|
||||
NEW.version = OLD.version + 1;
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
|
||||
DROP TRIGGER IF EXISTS trg_update_player_configs ON player_configs;
|
||||
|
||||
CREATE TRIGGER trg_update_player_configs
|
||||
BEFORE UPDATE ON player_configs
|
||||
FOR EACH ROW
|
||||
EXECUTE FUNCTION update_player_configs_updated_at();
|
||||
|
||||
-- ============================================================
|
||||
-- PART 2: Migrate data from user_preferences
|
||||
-- ============================================================
|
||||
|
||||
INSERT INTO player_configs (
|
||||
id, user_id, name, description,
|
||||
autoplay, loop, muted,
|
||||
show_controls, pip, airplay, chromecast,
|
||||
is_active, is_default,
|
||||
created_at, updated_at, version
|
||||
)
|
||||
SELECT
|
||||
gen_random_uuid(),
|
||||
up.user_id,
|
||||
'Default Config',
|
||||
'Migrated from user_preferences',
|
||||
COALESCE(up.autoplay, FALSE),
|
||||
COALESCE(up.loop, FALSE),
|
||||
COALESCE(up.muted, FALSE),
|
||||
COALESCE(up.show_controls, TRUE),
|
||||
COALESCE(up.pip, TRUE),
|
||||
COALESCE(up.airplay, TRUE),
|
||||
COALESCE(up.chromecast, TRUE),
|
||||
TRUE,
|
||||
TRUE,
|
||||
COALESCE(up.created_at, CURRENT_TIMESTAMP),
|
||||
CURRENT_TIMESTAMP,
|
||||
COALESCE(up.version, 1)
|
||||
FROM user_preferences up
|
||||
WHERE NOT EXISTS (
|
||||
SELECT 1 FROM player_configs pc WHERE pc.user_id = up.user_id AND pc.is_default = TRUE
|
||||
);
|
||||
|
||||
-- ============================================================
|
||||
-- PART 3: Remove old columns from user_preferences
|
||||
-- ============================================================
|
||||
|
||||
ALTER TABLE user_preferences
|
||||
DROP COLUMN IF EXISTS autoplay,
|
||||
DROP COLUMN IF EXISTS loop,
|
||||
DROP COLUMN IF EXISTS muted,
|
||||
DROP COLUMN IF EXISTS show_controls,
|
||||
DROP COLUMN IF EXISTS pip,
|
||||
DROP COLUMN IF EXISTS airplay,
|
||||
DROP COLUMN IF EXISTS chromecast,
|
||||
DROP COLUMN IF EXISTS encrytion_m3u8;
|
||||
|
||||
-- ============================================================
|
||||
-- PART 4: Add constraints
|
||||
-- ============================================================
|
||||
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS idx_player_configs_one_default_per_user
|
||||
ON player_configs(user_id)
|
||||
WHERE is_default = TRUE;
|
||||
|
||||
-- ============================================================
|
||||
-- Verification
|
||||
-- ============================================================
|
||||
|
||||
DO $$
|
||||
DECLARE
|
||||
migrated_count INTEGER;
|
||||
prefs_count INTEGER;
|
||||
BEGIN
|
||||
SELECT COUNT(*) INTO migrated_count FROM player_configs WHERE description = 'Migrated from user_preferences';
|
||||
SELECT COUNT(*) INTO prefs_count FROM user_preferences;
|
||||
|
||||
RAISE NOTICE '============================================';
|
||||
RAISE NOTICE 'Migration completed!';
|
||||
RAISE NOTICE 'User preferences rows: %', prefs_count;
|
||||
RAISE NOTICE 'Player configs created: %', migrated_count;
|
||||
RAISE NOTICE '============================================';
|
||||
END $$;
|
||||
|
||||
COMMIT;
|
||||
|
||||
-- Verify columns removed from user_preferences
|
||||
SELECT 'user_preferences columns:' AS info;
|
||||
SELECT column_name, data_type FROM information_schema.columns WHERE table_name = 'user_preferences' ORDER BY ordinal_position;
|
||||
|
||||
-- Verify player_configs structure
|
||||
SELECT 'player_configs columns:' AS info;
|
||||
SELECT column_name, data_type FROM information_schema.columns WHERE table_name = 'player_configs' ORDER BY ordinal_position;
|
||||
|
||||
-- Sample data
|
||||
SELECT 'Sample migrated data:' AS info;
|
||||
SELECT pc.user_id, pc.name, pc.autoplay, pc.loop, pc.muted, pc.show_controls, pc.is_default
|
||||
FROM player_configs pc
|
||||
WHERE pc.description = 'Migrated from user_preferences'
|
||||
LIMIT 5;
|
||||
55
script/install_player_configs.sql
Normal file
55
script/install_player_configs.sql
Normal file
@@ -0,0 +1,55 @@
|
||||
-- Quick install script for player_configs table
|
||||
-- Run this directly in your PostgreSQL database
|
||||
-- Usage: psql -d video_db -f install_player_configs.sql
|
||||
|
||||
BEGIN;
|
||||
|
||||
-- Create player_configs table
|
||||
CREATE TABLE IF NOT EXISTS player_configs (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
user_id UUID NOT NULL REFERENCES "user"(id) ON DELETE CASCADE,
|
||||
name TEXT NOT NULL,
|
||||
description TEXT,
|
||||
autoplay BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
loop BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
muted BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
show_controls BOOLEAN NOT NULL DEFAULT TRUE,
|
||||
pip BOOLEAN NOT NULL DEFAULT TRUE,
|
||||
airplay BOOLEAN NOT NULL DEFAULT TRUE,
|
||||
chromecast BOOLEAN NOT NULL DEFAULT TRUE,
|
||||
is_active BOOLEAN NOT NULL DEFAULT TRUE,
|
||||
is_default BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
created_at TIMESTAMP(3) WITHOUT TIME ZONE NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP(3) WITHOUT TIME ZONE NOT NULL,
|
||||
version BIGINT NOT NULL DEFAULT 1
|
||||
);
|
||||
|
||||
-- Indexes
|
||||
CREATE INDEX IF NOT EXISTS idx_player_configs_user_id ON player_configs(user_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_player_configs_is_default ON player_configs(is_default);
|
||||
CREATE INDEX IF NOT EXISTS idx_player_configs_user_default ON player_configs(user_id, is_default);
|
||||
|
||||
-- Trigger to auto-update updated_at and version
|
||||
CREATE OR REPLACE FUNCTION update_player_configs_updated_at()
|
||||
RETURNS TRIGGER AS $$
|
||||
BEGIN
|
||||
NEW.updated_at = CURRENT_TIMESTAMP;
|
||||
NEW.version = OLD.version + 1;
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
|
||||
DROP TRIGGER IF EXISTS trg_update_player_configs ON player_configs;
|
||||
|
||||
CREATE TRIGGER trg_update_player_configs
|
||||
BEFORE UPDATE ON player_configs
|
||||
FOR EACH ROW
|
||||
EXECUTE FUNCTION update_player_configs_updated_at();
|
||||
|
||||
COMMIT;
|
||||
|
||||
-- Verify installation
|
||||
SELECT table_name, column_name, data_type
|
||||
FROM information_schema.columns
|
||||
WHERE table_name = 'player_configs'
|
||||
ORDER BY ordinal_position;
|
||||
84
script/migrate_player_configs.sh
Executable file
84
script/migrate_player_configs.sh
Executable file
@@ -0,0 +1,84 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Full migration script for player_configs feature
|
||||
# This script:
|
||||
# 1. Runs the SQL migration to create player_configs table
|
||||
# 2. Migrates data from user_preferences to player_configs
|
||||
# 3. Removes player-related columns from user_preferences
|
||||
# 4. Regenerates Go models to reflect schema changes
|
||||
#
|
||||
# Usage: ./migrate_player_configs.sh
|
||||
|
||||
set -e
|
||||
|
||||
# Colors
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m'
|
||||
|
||||
# Database config from config.yaml or environment
|
||||
DB_HOST="${DB_HOST:-47.84.63.130}"
|
||||
DB_PORT="${DB_PORT:-5432}"
|
||||
DB_NAME="${DB_NAME:-video_db}"
|
||||
DB_USER="${DB_USER:-postgres}"
|
||||
DB_PASSWORD="${DB_PASSWORD:-D@tkhong9}"
|
||||
|
||||
export PGPASSWORD="$DB_PASSWORD"
|
||||
|
||||
echo -e "${BLUE}============================================${NC}"
|
||||
echo -e "${BLUE} Player Configs Migration Script${NC}"
|
||||
echo -e "${BLUE}============================================${NC}"
|
||||
echo ""
|
||||
|
||||
# Step 1: Run SQL migration
|
||||
echo -e "${YELLOW}[Step 1/3] Running SQL migration...${NC}"
|
||||
psql -h "$DB_HOST" -p "$DB_PORT" -U "$DB_USER" -d "$DB_NAME" -f full_player_configs_migration.sql
|
||||
|
||||
if [ $? -ne 0 ]; then
|
||||
echo -e "${RED}SQL migration failed!${NC}"
|
||||
exit 1
|
||||
fi
|
||||
echo -e "${GREEN}✓ SQL migration completed${NC}"
|
||||
echo ""
|
||||
|
||||
# Step 2: Regenerate Go models
|
||||
echo -e "${YELLOW}[Step 2/3] Regenerating Go models...${NC}"
|
||||
go run cmd/gendb/main.go
|
||||
|
||||
if [ $? -ne 0 ]; then
|
||||
echo -e "${RED}Model generation failed!${NC}"
|
||||
echo -e "${YELLOW}Note: You may need to manually update the model files.${NC}"
|
||||
exit 1
|
||||
fi
|
||||
echo -e "${GREEN}✓ Go models regenerated${NC}"
|
||||
echo ""
|
||||
|
||||
# Step 3: Build to verify
|
||||
echo -e "${YELLOW}[Step 3/3] Building to verify changes...${NC}"
|
||||
go build -o bin/api ./cmd/api
|
||||
|
||||
if [ $? -ne 0 ]; then
|
||||
echo -e "${RED}Build failed!${NC}"
|
||||
exit 1
|
||||
fi
|
||||
echo -e "${GREEN}✓ Build successful${NC}"
|
||||
echo ""
|
||||
|
||||
unset PGPASSWORD
|
||||
|
||||
echo -e "${GREEN}============================================${NC}"
|
||||
echo -e "${GREEN} Migration completed successfully!${NC}"
|
||||
echo -e "${GREEN}============================================${NC}"
|
||||
echo ""
|
||||
echo -e "${BLUE}Summary of changes:${NC}"
|
||||
echo " ✓ player_configs table created"
|
||||
echo " ✓ Data migrated from user_preferences"
|
||||
echo " ✓ Player columns removed from user_preferences:"
|
||||
echo " - autoplay, loop, muted"
|
||||
echo " - show_controls, pip, airplay, chromecast"
|
||||
echo " - encrytion_m3u8"
|
||||
echo " ✓ Go models regenerated"
|
||||
echo ""
|
||||
echo -e "${YELLOW}Note: Please restart your application to apply changes.${NC}"
|
||||
59
script/run_migration.sh
Executable file
59
script/run_migration.sh
Executable file
@@ -0,0 +1,59 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Migration runner script for stream.api
|
||||
# Usage: ./run_migration.sh [migration_file.sql]
|
||||
|
||||
set -e
|
||||
|
||||
# Load environment variables from config if exists
|
||||
if [ -f "config.yaml" ]; then
|
||||
echo "Loading configuration from config.yaml..."
|
||||
fi
|
||||
|
||||
# Database connection parameters (adjust these based on your config)
|
||||
DB_HOST="${DB_HOST:-localhost}"
|
||||
DB_PORT="${DB_PORT:-5432}"
|
||||
DB_NAME="${DB_NAME:-stream}"
|
||||
DB_USER="${DB_USER:-postgres}"
|
||||
DB_PASSWORD="${DB_PASSWORD:-}"
|
||||
|
||||
# Colors for output
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
echo -e "${GREEN}=== Stream API Migration Runner ===${NC}"
|
||||
|
||||
# Check if psql is available
|
||||
if ! command -v psql &> /dev/null; then
|
||||
echo -e "${RED}Error: psql command not found. Please install PostgreSQL client.${NC}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Build connection string
|
||||
export PGPASSWORD="$DB_PASSWORD"
|
||||
|
||||
# Run specific migration or all migrations
|
||||
if [ -n "$1" ]; then
|
||||
MIGRATION_FILE="$1"
|
||||
if [ ! -f "$MIGRATION_FILE" ]; then
|
||||
echo -e "${RED}Error: Migration file '$MIGRATION_FILE' not found${NC}"
|
||||
exit 1
|
||||
fi
|
||||
echo -e "${YELLOW}Running migration: $MIGRATION_FILE${NC}"
|
||||
psql -h "$DB_HOST" -p "$DB_PORT" -U "$DB_USER" -d "$DB_NAME" -f "$MIGRATION_FILE"
|
||||
echo -e "${GREEN}✓ Migration completed successfully${NC}"
|
||||
else
|
||||
# Run all migrations in order
|
||||
echo -e "${YELLOW}Running all migrations in ./migrations/...${NC}"
|
||||
for migration in $(ls -1 migrations/*.sql 2>/dev/null | sort); do
|
||||
echo -e "${YELLOW}Running: $migration${NC}"
|
||||
psql -h "$DB_HOST" -p "$DB_PORT" -U "$DB_USER" -d "$DB_NAME" -f "$migration"
|
||||
echo -e "${GREEN}✓ Completed: $migration${NC}"
|
||||
echo ""
|
||||
done
|
||||
echo -e "${GREEN}=== All migrations completed ===${NC}"
|
||||
fi
|
||||
|
||||
unset PGPASSWORD
|
||||
Reference in New Issue
Block a user