11import 'package:logging/logging.dart' ;
22import 'package:postgres/postgres.dart' ;
3+ import 'package:ht_shared/ht_shared.dart' ;
34
45/// {@template database_seeding_service}
56/// A service responsible for initializing the database schema and seeding it
@@ -20,5 +21,120 @@ class DatabaseSeedingService {
2021 final Connection _connection;
2122 final Logger _log;
2223
23- // Methods for table creation and data seeding will be added here.
24+ /// Creates all necessary tables in the database if they do not already exist.
25+ ///
26+ /// This method executes a series of `CREATE TABLE IF NOT EXISTS` statements
27+ /// within a single transaction to ensure atomicity.
28+ Future <void > createTables () async {
29+ _log.info ('Starting database schema creation...' );
30+ try {
31+ await _connection.transaction ((ctx) async {
32+ _log.fine ('Creating "users" table...' );
33+ await ctx.execute ('''
34+ CREATE TABLE IF NOT EXISTS users (
35+ id TEXT PRIMARY KEY,
36+ email TEXT UNIQUE,
37+ roles JSONB NOT NULL,
38+ created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
39+ last_engagement_shown_at TIMESTAMPTZ
40+ );
41+ ''' );
42+
43+ _log.fine ('Creating "app_config" table...' );
44+ await ctx.execute ('''
45+ CREATE TABLE IF NOT EXISTS app_config (
46+ id TEXT PRIMARY KEY,
47+ user_preference_limits JSONB NOT NULL,
48+ created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
49+ updated_at TIMESTAMPTZ
50+ );
51+ ''' );
52+
53+ _log.fine ('Creating "categories" table...' );
54+ await ctx.execute ('''
55+ CREATE TABLE IF NOT EXISTS categories (
56+ id TEXT PRIMARY KEY,
57+ name TEXT NOT NULL UNIQUE,
58+ created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
59+ updated_at TIMESTAMPTZ
60+ );
61+ ''' );
62+
63+ _log.fine ('Creating "sources" table...' );
64+ await ctx.execute ('''
65+ CREATE TABLE IF NOT EXISTS sources (
66+ id TEXT PRIMARY KEY,
67+ name TEXT NOT NULL UNIQUE,
68+ created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
69+ updated_at TIMESTAMPTZ
70+ );
71+ ''' );
72+
73+ _log.fine ('Creating "countries" table...' );
74+ await ctx.execute ('''
75+ CREATE TABLE IF NOT EXISTS countries (
76+ id TEXT PRIMARY KEY,
77+ name TEXT NOT NULL UNIQUE,
78+ code TEXT NOT NULL UNIQUE,
79+ created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
80+ updated_at TIMESTAMPTZ
81+ );
82+ ''' );
83+
84+ _log.fine ('Creating "headlines" table...' );
85+ await ctx.execute ('''
86+ CREATE TABLE IF NOT EXISTS headlines (
87+ id TEXT PRIMARY KEY,
88+ title TEXT NOT NULL,
89+ source_id TEXT NOT NULL,
90+ category_id TEXT NOT NULL,
91+ image_url TEXT NOT NULL,
92+ url TEXT NOT NULL,
93+ published_at TIMESTAMPTZ NOT NULL,
94+ description TEXT,
95+ content TEXT,
96+ created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
97+ updated_at TIMESTAMPTZ
98+ );
99+ ''' );
100+
101+ _log.fine ('Creating "user_app_settings" table...' );
102+ await ctx.execute ('''
103+ CREATE TABLE IF NOT EXISTS user_app_settings (
104+ id TEXT PRIMARY KEY,
105+ user_id TEXT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
106+ display_settings JSONB NOT NULL,
107+ language JSONB NOT NULL,
108+ created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
109+ updated_at TIMESTAMPTZ
110+ );
111+ ''' );
112+
113+ _log.fine ('Creating "user_content_preferences" table...' );
114+ await ctx.execute ('''
115+ CREATE TABLE IF NOT EXISTS user_content_preferences (
116+ id TEXT PRIMARY KEY,
117+ user_id TEXT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
118+ followed_categories JSONB NOT NULL,
119+ followed_sources JSONB NOT NULL,
120+ followed_countries JSONB NOT NULL,
121+ saved_headlines JSONB NOT NULL,
122+ created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
123+ updated_at TIMESTAMPTZ
124+ );
125+ ''' );
126+ });
127+ _log.info ('Database schema creation completed successfully.' );
128+ } on Object catch (e, st) {
129+ _log.severe (
130+ 'An error occurred during database schema creation.' ,
131+ e,
132+ st,
133+ );
134+ // Propagate as a standard exception for the server to handle.
135+ throw OperationFailedException (
136+ 'Failed to initialize database schema: $e ' ,
137+ );
138+ }
139+ }
24140}
0 commit comments