diff --git a/backend/app.ts b/backend/app.ts index 44b67e0..20088ec 100644 --- a/backend/app.ts +++ b/backend/app.ts @@ -1,30 +1,29 @@ +import { toNodeHandler } from "better-auth/node"; import express from "express"; import morgan from "morgan"; import { createLogger } from "./src/logger/logger.controller.js"; -import { connectProdSql } from "./src/prodSql/prodSqlConnection.controller.js"; import { setupRoutes } from "./src/routeHandler.routes.js"; +import { auth } from "./src/utils/auth.utils.js"; +import { lstCors } from "./src/utils/cors.utils.js"; -const port = Number(process.env.PORT); - -const startApp = async () => { +const createApp = async () => { const log = createLogger({ module: "system", subModule: "main start" }); const app = express(); let baseUrl = "/"; - // global env that run only in dev if (process.env.NODE_ENV?.trim() !== "production") { app.use(morgan("tiny")); baseUrl = "/lst"; } - // start the connection to the prod sql server - connectProdSql(); - + app.set("trust proxy", true); + app.all(`${baseUrl}api/auth/*splat`, toNodeHandler(auth)); + app.use(express.json()); + app.use(lstCors()); setupRoutes(baseUrl, app); - app.listen(port, () => { - log.info(`Listening on port ${port}`); - }); + log.info("Express app created"); + return { app, baseUrl }; }; -startApp(); +export default createApp; diff --git a/backend/server.ts b/backend/server.ts new file mode 100644 index 0000000..b21d1da --- /dev/null +++ b/backend/server.ts @@ -0,0 +1,22 @@ +import os from "node:os"; +import createApp from "./app.js"; +import { createLogger } from "./src/logger/logger.controller.js"; +import { connectProdSql } from "./src/prodSql/prodSqlConnection.controller.js"; + +const port = Number(process.env.PORT); + +const start = async () => { + const log = createLogger({ module: "system", subModule: "main start" }); + + connectProdSql(); + + const { app, baseUrl } = await createApp(); + + app.listen(port, async () => { + log.info( + `Listening on http://${os.hostname()}:${port}${baseUrl}, logging in ${process.env.LOG_LEVEL}`, + ); + }); +}; + +start(); diff --git a/backend/src/db/schema/auth.schema.ts b/backend/src/db/schema/auth.schema.ts new file mode 100644 index 0000000..4c67171 --- /dev/null +++ b/backend/src/db/schema/auth.schema.ts @@ -0,0 +1,157 @@ +import { relations } from "drizzle-orm"; +import { + boolean, + index, + integer, + pgTable, + text, + timestamp, +} from "drizzle-orm/pg-core"; + +export const user = pgTable("user", { + id: text("id").primaryKey(), + name: text("name").notNull(), + email: text("email").notNull().unique(), + emailVerified: boolean("email_verified").default(false).notNull(), + image: text("image"), + createdAt: timestamp("created_at").defaultNow().notNull(), + updatedAt: timestamp("updated_at") + .defaultNow() + .$onUpdate(() => /* @__PURE__ */ new Date()) + .notNull(), + role: text("role"), + banned: boolean("banned").default(false), + banReason: text("ban_reason"), + banExpires: timestamp("ban_expires"), + username: text("username").unique(), + displayUsername: text("display_username"), + lastLogin: timestamp("last_login").defaultNow(), +}); + +export const session = pgTable( + "session", + { + id: text("id").primaryKey(), + expiresAt: timestamp("expires_at").notNull(), + token: text("token").notNull().unique(), + createdAt: timestamp("created_at").defaultNow().notNull(), + updatedAt: timestamp("updated_at") + .$onUpdate(() => /* @__PURE__ */ new Date()) + .notNull(), + ipAddress: text("ip_address"), + userAgent: text("user_agent"), + userId: text("user_id") + .notNull() + .references(() => user.id, { onDelete: "cascade" }), + impersonatedBy: text("impersonated_by"), + }, + (table) => [index("session_userId_idx").on(table.userId)], +); + +export const account = pgTable( + "account", + { + id: text("id").primaryKey(), + accountId: text("account_id").notNull(), + providerId: text("provider_id").notNull(), + userId: text("user_id") + .notNull() + .references(() => user.id, { onDelete: "cascade" }), + accessToken: text("access_token"), + refreshToken: text("refresh_token"), + idToken: text("id_token"), + accessTokenExpiresAt: timestamp("access_token_expires_at"), + refreshTokenExpiresAt: timestamp("refresh_token_expires_at"), + scope: text("scope"), + password: text("password"), + createdAt: timestamp("created_at").defaultNow().notNull(), + updatedAt: timestamp("updated_at") + .$onUpdate(() => /* @__PURE__ */ new Date()) + .notNull(), + }, + (table) => [index("account_userId_idx").on(table.userId)], +); + +export const verification = pgTable( + "verification", + { + id: text("id").primaryKey(), + identifier: text("identifier").notNull(), + value: text("value").notNull(), + expiresAt: timestamp("expires_at").notNull(), + createdAt: timestamp("created_at").defaultNow().notNull(), + updatedAt: timestamp("updated_at") + .defaultNow() + .$onUpdate(() => /* @__PURE__ */ new Date()) + .notNull(), + }, + (table) => [index("verification_identifier_idx").on(table.identifier)], +); + +export const jwks = pgTable("jwks", { + id: text("id").primaryKey(), + publicKey: text("public_key").notNull(), + privateKey: text("private_key").notNull(), + createdAt: timestamp("created_at").notNull(), + expiresAt: timestamp("expires_at"), +}); + +export const apikey = pgTable( + "apikey", + { + id: text("id").primaryKey(), + name: text("name"), + start: text("start"), + prefix: text("prefix"), + key: text("key").notNull(), + userId: text("user_id") + .notNull() + .references(() => user.id, { onDelete: "cascade" }), + refillInterval: integer("refill_interval"), + refillAmount: integer("refill_amount"), + lastRefillAt: timestamp("last_refill_at"), + enabled: boolean("enabled").default(true), + rateLimitEnabled: boolean("rate_limit_enabled").default(true), + rateLimitTimeWindow: integer("rate_limit_time_window").default(86400000), + rateLimitMax: integer("rate_limit_max").default(10), + requestCount: integer("request_count").default(0), + remaining: integer("remaining"), + lastRequest: timestamp("last_request"), + expiresAt: timestamp("expires_at"), + createdAt: timestamp("created_at").notNull(), + updatedAt: timestamp("updated_at").notNull(), + permissions: text("permissions"), + metadata: text("metadata"), + }, + (table) => [ + index("apikey_key_idx").on(table.key), + index("apikey_userId_idx").on(table.userId), + ], +); + +export const userRelations = relations(user, ({ many }) => ({ + sessions: many(session), + accounts: many(account), + apikeys: many(apikey), +})); + +export const sessionRelations = relations(session, ({ one }) => ({ + user: one(user, { + fields: [session.userId], + references: [user.id], + }), +})); + +export const accountRelations = relations(account, ({ one }) => ({ + user: one(user, { + fields: [account.userId], + references: [user.id], + }), +})); + +export const apikeyRelations = relations(apikey, ({ one }) => ({ + user: one(user, { + fields: [apikey.userId], + references: [user.id], + }), +})); diff --git a/backend/src/db/schema/logs.schema.ts b/backend/src/db/schema/logs.schema.ts index 0f1b0dd..beaefa6 100644 --- a/backend/src/db/schema/logs.schema.ts +++ b/backend/src/db/schema/logs.schema.ts @@ -18,7 +18,7 @@ export const logs = pgTable("logs", { stack: jsonb("stack").default([]), checked: boolean("checked").default(false), hostname: text("hostname"), - createdAt: timestamp("createdAt").defaultNow(), + createdAt: timestamp("created_at").defaultNow(), }); export const logSchema = createSelectSchema(logs); diff --git a/backend/src/utils/auth.utils.ts b/backend/src/utils/auth.utils.ts new file mode 100644 index 0000000..f5f0a0e --- /dev/null +++ b/backend/src/utils/auth.utils.ts @@ -0,0 +1,87 @@ +import { betterAuth, type User } from "better-auth"; +import { drizzleAdapter } from "better-auth/adapters/drizzle"; +import { admin, apiKey, jwt, username } from "better-auth/plugins"; +import { eq } from "drizzle-orm"; +import { db } from "../db/db.controller.js"; +import * as rawSchema from "../db/schema/auth.schema.js"; +import { allowedOrigins } from "./cors.utils.js"; +import { sendEmail } from "./sendEmail.utils.js"; + +export const schema = { + user: rawSchema.user, + session: rawSchema.session, + account: rawSchema.account, + verification: rawSchema.verification, + jwks: rawSchema.jwks, + apiKey: rawSchema.apikey, // 🔑 rename to apiKey +}; + +export const auth = betterAuth({ + appName: "lst", + baseURL: process.env.URL, + database: drizzleAdapter(db, { + provider: "pg", + }), + plugins: [ + jwt({ jwt: { expirationTime: "1h" } }), + apiKey(), + admin(), + username(), + ], + trustedOrigins: allowedOrigins, + // email or username and password. + emailAndPassword: { + enabled: true, + minPasswordLength: 8, // optional config + resetPasswordTokenExpirySeconds: process.env.RESET_EXPIRY_SECONDS, // time in seconds + sendResetPassword: async ({ user, token }) => { + const frontendUrl = `${process.env.BETTER_AUTH_URL}/lst/app/user/resetpassword?token=${token}`; + const expiryMinutes = Math.floor( + parseInt(process.env.RESET_EXPIRY_SECONDS ?? "3600", 10) / 60, + ); + const expiryText = + expiryMinutes >= 60 + ? `${expiryMinutes / 60} hour${expiryMinutes === 60 ? "" : "s"}` + : `${expiryMinutes} minutes`; + const emailData = { + email: user.email, + subject: "LST- Forgot password request", + template: "forgotPassword", + context: { + username: user.name, + email: user.email, + url: frontendUrl, + expiry: expiryText, + }, + }; + await sendEmail(emailData); + }, + // onPasswordReset: async ({ user }, request) => { + // // your logic here + // console.log(`Password for user ${user.email} has been reset.`); + // }, + }, + session: { + expiresIn: 60 * 60, + updateAge: 60 * 5, + freshAge: 60 * 2, + cookieCache: { + enabled: true, + maxAge: 5 * 60, + }, + }, + cookie: { + path: "/lst/app", + sameSite: "lax", + secure: false, + httpOnly: true, + }, + events: { + async onSignInSuccess({ user }: { user: User }) { + await db + .update(rawSchema.user) + .set({ lastLogin: new Date() }) + .where(eq(schema.user.id, user.id)); + }, + }, +}); diff --git a/backend/src/utils/cors.utils.ts b/backend/src/utils/cors.utils.ts new file mode 100644 index 0000000..f2d8467 --- /dev/null +++ b/backend/src/utils/cors.utils.ts @@ -0,0 +1,53 @@ +import cors from "cors"; + +export const allowedOrigins = [ + "*.alpla.net", + "http://localhost:4173", + "http://localhost:4200", + "http://localhost:3000", + "http://localhost:3001", + "http://localhost:4000", + "http://localhost:4001", + "http://localhost:5500", + `${process.env.URL}`, +]; +export const lstCors = () => { + return cors({ + origin: (origin, callback) => { + //console.log("CORS request from origin:", origin); + + if (!origin) return callback(null, true); // allow same-site or direct calls + + try { + const hostname = new URL(origin).hostname; // strips protocol/port + //console.log("Parsed hostname:", hostname); + + if (allowedOrigins.includes(origin)) { + return callback(null, true); + } + + // Now this works for *.alpla.net + if (hostname.endsWith(".alpla.net") || hostname === "alpla.net") { + return callback(null, true); + } + } catch (_) { + //console.error("Invalid Origin header:", origin); + } + + return callback(new Error(`Not allowed by CORS: ${origin}`)); + }, + methods: ["GET", "POST", "PUT", "DELETE", "PATCH", "OPTIONS"], + credentials: true, + exposedHeaders: ["set-cookie", "expo-protocol-version", "expo-sfv-version"], + allowedHeaders: [ + "Content-Type", + "Authorization", + "X-Requested-With", + "XMLHttpRequest", + "expo-runtime-version", + "expo-platform", + "expo-channel-name", + "*", + ], + }); +}; diff --git a/backend/src/utils/mailViews/forgotPassword.hbs b/backend/src/utils/mailViews/forgotPassword.hbs new file mode 100644 index 0000000..922e306 --- /dev/null +++ b/backend/src/utils/mailViews/forgotPassword.hbs @@ -0,0 +1,69 @@ + + + + + Password Reset + {{> styles}} + + + +
+

Password Reset Request

+

Hi {{username}},

+ +

We received a request to reset the password for your account ({{email}}). If this was you, you can create a new password by clicking the button below:

+ +

+ Reset Your Password +

+ +

If the button above doesn’t work, copy and paste the following link into your web browser:

+

{{url}}

+ +

Note: This link will expire in {{expiry}} for your security.

+ +

If you did not request a password reset, you can safely ignore this email — your account will remain secure.

+ +

Thanks,
The LST Team

+ + +
+ + \ No newline at end of file diff --git a/backend/src/utils/mailViews/styles.hbs b/backend/src/utils/mailViews/styles.hbs new file mode 100644 index 0000000..09e1061 --- /dev/null +++ b/backend/src/utils/mailViews/styles.hbs @@ -0,0 +1,6 @@ + \ No newline at end of file diff --git a/backend/src/utils/mailViews/testEmail.hbs b/backend/src/utils/mailViews/testEmail.hbs new file mode 100644 index 0000000..8c697b3 --- /dev/null +++ b/backend/src/utils/mailViews/testEmail.hbs @@ -0,0 +1,33 @@ + + + + + Order Summary + {{> styles}} + {{!-- --}} + + +

{{name}}, This is a test that the emailing actually works as intended.

+

All, + This is an example of the test email +

+ {{!-- + + + + + + + + + {{#each items}} + + + + + + {{/each}} + +
Item NameQuantityPrice
{{name}}{{quantity}}{{price}}
--}} + + \ No newline at end of file diff --git a/backend/src/utils/returnHelper.utils.ts b/backend/src/utils/returnHelper.utils.ts index bc49833..726f303 100644 --- a/backend/src/utils/returnHelper.utils.ts +++ b/backend/src/utils/returnHelper.utils.ts @@ -3,8 +3,8 @@ import { createLogger } from "../logger/logger.controller.js"; interface Data { success: boolean; - module: "system" | "ocp" | "routes" | "datamart"; - subModule: "db" | "labeling" | "printer" | "prodSql" | "query"; + module: "system" | "ocp" | "routes" | "datamart" | "utils"; + subModule: "db" | "labeling" | "printer" | "prodSql" | "query" | "sendmail"; level: "info" | "error" | "debug" | "fatal"; message: string; data?: unknown[]; @@ -47,6 +47,9 @@ export const returnFunc = (data: Data) => { return { success: data.success, message: data.message, + level: data.level, + module: data.module, + subModule: data.subModule, data: data.data || [], }; }; diff --git a/backend/src/utils/sendEmail.utils.ts b/backend/src/utils/sendEmail.utils.ts new file mode 100644 index 0000000..45393e7 --- /dev/null +++ b/backend/src/utils/sendEmail.utils.ts @@ -0,0 +1,125 @@ +import type { Transporter } from "nodemailer"; +import nodemailer from "nodemailer"; +import type Mail from "nodemailer/lib/mailer/index.js"; +import type { Address } from "nodemailer/lib/mailer/index.js"; +import type SMTPTransport from "nodemailer/lib/smtp-transport/index.js"; +import hbs from "nodemailer-express-handlebars"; +import os from "os"; +import path from "path"; +import { fileURLToPath } from "url"; +import { promisify } from "util"; +import { returnFunc } from "./returnHelper.utils.js"; +import { tryCatch } from "./trycatch.utils.js"; + +interface HandlebarsMailOptions extends Mail.Options { + template: string; + context: Record; +} + +interface EmailData { + email: string; + subject: string; + template: string; + context: Record; +} + +export const sendEmail = async (data: EmailData) => { + let transporter: Transporter; + let fromEmail: string | Address; + + if (os.hostname().includes("OLP")) { + transporter = nodemailer.createTransport({ + service: "gmail", + auth: { + user: process.env.EMAIL_USER, + pass: process.env.EMAIL_PASSWORD, // The 16-character App Password + }, + //debug: true, + }); + + // update the from email + fromEmail = process.env.EMAIL_USER as string; + + // update the from email + fromEmail = `noreply@alpla.com`; + } else { + //create the servers smtp config + let host = `${os.hostname().replace("VMS006", "")}-smtp.alpla.net`; + + if (os.hostname().includes("VMS036")) { + host = "USMCD1-smtp.alpla.net"; + } + + transporter = nodemailer.createTransport({ + host: host, + port: 25, + rejectUnauthorized: false, + //secure: false, + // auth: { + // user: "alplaprod", + // pass: "obelix", + // }, + debug: true, + } as SMTPTransport.Options); + + // update the from email + fromEmail = `noreply@alpla.com`; + } + + // create the handlebars view + const viewPath = path.resolve( + path.dirname(fileURLToPath(import.meta.url)), + "./mailViews", + ); + + const handlebarOptions = { + viewEngine: { + extname: ".hbs", + defaultLayout: "", + partialsDir: viewPath, + }, + viewPath: viewPath, + extName: ".hbs", + }; + + transporter.use("compile", hbs(handlebarOptions)); + + const mailOptions: HandlebarsMailOptions = { + from: fromEmail, + to: data.email, + subject: data.subject, + //text: "You will have a reset token here and only have 30min to click the link before it expires.", + //html: emailTemplate("Blake's Test", "This is an example with css"), + template: data.template, // Name of the Handlebars template (e.g., 'welcome.hbs') + context: data.context, + }; + + // now verify and send the email + const sendMailPromise = promisify(transporter.sendMail).bind(transporter); + + const { data: mail, error } = await tryCatch(sendMailPromise(mailOptions)); + + if (mail) { + return returnFunc({ + success: true, + level: "info", + module: "utils", + subModule: "sendmail", + message: `Email was sent to: ${data.email}`, + data: [], + notify: false, + }); + } + + if (error) { + return returnFunc({ + success: false, + level: "error", + module: "utils", + subModule: "sendmail", + message: `Error sending Email to : ${data.email}`, + data: [{ error: error }], + notify: false, + }); + } +}; diff --git a/migrations/0001_puzzling_enchantress.sql b/migrations/0001_puzzling_enchantress.sql new file mode 100644 index 0000000..734cb61 --- /dev/null +++ b/migrations/0001_puzzling_enchantress.sql @@ -0,0 +1,15 @@ +CREATE TABLE "datamart" ( + "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL, + "name" text, + "description" text NOT NULL, + "query" text, + "version" integer NOT NULL, + "active" boolean DEFAULT true, + "checked" text DEFAULT '', + "add_date" timestamp DEFAULT now(), + "add_user" text DEFAULT 'lst-system', + "upd_date" text DEFAULT 'lst-system' +); +--> statement-breakpoint +ALTER TABLE "logs" ADD COLUMN "created_at" timestamp DEFAULT now();--> statement-breakpoint +ALTER TABLE "logs" DROP COLUMN "createdAt"; \ No newline at end of file diff --git a/migrations/0002_pink_nightshade.sql b/migrations/0002_pink_nightshade.sql new file mode 100644 index 0000000..682e8eb --- /dev/null +++ b/migrations/0002_pink_nightshade.sql @@ -0,0 +1,53 @@ +CREATE TABLE "account" ( + "id" text PRIMARY KEY NOT NULL, + "account_id" text NOT NULL, + "provider_id" text NOT NULL, + "user_id" text NOT NULL, + "access_token" text, + "refresh_token" text, + "id_token" text, + "access_token_expires_at" timestamp, + "refresh_token_expires_at" timestamp, + "scope" text, + "password" text, + "created_at" timestamp DEFAULT now() NOT NULL, + "updated_at" timestamp NOT NULL +); +--> statement-breakpoint +CREATE TABLE "session" ( + "id" text PRIMARY KEY NOT NULL, + "expires_at" timestamp NOT NULL, + "token" text NOT NULL, + "created_at" timestamp DEFAULT now() NOT NULL, + "updated_at" timestamp NOT NULL, + "ip_address" text, + "user_agent" text, + "user_id" text NOT NULL, + CONSTRAINT "session_token_unique" UNIQUE("token") +); +--> statement-breakpoint +CREATE TABLE "user" ( + "id" text PRIMARY KEY NOT NULL, + "name" text NOT NULL, + "email" text NOT NULL, + "email_verified" boolean DEFAULT false NOT NULL, + "image" text, + "created_at" timestamp DEFAULT now() NOT NULL, + "updated_at" timestamp DEFAULT now() NOT NULL, + CONSTRAINT "user_email_unique" UNIQUE("email") +); +--> statement-breakpoint +CREATE TABLE "verification" ( + "id" text PRIMARY KEY NOT NULL, + "identifier" text NOT NULL, + "value" text NOT NULL, + "expires_at" timestamp NOT NULL, + "created_at" timestamp DEFAULT now() NOT NULL, + "updated_at" timestamp DEFAULT now() NOT NULL +); +--> statement-breakpoint +ALTER TABLE "account" ADD CONSTRAINT "account_user_id_user_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."user"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "session" ADD CONSTRAINT "session_user_id_user_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."user"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +CREATE INDEX "account_userId_idx" ON "account" USING btree ("user_id");--> statement-breakpoint +CREATE INDEX "session_userId_idx" ON "session" USING btree ("user_id");--> statement-breakpoint +CREATE INDEX "verification_identifier_idx" ON "verification" USING btree ("identifier"); \ No newline at end of file diff --git a/migrations/0003_orange_vision.sql b/migrations/0003_orange_vision.sql new file mode 100644 index 0000000..6747b17 --- /dev/null +++ b/migrations/0003_orange_vision.sql @@ -0,0 +1,44 @@ +CREATE TABLE "apikey" ( + "id" text PRIMARY KEY NOT NULL, + "name" text, + "start" text, + "prefix" text, + "key" text NOT NULL, + "user_id" text NOT NULL, + "refill_interval" integer, + "refill_amount" integer, + "last_refill_at" timestamp, + "enabled" boolean DEFAULT true, + "rate_limit_enabled" boolean DEFAULT true, + "rate_limit_time_window" integer DEFAULT 86400000, + "rate_limit_max" integer DEFAULT 10, + "request_count" integer DEFAULT 0, + "remaining" integer, + "last_request" timestamp, + "expires_at" timestamp, + "created_at" timestamp NOT NULL, + "updated_at" timestamp NOT NULL, + "permissions" text, + "metadata" text +); +--> statement-breakpoint +CREATE TABLE "jwks" ( + "id" text PRIMARY KEY NOT NULL, + "public_key" text NOT NULL, + "private_key" text NOT NULL, + "created_at" timestamp NOT NULL, + "expires_at" timestamp +); +--> statement-breakpoint +ALTER TABLE "session" ADD COLUMN "impersonated_by" text;--> statement-breakpoint +ALTER TABLE "user" ADD COLUMN "role" text;--> statement-breakpoint +ALTER TABLE "user" ADD COLUMN "banned" boolean DEFAULT false;--> statement-breakpoint +ALTER TABLE "user" ADD COLUMN "ban_reason" text;--> statement-breakpoint +ALTER TABLE "user" ADD COLUMN "ban_expires" timestamp;--> statement-breakpoint +ALTER TABLE "user" ADD COLUMN "username" text;--> statement-breakpoint +ALTER TABLE "user" ADD COLUMN "display_username" text;--> statement-breakpoint +ALTER TABLE "user" ADD COLUMN "last_login" timestamp DEFAULT now();--> statement-breakpoint +ALTER TABLE "apikey" ADD CONSTRAINT "apikey_user_id_user_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."user"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +CREATE INDEX "apikey_key_idx" ON "apikey" USING btree ("key");--> statement-breakpoint +CREATE INDEX "apikey_userId_idx" ON "apikey" USING btree ("user_id");--> statement-breakpoint +ALTER TABLE "user" ADD CONSTRAINT "user_username_unique" UNIQUE("username"); \ No newline at end of file diff --git a/migrations/meta/0001_snapshot.json b/migrations/meta/0001_snapshot.json new file mode 100644 index 0000000..3a77feb --- /dev/null +++ b/migrations/meta/0001_snapshot.json @@ -0,0 +1,169 @@ +{ + "id": "dd230ac4-0bd9-4df4-b1da-4d97ac4cf7c4", + "prevId": "a42ab1d2-29ee-43ca-bcad-7b14c545172e", + "version": "7", + "dialect": "postgresql", + "tables": { + "public.datamart": { + "name": "datamart", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "query": { + "name": "query", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "version": { + "name": "version", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "active": { + "name": "active", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": true + }, + "checked": { + "name": "checked", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "''" + }, + "add_date": { + "name": "add_date", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "add_user": { + "name": "add_user", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'lst-system'" + }, + "upd_date": { + "name": "upd_date", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'lst-system'" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.logs": { + "name": "logs", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "level": { + "name": "level", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "module": { + "name": "module", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "subModule": { + "name": "subModule", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "message": { + "name": "message", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "stack": { + "name": "stack", + "type": "jsonb", + "primaryKey": false, + "notNull": false, + "default": "'[]'::jsonb" + }, + "checked": { + "name": "checked", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "hostname": { + "name": "hostname", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + } + }, + "enums": {}, + "schemas": {}, + "sequences": {}, + "roles": {}, + "policies": {}, + "views": {}, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} \ No newline at end of file diff --git a/migrations/meta/0002_snapshot.json b/migrations/meta/0002_snapshot.json new file mode 100644 index 0000000..03d18d6 --- /dev/null +++ b/migrations/meta/0002_snapshot.json @@ -0,0 +1,524 @@ +{ + "id": "27d37c0d-a6f0-4acc-9a9b-3d50096e56dd", + "prevId": "dd230ac4-0bd9-4df4-b1da-4d97ac4cf7c4", + "version": "7", + "dialect": "postgresql", + "tables": { + "public.account": { + "name": "account", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "account_id": { + "name": "account_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "provider_id": { + "name": "provider_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "access_token": { + "name": "access_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "refresh_token": { + "name": "refresh_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "id_token": { + "name": "id_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "access_token_expires_at": { + "name": "access_token_expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "refresh_token_expires_at": { + "name": "refresh_token_expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "scope": { + "name": "scope", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "password": { + "name": "password", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "account_userId_idx": { + "name": "account_userId_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "account_user_id_user_id_fk": { + "name": "account_user_id_user_id_fk", + "tableFrom": "account", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.session": { + "name": "session", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "token": { + "name": "token", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "ip_address": { + "name": "ip_address", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "user_agent": { + "name": "user_agent", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "session_userId_idx": { + "name": "session_userId_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "session_user_id_user_id_fk": { + "name": "session_user_id_user_id_fk", + "tableFrom": "session", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "session_token_unique": { + "name": "session_token_unique", + "nullsNotDistinct": false, + "columns": [ + "token" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.user": { + "name": "user", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "email_verified": { + "name": "email_verified", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "image": { + "name": "image", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "user_email_unique": { + "name": "user_email_unique", + "nullsNotDistinct": false, + "columns": [ + "email" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.verification": { + "name": "verification", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "identifier": { + "name": "identifier", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "value": { + "name": "value", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "verification_identifier_idx": { + "name": "verification_identifier_idx", + "columns": [ + { + "expression": "identifier", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.datamart": { + "name": "datamart", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "query": { + "name": "query", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "version": { + "name": "version", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "active": { + "name": "active", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": true + }, + "checked": { + "name": "checked", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "''" + }, + "add_date": { + "name": "add_date", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "add_user": { + "name": "add_user", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'lst-system'" + }, + "upd_date": { + "name": "upd_date", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'lst-system'" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.logs": { + "name": "logs", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "level": { + "name": "level", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "module": { + "name": "module", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "subModule": { + "name": "subModule", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "message": { + "name": "message", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "stack": { + "name": "stack", + "type": "jsonb", + "primaryKey": false, + "notNull": false, + "default": "'[]'::jsonb" + }, + "checked": { + "name": "checked", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "hostname": { + "name": "hostname", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + } + }, + "enums": {}, + "schemas": {}, + "sequences": {}, + "roles": {}, + "policies": {}, + "views": {}, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} \ No newline at end of file diff --git a/migrations/meta/0003_snapshot.json b/migrations/meta/0003_snapshot.json new file mode 100644 index 0000000..449038f --- /dev/null +++ b/migrations/meta/0003_snapshot.json @@ -0,0 +1,813 @@ +{ + "id": "f82bd918-f5f0-4c05-ba25-8a0e97891f5f", + "prevId": "27d37c0d-a6f0-4acc-9a9b-3d50096e56dd", + "version": "7", + "dialect": "postgresql", + "tables": { + "public.account": { + "name": "account", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "account_id": { + "name": "account_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "provider_id": { + "name": "provider_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "access_token": { + "name": "access_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "refresh_token": { + "name": "refresh_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "id_token": { + "name": "id_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "access_token_expires_at": { + "name": "access_token_expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "refresh_token_expires_at": { + "name": "refresh_token_expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "scope": { + "name": "scope", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "password": { + "name": "password", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "account_userId_idx": { + "name": "account_userId_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "account_user_id_user_id_fk": { + "name": "account_user_id_user_id_fk", + "tableFrom": "account", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.apikey": { + "name": "apikey", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "start": { + "name": "start", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "prefix": { + "name": "prefix", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "key": { + "name": "key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "refill_interval": { + "name": "refill_interval", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "refill_amount": { + "name": "refill_amount", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "last_refill_at": { + "name": "last_refill_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "enabled": { + "name": "enabled", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": true + }, + "rate_limit_enabled": { + "name": "rate_limit_enabled", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": true + }, + "rate_limit_time_window": { + "name": "rate_limit_time_window", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 86400000 + }, + "rate_limit_max": { + "name": "rate_limit_max", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 10 + }, + "request_count": { + "name": "request_count", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 0 + }, + "remaining": { + "name": "remaining", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "last_request": { + "name": "last_request", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "permissions": { + "name": "permissions", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "metadata": { + "name": "metadata", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "apikey_key_idx": { + "name": "apikey_key_idx", + "columns": [ + { + "expression": "key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "apikey_userId_idx": { + "name": "apikey_userId_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "apikey_user_id_user_id_fk": { + "name": "apikey_user_id_user_id_fk", + "tableFrom": "apikey", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.jwks": { + "name": "jwks", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "public_key": { + "name": "public_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "private_key": { + "name": "private_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.session": { + "name": "session", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "token": { + "name": "token", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "ip_address": { + "name": "ip_address", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "user_agent": { + "name": "user_agent", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "impersonated_by": { + "name": "impersonated_by", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "session_userId_idx": { + "name": "session_userId_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "session_user_id_user_id_fk": { + "name": "session_user_id_user_id_fk", + "tableFrom": "session", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "session_token_unique": { + "name": "session_token_unique", + "nullsNotDistinct": false, + "columns": [ + "token" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.user": { + "name": "user", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "email_verified": { + "name": "email_verified", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "image": { + "name": "image", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "role": { + "name": "role", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "banned": { + "name": "banned", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "ban_reason": { + "name": "ban_reason", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "ban_expires": { + "name": "ban_expires", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "username": { + "name": "username", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "display_username": { + "name": "display_username", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "last_login": { + "name": "last_login", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "user_email_unique": { + "name": "user_email_unique", + "nullsNotDistinct": false, + "columns": [ + "email" + ] + }, + "user_username_unique": { + "name": "user_username_unique", + "nullsNotDistinct": false, + "columns": [ + "username" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.verification": { + "name": "verification", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "identifier": { + "name": "identifier", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "value": { + "name": "value", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "verification_identifier_idx": { + "name": "verification_identifier_idx", + "columns": [ + { + "expression": "identifier", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.datamart": { + "name": "datamart", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "query": { + "name": "query", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "version": { + "name": "version", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "active": { + "name": "active", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": true + }, + "checked": { + "name": "checked", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "''" + }, + "add_date": { + "name": "add_date", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "add_user": { + "name": "add_user", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'lst-system'" + }, + "upd_date": { + "name": "upd_date", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'lst-system'" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.logs": { + "name": "logs", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "level": { + "name": "level", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "module": { + "name": "module", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "subModule": { + "name": "subModule", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "message": { + "name": "message", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "stack": { + "name": "stack", + "type": "jsonb", + "primaryKey": false, + "notNull": false, + "default": "'[]'::jsonb" + }, + "checked": { + "name": "checked", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "hostname": { + "name": "hostname", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + } + }, + "enums": {}, + "schemas": {}, + "sequences": {}, + "roles": {}, + "policies": {}, + "views": {}, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} \ No newline at end of file diff --git a/migrations/meta/_journal.json b/migrations/meta/_journal.json index 8cad11c..5e7f1df 100644 --- a/migrations/meta/_journal.json +++ b/migrations/meta/_journal.json @@ -8,6 +8,27 @@ "when": 1766759965948, "tag": "0000_ordinary_justice", "breakpoints": true + }, + { + "idx": 1, + "version": "7", + "when": 1767015305152, + "tag": "0001_puzzling_enchantress", + "breakpoints": true + }, + { + "idx": 2, + "version": "7", + "when": 1767016655572, + "tag": "0002_pink_nightshade", + "breakpoints": true + }, + { + "idx": 3, + "version": "7", + "when": 1767020576353, + "tag": "0003_orange_vision", + "breakpoints": true } ] } \ No newline at end of file