fix(contorller): env corrections on where to look for the file when running

This commit is contained in:
2025-09-30 19:54:10 -05:00
parent 18e57127e2
commit b84ecbf30c
26 changed files with 2637 additions and 27 deletions

1
.gitignore vendored
View File

@@ -194,3 +194,4 @@ controller/Dockerfile
controller/Dockerfile-ignore controller/Dockerfile-ignore
controller/docker-compose.yml controller/docker-compose.yml
controller/index.html controller/index.html
controller/index.html

View File

@@ -43,5 +43,6 @@
}, },
// Optional: Configure goimports instead of gofmt // Optional: Configure goimports instead of gofmt
"go.formatTool": "goimports" "go.formatTool": "goimports",
"cSpell.words": ["alpla", "alplamart", "alplaprod", "ppoo"]
} }

View File

@@ -0,0 +1,8 @@
meta {
name: admin
seq: 3
}
auth {
mode: inherit
}

View File

@@ -0,0 +1,27 @@
meta {
name: Add Server
type: http
seq: 3
}
post {
url: {{url}}/lst/api/admin/server
body: json
auth: inherit
}
body:json {
{
"name": "Test Server",
"serverDNS": "USMCD1VMS036",
"plantToken": "test3",
"ipAddress": "10.193.0.56",
"greatPlainsPlantCode": 0,
"lstServerPort": 4000,
"serverLoc": "E$\\LST"
}
}
settings {
encodeUrl: true
}

View File

@@ -0,0 +1,15 @@
meta {
name: Get Servers
type: http
seq: 1
}
get {
url: {{url}}/lst/api/admin/server
body: none
auth: inherit
}
settings {
encodeUrl: true
}

View File

@@ -0,0 +1,15 @@
meta {
name: Update Server
type: http
seq: 2
}
patch {
url: {{url}}/lst/api/admin/server
body: none
auth: inherit
}
settings {
encodeUrl: true
}

View File

@@ -0,0 +1,8 @@
meta {
name: server
seq: 2
}
auth {
mode: inherit
}

View File

@@ -4,6 +4,8 @@ import { requireAuth } from "../../pkg/middleware/authMiddleware.js";
//admin routes //admin routes
import users from "./routes/getUserRoles.js"; import users from "./routes/getUserRoles.js";
import grantRoles from "./routes/grantRole.js"; import grantRoles from "./routes/grantRole.js";
import servers from "./routes/servers/serverRoutes.js";
import { restrictToHosts } from "../../pkg/middleware/restrictToHosts.js";
export const setupAdminRoutes = (app: Express, basePath: string) => { export const setupAdminRoutes = (app: Express, basePath: string) => {
app.use( app.use(
@@ -16,4 +18,11 @@ export const setupAdminRoutes = (app: Express, basePath: string) => {
requireAuth("user", ["systemAdmin", "admin"]), // will pass bc system admin but this is just telling us we need this requireAuth("user", ["systemAdmin", "admin"]), // will pass bc system admin but this is just telling us we need this
grantRoles grantRoles
); );
app.use(
basePath + "/api/admin/server",
requireAuth("user", ["systemAdmin", "admin"]), // will pass bc system admin but this is just telling us we need this
restrictToHosts(["usmcd1vms036", "USMCD1VMS036"]), // what servers are allowed to see the server section
servers
);
}; };

View File

@@ -0,0 +1,74 @@
import { Router } from "express";
import type { Request, Response } from "express";
import {
insertServerDataSchema,
serverData,
} from "../../../../pkg/db/schema/servers.js";
import { db } from "../../../../pkg/db/db.js";
import { tryCatch } from "../../../../pkg/utils/tryCatch.js";
import type { DrizzleError } from "drizzle-orm";
import axios from "axios";
import { createLogger } from "../../../../pkg/logger/logger.js";
const router = Router();
router.post("/", async (req: Request, res: Response) => {
// when a new server is posted from localhost or 127.0.0.1 we also want to post it to the test server so we can see it from there
//res.status(200).json({ message: "Server added", ip: req.hostname });
const log = createLogger({ module: "admin", subModule: "add server" });
const parsed = insertServerDataSchema.safeParse(req.body);
if (!parsed.success) {
return res.status(400).json({ errors: parsed.error.flatten() });
}
const { data, error } = await tryCatch(
db
.insert(serverData)
.values(parsed.data)
//.onConflictDoNothing()
.returning({
name: serverData.name,
plantToken: serverData.plantToken,
})
);
if (error) {
const err: DrizzleError = error;
return res.status(400).json({
message: `Error adding the server`,
error: err.cause,
});
}
if (req.hostname === "localhost" && process.env.MAIN_SERVER) {
log.info({}, "Running in dev server about to add in a new server");
const { data, error } = await tryCatch(
axios.post(
`${process.env.MAIN_SERVER}/lst/api/admin/server`,
parsed.data,
{
headers: {
"Content-Type": "application/json",
Cookie: req.headers.cookie ?? "",
},
withCredentials: true,
}
)
);
if (error) {
log.error(
{ stack: error },
"There was an error adding the server to Main Server"
);
}
log.info({ stack: data }, "A new Server was just added to the server.");
}
return res
.status(201)
.json({ message: `Server ${data[0]?.name} added`, data: data });
});
export default router;

View File

@@ -0,0 +1,25 @@
import { Router } from "express";
import type { Request, Response } from "express";
import { tryCatch } from "../../../../pkg/utils/tryCatch.js";
import { db } from "../../../../pkg/db/db.js";
import { serverData } from "../../../../pkg/db/schema/servers.js";
import { and, asc, eq } from "drizzle-orm";
const router = Router();
router.get("/", async (req: Request, res: Response) => {
const { data, error } = await tryCatch(
db
.select()
.from(serverData)
.where(eq(serverData.active, true))
.orderBy(asc(serverData.name))
);
if (error) {
return res.status(400).json({ error: error });
}
res.status(200).json({ message: "Current Active server", data: data });
});
export default router;

View File

@@ -0,0 +1,12 @@
import { Router } from "express";
import addServer from "./addServer.js";
import getServers from "./getServers.js";
import updateServer from "./updateServer.js";
const router = Router();
router.get("/", getServers);
router.post("/", addServer);
router.patch("/", updateServer);
export default router;

View File

@@ -0,0 +1,11 @@
import { Router } from "express";
import type { Request, Response } from "express";
const router = Router();
router.patch("/", async (req: Request, res: Response) => {
// when a server is updated and is posted from localhost or 127.0.0.1 we also want to post it to the test server so we can see it from there, we want to insert with update on conflict.
res.status(200).json({ message: "Server added" });
});
export default router;

View File

@@ -27,7 +27,9 @@ router.get("/", async (req, res) => {
uptime: process.uptime(), uptime: process.uptime(),
build: statData[0]?.build, build: statData[0]?.build,
pendingUpdateFile: await checkBuildUpdate(["."]), pendingUpdateFile: await checkBuildUpdate(["."]),
lastUpdate: format(statData[0].lastUpdate!, "MM/dd/yyyy HH:mm"), lastUpdate: statData[0]?.lastUpdate
? format(statData[0].lastUpdate, "MM/dd/yyyy HH:mm")
: "",
}); });
}); });

View File

@@ -0,0 +1,49 @@
import {
boolean,
integer,
pgTable,
text,
timestamp,
uniqueIndex,
uuid,
} from "drizzle-orm/pg-core";
import { createInsertSchema, createSelectSchema } from "drizzle-zod";
import z from "zod";
export const serverData = pgTable(
"serverData",
{
server_id: uuid("server_id").defaultRandom().primaryKey(),
name: text("name").notNull(),
serverDNS: text("serverDNS").notNull(),
plantToken: text("plantToken").notNull(),
ipAddress: text("ipAddress").notNull(),
greatPlainsPlantCode: integer("greatPlainsPlantCode").notNull(),
streetAddress: text("streetAddress"),
cityState: text("cityState"),
zipcode: integer("zipcode"),
contactEmail: text("contactEmail"),
contactPhone: text("contactPhone"),
customerTiAcc: text("customerTiAcc"),
lstServerPort: integer("lstServerPort").notNull(),
active: boolean("active").default(true),
serverLoc: text("serverLoc").notNull(),
lastUpdated: timestamp("lastUpdated").defaultNow(),
isUpgrading: boolean("isUpgrading").default(false),
},
(table) => [
// uniqueIndex('emailUniqueIndex').on(sql`lower(${table.email})`),
uniqueIndex("plantToken").on(table.plantToken),
]
);
export const selectServerDataSchema = createSelectSchema(serverData);
export const insertServerDataSchema = createInsertSchema(serverData).extend({
contactEmail: z.email().optional(),
// zipcode: z
// .string()
// .regex(/^\d{5}$/)
// .optional(),
});

View File

@@ -0,0 +1,30 @@
import type { Request, Response, NextFunction } from "express";
/**
* Middleware to restrict access only to localhost or a whitelist of hosts.
*/
export function restrictToHosts(allowedHosts: string[] = []) {
return (req: Request, res: Response, next: NextFunction) => {
// `req.ip` gives the remote IP
const ip = req.ip!.replace("::ffff:", ""); // strip IPv6 prefix if present
// Express sets req.hostname from the Host header
const hostname = req.hostname;
const isLocal =
ip === "127.0.0.1" || ip === "::1" || hostname === "localhost";
const isAllowed =
isLocal ||
allowedHosts.includes(ip) ||
allowedHosts.includes(hostname);
if (!isAllowed) {
return res
.status(403)
.json({ error: "Access not allowed from this host" });
}
next();
};
}

View File

@@ -3,13 +3,15 @@ module lst.net
go 1.24.3 go 1.24.3
require ( require (
github.com/bwmarrin/discordgo v0.29.0
github.com/gin-gonic/gin v1.10.1 github.com/gin-gonic/gin v1.10.1
github.com/googollee/go-socket.io v1.7.0 github.com/googollee/go-socket.io v1.7.0
github.com/jackc/pgx/v5 v5.7.6
github.com/joho/godotenv v1.5.1 github.com/joho/godotenv v1.5.1
github.com/robfig/cron/v3 v3.0.0
) )
require ( require (
github.com/bwmarrin/discordgo v0.29.0 // indirect
github.com/bytedance/sonic v1.11.6 // indirect github.com/bytedance/sonic v1.11.6 // indirect
github.com/bytedance/sonic/loader v0.1.1 // indirect github.com/bytedance/sonic/loader v0.1.1 // indirect
github.com/cloudwego/base64x v0.1.4 // indirect github.com/cloudwego/base64x v0.1.4 // indirect
@@ -29,7 +31,6 @@ require (
github.com/hirochachacha/go-smb2 v1.1.0 // indirect github.com/hirochachacha/go-smb2 v1.1.0 // indirect
github.com/jackc/pgpassfile v1.0.0 // indirect github.com/jackc/pgpassfile v1.0.0 // indirect
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect
github.com/jackc/pgx/v5 v5.7.6 // indirect
github.com/json-iterator/go v1.1.12 // indirect github.com/json-iterator/go v1.1.12 // indirect
github.com/klauspost/cpuid/v2 v2.2.7 // indirect github.com/klauspost/cpuid/v2 v2.2.7 // indirect
github.com/leodido/go-urn v1.4.0 // indirect github.com/leodido/go-urn v1.4.0 // indirect
@@ -39,7 +40,6 @@ require (
github.com/pelletier/go-toml/v2 v2.2.2 // indirect github.com/pelletier/go-toml/v2 v2.2.2 // indirect
github.com/prometheus-community/pro-bing v0.7.0 // indirect github.com/prometheus-community/pro-bing v0.7.0 // indirect
github.com/robfig/cron v1.2.0 // indirect github.com/robfig/cron v1.2.0 // indirect
github.com/robfig/cron/v3 v3.0.0 // indirect
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
github.com/ugorji/go/codec v1.2.12 // indirect github.com/ugorji/go/codec v1.2.12 // indirect
golang.org/x/arch v0.8.0 // indirect golang.org/x/arch v0.8.0 // indirect

View File

@@ -180,22 +180,22 @@
{ {
name: "Houston", name: "Houston",
server: "USHOU1VMS006", server: "USHOU1VMS006",
location: "E$\\LST\\LST", location: "E$\\LST",
}, },
{ {
name: "Sherman", name: "Sherman",
server: "USSHE1VMS006", server: "USSHE1VMS006",
location: "E$\\LST\\LST", location: "E$\\LST",
}, },
{ {
name: "West Bend", name: "West Bend",
server: "USWEB1VMS006", server: "USWEB1VMS006",
location: "E$\\LST\\LST", location: "E$\\LST",
}, },
{ {
name: "Jerfferson City", name: "Jerfferson City",
server: "USJCI1VMS006", server: "USJCI1VMS006",
location: "E$\\LST\\LST", location: "E$\\LST",
}, },
]; ];
@@ -324,9 +324,7 @@
}); });
logMessage( logMessage(
"info", "info",
`Copying to ${ `Copying to ${srv.name} (location ${srv.location})`
srv.name
} (drive ${srv.drive.toUpperCase()})`
); );
}); });
@@ -364,15 +362,13 @@
currentServer = copyQueue.shift(); currentServer = copyQueue.shift();
logMessage( logMessage(
"info", "info",
`🚀 Copying to ${ `Copying to ${currentServer.name} (location ${currentServer.location})`
currentServer.name
} (drive ${currentServer.drive.toUpperCase()})`
); );
socket.emit("update", { socket.emit("update", {
action: "copy", action: "copy",
target: currentServer.name, target: currentServer.server,
drive: currentServer.drive, location: currentServer.location,
}); });
} }
@@ -381,7 +377,8 @@
// Only check queue progress if we're in All mode and have a currentServer // Only check queue progress if we're in All mode and have a currentServer
if (isRunningAll && currentServer) { if (isRunningAll && currentServer) {
const expected = `✅ Copy to ${currentServer.name} successful`; //const expected = `✅ Copy to ${currentServer.name} successful`;
const expected = "done";
if (msg.includes(expected)) { if (msg.includes(expected)) {
logMessage( logMessage(

45
controller/load_env.go Normal file
View File

@@ -0,0 +1,45 @@
package main
import (
"fmt"
"os"
"path/filepath"
"strings"
"github.com/joho/godotenv"
)
func loadEnv() {
exePath, _ := os.Executable()
exeDir := filepath.Dir(exePath)
// Normalize both to lowercase absolute paths for Windows safety
exePathLower := strings.ToLower(exePath)
tempDirLower := strings.ToLower(filepath.Clean(os.TempDir()))
// Heuristic: if exe lives *inside* the system temp dir → assume go run
if strings.HasPrefix(exePathLower, tempDirLower) {
fmt.Println("Detected go run loading ../.env")
err := godotenv.Load("../.env")
if err != nil {
fmt.Println("ERROR loading .env:", err)
} else {
fmt.Println(".env successfully loaded")
}
return
}
// Otherwise → normal compiled exe
fmt.Println("Detected compiled exe loading exeDir/.env")
if err := godotenv.Load(filepath.Join(exeDir, ".env")); err != nil {
fmt.Println("Didn't find exeDir/.env trying ../.env as fallback")
err := godotenv.Load("../.env")
if err != nil {
fmt.Println("ERROR loading .env:", err)
} else {
fmt.Println(".env successfully loaded")
}
}
}

View File

@@ -5,26 +5,18 @@ import (
"log" "log"
"net/http" "net/http"
"os" "os"
"path/filepath"
"strings" "strings"
"time" "time"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
socketio "github.com/googollee/go-socket.io" socketio "github.com/googollee/go-socket.io"
"github.com/joho/godotenv"
"lst.net/internal/bot" "lst.net/internal/bot"
"lst.net/pkg" "lst.net/pkg"
) )
func main() { func main() {
exePath, _ := os.Executable() loadEnv()
exeDir := filepath.Dir(exePath)
if err := godotenv.Load(filepath.Join(exeDir, ".env")); err != nil {
// fallback dev path
_ = godotenv.Load("../.env")
}
// gin stuff // gin stuff
basePath := "/api/controller" basePath := "/api/controller"

View File

@@ -0,0 +1,21 @@
CREATE TABLE "serverData" (
"server_id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
"name" text NOT NULL,
"serverDNS" text,
"plantToken" text,
"ipAddress" text,
"greatPlainsPlantCode" numeric,
"streetAddress" text,
"cityState" text,
"zipcode" numeric,
"contactEmail" text,
"contactPhone" text,
"customerTiAcc" text,
"lstServerPort" numeric,
"active" boolean DEFAULT true,
"serverLoc" text,
"lastUpdated" timestamp DEFAULT now(),
"isUpgrading" boolean DEFAULT false
);
--> statement-breakpoint
CREATE UNIQUE INDEX "plantToken" ON "serverData" USING btree ("plantToken");

View File

@@ -0,0 +1,9 @@
ALTER TABLE "serverData" ALTER COLUMN "serverDNS" SET NOT NULL;--> statement-breakpoint
ALTER TABLE "serverData" ALTER COLUMN "plantToken" SET NOT NULL;--> statement-breakpoint
ALTER TABLE "serverData" ALTER COLUMN "ipAddress" SET NOT NULL;--> statement-breakpoint
ALTER TABLE "serverData" ALTER COLUMN "greatPlainsPlantCode" SET DATA TYPE integer;--> statement-breakpoint
ALTER TABLE "serverData" ALTER COLUMN "greatPlainsPlantCode" SET NOT NULL;--> statement-breakpoint
ALTER TABLE "serverData" ALTER COLUMN "zipcode" SET DATA TYPE integer;--> statement-breakpoint
ALTER TABLE "serverData" ALTER COLUMN "lstServerPort" SET DATA TYPE integer;--> statement-breakpoint
ALTER TABLE "serverData" ALTER COLUMN "lstServerPort" SET NOT NULL;--> statement-breakpoint
ALTER TABLE "serverData" ALTER COLUMN "serverLoc" SET NOT NULL;

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -71,6 +71,20 @@
"when": 1758891252758, "when": 1758891252758,
"tag": "0009_cultured_slayback", "tag": "0009_cultured_slayback",
"breakpoints": true "breakpoints": true
},
{
"idx": 10,
"version": "7",
"when": 1759193957132,
"tag": "0010_watery_gravity",
"breakpoints": true
},
{
"idx": 11,
"version": "7",
"when": 1759195276875,
"tag": "0011_careless_banshee",
"breakpoints": true
} }
] ]
} }

132
package-lock.json generated
View File

@@ -12,6 +12,7 @@
"@dotenvx/dotenvx": "^1.49.0", "@dotenvx/dotenvx": "^1.49.0",
"@tanstack/react-table": "^8.21.3", "@tanstack/react-table": "^8.21.3",
"@types/cors": "^2.8.19", "@types/cors": "^2.8.19",
"axios": "^1.12.2",
"better-auth": "^1.3.9", "better-auth": "^1.3.9",
"cors": "^2.8.5", "cors": "^2.8.5",
"date-fns": "^4.1.0", "date-fns": "^4.1.0",
@@ -3438,6 +3439,12 @@
"node": ">=12.0.0" "node": ">=12.0.0"
} }
}, },
"node_modules/asynckit": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==",
"license": "MIT"
},
"node_modules/at-least-node": { "node_modules/at-least-node": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz",
@@ -3457,6 +3464,17 @@
"node": ">=8.0.0" "node": ">=8.0.0"
} }
}, },
"node_modules/axios": {
"version": "1.12.2",
"resolved": "https://registry.npmjs.org/axios/-/axios-1.12.2.tgz",
"integrity": "sha512-vMJzPewAlRyOgxV2dU0Cuz2O8zzzx9VYtbJOaBgXFeLc4IV/Eg50n4LowmehOOR61S8ZMpc2K5Sa7g6A4jfkUw==",
"license": "MIT",
"dependencies": {
"follow-redirects": "^1.15.6",
"form-data": "^4.0.4",
"proxy-from-env": "^1.1.0"
}
},
"node_modules/balanced-match": { "node_modules/balanced-match": {
"version": "1.0.2", "version": "1.0.2",
"dev": true, "dev": true,
@@ -3892,6 +3910,18 @@
"integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==", "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==",
"license": "MIT" "license": "MIT"
}, },
"node_modules/combined-stream": {
"version": "1.0.8",
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
"integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
"license": "MIT",
"dependencies": {
"delayed-stream": "~1.0.0"
},
"engines": {
"node": ">= 0.8"
}
},
"node_modules/commander": { "node_modules/commander": {
"version": "11.1.0", "version": "11.1.0",
"resolved": "https://registry.npmjs.org/commander/-/commander-11.1.0.tgz", "resolved": "https://registry.npmjs.org/commander/-/commander-11.1.0.tgz",
@@ -4760,6 +4790,15 @@
"integrity": "sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg==", "integrity": "sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg==",
"license": "MIT" "license": "MIT"
}, },
"node_modules/delayed-stream": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
"integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
"license": "MIT",
"engines": {
"node": ">=0.4.0"
}
},
"node_modules/depd": { "node_modules/depd": {
"version": "2.0.0", "version": "2.0.0",
"license": "MIT", "license": "MIT",
@@ -5184,6 +5223,21 @@
"node": ">= 0.4" "node": ">= 0.4"
} }
}, },
"node_modules/es-set-tostringtag": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz",
"integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==",
"license": "MIT",
"dependencies": {
"es-errors": "^1.3.0",
"get-intrinsic": "^1.2.6",
"has-tostringtag": "^1.0.2",
"hasown": "^2.0.2"
},
"engines": {
"node": ">= 0.4"
}
},
"node_modules/esbuild": { "node_modules/esbuild": {
"version": "0.25.9", "version": "0.25.9",
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.9.tgz", "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.9.tgz",
@@ -5604,6 +5658,26 @@
"node": ">= 8" "node": ">= 8"
} }
}, },
"node_modules/follow-redirects": {
"version": "1.15.11",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.11.tgz",
"integrity": "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==",
"funding": [
{
"type": "individual",
"url": "https://github.com/sponsors/RubenVerborgh"
}
],
"license": "MIT",
"engines": {
"node": ">=4.0"
},
"peerDependenciesMeta": {
"debug": {
"optional": true
}
}
},
"node_modules/foreground-child": { "node_modules/foreground-child": {
"version": "3.3.1", "version": "3.3.1",
"resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz",
@@ -5634,6 +5708,43 @@
"url": "https://github.com/sponsors/isaacs" "url": "https://github.com/sponsors/isaacs"
} }
}, },
"node_modules/form-data": {
"version": "4.0.4",
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.4.tgz",
"integrity": "sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==",
"license": "MIT",
"dependencies": {
"asynckit": "^0.4.0",
"combined-stream": "^1.0.8",
"es-set-tostringtag": "^2.1.0",
"hasown": "^2.0.2",
"mime-types": "^2.1.12"
},
"engines": {
"node": ">= 6"
}
},
"node_modules/form-data/node_modules/mime-db": {
"version": "1.52.0",
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
"integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
"license": "MIT",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/form-data/node_modules/mime-types": {
"version": "2.1.35",
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
"integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
"license": "MIT",
"dependencies": {
"mime-db": "1.52.0"
},
"engines": {
"node": ">= 0.6"
}
},
"node_modules/forwarded": { "node_modules/forwarded": {
"version": "0.2.0", "version": "0.2.0",
"license": "MIT", "license": "MIT",
@@ -6072,6 +6183,21 @@
"url": "https://github.com/sponsors/ljharb" "url": "https://github.com/sponsors/ljharb"
} }
}, },
"node_modules/has-tostringtag": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz",
"integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==",
"license": "MIT",
"dependencies": {
"has-symbols": "^1.0.3"
},
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/hasown": { "node_modules/hasown": {
"version": "2.0.2", "version": "2.0.2",
"license": "MIT", "license": "MIT",
@@ -8294,6 +8420,12 @@
"node": ">= 0.10" "node": ">= 0.10"
} }
}, },
"node_modules/proxy-from-env": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
"integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==",
"license": "MIT"
},
"node_modules/pump": { "node_modules/pump": {
"version": "3.0.3", "version": "3.0.3",
"resolved": "https://registry.npmjs.org/pump/-/pump-3.0.3.tgz", "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.3.tgz",

View File

@@ -44,6 +44,7 @@
"@dotenvx/dotenvx": "^1.49.0", "@dotenvx/dotenvx": "^1.49.0",
"@tanstack/react-table": "^8.21.3", "@tanstack/react-table": "^8.21.3",
"@types/cors": "^2.8.19", "@types/cors": "^2.8.19",
"axios": "^1.12.2",
"better-auth": "^1.3.9", "better-auth": "^1.3.9",
"cors": "^2.8.5", "cors": "^2.8.5",
"date-fns": "^4.1.0", "date-fns": "^4.1.0",