feat(logger): setup logger with discord and db logging
This commit is contained in:
@@ -13,3 +13,6 @@ PROD_SERVER=usmcd1vms036
|
|||||||
PROD_PLANT_TOKEN=test3
|
PROD_PLANT_TOKEN=test3
|
||||||
PROD_USER=alplaprod
|
PROD_USER=alplaprod
|
||||||
PROD_PASSWORD=password
|
PROD_PASSWORD=password
|
||||||
|
|
||||||
|
# admin
|
||||||
|
WEBHOOK_URL
|
||||||
@@ -7,13 +7,22 @@ import path, { dirname, join } from "path";
|
|||||||
import { fileURLToPath } from "url";
|
import { fileURLToPath } from "url";
|
||||||
import { db } from "./pkg/db/db.js";
|
import { db } from "./pkg/db/db.js";
|
||||||
import { settings } from "./pkg/db/schema/settings.js";
|
import { settings } from "./pkg/db/schema/settings.js";
|
||||||
|
import { env } from "./pkg/utils/envValidator.js";
|
||||||
|
import { createLogger } from "./pkg/logger/logger.js";
|
||||||
|
|
||||||
const PORT = Number(process.env.VITE_PORT) || 4200;
|
const PORT = Number(env.VITE_PORT) || 4200;
|
||||||
|
|
||||||
const main = async () => {
|
const main = async () => {
|
||||||
|
//create the logger
|
||||||
|
const log = createLogger({ module: "system", subModule: "main start" });
|
||||||
|
|
||||||
// base path
|
// base path
|
||||||
let basePath: string = "";
|
let basePath: string = "";
|
||||||
if (process.env.NODE_ENV?.trim() !== "production") {
|
|
||||||
|
if (
|
||||||
|
process.env.NODE_ENV?.trim() !== "production" &&
|
||||||
|
!env.RUNNING_IN_DOCKER
|
||||||
|
) {
|
||||||
basePath = "/lst";
|
basePath = "/lst";
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -24,7 +33,11 @@ const main = async () => {
|
|||||||
try {
|
try {
|
||||||
const set = await db.select().from(settings);
|
const set = await db.select().from(settings);
|
||||||
|
|
||||||
console.log(set);
|
if (set.length === 0) {
|
||||||
|
return log.fatal(
|
||||||
|
"Seems like the DB is not setup yet the app will close now"
|
||||||
|
);
|
||||||
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error getting settings", error);
|
console.error("Error getting settings", error);
|
||||||
}
|
}
|
||||||
@@ -63,13 +76,16 @@ const main = async () => {
|
|||||||
|
|
||||||
// start the server up
|
// start the server up
|
||||||
server.listen(PORT, "0.0.0.0", () =>
|
server.listen(PORT, "0.0.0.0", () =>
|
||||||
console.log(
|
log.info(
|
||||||
`Server running in ${process.env.NODE_ENV}, on http://0.0.0.0:${PORT}${basePath}`
|
`Server running in ${
|
||||||
|
process.env.NODE_ENV ? process.env.NODE_ENV : "dev"
|
||||||
|
}, on http://0.0.0.0:${PORT}${basePath}`
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
main().catch((err) => {
|
main().catch((err) => {
|
||||||
console.error("Startup error:", err);
|
const log = createLogger({ module: "system", subModule: "main start" });
|
||||||
|
log.fatal("Startup error:", err);
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
});
|
});
|
||||||
|
|||||||
21
app/src/pkg/db/schema/logs.ts
Normal file
21
app/src/pkg/db/schema/logs.ts
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
import { boolean, pgTable, text, timestamp, uuid } from "drizzle-orm/pg-core";
|
||||||
|
import { createInsertSchema, createSelectSchema } from "drizzle-zod";
|
||||||
|
import { z } from "zod";
|
||||||
|
|
||||||
|
export const logs = pgTable("logs", {
|
||||||
|
log_id: uuid("log_id").defaultRandom().primaryKey(),
|
||||||
|
level: text("level"),
|
||||||
|
module: text("module").notNull(),
|
||||||
|
subModule: text("subModule"),
|
||||||
|
message: text("message").notNull(),
|
||||||
|
stack: text("stack"),
|
||||||
|
checked: boolean("checked").default(false),
|
||||||
|
hostname: text("hostname"),
|
||||||
|
createdAt: timestamp("createdAt").defaultNow(),
|
||||||
|
});
|
||||||
|
|
||||||
|
export const logSchema = createSelectSchema(logs);
|
||||||
|
export const newSettingSchema = createInsertSchema(logs);
|
||||||
|
|
||||||
|
export type Log = z.infer<typeof logSchema>;
|
||||||
|
export type NewLog = z.infer<typeof newSettingSchema>;
|
||||||
34
app/src/pkg/logger/dbTransport.ts
Normal file
34
app/src/pkg/logger/dbTransport.ts
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
import build from "pino-abstract-transport";
|
||||||
|
import { db } from "../db/db.js";
|
||||||
|
import { logs, type Log } from "../db/schema/logs.js";
|
||||||
|
|
||||||
|
const pinoLogLevels: any = {
|
||||||
|
10: "trace",
|
||||||
|
20: "debug",
|
||||||
|
30: "info",
|
||||||
|
40: "warn",
|
||||||
|
50: "error",
|
||||||
|
60: "fatal",
|
||||||
|
};
|
||||||
|
// Create a custom transport function
|
||||||
|
export default async function (log: Log) {
|
||||||
|
//const {username, service, level, msg, ...extra} = log;
|
||||||
|
try {
|
||||||
|
return build(async function (source) {
|
||||||
|
for await (let obj of source) {
|
||||||
|
// convert to the name to make it more easy to find later :P
|
||||||
|
const levelName = pinoLogLevels[obj.level] || "unknown";
|
||||||
|
await db.insert(logs).values({
|
||||||
|
level: levelName,
|
||||||
|
module: obj?.module.toLowerCase(),
|
||||||
|
subModule: obj?.subModule.toLowerCase(),
|
||||||
|
hostname: obj?.hostname.toLowerCase(),
|
||||||
|
message: obj.msg,
|
||||||
|
stack: obj?.stack,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} catch (err) {
|
||||||
|
console.error("Error inserting log into database:", err);
|
||||||
|
}
|
||||||
|
}
|
||||||
53
app/src/pkg/logger/logger.ts
Normal file
53
app/src/pkg/logger/logger.ts
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
import pino, { type Logger } from "pino";
|
||||||
|
import { env } from "../utils/envValidator.js";
|
||||||
|
|
||||||
|
export let logLevel = process.env.LOG_LEVEL || "info";
|
||||||
|
|
||||||
|
interface CustomLogger extends pino.Logger {
|
||||||
|
specialMonitor: pino.LogFn;
|
||||||
|
}
|
||||||
|
|
||||||
|
const transport = pino.transport({
|
||||||
|
targets: [
|
||||||
|
{
|
||||||
|
target: "pino-pretty",
|
||||||
|
options: {
|
||||||
|
colorize: true,
|
||||||
|
singleLine: true,
|
||||||
|
// customPrettifiers: {
|
||||||
|
// time: (time) => `🕰 ${time}`,
|
||||||
|
// },
|
||||||
|
destination: process.stdout.fd,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
target: "./dbTransport.js",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
target: "./notification.js",
|
||||||
|
},
|
||||||
|
// Only log to Go if LST_USE_GO=true
|
||||||
|
...(process.env.LST_USE_GO === "true"
|
||||||
|
? [
|
||||||
|
{
|
||||||
|
target: "./goTransport.js", // New transport for Go
|
||||||
|
},
|
||||||
|
]
|
||||||
|
: []),
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
export const rootLogger: Logger = pino(
|
||||||
|
{
|
||||||
|
level: logLevel,
|
||||||
|
redact: { paths: ["email", "password"], remove: true },
|
||||||
|
},
|
||||||
|
transport
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* factory to create child to log things for us
|
||||||
|
*/
|
||||||
|
export function createLogger(bindings: Record<string, unknown>): Logger {
|
||||||
|
return rootLogger.child(bindings);
|
||||||
|
}
|
||||||
87
app/src/pkg/logger/notification.ts
Normal file
87
app/src/pkg/logger/notification.ts
Normal file
@@ -0,0 +1,87 @@
|
|||||||
|
import build from "pino-abstract-transport";
|
||||||
|
import { db } from "../db/db.js";
|
||||||
|
import { logs, type Log } from "../db/schema/logs.js";
|
||||||
|
import { env } from "../utils/envValidator.js";
|
||||||
|
|
||||||
|
const pinoLogLevels: any = {
|
||||||
|
10: "trace",
|
||||||
|
20: "debug",
|
||||||
|
30: "info",
|
||||||
|
40: "warn",
|
||||||
|
50: "error",
|
||||||
|
60: "fatal",
|
||||||
|
};
|
||||||
|
// discord function
|
||||||
|
async function sendFatal(log: Log) {
|
||||||
|
const webhookUrl = process.env.WEBHOOK_URL!;
|
||||||
|
|
||||||
|
const payload = {
|
||||||
|
embeds: [
|
||||||
|
{
|
||||||
|
title: `🚨 ${env.PROD_PLANT_TOKEN}: encounter a critical error `,
|
||||||
|
description: `Where was the error: ${log.module}${
|
||||||
|
log.subModule ? `-${log.subModule}` : null
|
||||||
|
}`,
|
||||||
|
color: 0xff0000, // red
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
name: "Message",
|
||||||
|
value: log.message,
|
||||||
|
inline: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Hostname",
|
||||||
|
value: log.hostname,
|
||||||
|
inline: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Stack",
|
||||||
|
value:
|
||||||
|
"```" +
|
||||||
|
(log.stack?.slice(0, 1000) ?? "no stack") +
|
||||||
|
"```",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
footer: {
|
||||||
|
text: "LST Logger 💀",
|
||||||
|
},
|
||||||
|
timestamp: new Date().toISOString(),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
await fetch(webhookUrl, {
|
||||||
|
method: "POST",
|
||||||
|
headers: { "Content-Type": "application/json" },
|
||||||
|
body: JSON.stringify(payload),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export default async function (log: Log) {
|
||||||
|
//const {username, service, level, msg, ...extra} = log;
|
||||||
|
try {
|
||||||
|
return build(async function (source) {
|
||||||
|
for await (let obj of source) {
|
||||||
|
// convert to the name to make it more easy to find later :P
|
||||||
|
const levelName = pinoLogLevels[obj.level] || "unknown";
|
||||||
|
|
||||||
|
const newlog = {
|
||||||
|
level: levelName,
|
||||||
|
module: obj?.module.toLowerCase(),
|
||||||
|
subModule: obj?.subModule.toLowerCase(),
|
||||||
|
hostname: obj?.hostname.toLowerCase(),
|
||||||
|
message: obj.msg,
|
||||||
|
};
|
||||||
|
if (!process.env.WEBHOOK_URL) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (obj.level >= 60) {
|
||||||
|
sendFatal(newlog as Log);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} catch (err) {
|
||||||
|
console.error("Error inserting log into database:", err);
|
||||||
|
}
|
||||||
|
}
|
||||||
60
app/src/pkg/utils/checkHostNamePort.ts
Normal file
60
app/src/pkg/utils/checkHostNamePort.ts
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
import dns from "dns";
|
||||||
|
import net from "net";
|
||||||
|
|
||||||
|
export async function checkHostnamePort(
|
||||||
|
hostnamePort: string
|
||||||
|
): Promise<boolean> {
|
||||||
|
try {
|
||||||
|
// Split the input into hostname and port
|
||||||
|
const [hostname, port] = hostnamePort.split(":");
|
||||||
|
if (!hostname || !port) {
|
||||||
|
return false; // Invalid format
|
||||||
|
}
|
||||||
|
|
||||||
|
// Resolve the hostname to an IP address
|
||||||
|
const ip = (await resolveHostname(hostname)) as string;
|
||||||
|
|
||||||
|
// Check if the port is open
|
||||||
|
const portCheck = await checkPort(ip, parseInt(port, 10));
|
||||||
|
|
||||||
|
return true; // Hostname:port is reachable
|
||||||
|
} catch (err) {
|
||||||
|
return false; // Any error means the hostname:port is not reachable
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function checkPort(ip: string, port: number): Promise<boolean> {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const socket = new net.Socket();
|
||||||
|
|
||||||
|
socket.setTimeout(2000); // Set a timeout for the connection attempt
|
||||||
|
|
||||||
|
socket.on("connect", () => {
|
||||||
|
socket.destroy(); // Close the connection
|
||||||
|
resolve(true); // Port is open
|
||||||
|
});
|
||||||
|
|
||||||
|
socket.on("timeout", () => {
|
||||||
|
socket.destroy(); // Close the connection
|
||||||
|
reject(new Error("Connection timed out")); // Port is not reachable
|
||||||
|
});
|
||||||
|
|
||||||
|
socket.on("error", (err: any) => {
|
||||||
|
reject(new Error(`Unknown error: ${err}`)); // Handle non-Error types
|
||||||
|
});
|
||||||
|
|
||||||
|
socket.connect(port, ip);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function resolveHostname(hostname: string) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
dns.lookup(hostname, (err, address) => {
|
||||||
|
if (err) {
|
||||||
|
reject(err);
|
||||||
|
} else {
|
||||||
|
resolve(address);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
53
app/src/pkg/utils/envValidator.ts
Normal file
53
app/src/pkg/utils/envValidator.ts
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
import { z } from "zod";
|
||||||
|
import { createLogger } from "../logger/logger.js";
|
||||||
|
/**
|
||||||
|
* This is where we will validate the required ENV parapmeters.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
const envSchema = z.object({
|
||||||
|
//Server stuff
|
||||||
|
VITE_PORT: z.string().default("4200"),
|
||||||
|
LOG_LEVEL: z.string().default("info"),
|
||||||
|
// app db stuff
|
||||||
|
DATABASE_HOST: z.string(),
|
||||||
|
DATABASE_PORT: z.string(),
|
||||||
|
DATABASE_USER: z.string(),
|
||||||
|
DATABASE_PASSWORD: z.string(),
|
||||||
|
DATABASE_DB: z.string().default("lst"),
|
||||||
|
// prod server checks
|
||||||
|
PROD_SERVER: z.string(),
|
||||||
|
PROD_PLANT_TOKEN: z.string(),
|
||||||
|
PROD_USER: z.string(),
|
||||||
|
PROD_PASSWORD: z.string(),
|
||||||
|
//docker specifics
|
||||||
|
RUNNING_IN_DOCKER: z.boolean().default(false),
|
||||||
|
});
|
||||||
|
|
||||||
|
// use safeParse instead of parse
|
||||||
|
const parsed = envSchema.safeParse(process.env);
|
||||||
|
|
||||||
|
const log = createLogger({ module: "envValidation" });
|
||||||
|
|
||||||
|
if (!parsed.success) {
|
||||||
|
log.fatal(
|
||||||
|
`Environment validation failed: Missing: ${parsed.error.issues
|
||||||
|
.map((e) => {
|
||||||
|
return e.path[0];
|
||||||
|
})
|
||||||
|
.join(", ")}`
|
||||||
|
);
|
||||||
|
// 🔔 Send a notification (e.g., email, webhook, Slack)
|
||||||
|
// sendNotification(parsed.error.format());
|
||||||
|
|
||||||
|
// gracefully exit if in production
|
||||||
|
//process.exit(1);
|
||||||
|
throw Error(
|
||||||
|
`Environment validation failed: Missing: ${parsed.error.issues
|
||||||
|
.map((e) => {
|
||||||
|
return e.path[0];
|
||||||
|
})
|
||||||
|
.join(", ")}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export const env = parsed.data;
|
||||||
11
migrations/0001_solid_kronos.sql
Normal file
11
migrations/0001_solid_kronos.sql
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
CREATE TABLE "logs" (
|
||||||
|
"log_id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
|
||||||
|
"level" text,
|
||||||
|
"module" text NOT NULL,
|
||||||
|
"subModule" text,
|
||||||
|
"message" text NOT NULL,
|
||||||
|
"stack" text,
|
||||||
|
"checked" boolean DEFAULT false,
|
||||||
|
"hostname" text,
|
||||||
|
"createdAt" timestamp DEFAULT now()
|
||||||
|
);
|
||||||
191
migrations/meta/0001_snapshot.json
Normal file
191
migrations/meta/0001_snapshot.json
Normal file
@@ -0,0 +1,191 @@
|
|||||||
|
{
|
||||||
|
"id": "5eac3348-eeab-4210-b686-29e570f87911",
|
||||||
|
"prevId": "c97e112f-6742-4754-be09-0abd659f1eb5",
|
||||||
|
"version": "7",
|
||||||
|
"dialect": "postgresql",
|
||||||
|
"tables": {
|
||||||
|
"public.logs": {
|
||||||
|
"name": "logs",
|
||||||
|
"schema": "",
|
||||||
|
"columns": {
|
||||||
|
"log_id": {
|
||||||
|
"name": "log_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": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
"checked": {
|
||||||
|
"name": "checked",
|
||||||
|
"type": "boolean",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"default": false
|
||||||
|
},
|
||||||
|
"hostname": {
|
||||||
|
"name": "hostname",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
"createdAt": {
|
||||||
|
"name": "createdAt",
|
||||||
|
"type": "timestamp",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"default": "now()"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"indexes": {},
|
||||||
|
"foreignKeys": {},
|
||||||
|
"compositePrimaryKeys": {},
|
||||||
|
"uniqueConstraints": {},
|
||||||
|
"policies": {},
|
||||||
|
"checkConstraints": {},
|
||||||
|
"isRLSEnabled": false
|
||||||
|
},
|
||||||
|
"public.settings": {
|
||||||
|
"name": "settings",
|
||||||
|
"schema": "",
|
||||||
|
"columns": {
|
||||||
|
"settings_id": {
|
||||||
|
"name": "settings_id",
|
||||||
|
"type": "uuid",
|
||||||
|
"primaryKey": true,
|
||||||
|
"notNull": true,
|
||||||
|
"default": "gen_random_uuid()"
|
||||||
|
},
|
||||||
|
"name": {
|
||||||
|
"name": "name",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"value": {
|
||||||
|
"name": "value",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"description": {
|
||||||
|
"name": "description",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
"moduleName": {
|
||||||
|
"name": "moduleName",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
"active": {
|
||||||
|
"name": "active",
|
||||||
|
"type": "boolean",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"default": true
|
||||||
|
},
|
||||||
|
"roles": {
|
||||||
|
"name": "roles",
|
||||||
|
"type": "jsonb",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"default": "'[\"systemAdmin\"]'::jsonb"
|
||||||
|
},
|
||||||
|
"add_User": {
|
||||||
|
"name": "add_User",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"default": "'LST_System'"
|
||||||
|
},
|
||||||
|
"add_Date": {
|
||||||
|
"name": "add_Date",
|
||||||
|
"type": "timestamp",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"default": "now()"
|
||||||
|
},
|
||||||
|
"upd_User": {
|
||||||
|
"name": "upd_User",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"default": "'LST_System'"
|
||||||
|
},
|
||||||
|
"upd_date": {
|
||||||
|
"name": "upd_date",
|
||||||
|
"type": "timestamp",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"default": "now()"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"indexes": {
|
||||||
|
"name": {
|
||||||
|
"name": "name",
|
||||||
|
"columns": [
|
||||||
|
{
|
||||||
|
"expression": "name",
|
||||||
|
"isExpression": false,
|
||||||
|
"asc": true,
|
||||||
|
"nulls": "last"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"isUnique": true,
|
||||||
|
"concurrently": false,
|
||||||
|
"method": "btree",
|
||||||
|
"with": {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"foreignKeys": {},
|
||||||
|
"compositePrimaryKeys": {},
|
||||||
|
"uniqueConstraints": {},
|
||||||
|
"policies": {},
|
||||||
|
"checkConstraints": {},
|
||||||
|
"isRLSEnabled": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"enums": {},
|
||||||
|
"schemas": {},
|
||||||
|
"sequences": {},
|
||||||
|
"roles": {},
|
||||||
|
"policies": {},
|
||||||
|
"views": {},
|
||||||
|
"_meta": {
|
||||||
|
"columns": {},
|
||||||
|
"schemas": {},
|
||||||
|
"tables": {}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -8,6 +8,13 @@
|
|||||||
"when": 1756669145189,
|
"when": 1756669145189,
|
||||||
"tag": "0000_wonderful_eternals",
|
"tag": "0000_wonderful_eternals",
|
||||||
"breakpoints": true
|
"breakpoints": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idx": 1,
|
||||||
|
"version": "7",
|
||||||
|
"when": 1756693049476,
|
||||||
|
"tag": "0001_solid_kronos",
|
||||||
|
"breakpoints": true
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
1039
package-lock.json
generated
1039
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -38,14 +38,20 @@
|
|||||||
"@dotenvx/dotenvx": "^1.49.0",
|
"@dotenvx/dotenvx": "^1.49.0",
|
||||||
"drizzle-kit": "^0.31.4",
|
"drizzle-kit": "^0.31.4",
|
||||||
"drizzle-orm": "^0.44.5",
|
"drizzle-orm": "^0.44.5",
|
||||||
|
"drizzle-zod": "^0.8.3",
|
||||||
"express": "^5.1.0",
|
"express": "^5.1.0",
|
||||||
"morgan": "^1.10.1",
|
"morgan": "^1.10.1",
|
||||||
|
"mssql": "^11.0.1",
|
||||||
"pg": "^8.16.3",
|
"pg": "^8.16.3",
|
||||||
"postgres": "^3.4.7"
|
"pino": "^9.9.0",
|
||||||
|
"pino-pretty": "^13.1.1",
|
||||||
|
"postgres": "^3.4.7",
|
||||||
|
"zod": "^4.1.5"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/express": "^5.0.3",
|
"@types/express": "^5.0.3",
|
||||||
"@types/morgan": "^1.9.10",
|
"@types/morgan": "^1.9.10",
|
||||||
|
"@types/mssql": "^9.1.7",
|
||||||
"@types/node": "^24.3.0",
|
"@types/node": "^24.3.0",
|
||||||
"cz-conventional-changelog": "^3.3.0",
|
"cz-conventional-changelog": "^3.3.0",
|
||||||
"standard-version": "^9.5.0",
|
"standard-version": "^9.5.0",
|
||||||
|
|||||||
Reference in New Issue
Block a user