1- import { createClient } from '@libsql/client'
1+ import { type Client , createClient } from '@libsql/client'
22import fs from 'node:fs/promises'
33import path from 'node:path'
44import dotenv from 'dotenv'
5+ import { get_client } from './shared'
56
67dotenv . config ( { quiet : true } )
78
8- const DB_VISITS_URL = process . env . DB_VISITS_URL
9- const DB_VISITS_AUTH_TOKEN = process . env . DB_VISITS_AUTH_TOKEN
9+ await migrate ( )
1010
11- if ( ! DB_VISITS_URL ) throw new Error ( 'No DB_VISITS_URL found' )
11+ /**
12+ * Creates the tables, indexes, triggers, and views.
13+ */
14+ async function migrate ( ) {
15+ await create_visits_table ( )
1216
13- const db_visits = createClient ( {
14- url : DB_VISITS_URL ,
15- authToken : DB_VISITS_AUTH_TOKEN ,
16- } )
17+ const db = get_client ( )
18+ await db . execute ( 'PRAGMA foreign_keys = ON' )
19+ await create_migrations_table ( db )
20+ await apply_migrations ( db )
21+ }
22+
23+ /**
24+ * Creates the visits table. It is stored in a separate database.
25+ */
26+ async function create_visits_table ( ) {
27+ const DB_VISITS_URL = process . env . DB_VISITS_URL
28+ const DB_VISITS_AUTH_TOKEN = process . env . DB_VISITS_AUTH_TOKEN
29+
30+ if ( ! DB_VISITS_URL ) throw new Error ( 'No DB_VISITS_URL found' )
31+
32+ const db_visits = createClient ( {
33+ url : DB_VISITS_URL ,
34+ authToken : DB_VISITS_AUTH_TOKEN ,
35+ } )
1736
18- await db_visits . execute ( `
37+ await db_visits . execute ( `
1938 CREATE TABLE IF NOT EXISTS visits (
2039 id INTEGER PRIMARY KEY,
2140 created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP,
@@ -25,63 +44,63 @@ await db_visits.execute(`
2544 )
2645` )
2746
28- console . info ( 'Created visits table' )
29-
30- const DB_URL = process . env . DB_URL
31- const DB_AUTH_TOKEN = process . env . DB_AUTH_TOKEN
32-
33- if ( ! DB_URL ) throw new Error ( 'No DB_URL found' )
34-
35- const db = createClient ( {
36- url : DB_URL ,
37- authToken : DB_AUTH_TOKEN ,
38- } )
39-
40- await db . execute ( 'PRAGMA foreign_keys = ON' )
47+ console . info ( 'Created visits table' )
48+ }
4149
42- await db . execute ( `
43- CREATE TABLE IF NOT EXISTS migrations (
44- file TEXT PRIMARY KEY,
45- applied_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP
46- )
47- ` )
50+ /**
51+ * Creates the migration table that records
52+ * which migrations have already been applied.
53+ */
54+ async function create_migrations_table ( db : Client ) {
55+ await db . execute ( `
56+ CREATE TABLE IF NOT EXISTS migrations (
57+ file TEXT PRIMARY KEY,
58+ applied_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP
59+ )
60+ ` )
61+ }
4862
49- const { rows } = await db . execute ( 'SELECT file FROM migrations' )
50- const applied_migrations = new Set < string > ( rows . map ( ( row ) => row . file ) as string [ ] )
63+ /**
64+ * Applies all migrations that have not been applied yet.
65+ */
66+ async function apply_migrations ( db : Client ) {
67+ const { rows } = await db . execute ( 'SELECT file FROM migrations' )
68+ const applied_migrations = new Set < string > ( rows . map ( ( row ) => row . file ) as string [ ] )
5169
52- const migrations_folder = path . join ( process . cwd ( ) , 'database' , 'migrations' )
53- const unsorted_files = await fs . readdir ( migrations_folder , 'utf8' )
54- const files = unsorted_files . filter ( ( f ) => f . endsWith ( '.sql' ) ) . sort ( )
70+ const migrations_folder = path . join ( process . cwd ( ) , 'database' , 'migrations' )
71+ const unsorted_files = await fs . readdir ( migrations_folder , 'utf8' )
72+ const files = unsorted_files . filter ( ( f ) => f . endsWith ( '.sql' ) ) . sort ( )
5573
56- const invalid_file = files . find ( ( file ) => ! file . match ( / ^ \d { 3 } _ / ) )
57- if ( invalid_file ) throw new Error ( `Invalid file name: ${ invalid_file } ` )
74+ const invalid_file = files . find ( ( file ) => ! file . match ( / ^ \d { 3 } _ / ) )
75+ if ( invalid_file ) throw new Error ( `Invalid file name: ${ invalid_file } ` )
5876
59- const all_done = files . every ( ( file ) => applied_migrations . has ( file ) )
77+ const all_done = files . every ( ( file ) => applied_migrations . has ( file ) )
6078
61- if ( all_done ) {
62- console . info ( 'No migrations need to be applied' )
63- process . exit ( 0 )
64- }
79+ if ( all_done ) {
80+ console . info ( 'No migrations need to be applied' )
81+ process . exit ( 0 )
82+ }
6583
66- for ( const file of files ) {
67- if ( applied_migrations . has ( file ) ) continue
68-
69- const sql = await fs . readFile ( path . join ( migrations_folder , file ) , 'utf8' )
70-
71- const tx = await db . transaction ( )
72- try {
73- await tx . executeMultiple ( sql )
74- await tx . execute ( {
75- sql : 'INSERT INTO migrations (file) VALUES (?)' ,
76- args : [ file ] ,
77- } )
78- await tx . commit ( )
79- console . info ( `Applied migration: ${ file } ` )
80- } catch ( err ) {
81- console . error ( `Failed migration: ${ file } ` , err )
82- await tx . rollback ( )
83- process . exit ( 1 )
84+ for ( const file of files ) {
85+ if ( applied_migrations . has ( file ) ) continue
86+
87+ const sql = await fs . readFile ( path . join ( migrations_folder , file ) , 'utf8' )
88+
89+ const tx = await db . transaction ( )
90+ try {
91+ await tx . executeMultiple ( sql )
92+ await tx . execute ( {
93+ sql : 'INSERT INTO migrations (file) VALUES (?)' ,
94+ args : [ file ] ,
95+ } )
96+ await tx . commit ( )
97+ console . info ( `Applied migration: ${ file } ` )
98+ } catch ( err ) {
99+ console . error ( `Failed migration: ${ file } ` , err )
100+ await tx . rollback ( )
101+ process . exit ( 1 )
102+ }
84103 }
85- }
86104
87- console . info ( 'Applied all migrations' )
105+ console . info ( 'Applied all migrations' )
106+ }
0 commit comments