Skip to content

Commit 8ec992a

Browse files
Add custom migration implementation
1 parent e2fc642 commit 8ec992a

12 files changed

+358
-0
lines changed

src/invidious.cr

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ require "./invidious/channels/*"
3434
require "./invidious/user/*"
3535
require "./invidious/routes/**"
3636
require "./invidious/jobs/**"
37+
require "./invidious/migrations/*"
3738

3839
CONFIG = Config.load
3940
HMAC_KEY = CONFIG.hmac_key || Random::Secure.hex(32)
@@ -111,6 +112,8 @@ end
111112
OUTPUT = CONFIG.output.upcase == "STDOUT" ? STDOUT : File.open(CONFIG.output, mode: "a")
112113
LOGGER = Invidious::LogHandler.new(OUTPUT, CONFIG.log_level)
113114

115+
# Run migrations
116+
Invidious::Migrator.new(PG_DB).migrate
114117
# Check table integrity
115118
Invidious::Database.check_integrity(CONFIG)
116119

src/invidious/migration.cr

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
abstract class Invidious::Migration
2+
macro inherited
3+
Invidious::Migrator.migrations << self
4+
end
5+
6+
@@version : Int64?
7+
8+
def self.version(version : Int32 | Int64)
9+
@@version = version.to_i64
10+
end
11+
12+
getter? completed = false
13+
14+
def initialize(@db : DB::Database)
15+
end
16+
17+
abstract def up(conn : DB::Connection)
18+
19+
def migrate
20+
# migrator already ignores completed migrations
21+
# but this is an extra check to make sure a migration doesn't run twice
22+
return if completed?
23+
24+
@db.transaction do |txn|
25+
up(txn.connection)
26+
track(txn.connection)
27+
@completed = true
28+
end
29+
end
30+
31+
def version : Int64
32+
@@version.not_nil!
33+
end
34+
35+
private def track(conn : DB::Connection)
36+
conn.exec("INSERT INTO #{Invidious::Migrator::MIGRATIONS_TABLE}(version) VALUES ($1)", version)
37+
end
38+
end
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
module Invidious::Migrations
2+
class CreateChannelsTable < Migration
3+
version 0
4+
5+
def up(conn : DB::Connection)
6+
conn.exec <<-SQL
7+
CREATE TABLE IF NOT EXISTS public.channels
8+
(
9+
id text NOT NULL,
10+
author text,
11+
updated timestamp with time zone,
12+
deleted boolean,
13+
subscribed timestamp with time zone,
14+
CONSTRAINT channels_id_key UNIQUE (id)
15+
);
16+
SQL
17+
18+
conn.exec <<-SQL
19+
GRANT ALL ON TABLE public.channels TO current_user;
20+
SQL
21+
22+
conn.exec <<-SQL
23+
CREATE INDEX IF NOT EXISTS channels_id_idx
24+
ON public.channels
25+
USING btree
26+
(id COLLATE pg_catalog."default");
27+
SQL
28+
end
29+
end
30+
end
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
module Invidious::Migrations
2+
class CreateVideosTable < Migration
3+
version 1
4+
5+
def up(conn : DB::Connection)
6+
conn.exec <<-SQL
7+
CREATE UNLOGGED TABLE IF NOT EXISTS public.videos
8+
(
9+
id text NOT NULL,
10+
info text,
11+
updated timestamp with time zone,
12+
CONSTRAINT videos_pkey PRIMARY KEY (id)
13+
);
14+
SQL
15+
16+
conn.exec <<-SQL
17+
GRANT ALL ON TABLE public.videos TO current_user;
18+
SQL
19+
20+
conn.exec <<-SQL
21+
CREATE UNIQUE INDEX IF NOT EXISTS id_idx
22+
ON public.videos
23+
USING btree
24+
(id COLLATE pg_catalog."default");
25+
SQL
26+
end
27+
end
28+
end
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
module Invidious::Migrations
2+
class CreateChannelVideosTable < Migration
3+
version 2
4+
5+
def up(conn : DB::Connection)
6+
conn.exec <<-SQL
7+
CREATE TABLE IF NOT EXISTS public.channel_videos
8+
(
9+
id text NOT NULL,
10+
title text,
11+
published timestamp with time zone,
12+
updated timestamp with time zone,
13+
ucid text,
14+
author text,
15+
length_seconds integer,
16+
live_now boolean,
17+
premiere_timestamp timestamp with time zone,
18+
views bigint,
19+
CONSTRAINT channel_videos_id_key UNIQUE (id)
20+
);
21+
SQL
22+
23+
conn.exec <<-SQL
24+
GRANT ALL ON TABLE public.channel_videos TO current_user;
25+
SQL
26+
27+
conn.exec <<-SQL
28+
CREATE INDEX IF NOT EXISTS channel_videos_ucid_idx
29+
ON public.channel_videos
30+
USING btree
31+
(ucid COLLATE pg_catalog."default");
32+
SQL
33+
end
34+
end
35+
end
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
module Invidious::Migrations
2+
class CreateUsersTable < Migration
3+
version 3
4+
5+
def up(conn : DB::Connection)
6+
conn.exec <<-SQL
7+
CREATE TABLE IF NOT EXISTS public.users
8+
(
9+
updated timestamp with time zone,
10+
notifications text[],
11+
subscriptions text[],
12+
email text NOT NULL,
13+
preferences text,
14+
password text,
15+
token text,
16+
watched text[],
17+
feed_needs_update boolean,
18+
CONSTRAINT users_email_key UNIQUE (email)
19+
);
20+
SQL
21+
22+
conn.exec <<-SQL
23+
GRANT ALL ON TABLE public.users TO current_user;
24+
SQL
25+
26+
conn.exec <<-SQL
27+
CREATE UNIQUE INDEX IF NOT EXISTS email_unique_idx
28+
ON public.users
29+
USING btree
30+
(lower(email) COLLATE pg_catalog."default");
31+
SQL
32+
end
33+
end
34+
end
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
module Invidious::Migrations
2+
class CreateSessionIdsTable < Migration
3+
version 4
4+
5+
def up(conn : DB::Connection)
6+
conn.exec <<-SQL
7+
CREATE TABLE IF NOT EXISTS public.session_ids
8+
(
9+
id text NOT NULL,
10+
email text,
11+
issued timestamp with time zone,
12+
CONSTRAINT session_ids_pkey PRIMARY KEY (id)
13+
);
14+
SQL
15+
16+
conn.exec <<-SQL
17+
GRANT ALL ON TABLE public.session_ids TO current_user;
18+
SQL
19+
20+
conn.exec <<-SQL
21+
CREATE INDEX IF NOT EXISTS session_ids_id_idx
22+
ON public.session_ids
23+
USING btree
24+
(id COLLATE pg_catalog."default");
25+
SQL
26+
end
27+
end
28+
end
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
module Invidious::Migrations
2+
class CreateNoncesTable < Migration
3+
version 5
4+
5+
def up(conn : DB::Connection)
6+
conn.exec <<-SQL
7+
CREATE TABLE IF NOT EXISTS public.nonces
8+
(
9+
nonce text,
10+
expire timestamp with time zone,
11+
CONSTRAINT nonces_id_key UNIQUE (nonce)
12+
);
13+
SQL
14+
15+
conn.exec <<-SQL
16+
GRANT ALL ON TABLE public.nonces TO current_user;
17+
SQL
18+
19+
conn.exec <<-SQL
20+
CREATE INDEX IF NOT EXISTS nonces_nonce_idx
21+
ON public.nonces
22+
USING btree
23+
(nonce COLLATE pg_catalog."default");
24+
SQL
25+
end
26+
end
27+
end
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
module Invidious::Migrations
2+
class CreateAnnotationsTable < Migration
3+
version 6
4+
5+
def up(conn : DB::Connection)
6+
conn.exec <<-SQL
7+
CREATE TABLE IF NOT EXISTS public.annotations
8+
(
9+
id text NOT NULL,
10+
annotations xml,
11+
CONSTRAINT annotations_id_key UNIQUE (id)
12+
);
13+
SQL
14+
15+
conn.exec <<-SQL
16+
GRANT ALL ON TABLE public.annotations TO current_user;
17+
SQL
18+
end
19+
end
20+
end
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
module Invidious::Migrations
2+
class CreatePlaylistsTable < Migration
3+
version 7
4+
5+
def up(conn : DB::Connection)
6+
conn.exec <<-SQL
7+
DO
8+
$$
9+
BEGIN
10+
IF NOT EXISTS (SELECT *
11+
FROM pg_type typ
12+
INNER JOIN pg_namespace nsp ON nsp.oid = typ.typnamespace
13+
WHERE nsp.nspname = 'public'
14+
AND typ.typname = 'privacy') THEN
15+
CREATE TYPE public.privacy AS ENUM
16+
(
17+
'Public',
18+
'Unlisted',
19+
'Private'
20+
);
21+
END IF;
22+
END;
23+
$$
24+
LANGUAGE plpgsql;
25+
SQL
26+
27+
conn.exec <<-SQL
28+
CREATE TABLE IF NOT EXISTS public.playlists
29+
(
30+
title text,
31+
id text primary key,
32+
author text,
33+
description text,
34+
video_count integer,
35+
created timestamptz,
36+
updated timestamptz,
37+
privacy privacy,
38+
index int8[]
39+
);
40+
SQL
41+
42+
conn.exec <<-SQL
43+
GRANT ALL ON public.playlists TO current_user;
44+
SQL
45+
end
46+
end
47+
end

0 commit comments

Comments
 (0)