From edfd6b226ab49a8e9a4b14360f1e106360786363 Mon Sep 17 00:00:00 2001 From: Malcolm-Wander Date: Thu, 25 Jun 2026 00:45:57 +0100 Subject: [PATCH] Updated project files Changes made --- apps/backend/notifications/package.json | 4 ++ apps/backend/notifications/src/db.ts | 31 ++++++++++++ apps/backend/notifications/src/index.ts | 4 ++ .../src/models/NotificationPreferences.ts | 47 +++++++++++++++++++ .../src/notificationPreferences.ts | 39 +++++++++++++++ .../004_notification_preferences.sql | 7 +++ 6 files changed, 132 insertions(+) create mode 100644 apps/backend/notifications/src/db.ts create mode 100644 apps/backend/notifications/src/models/NotificationPreferences.ts create mode 100644 apps/backend/notifications/src/notificationPreferences.ts create mode 100644 database/migrations/004_notification_preferences.sql diff --git a/apps/backend/notifications/package.json b/apps/backend/notifications/package.json index 475f3b8..f4285b1 100644 --- a/apps/backend/notifications/package.json +++ b/apps/backend/notifications/package.json @@ -18,11 +18,15 @@ "@delego/utils": "workspace:*", "ioredis": "^5.11.1", "jsonwebtoken": "^9.0.3", + "pg": "^8.21.0", + "pg-hstore": "^2.3.4", + "sequelize": "^6.37.8", "ws": "^8.21.0" }, "devDependencies": { "@types/jsonwebtoken": "^9.0.10", "@types/node": "^22.0.0", + "@types/validator": "^13.15.10", "@types/ws": "^8.18.1", "tsx": "^4.19.0", "typescript": "^5.7.0" diff --git a/apps/backend/notifications/src/db.ts b/apps/backend/notifications/src/db.ts new file mode 100644 index 0000000..4bcf106 --- /dev/null +++ b/apps/backend/notifications/src/db.ts @@ -0,0 +1,31 @@ +import { Sequelize } from "sequelize"; +import { createLogger } from "@delego/utils"; + +const log = createLogger("notifications:db", process.env.LOG_LEVEL ?? "info"); + +const databaseUrl = process.env.DATABASE_URL ?? "postgresql://delego:delego@localhost:5432/delego"; + +export const sequelize = new Sequelize(databaseUrl, { + dialect: "postgres", + logging: (msg) => log.debug(msg), + pool: { + min: Number(process.env.DATABASE_POOL_MIN ?? 2), + max: Number(process.env.DATABASE_POOL_MAX ?? 10), + acquire: 30000, + idle: 10000, + }, + define: { + underscored: true, + timestamps: true, + }, +}); + +export async function connectDb(): Promise { + try { + await sequelize.authenticate(); + log.info("Database connection established successfully in notifications service."); + } catch (err) { + log.error("Unable to connect to the database in notifications service", err instanceof Error ? { error: err.message } : { error: String(err) }); + throw err; + } +} diff --git a/apps/backend/notifications/src/index.ts b/apps/backend/notifications/src/index.ts index 0eb9b14..509968d 100644 --- a/apps/backend/notifications/src/index.ts +++ b/apps/backend/notifications/src/index.ts @@ -4,6 +4,7 @@ import { createLogger } from "@delego/utils"; import { startHttpServer } from "@delego/utils"; import { initWebSocketServer } from "./websocket.js"; +import { connectDb } from "./db.js"; const SERVICE_NAME = "notifications"; const DEFAULT_PORT = 3015; @@ -15,6 +16,9 @@ const port = Number(process.env.NOTIFICATIONS_PORT ?? DEFAULT_PORT); log.info("Starting service", { port, nodeEnv }); +// Connect to database first +await connectDb(); + const server = startHttpServer({ port, serviceName: SERVICE_NAME, diff --git a/apps/backend/notifications/src/models/NotificationPreferences.ts b/apps/backend/notifications/src/models/NotificationPreferences.ts new file mode 100644 index 0000000..6485fe9 --- /dev/null +++ b/apps/backend/notifications/src/models/NotificationPreferences.ts @@ -0,0 +1,47 @@ +import { Model, DataTypes } from "sequelize"; +import { sequelize } from "../db.js"; + +export interface NotificationPreferencesAttributes { + userId: string; + emailEnabled: boolean; + pushEnabled: boolean; + createdAt: Date; + updatedAt: Date; +} + +export class NotificationPreferences extends Model implements NotificationPreferencesAttributes { + public userId!: string; + public emailEnabled!: boolean; + public pushEnabled!: boolean; + public readonly createdAt!: Date; + public readonly updatedAt!: Date; +} + +NotificationPreferences.init( + { + userId: { + type: DataTypes.UUID, + primaryKey: true, + field: "user_id", + }, + emailEnabled: { + type: DataTypes.BOOLEAN, + allowNull: false, + defaultValue: true, + field: "email_enabled", + }, + pushEnabled: { + type: DataTypes.BOOLEAN, + allowNull: false, + defaultValue: true, + field: "push_enabled", + }, + }, + { + sequelize, + modelName: "NotificationPreferences", + tableName: "notification_preferences", + timestamps: true, + underscored: true, + } +); diff --git a/apps/backend/notifications/src/notificationPreferences.ts b/apps/backend/notifications/src/notificationPreferences.ts new file mode 100644 index 0000000..c9f6468 --- /dev/null +++ b/apps/backend/notifications/src/notificationPreferences.ts @@ -0,0 +1,39 @@ +import { NotificationPreferences } from "./models/NotificationPreferences.js"; + +export interface ChannelPreferences { + emailEnabled: boolean; + pushEnabled: boolean; +} + +export async function getUserPreferences(userId: string): Promise { + const preferences = await NotificationPreferences.findByPk(userId); + if (preferences) { + return { + emailEnabled: preferences.emailEnabled, + pushEnabled: preferences.pushEnabled, + }; + } + // Default to both enabled if no preferences found + return { + emailEnabled: true, + pushEnabled: true, + }; +} + +export async function upsertUserPreferences( + userId: string, + preferences: Partial +): Promise { + const [record] = await NotificationPreferences.upsert( + { + userId, + emailEnabled: preferences.emailEnabled ?? true, + pushEnabled: preferences.pushEnabled ?? true, + }, + { returning: true } + ); + return { + emailEnabled: record.emailEnabled, + pushEnabled: record.pushEnabled, + }; +} diff --git a/database/migrations/004_notification_preferences.sql b/database/migrations/004_notification_preferences.sql new file mode 100644 index 0000000..f1befd3 --- /dev/null +++ b/database/migrations/004_notification_preferences.sql @@ -0,0 +1,7 @@ +CREATE TABLE IF NOT EXISTS notification_preferences ( + user_id UUID PRIMARY KEY REFERENCES users(id) ON DELETE CASCADE, + email_enabled BOOLEAN NOT NULL DEFAULT TRUE, + push_enabled BOOLEAN NOT NULL DEFAULT TRUE, + created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), + updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW() +);