Update web server and dashboard styling for improved user experience

Refactor bot.js to separate HTTP server logic and introduce Express web portal. Update dashboard.html with new styling, color themes, and font imports.

Replit-Commit-Author: Agent
Replit-Commit-Session-Id: aed2e46d-25bb-4b73-81a1-bb9e8437c261
Replit-Commit-Checkpoint-Type: intermediate_checkpoint
Replit-Commit-Event-Id: 17beead7-b08f-41f9-a6ea-f8e2ff20a502
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/3bdfff67-975a-46ad-9845-fbb6b4a4c4b5/aed2e46d-25bb-4b73-81a1-bb9e8437c261/b2aEQYO
Replit-Helium-Checkpoint-Created: true
This commit is contained in:
sirpiglr 2025-12-08 23:59:05 +00:00
parent 57382c58b8
commit be74f5f0bf
10 changed files with 3523 additions and 3744 deletions

View file

@ -22,6 +22,10 @@ externalPort = 80
localPort = 8080
externalPort = 8080
[[ports]]
localPort = 36319
externalPort = 3000
[workflows]
runButton = "Project"

View file

@ -13,9 +13,8 @@ const http = require("http");
const fs = require("fs");
const path = require("path");
const WebSocket = require("ws");
const { createWebServer } = require("./server/webServer");
// Dashboard HTML path
const dashboardPath = path.join(__dirname, "public", "dashboard.html");
require("dotenv").config();
// =============================================================================
@ -2463,11 +2462,25 @@ httpServer.on('upgrade', (request, socket, head) => {
});
httpServer.listen(healthPort, () => {
console.log(`Health check server running on port ${healthPort}`);
console.log(`Health/API server running on port ${healthPort}`);
console.log(`WebSocket server available at ws://localhost:${healthPort}`);
console.log(`Register commands at: POST http://localhost:${healthPort}/register-commands`);
});
// =============================================================================
// EXPRESS WEB PORTAL (Discord OAuth, User Dashboard, API)
// =============================================================================
const webPort = process.env.PORT || 5000;
const expressApp = createWebServer(client, supabase);
const webServer = http.createServer(expressApp);
webServer.listen(webPort, '0.0.0.0', () => {
console.log(`Web portal running on port ${webPort}`);
console.log(`Dashboard: http://localhost:${webPort}/dashboard`);
console.log(`Landing page: http://localhost:${webPort}/`);
});
// =============================================================================
// BOT LOGIN AND READY

View file

@ -0,0 +1,620 @@
@import url("https://fonts.googleapis.com/css2?family=VT323&family=Press+Start+2P&family=Merriweather:wght@400;700&family=Roboto+Mono:wght@300;400;500&display=swap");
@tailwind base;
@tailwind components;
@tailwind utilities;
@layer base {
/**
* Tailwind CSS theme
* tailwind.config.ts expects the following color variables to be expressed as HSL values.
* A different format will require also updating the theme in tailwind.config.ts.
*/
:root {
--background: 222 84% 4.9%;
/* Spacing tokens */
--space-1: 4px;
--space-2: 8px;
--space-3: 12px;
--space-4: 16px;
--space-5: 24px;
--space-6: 32px;
--space-section-y: var(--space-6);
--foreground: 210 40% 98%;
--card: 222 84% 4.9%;
--card-foreground: 210 40% 98%;
--popover: 222 84% 4.9%;
--popover-foreground: 210 40% 98%;
--primary: 250 100% 60%;
--primary-foreground: 210 40% 98%;
--secondary: 217.2 32.6% 17.5%;
--secondary-foreground: 210 40% 98%;
--muted: 217.2 32.6% 17.5%;
--muted-foreground: 215 20.2% 65.1%;
--accent: 217.2 32.6% 17.5%;
--accent-foreground: 210 40% 98%;
--destructive: 0 62.8% 30.6%;
--destructive-foreground: 210 40% 98%;
--border: 217.2 32.6% 17.5%;
--input: 217.2 32.6% 17.5%;
--ring: 250 100% 70%;
--radius: 0.5rem;
--sidebar-background: 240 5.9% 10%;
--sidebar-foreground: 240 4.8% 95.9%;
--sidebar-primary: 250 100% 60%;
--sidebar-primary-foreground: 0 0% 100%;
--sidebar-accent: 240 3.7% 15.9%;
--sidebar-accent-foreground: 240 4.8% 95.9%;
--sidebar-border: 240 3.7% 15.9%;
--sidebar-ring: 217.2 91.2% 59.8%;
/* AeThex Brand Colors */
--aethex-50: 250 100% 97%;
--aethex-100: 250 100% 95%;
--aethex-200: 250 100% 90%;
--aethex-300: 250 100% 80%;
--aethex-400: 250 100% 70%;
--aethex-500: 250 100% 60%;
--aethex-600: 250 100% 50%;
--aethex-700: 250 100% 40%;
--aethex-800: 250 100% 30%;
--aethex-900: 250 100% 20%;
--aethex-950: 250 100% 10%;
/* Neon Colors for Accents */
--neon-purple: 280 100% 70%;
--neon-blue: 210 100% 70%;
--neon-green: 120 100% 70%;
--neon-yellow: 50 100% 70%;
}
}
@layer base {
* {
@apply border-border;
}
body {
@apply bg-background text-foreground;
font-family: "Courier New", "Courier", monospace;
letter-spacing: 0.025em;
}
html {
scroll-behavior: smooth;
/* Hide scrollbar while keeping functionality */
-ms-overflow-style: none; /* IE and Edge */
scrollbar-width: none; /* Firefox */
}
html::-webkit-scrollbar {
display: none; /* Chrome, Safari and Opera */
}
/* Hide horizontal scrollbar on all elements */
* {
scrollbar-width: none;
}
*::-webkit-scrollbar {
display: none;
}
.container {
@apply px-4 sm:px-6 lg:px-8;
}
}
@layer utilities {
/* Arm Theme Font Classes */
.font-labs {
font-family: "VT323", "Courier New", monospace;
letter-spacing: 0.05em;
}
.font-gameforge {
font-family: "Press Start 2P", "Arial Black", sans-serif;
letter-spacing: 0.1em;
font-size: 0.875em;
}
.font-corp {
font-family: "Inter", "-apple-system", "BlinkMacSystemFont", "Segoe UI",
sans-serif;
font-weight: 600;
}
.font-foundation {
font-family: "Merriweather", "Georgia", serif;
font-weight: 700;
letter-spacing: -0.02em;
}
.font-devlink {
font-family: "Roboto Mono", "Courier New", monospace;
font-weight: 400;
letter-spacing: 0.02em;
}
.font-staff {
font-family: "Inter", "-apple-system", "BlinkMacSystemFont", "Segoe UI",
sans-serif;
font-weight: 600;
}
.font-nexus {
font-family: "Inter", "-apple-system", "BlinkMacSystemFont", "Segoe UI",
sans-serif;
font-weight: 600;
}
.font-default {
font-family: "Inter", "-apple-system", "BlinkMacSystemFont", "Segoe UI",
sans-serif;
}
/* Arm Theme Wallpaper Patterns */
.wallpaper-labs {
background-image: radial-gradient(
circle,
rgba(251, 191, 36, 0.08) 1px,
transparent 1px
);
background-size: 20px 20px;
background-attachment: fixed;
}
.wallpaper-gameforge {
background-image: linear-gradient(
45deg,
rgba(34, 197, 94, 0.06) 25%,
transparent 25%,
transparent 75%,
rgba(34, 197, 94, 0.06) 75%
),
linear-gradient(
45deg,
rgba(34, 197, 94, 0.06) 25%,
transparent 25%,
transparent 75%,
rgba(34, 197, 94, 0.06) 75%
);
background-size: 40px 40px;
background-position: 0 0, 20px 20px;
background-attachment: fixed;
}
.wallpaper-corp {
background-image: linear-gradient(
90deg,
rgba(59, 130, 246, 0.05) 1px,
transparent 1px
),
linear-gradient(rgba(59, 130, 246, 0.05) 1px, transparent 1px);
background-size: 20px 20px;
background-attachment: fixed;
}
.wallpaper-foundation {
background-image: repeating-linear-gradient(
0deg,
rgba(239, 68, 68, 0.04) 0px,
rgba(239, 68, 68, 0.04) 1px,
transparent 1px,
transparent 2px
);
background-attachment: fixed;
}
.wallpaper-devlink {
background-image: linear-gradient(
0deg,
transparent 24%,
rgba(6, 182, 212, 0.08) 25%,
rgba(6, 182, 212, 0.08) 26%,
transparent 27%,
transparent 74%,
rgba(6, 182, 212, 0.08) 75%,
rgba(6, 182, 212, 0.08) 76%,
transparent 77%,
transparent
),
linear-gradient(
90deg,
transparent 24%,
rgba(6, 182, 212, 0.08) 25%,
rgba(6, 182, 212, 0.08) 26%,
transparent 27%,
transparent 74%,
rgba(6, 182, 212, 0.08) 75%,
rgba(6, 182, 212, 0.08) 76%,
transparent 77%,
transparent
);
background-size: 50px 50px;
background-attachment: fixed;
}
.wallpaper-staff {
background-image: radial-gradient(
circle,
rgba(168, 85, 247, 0.08) 1px,
transparent 1px
);
background-size: 20px 20px;
background-attachment: fixed;
}
.wallpaper-nexus {
background-image: linear-gradient(
45deg,
rgba(236, 72, 153, 0.06) 25%,
transparent 25%,
transparent 75%,
rgba(236, 72, 153, 0.06) 75%
),
linear-gradient(
45deg,
rgba(236, 72, 153, 0.06) 25%,
transparent 25%,
transparent 75%,
rgba(236, 72, 153, 0.06) 75%
);
background-size: 40px 40px;
background-position: 0 0, 20px 20px;
background-attachment: fixed;
}
.wallpaper-default {
background-image: linear-gradient(
135deg,
rgba(167, 139, 250, 0.05) 0%,
rgba(96, 165, 250, 0.05) 100%
);
}
.section-cozy {
padding-block: var(--space-section-y);
}
.gap-cozy {
gap: var(--space-5);
}
.pad-cozy {
padding: var(--space-5);
}
.text-gradient {
@apply bg-gradient-to-r from-aethex-400 via-neon-blue to-aethex-600 bg-clip-text text-transparent;
background-size: 200% 200%;
animation: gradient-shift 3s ease-in-out infinite;
}
.text-gradient-purple {
@apply bg-gradient-to-r from-neon-purple via-aethex-500 to-neon-blue bg-clip-text text-transparent;
background-size: 200% 200%;
animation: gradient-shift 4s ease-in-out infinite;
}
.bg-aethex-gradient {
@apply bg-gradient-to-br from-aethex-900 via-background to-aethex-800;
}
.border-gradient {
@apply relative overflow-hidden;
}
.border-gradient::before {
content: "";
@apply absolute inset-0 rounded-[inherit] p-[1px] bg-gradient-to-r from-aethex-400 via-neon-blue to-aethex-600;
mask:
linear-gradient(#fff 0 0) content-box,
linear-gradient(#fff 0 0);
mask-composite: xor;
background-size: 200% 200%;
animation: gradient-shift 2s ease-in-out infinite;
}
.glow-purple {
box-shadow:
0 0 20px rgba(139, 92, 246, 0.3),
0 0 40px rgba(139, 92, 246, 0.2);
transition: box-shadow 0.3s ease;
}
.glow-purple:hover {
box-shadow:
0 0 30px rgba(139, 92, 246, 0.5),
0 0 60px rgba(139, 92, 246, 0.3);
}
.glow-blue {
box-shadow:
0 0 20px rgba(59, 130, 246, 0.3),
0 0 40px rgba(59, 130, 246, 0.2);
transition: box-shadow 0.3s ease;
}
.glow-blue:hover {
box-shadow:
0 0 30px rgba(59, 130, 246, 0.5),
0 0 60px rgba(59, 130, 246, 0.3);
}
.glow-green {
box-shadow:
0 0 20px rgba(34, 197, 94, 0.3),
0 0 40px rgba(34, 197, 94, 0.2);
transition: box-shadow 0.3s ease;
}
.glow-yellow {
box-shadow:
0 0 20px rgba(251, 191, 36, 0.3),
0 0 40px rgba(251, 191, 36, 0.2);
transition: box-shadow 0.3s ease;
}
.animate-fade-in {
animation: fade-in 0.6s ease-out;
}
.animate-slide-up {
animation: slide-up 0.6s ease-out;
}
.animate-slide-down {
animation: slide-down 0.6s ease-out;
}
.animate-slide-left {
animation: slide-left 0.6s ease-out;
}
.animate-slide-right {
animation: slide-right 0.6s ease-out;
}
.animate-scale-in {
animation: scale-in 0.4s ease-out;
}
.animate-bounce-gentle {
animation: bounce-gentle 2s ease-in-out infinite;
}
.animate-float {
animation: float 3s ease-in-out infinite;
}
.animate-pulse-glow {
animation: pulse-glow 2s ease-in-out infinite;
}
.animate-typing {
animation:
typing 3s steps(40, end),
blink-caret 0.75s step-end infinite;
overflow: hidden;
border-right: 3px solid;
white-space: nowrap;
margin: 0 auto;
}
.hover-lift {
transition:
transform 0.3s ease,
box-shadow 0.3s ease;
}
.hover-lift:hover {
transform: translateY(-8px);
}
.hover-glow {
transition: all 0.3s ease;
}
.hover-glow:hover {
filter: brightness(1.1) drop-shadow(0 0 10px currentColor);
}
.interactive-scale {
transition: transform 0.2s ease;
}
.interactive-scale:hover {
transform: scale(1.05);
}
.interactive-scale:active {
transform: scale(0.98);
}
.loading-dots::after {
content: "";
animation: loading-dots 1.5s infinite;
}
.skeleton {
background: linear-gradient(
90deg,
transparent,
rgba(255, 255, 255, 0.1),
transparent
);
background-size: 200% 100%;
animation: skeleton-loading 1.5s infinite;
}
}
@keyframes gradient-shift {
0%,
100% {
background-position: 0% 50%;
}
50% {
background-position: 100% 50%;
}
}
@keyframes fade-in {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
@keyframes slide-up {
from {
opacity: 0;
transform: translateY(30px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
@keyframes slide-down {
from {
opacity: 0;
transform: translateY(-30px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
@keyframes slide-left {
from {
opacity: 0;
transform: translateX(30px);
}
to {
opacity: 1;
transform: translateX(0);
}
}
@keyframes slide-right {
from {
opacity: 0;
transform: translateX(-30px);
}
to {
opacity: 1;
transform: translateX(0);
}
}
@keyframes scale-in {
from {
opacity: 0;
transform: scale(0.9);
}
to {
opacity: 1;
transform: scale(1);
}
}
@keyframes bounce-gentle {
0%,
100% {
transform: translateY(0);
}
50% {
transform: translateY(-10px);
}
}
@keyframes float {
0%,
100% {
transform: translateY(0px);
}
50% {
transform: translateY(-20px);
}
}
@keyframes pulse-glow {
0%,
100% {
opacity: 1;
transform: scale(1);
}
50% {
opacity: 0.8;
transform: scale(1.05);
}
}
@keyframes typing {
from {
width: 0;
}
to {
width: 100%;
}
}
@keyframes blink-caret {
from,
to {
border-color: transparent;
}
50% {
border-color: currentColor;
}
}
@keyframes loading-dots {
0% {
content: "";
}
25% {
content: ".";
}
50% {
content: "..";
}
75% {
content: "...";
}
100% {
content: "";
}
}
@keyframes skeleton-loading {
0% {
background-position: -200% 0;
}
100% {
background-position: 200% 0;
}
}
@media (prefers-reduced-motion: reduce) {
* {
animation-duration: 0.001ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.001ms !important;
scroll-behavior: auto !important;
}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,536 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>AeThex Bot - Level Up Your Discord Server</title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&family=Roboto+Mono:wght@400;500&display=swap" rel="stylesheet">
<style>
:root {
--background: hsl(222, 84%, 4.9%);
--foreground: hsl(210, 40%, 98%);
--card: hsl(222, 84%, 6%);
--card-foreground: hsl(210, 40%, 98%);
--primary: hsl(250, 100%, 60%);
--primary-foreground: hsl(210, 40%, 98%);
--secondary: hsl(217.2, 32.6%, 17.5%);
--muted: hsl(217.2, 32.6%, 17.5%);
--muted-foreground: hsl(215, 20.2%, 65.1%);
--border: hsl(217.2, 32.6%, 17.5%);
--aethex-400: hsl(250, 100%, 70%);
--aethex-500: hsl(250, 100%, 60%);
--aethex-600: hsl(250, 100%, 50%);
--neon-purple: hsl(280, 100%, 70%);
--neon-blue: hsl(210, 100%, 70%);
--neon-green: hsl(120, 100%, 70%);
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
background: var(--background);
color: var(--foreground);
min-height: 100vh;
overflow-x: hidden;
}
.wallpaper {
position: fixed;
inset: 0;
background-image: linear-gradient(135deg, rgba(167, 139, 250, 0.05) 0%, rgba(96, 165, 250, 0.05) 100%);
pointer-events: none;
z-index: -1;
}
.container {
max-width: 1200px;
margin: 0 auto;
padding: 0 1.5rem;
}
header {
position: sticky;
top: 0;
z-index: 50;
background: rgba(10, 10, 20, 0.95);
backdrop-filter: blur(12px);
border-bottom: 2px solid var(--aethex-500);
box-shadow: 0 8px 32px rgba(139, 92, 246, 0.15);
}
.header-content {
display: flex;
align-items: center;
justify-content: space-between;
padding: 1rem 0;
}
.logo {
display: flex;
align-items: center;
gap: 0.75rem;
text-decoration: none;
color: var(--foreground);
}
.logo-icon {
width: 40px;
height: 40px;
background: linear-gradient(135deg, var(--aethex-500), var(--neon-blue));
border-radius: 10px;
display: flex;
align-items: center;
justify-content: center;
font-weight: 700;
font-size: 1.25rem;
}
.logo-text {
font-size: 1.5rem;
font-weight: 700;
background: linear-gradient(to right, var(--aethex-400), var(--neon-blue), var(--aethex-600));
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
}
.nav-links {
display: flex;
gap: 2rem;
align-items: center;
}
.nav-links a {
color: var(--muted-foreground);
text-decoration: none;
font-weight: 500;
transition: color 0.2s;
}
.nav-links a:hover {
color: var(--foreground);
}
.btn {
display: inline-flex;
align-items: center;
gap: 0.5rem;
padding: 0.625rem 1.25rem;
border-radius: 8px;
font-weight: 600;
text-decoration: none;
transition: all 0.3s;
border: none;
cursor: pointer;
font-size: 0.875rem;
}
.btn-primary {
background: linear-gradient(135deg, var(--aethex-500), var(--neon-blue));
color: white;
box-shadow: 0 0 20px rgba(139, 92, 246, 0.3);
}
.btn-primary:hover {
transform: translateY(-2px);
box-shadow: 0 0 30px rgba(139, 92, 246, 0.5);
}
.btn-outline {
background: transparent;
border: 1px solid var(--border);
color: var(--foreground);
}
.btn-outline:hover {
background: var(--secondary);
border-color: var(--aethex-500);
}
.hero {
padding: 6rem 0;
text-align: center;
}
.hero h1 {
font-size: clamp(2.5rem, 5vw, 4rem);
font-weight: 700;
margin-bottom: 1.5rem;
line-height: 1.1;
}
.text-gradient {
background: linear-gradient(to right, var(--aethex-400), var(--neon-blue), var(--aethex-600));
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
background-size: 200% 200%;
animation: gradient-shift 3s ease-in-out infinite;
}
@keyframes gradient-shift {
0%, 100% { background-position: 0% 50%; }
50% { background-position: 100% 50%; }
}
.hero p {
font-size: 1.25rem;
color: var(--muted-foreground);
max-width: 600px;
margin: 0 auto 2.5rem;
}
.hero-buttons {
display: flex;
gap: 1rem;
justify-content: center;
flex-wrap: wrap;
}
.stats-bar {
display: flex;
justify-content: center;
gap: 3rem;
margin-top: 4rem;
flex-wrap: wrap;
}
.stat {
text-align: center;
}
.stat-value {
font-size: 2.5rem;
font-weight: 700;
color: var(--aethex-400);
}
.stat-label {
color: var(--muted-foreground);
font-size: 0.875rem;
margin-top: 0.25rem;
}
.features {
padding: 5rem 0;
}
.section-title {
text-align: center;
font-size: 2rem;
font-weight: 700;
margin-bottom: 3rem;
}
.features-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 1.5rem;
}
.feature-card {
background: linear-gradient(135deg, rgba(139, 92, 246, 0.1), rgba(59, 130, 246, 0.05));
border: 1px solid rgba(139, 92, 246, 0.2);
border-radius: 16px;
padding: 2rem;
transition: all 0.3s;
}
.feature-card:hover {
transform: translateY(-4px);
border-color: var(--aethex-500);
box-shadow: 0 0 30px rgba(139, 92, 246, 0.2);
}
.feature-icon {
width: 48px;
height: 48px;
background: linear-gradient(135deg, var(--aethex-500), var(--neon-blue));
border-radius: 12px;
display: flex;
align-items: center;
justify-content: center;
font-size: 1.5rem;
margin-bottom: 1rem;
}
.feature-card h3 {
font-size: 1.25rem;
font-weight: 600;
margin-bottom: 0.75rem;
}
.feature-card p {
color: var(--muted-foreground);
line-height: 1.6;
}
.cta {
padding: 5rem 0;
text-align: center;
}
.cta-box {
background: linear-gradient(135deg, rgba(139, 92, 246, 0.15), rgba(59, 130, 246, 0.1));
border: 1px solid rgba(139, 92, 246, 0.3);
border-radius: 24px;
padding: 4rem 2rem;
max-width: 800px;
margin: 0 auto;
}
.cta h2 {
font-size: 2rem;
margin-bottom: 1rem;
}
.cta p {
color: var(--muted-foreground);
margin-bottom: 2rem;
}
footer {
border-top: 1px solid var(--border);
padding: 3rem 0;
text-align: center;
color: var(--muted-foreground);
}
.footer-links {
display: flex;
justify-content: center;
gap: 2rem;
margin-bottom: 1.5rem;
flex-wrap: wrap;
}
.footer-links a {
color: var(--muted-foreground);
text-decoration: none;
}
.footer-links a:hover {
color: var(--foreground);
}
.loading {
display: inline-block;
width: 20px;
height: 20px;
border: 2px solid transparent;
border-top-color: currentColor;
border-radius: 50%;
animation: spin 1s linear infinite;
}
@keyframes spin {
to { transform: rotate(360deg); }
}
@media (max-width: 768px) {
.nav-links {
display: none;
}
.stats-bar {
gap: 1.5rem;
}
.stat-value {
font-size: 1.75rem;
}
}
</style>
</head>
<body>
<div class="wallpaper"></div>
<header>
<div class="container header-content">
<a href="/" class="logo">
<div class="logo-icon">A</div>
<span class="logo-text">AeThex</span>
</a>
<nav class="nav-links">
<a href="#features">Features</a>
<a href="#leaderboard">Leaderboard</a>
<a href="https://discord.gg/aethex" target="_blank">Support</a>
</nav>
<div class="auth-buttons">
<a href="/auth/discord" class="btn btn-primary" id="loginBtn">
<svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor"><path d="M20.317 4.37a19.791 19.791 0 0 0-4.885-1.515a.074.074 0 0 0-.079.037c-.21.375-.444.864-.608 1.25a18.27 18.27 0 0 0-5.487 0a12.64 12.64 0 0 0-.617-1.25a.077.077 0 0 0-.079-.037A19.736 19.736 0 0 0 3.677 4.37a.07.07 0 0 0-.032.027C.533 9.046-.32 13.58.099 18.057a.082.082 0 0 0 .031.057a19.9 19.9 0 0 0 5.993 3.03a.078.078 0 0 0 .084-.028a14.09 14.09 0 0 0 1.226-1.994a.076.076 0 0 0-.041-.106a13.107 13.107 0 0 1-1.872-.892a.077.077 0 0 1-.008-.128a10.2 10.2 0 0 0 .372-.292a.074.074 0 0 1 .077-.01c3.928 1.793 8.18 1.793 12.062 0a.074.074 0 0 1 .078.01c.12.098.246.198.373.292a.077.077 0 0 1-.006.127a12.299 12.299 0 0 1-1.873.892a.077.077 0 0 0-.041.107c.36.698.772 1.362 1.225 1.993a.076.076 0 0 0 .084.028a19.839 19.839 0 0 0 6.002-3.03a.077.077 0 0 0 .032-.054c.5-5.177-.838-9.674-3.549-13.66a.061.061 0 0 0-.031-.03zM8.02 15.33c-1.183 0-2.157-1.085-2.157-2.419c0-1.333.956-2.419 2.157-2.419c1.21 0 2.176 1.096 2.157 2.42c0 1.333-.956 2.418-2.157 2.418zm7.975 0c-1.183 0-2.157-1.085-2.157-2.419c0-1.333.955-2.419 2.157-2.419c1.21 0 2.176 1.096 2.157 2.42c0 1.333-.946 2.418-2.157 2.418z"/></svg>
Login with Discord
</a>
</div>
</div>
</header>
<main>
<section class="hero">
<div class="container">
<h1>Level Up Your<br><span class="text-gradient">Discord Server</span></h1>
<p>The ultimate XP, leveling, and community engagement bot. Track progress, earn achievements, complete quests, and unlock rewards.</p>
<div class="hero-buttons">
<a href="https://discord.com/api/oauth2/authorize?client_id=1307534067556167720&permissions=8&scope=bot%20applications.commands" class="btn btn-primary" target="_blank">
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M12 5v14M5 12h14"/></svg>
Add to Server
</a>
<a href="/auth/discord" class="btn btn-outline">
View Your Profile
</a>
</div>
<div class="stats-bar" id="statsBar">
<div class="stat">
<div class="stat-value" id="serverCount">-</div>
<div class="stat-label">Servers</div>
</div>
<div class="stat">
<div class="stat-value" id="userCount">-</div>
<div class="stat-label">Users</div>
</div>
<div class="stat">
<div class="stat-value" id="commandCount">-</div>
<div class="stat-label">Commands</div>
</div>
<div class="stat">
<div class="stat-value" id="uptimeValue">-</div>
<div class="stat-label">Uptime</div>
</div>
</div>
</div>
</section>
<section class="features" id="features">
<div class="container">
<h2 class="section-title">Powerful <span class="text-gradient">Features</span></h2>
<div class="features-grid">
<div class="feature-card">
<div class="feature-icon">📈</div>
<h3>XP & Leveling System</h3>
<p>Earn XP from messages, reactions, and voice chat. Level up with customizable curves and unlock role rewards automatically.</p>
</div>
<div class="feature-card">
<div class="feature-icon">🏆</div>
<h3>Achievements & Badges</h3>
<p>Create custom achievements with various triggers. Users earn badges for milestones, building their profile showcase.</p>
</div>
<div class="feature-card">
<div class="feature-icon">🎯</div>
<h3>Quests & Challenges</h3>
<p>Set up rotating quests with objectives like sending messages, earning XP, or spending time in voice. Reward completion with XP and roles.</p>
</div>
<div class="feature-card">
<div class="feature-icon">🛒</div>
<h3>XP Shop System</h3>
<p>Let users spend XP on cosmetics, perks, and custom roles. Fully configurable items and pricing.</p>
</div>
<div class="feature-card">
<div class="feature-icon"></div>
<h3>Prestige System</h3>
<p>Reset at max level for permanent bonuses. Prestige levels unlock exclusive perks and XP multipliers.</p>
</div>
<div class="feature-card">
<div class="feature-icon">🏅</div>
<h3>Leaderboards</h3>
<p>Weekly, monthly, and all-time leaderboards with automatic resets. See who's on top across your server.</p>
</div>
<div class="feature-card">
<div class="feature-icon">🛡️</div>
<h3>Sentinel Security</h3>
<p>Anti-nuke protection with heat tracking. Protect your server from mass bans, kicks, and channel deletions.</p>
</div>
<div class="feature-card">
<div class="feature-icon">🎫</div>
<h3>Ticket System</h3>
<p>Create support tickets with categories, transcripts, and staff assignment. Keep your community organized.</p>
</div>
<div class="feature-card">
<div class="feature-icon">🔗</div>
<h3>Federation Sync</h3>
<p>Sync roles across multiple servers. Perfect for multi-server communities and partnerships.</p>
</div>
</div>
</div>
</section>
<section class="cta">
<div class="container">
<div class="cta-box">
<h2 class="text-gradient">Ready to Level Up?</h2>
<p>Join thousands of servers using AeThex to build engaged communities.</p>
<a href="https://discord.com/api/oauth2/authorize?client_id=1307534067556167720&permissions=8&scope=bot%20applications.commands" class="btn btn-primary" target="_blank">
Add AeThex to Your Server
</a>
</div>
</div>
</section>
</main>
<footer>
<div class="container">
<div class="footer-links">
<a href="https://aethex.studio" target="_blank">AeThex Studio</a>
<a href="https://aethex.foundation" target="_blank">Foundation</a>
<a href="https://discord.gg/aethex" target="_blank">Discord</a>
<a href="/dashboard">Dashboard</a>
</div>
<p>&copy; 2024 AeThex. All rights reserved.</p>
</div>
</footer>
<script>
async function loadStats() {
try {
const res = await fetch('/api/bot-stats');
const data = await res.json();
document.getElementById('serverCount').textContent = data.guilds.toLocaleString();
document.getElementById('userCount').textContent = data.users.toLocaleString();
document.getElementById('commandCount').textContent = data.commands;
const hours = Math.floor(data.uptime / 3600);
const days = Math.floor(hours / 24);
document.getElementById('uptimeValue').textContent = days > 0 ? `${days}d` : `${hours}h`;
} catch (e) {
console.log('Stats unavailable');
}
}
async function checkAuth() {
try {
const res = await fetch('/api/me');
if (res.ok) {
const user = await res.json();
const btn = document.getElementById('loginBtn');
btn.innerHTML = `<img src="${user.avatarUrl}" style="width:24px;height:24px;border-radius:50%;margin-right:8px"> ${user.globalName || user.username}`;
btn.href = '/dashboard';
}
} catch (e) {}
}
loadStats();
checkAuth();
setInterval(loadStats, 30000);
</script>
</body>
</html>

View file

@ -0,0 +1,515 @@
const express = require('express');
const session = require('express-session');
const cookieParser = require('cookie-parser');
const cors = require('cors');
const path = require('path');
const crypto = require('crypto');
function createWebServer(discordClient, supabase, options = {}) {
const app = express();
const DISCORD_CLIENT_ID = process.env.DISCORD_CLIENT_ID;
const DISCORD_CLIENT_SECRET = process.env.DISCORD_CLIENT_SECRET;
const SESSION_SECRET = process.env.SESSION_SECRET || crypto.randomBytes(32).toString('hex');
const BASE_URL = process.env.REPLIT_DEV_DOMAIN
? `https://${process.env.REPLIT_DEV_DOMAIN}`
: process.env.BASE_URL || 'http://localhost:5000';
app.use(cors({
origin: true,
credentials: true
}));
app.use(cookieParser());
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
app.use(session({
secret: SESSION_SECRET,
resave: false,
saveUninitialized: false,
cookie: {
secure: process.env.NODE_ENV === 'production',
httpOnly: true,
maxAge: 7 * 24 * 60 * 60 * 1000
}
}));
app.use((req, res, next) => {
res.setHeader('Cache-Control', 'no-cache, no-store, must-revalidate');
res.setHeader('Pragma', 'no-cache');
res.setHeader('Expires', '0');
next();
});
app.use(express.static(path.join(__dirname, '../public')));
app.get('/auth/discord', (req, res) => {
if (!DISCORD_CLIENT_SECRET) {
return res.status(500).json({ error: 'Discord OAuth not configured. Please set DISCORD_CLIENT_SECRET.' });
}
const state = crypto.randomBytes(16).toString('hex');
req.session.oauthState = state;
const redirectUri = `${BASE_URL}/auth/callback`;
const scope = 'identify guilds';
const authUrl = `https://discord.com/api/oauth2/authorize?` +
`client_id=${DISCORD_CLIENT_ID}` +
`&redirect_uri=${encodeURIComponent(redirectUri)}` +
`&response_type=code` +
`&scope=${encodeURIComponent(scope)}` +
`&state=${state}`;
res.redirect(authUrl);
});
app.get('/auth/callback', async (req, res) => {
const { code, state } = req.query;
if (!code) {
return res.redirect('/?error=no_code');
}
if (state !== req.session.oauthState) {
return res.redirect('/?error=invalid_state');
}
try {
const redirectUri = `${BASE_URL}/auth/callback`;
const tokenResponse = await fetch('https://discord.com/api/oauth2/token', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
body: new URLSearchParams({
client_id: DISCORD_CLIENT_ID,
client_secret: DISCORD_CLIENT_SECRET,
grant_type: 'authorization_code',
code,
redirect_uri: redirectUri
})
});
if (!tokenResponse.ok) {
console.error('Token exchange failed:', await tokenResponse.text());
return res.redirect('/?error=token_failed');
}
const tokens = await tokenResponse.json();
const userResponse = await fetch('https://discord.com/api/users/@me', {
headers: {
Authorization: `Bearer ${tokens.access_token}`
}
});
if (!userResponse.ok) {
return res.redirect('/?error=user_fetch_failed');
}
const user = await userResponse.json();
const guildsResponse = await fetch('https://discord.com/api/users/@me/guilds', {
headers: {
Authorization: `Bearer ${tokens.access_token}`
}
});
let guilds = [];
if (guildsResponse.ok) {
guilds = await guildsResponse.json();
}
req.session.user = {
id: user.id,
username: user.username,
discriminator: user.discriminator,
avatar: user.avatar,
globalName: user.global_name,
accessToken: tokens.access_token,
refreshToken: tokens.refresh_token,
guilds: guilds.map(g => ({
id: g.id,
name: g.name,
icon: g.icon,
owner: g.owner,
permissions: g.permissions,
isAdmin: (parseInt(g.permissions) & 0x20) === 0x20
}))
};
res.redirect('/dashboard');
} catch (error) {
console.error('OAuth callback error:', error);
res.redirect('/?error=auth_failed');
}
});
app.get('/auth/logout', (req, res) => {
req.session.destroy();
res.redirect('/');
});
app.get('/api/me', (req, res) => {
if (!req.session.user) {
return res.status(401).json({ error: 'Not authenticated' });
}
const user = req.session.user;
res.json({
id: user.id,
username: user.username,
discriminator: user.discriminator,
avatar: user.avatar,
globalName: user.globalName,
avatarUrl: user.avatar
? `https://cdn.discordapp.com/avatars/${user.id}/${user.avatar}.png`
: `https://cdn.discordapp.com/embed/avatars/${parseInt(user.discriminator || '0') % 5}.png`,
guilds: user.guilds
});
});
// Profile handler function
async function handleProfileRequest(req, res, userId) {
if (!supabase) {
return res.status(503).json({ error: 'Database not available' });
}
const targetUserId = userId || req.session.user?.id;
if (!targetUserId) {
return res.status(400).json({ error: 'User ID required' });
}
try {
const { data: link } = await supabase
.from('discord_links')
.select('user_id')
.eq('discord_id', targetUserId)
.maybeSingle();
if (!link) {
return res.json({ linked: false, message: 'Discord account not linked' });
}
const { data: profile } = await supabase
.from('user_profiles')
.select('*')
.eq('id', link.user_id)
.maybeSingle();
if (!profile) {
return res.json({ linked: true, profile: null });
}
res.json({
linked: true,
profile: {
id: profile.id,
username: profile.username,
xp: profile.xp || 0,
prestigeLevel: profile.prestige_level || 0,
totalXpEarned: profile.total_xp_earned || 0,
dailyStreak: profile.daily_streak || 0,
title: profile.title,
bio: profile.bio,
avatarUrl: profile.avatar_url,
createdAt: profile.created_at
}
});
} catch (error) {
console.error('Profile fetch error:', error);
res.status(500).json({ error: 'Failed to fetch profile' });
}
}
// Route for current user's profile
app.get('/api/profile', (req, res) => handleProfileRequest(req, res, null));
// Route for specific user's profile
app.get('/api/profile/:userId', (req, res) => handleProfileRequest(req, res, req.params.userId));
app.get('/api/stats/:userId/:guildId', async (req, res) => {
if (!supabase) {
return res.status(503).json({ error: 'Database not available' });
}
const { userId, guildId } = req.params;
try {
const { data: link } = await supabase
.from('discord_links')
.select('user_id')
.eq('discord_id', userId)
.maybeSingle();
if (!link) {
return res.json({ stats: null });
}
const { data: stats } = await supabase
.from('user_stats')
.select('*')
.eq('user_id', link.user_id)
.eq('guild_id', guildId)
.maybeSingle();
res.json({ stats });
} catch (error) {
res.status(500).json({ error: 'Failed to fetch stats' });
}
});
app.get('/api/achievements/:guildId', async (req, res) => {
if (!supabase) {
return res.status(503).json({ error: 'Database not available' });
}
const { guildId } = req.params;
const userId = req.session.user?.id;
try {
const { data: achievements } = await supabase
.from('achievements')
.select('*')
.eq('guild_id', guildId)
.eq('hidden', false)
.order('created_at', { ascending: true });
let userAchievements = [];
if (userId) {
const { data: link } = await supabase
.from('discord_links')
.select('user_id')
.eq('discord_id', userId)
.maybeSingle();
if (link) {
const { data } = await supabase
.from('user_achievements')
.select('achievement_id, earned_at')
.eq('user_id', link.user_id)
.eq('guild_id', guildId);
userAchievements = data || [];
}
}
res.json({ achievements, userAchievements });
} catch (error) {
res.status(500).json({ error: 'Failed to fetch achievements' });
}
});
app.get('/api/quests/:guildId', async (req, res) => {
if (!supabase) {
return res.status(503).json({ error: 'Database not available' });
}
const { guildId } = req.params;
const userId = req.session.user?.id;
try {
const now = new Date().toISOString();
const { data: quests } = await supabase
.from('quests')
.select('*')
.eq('guild_id', guildId)
.eq('active', true)
.or(`starts_at.is.null,starts_at.lte.${now}`)
.or(`expires_at.is.null,expires_at.gt.${now}`)
.order('created_at', { ascending: false });
let userQuests = [];
if (userId) {
const { data: link } = await supabase
.from('discord_links')
.select('user_id')
.eq('discord_id', userId)
.maybeSingle();
if (link) {
const { data } = await supabase
.from('user_quests')
.select('*')
.eq('user_id', link.user_id)
.eq('guild_id', guildId);
userQuests = data || [];
}
}
res.json({ quests, userQuests });
} catch (error) {
res.status(500).json({ error: 'Failed to fetch quests' });
}
});
app.get('/api/shop/:guildId', async (req, res) => {
if (!supabase) {
return res.status(503).json({ error: 'Database not available' });
}
const { guildId } = req.params;
try {
const { data: items } = await supabase
.from('shop_items')
.select('*')
.eq('guild_id', guildId)
.eq('available', true)
.order('price', { ascending: true });
res.json({ items: items || [] });
} catch (error) {
res.status(500).json({ error: 'Failed to fetch shop items' });
}
});
app.get('/api/inventory/:guildId', async (req, res) => {
if (!supabase) {
return res.status(503).json({ error: 'Database not available' });
}
const userId = req.session.user?.id;
if (!userId) {
return res.status(401).json({ error: 'Not authenticated' });
}
const { guildId } = req.params;
try {
const { data: link } = await supabase
.from('discord_links')
.select('user_id')
.eq('discord_id', userId)
.maybeSingle();
if (!link) {
return res.json({ inventory: [] });
}
const { data: inventory } = await supabase
.from('user_inventory')
.select(`
*,
shop_items (name, description, item_type, value)
`)
.eq('user_id', link.user_id)
.eq('guild_id', guildId);
res.json({ inventory: inventory || [] });
} catch (error) {
res.status(500).json({ error: 'Failed to fetch inventory' });
}
});
app.get('/api/leaderboard/:guildId', async (req, res) => {
if (!supabase) {
return res.status(503).json({ error: 'Database not available' });
}
const { guildId } = req.params;
const { type = 'all', limit = 50 } = req.query;
try {
let tableName = 'leaderboard_alltime';
if (type === 'weekly') tableName = 'leaderboard_weekly';
else if (type === 'monthly') tableName = 'leaderboard_monthly';
const { data: leaderboard } = await supabase
.from(tableName)
.select(`
*,
user_profiles!inner (username, avatar_url, prestige_level)
`)
.eq('guild_id', guildId)
.order('xp', { ascending: false })
.limit(parseInt(limit));
res.json({ leaderboard: leaderboard || [], type });
} catch (error) {
res.status(500).json({ error: 'Failed to fetch leaderboard' });
}
});
app.get('/api/bot-stats', (req, res) => {
const stats = {
guilds: discordClient.guilds.cache.size,
users: discordClient.guilds.cache.reduce((acc, g) => acc + g.memberCount, 0),
commands: discordClient.commands?.size || 0,
uptime: Math.floor(process.uptime()),
status: discordClient.user ? 'online' : 'offline',
botName: discordClient.user?.username || 'AeThex',
botAvatar: discordClient.user?.displayAvatarURL() || null
};
res.json(stats);
});
app.get('/api/guild/:guildId/config', async (req, res) => {
if (!supabase) {
return res.status(503).json({ error: 'Database not available' });
}
const userId = req.session.user?.id;
if (!userId) {
return res.status(401).json({ error: 'Not authenticated' });
}
const { guildId } = req.params;
const userGuild = req.session.user.guilds.find(g => g.id === guildId);
if (!userGuild || !userGuild.isAdmin) {
return res.status(403).json({ error: 'No admin access to this server' });
}
try {
const { data: xpConfig } = await supabase
.from('xp_config')
.select('*')
.eq('guild_id', guildId)
.maybeSingle();
const { data: serverConfig } = await supabase
.from('server_config')
.select('*')
.eq('guild_id', guildId)
.maybeSingle();
res.json({ xpConfig, serverConfig });
} catch (error) {
res.status(500).json({ error: 'Failed to fetch config' });
}
});
app.get('/health', (req, res) => {
res.json({
status: 'ok',
bot: discordClient.user ? 'online' : 'offline',
uptime: process.uptime()
});
});
app.get('/dashboard', (req, res) => {
res.sendFile(path.join(__dirname, '../public/dashboard.html'));
});
app.get('/', (req, res) => {
res.sendFile(path.join(__dirname, '../public/index.html'));
});
// Catch-all route for SPA - use middleware instead of wildcard
app.use((req, res, next) => {
if (req.method === 'GET' && !req.path.startsWith('/api/')) {
res.sendFile(path.join(__dirname, '../public/index.html'));
} else if (req.path.startsWith('/api/')) {
res.status(404).json({ error: 'API endpoint not found' });
} else {
next();
}
});
return app;
}
module.exports = { createWebServer };

Binary file not shown.

938
package-lock.json generated
View file

@ -9,10 +9,662 @@
"version": "1.0.0",
"license": "ISC",
"dependencies": {
"cookie-parser": "^1.4.7",
"cors": "^2.8.5",
"express": "^5.2.1",
"express-session": "^1.18.2",
"pg": "^8.16.3",
"ws": "^8.18.3"
}
},
"node_modules/accepts": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz",
"integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==",
"license": "MIT",
"dependencies": {
"mime-types": "^3.0.0",
"negotiator": "^1.0.0"
},
"engines": {
"node": ">= 0.6"
}
},
"node_modules/body-parser": {
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.1.tgz",
"integrity": "sha512-nfDwkulwiZYQIGwxdy0RUmowMhKcFVcYXUU7m4QlKYim1rUtg83xm2yjZ40QjDuc291AJjjeSc9b++AWHSgSHw==",
"license": "MIT",
"dependencies": {
"bytes": "^3.1.2",
"content-type": "^1.0.5",
"debug": "^4.4.3",
"http-errors": "^2.0.0",
"iconv-lite": "^0.7.0",
"on-finished": "^2.4.1",
"qs": "^6.14.0",
"raw-body": "^3.0.1",
"type-is": "^2.0.1"
},
"engines": {
"node": ">=18"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/express"
}
},
"node_modules/bytes": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
"integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==",
"license": "MIT",
"engines": {
"node": ">= 0.8"
}
},
"node_modules/call-bind-apply-helpers": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz",
"integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==",
"license": "MIT",
"dependencies": {
"es-errors": "^1.3.0",
"function-bind": "^1.1.2"
},
"engines": {
"node": ">= 0.4"
}
},
"node_modules/call-bound": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz",
"integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==",
"license": "MIT",
"dependencies": {
"call-bind-apply-helpers": "^1.0.2",
"get-intrinsic": "^1.3.0"
},
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/content-disposition": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.0.1.tgz",
"integrity": "sha512-oIXISMynqSqm241k6kcQ5UwttDILMK4BiurCfGEREw6+X9jkkpEe5T9FZaApyLGGOnFuyMWZpdolTXMtvEJ08Q==",
"license": "MIT",
"engines": {
"node": ">=18"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/express"
}
},
"node_modules/content-type": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz",
"integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==",
"license": "MIT",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/cookie": {
"version": "0.7.2",
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz",
"integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==",
"license": "MIT",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/cookie-parser": {
"version": "1.4.7",
"resolved": "https://registry.npmjs.org/cookie-parser/-/cookie-parser-1.4.7.tgz",
"integrity": "sha512-nGUvgXnotP3BsjiLX2ypbQnWoGUPIIfHQNZkkC668ntrzGWEZVW70HDEB1qnNGMicPje6EttlIgzo51YSwNQGw==",
"license": "MIT",
"dependencies": {
"cookie": "0.7.2",
"cookie-signature": "1.0.6"
},
"engines": {
"node": ">= 0.8.0"
}
},
"node_modules/cookie-signature": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
"integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==",
"license": "MIT"
},
"node_modules/cors": {
"version": "2.8.5",
"resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz",
"integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==",
"license": "MIT",
"dependencies": {
"object-assign": "^4",
"vary": "^1"
},
"engines": {
"node": ">= 0.10"
}
},
"node_modules/debug": {
"version": "4.4.3",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
"integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
"license": "MIT",
"dependencies": {
"ms": "^2.1.3"
},
"engines": {
"node": ">=6.0"
},
"peerDependenciesMeta": {
"supports-color": {
"optional": true
}
}
},
"node_modules/depd": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
"integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==",
"license": "MIT",
"engines": {
"node": ">= 0.8"
}
},
"node_modules/dunder-proto": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
"integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==",
"license": "MIT",
"dependencies": {
"call-bind-apply-helpers": "^1.0.1",
"es-errors": "^1.3.0",
"gopd": "^1.2.0"
},
"engines": {
"node": ">= 0.4"
}
},
"node_modules/ee-first": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
"integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==",
"license": "MIT"
},
"node_modules/encodeurl": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz",
"integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==",
"license": "MIT",
"engines": {
"node": ">= 0.8"
}
},
"node_modules/es-define-property": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz",
"integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==",
"license": "MIT",
"engines": {
"node": ">= 0.4"
}
},
"node_modules/es-errors": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz",
"integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
"license": "MIT",
"engines": {
"node": ">= 0.4"
}
},
"node_modules/es-object-atoms": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz",
"integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==",
"license": "MIT",
"dependencies": {
"es-errors": "^1.3.0"
},
"engines": {
"node": ">= 0.4"
}
},
"node_modules/escape-html": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
"integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==",
"license": "MIT"
},
"node_modules/etag": {
"version": "1.8.1",
"resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
"integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==",
"license": "MIT",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/express": {
"version": "5.2.1",
"resolved": "https://registry.npmjs.org/express/-/express-5.2.1.tgz",
"integrity": "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==",
"license": "MIT",
"dependencies": {
"accepts": "^2.0.0",
"body-parser": "^2.2.1",
"content-disposition": "^1.0.0",
"content-type": "^1.0.5",
"cookie": "^0.7.1",
"cookie-signature": "^1.2.1",
"debug": "^4.4.0",
"depd": "^2.0.0",
"encodeurl": "^2.0.0",
"escape-html": "^1.0.3",
"etag": "^1.8.1",
"finalhandler": "^2.1.0",
"fresh": "^2.0.0",
"http-errors": "^2.0.0",
"merge-descriptors": "^2.0.0",
"mime-types": "^3.0.0",
"on-finished": "^2.4.1",
"once": "^1.4.0",
"parseurl": "^1.3.3",
"proxy-addr": "^2.0.7",
"qs": "^6.14.0",
"range-parser": "^1.2.1",
"router": "^2.2.0",
"send": "^1.1.0",
"serve-static": "^2.2.0",
"statuses": "^2.0.1",
"type-is": "^2.0.1",
"vary": "^1.1.2"
},
"engines": {
"node": ">= 18"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/express"
}
},
"node_modules/express-session": {
"version": "1.18.2",
"resolved": "https://registry.npmjs.org/express-session/-/express-session-1.18.2.tgz",
"integrity": "sha512-SZjssGQC7TzTs9rpPDuUrR23GNZ9+2+IkA/+IJWmvQilTr5OSliEHGF+D9scbIpdC6yGtTI0/VhaHoVes2AN/A==",
"license": "MIT",
"dependencies": {
"cookie": "0.7.2",
"cookie-signature": "1.0.7",
"debug": "2.6.9",
"depd": "~2.0.0",
"on-headers": "~1.1.0",
"parseurl": "~1.3.3",
"safe-buffer": "5.2.1",
"uid-safe": "~2.1.5"
},
"engines": {
"node": ">= 0.8.0"
}
},
"node_modules/express-session/node_modules/cookie-signature": {
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.7.tgz",
"integrity": "sha512-NXdYc3dLr47pBkpUCHtKSwIOQXLVn8dZEuywboCOJY/osA0wFSLlSawr3KN8qXJEyX66FcONTH8EIlVuK0yyFA==",
"license": "MIT"
},
"node_modules/express-session/node_modules/debug": {
"version": "2.6.9",
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
"license": "MIT",
"dependencies": {
"ms": "2.0.0"
}
},
"node_modules/express-session/node_modules/ms": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
"integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
"license": "MIT"
},
"node_modules/express/node_modules/cookie-signature": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz",
"integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==",
"license": "MIT",
"engines": {
"node": ">=6.6.0"
}
},
"node_modules/finalhandler": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.1.tgz",
"integrity": "sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA==",
"license": "MIT",
"dependencies": {
"debug": "^4.4.0",
"encodeurl": "^2.0.0",
"escape-html": "^1.0.3",
"on-finished": "^2.4.1",
"parseurl": "^1.3.3",
"statuses": "^2.0.1"
},
"engines": {
"node": ">= 18.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/express"
}
},
"node_modules/forwarded": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz",
"integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==",
"license": "MIT",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/fresh": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz",
"integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==",
"license": "MIT",
"engines": {
"node": ">= 0.8"
}
},
"node_modules/function-bind": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
"integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
"license": "MIT",
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/get-intrinsic": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz",
"integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==",
"license": "MIT",
"dependencies": {
"call-bind-apply-helpers": "^1.0.2",
"es-define-property": "^1.0.1",
"es-errors": "^1.3.0",
"es-object-atoms": "^1.1.1",
"function-bind": "^1.1.2",
"get-proto": "^1.0.1",
"gopd": "^1.2.0",
"has-symbols": "^1.1.0",
"hasown": "^2.0.2",
"math-intrinsics": "^1.1.0"
},
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/get-proto": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz",
"integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==",
"license": "MIT",
"dependencies": {
"dunder-proto": "^1.0.1",
"es-object-atoms": "^1.0.0"
},
"engines": {
"node": ">= 0.4"
}
},
"node_modules/gopd": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz",
"integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==",
"license": "MIT",
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/has-symbols": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz",
"integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==",
"license": "MIT",
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/hasown": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
"integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
"license": "MIT",
"dependencies": {
"function-bind": "^1.1.2"
},
"engines": {
"node": ">= 0.4"
}
},
"node_modules/http-errors": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz",
"integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==",
"license": "MIT",
"dependencies": {
"depd": "~2.0.0",
"inherits": "~2.0.4",
"setprototypeof": "~1.2.0",
"statuses": "~2.0.2",
"toidentifier": "~1.0.1"
},
"engines": {
"node": ">= 0.8"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/express"
}
},
"node_modules/iconv-lite": {
"version": "0.7.0",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.0.tgz",
"integrity": "sha512-cf6L2Ds3h57VVmkZe+Pn+5APsT7FpqJtEhhieDCvrE2MK5Qk9MyffgQyuxQTm6BChfeZNtcOLHp9IcWRVcIcBQ==",
"license": "MIT",
"dependencies": {
"safer-buffer": ">= 2.1.2 < 3.0.0"
},
"engines": {
"node": ">=0.10.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/express"
}
},
"node_modules/inherits": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
"license": "ISC"
},
"node_modules/ipaddr.js": {
"version": "1.9.1",
"resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
"integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==",
"license": "MIT",
"engines": {
"node": ">= 0.10"
}
},
"node_modules/is-promise": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz",
"integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==",
"license": "MIT"
},
"node_modules/math-intrinsics": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
"integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==",
"license": "MIT",
"engines": {
"node": ">= 0.4"
}
},
"node_modules/media-typer": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz",
"integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==",
"license": "MIT",
"engines": {
"node": ">= 0.8"
}
},
"node_modules/merge-descriptors": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz",
"integrity": "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==",
"license": "MIT",
"engines": {
"node": ">=18"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/mime-db": {
"version": "1.54.0",
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz",
"integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==",
"license": "MIT",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/mime-types": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.2.tgz",
"integrity": "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==",
"license": "MIT",
"dependencies": {
"mime-db": "^1.54.0"
},
"engines": {
"node": ">=18"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/express"
}
},
"node_modules/ms": {
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
"license": "MIT"
},
"node_modules/negotiator": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz",
"integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==",
"license": "MIT",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/object-assign": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
"integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
"license": "MIT",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/object-inspect": {
"version": "1.13.4",
"resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz",
"integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==",
"license": "MIT",
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/on-finished": {
"version": "2.4.1",
"resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz",
"integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==",
"license": "MIT",
"dependencies": {
"ee-first": "1.1.1"
},
"engines": {
"node": ">= 0.8"
}
},
"node_modules/on-headers": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.1.0.tgz",
"integrity": "sha512-737ZY3yNnXy37FHkQxPzt4UZ2UWPWiCZWLvFZ4fu5cueciegX0zGPnrlY6bwRg4FdQOe9YU8MkmJwGhoMybl8A==",
"license": "MIT",
"engines": {
"node": ">= 0.8"
}
},
"node_modules/once": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
"integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
"license": "ISC",
"dependencies": {
"wrappy": "1"
}
},
"node_modules/parseurl": {
"version": "1.3.3",
"resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
"integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==",
"license": "MIT",
"engines": {
"node": ">= 0.8"
}
},
"node_modules/path-to-regexp": {
"version": "8.3.0",
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.3.0.tgz",
"integrity": "sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA==",
"license": "MIT",
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/express"
}
},
"node_modules/pg": {
"version": "8.16.3",
"resolved": "https://registry.npmjs.org/pg/-/pg-8.16.3.tgz",
@ -141,6 +793,224 @@
"node": ">=0.10.0"
}
},
"node_modules/proxy-addr": {
"version": "2.0.7",
"resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
"integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==",
"license": "MIT",
"dependencies": {
"forwarded": "0.2.0",
"ipaddr.js": "1.9.1"
},
"engines": {
"node": ">= 0.10"
}
},
"node_modules/qs": {
"version": "6.14.0",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.14.0.tgz",
"integrity": "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==",
"license": "BSD-3-Clause",
"dependencies": {
"side-channel": "^1.1.0"
},
"engines": {
"node": ">=0.6"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/random-bytes": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/random-bytes/-/random-bytes-1.0.0.tgz",
"integrity": "sha512-iv7LhNVO047HzYR3InF6pUcUsPQiHTM1Qal51DcGSuZFBil1aBBWG5eHPNek7bvILMaYJ/8RU1e8w1AMdHmLQQ==",
"license": "MIT",
"engines": {
"node": ">= 0.8"
}
},
"node_modules/range-parser": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
"integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==",
"license": "MIT",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/raw-body": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.2.tgz",
"integrity": "sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA==",
"license": "MIT",
"dependencies": {
"bytes": "~3.1.2",
"http-errors": "~2.0.1",
"iconv-lite": "~0.7.0",
"unpipe": "~1.0.0"
},
"engines": {
"node": ">= 0.10"
}
},
"node_modules/router": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz",
"integrity": "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==",
"license": "MIT",
"dependencies": {
"debug": "^4.4.0",
"depd": "^2.0.0",
"is-promise": "^4.0.0",
"parseurl": "^1.3.3",
"path-to-regexp": "^8.0.0"
},
"engines": {
"node": ">= 18"
}
},
"node_modules/safe-buffer": {
"version": "5.2.1",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
"integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/feross"
},
{
"type": "patreon",
"url": "https://www.patreon.com/feross"
},
{
"type": "consulting",
"url": "https://feross.org/support"
}
],
"license": "MIT"
},
"node_modules/safer-buffer": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
"license": "MIT"
},
"node_modules/send": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/send/-/send-1.2.0.tgz",
"integrity": "sha512-uaW0WwXKpL9blXE2o0bRhoL2EGXIrZxQ2ZQ4mgcfoBxdFmQold+qWsD2jLrfZ0trjKL6vOw0j//eAwcALFjKSw==",
"license": "MIT",
"dependencies": {
"debug": "^4.3.5",
"encodeurl": "^2.0.0",
"escape-html": "^1.0.3",
"etag": "^1.8.1",
"fresh": "^2.0.0",
"http-errors": "^2.0.0",
"mime-types": "^3.0.1",
"ms": "^2.1.3",
"on-finished": "^2.4.1",
"range-parser": "^1.2.1",
"statuses": "^2.0.1"
},
"engines": {
"node": ">= 18"
}
},
"node_modules/serve-static": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.0.tgz",
"integrity": "sha512-61g9pCh0Vnh7IutZjtLGGpTA355+OPn2TyDv/6ivP2h/AdAVX9azsoxmg2/M6nZeQZNYBEwIcsne1mJd9oQItQ==",
"license": "MIT",
"dependencies": {
"encodeurl": "^2.0.0",
"escape-html": "^1.0.3",
"parseurl": "^1.3.3",
"send": "^1.2.0"
},
"engines": {
"node": ">= 18"
}
},
"node_modules/setprototypeof": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz",
"integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==",
"license": "ISC"
},
"node_modules/side-channel": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz",
"integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==",
"license": "MIT",
"dependencies": {
"es-errors": "^1.3.0",
"object-inspect": "^1.13.3",
"side-channel-list": "^1.0.0",
"side-channel-map": "^1.0.1",
"side-channel-weakmap": "^1.0.2"
},
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/side-channel-list": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz",
"integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==",
"license": "MIT",
"dependencies": {
"es-errors": "^1.3.0",
"object-inspect": "^1.13.3"
},
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/side-channel-map": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz",
"integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==",
"license": "MIT",
"dependencies": {
"call-bound": "^1.0.2",
"es-errors": "^1.3.0",
"get-intrinsic": "^1.2.5",
"object-inspect": "^1.13.3"
},
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/side-channel-weakmap": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz",
"integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==",
"license": "MIT",
"dependencies": {
"call-bound": "^1.0.2",
"es-errors": "^1.3.0",
"get-intrinsic": "^1.2.5",
"object-inspect": "^1.13.3",
"side-channel-map": "^1.0.1"
},
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/split2": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz",
@ -150,6 +1020,74 @@
"node": ">= 10.x"
}
},
"node_modules/statuses": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz",
"integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==",
"license": "MIT",
"engines": {
"node": ">= 0.8"
}
},
"node_modules/toidentifier": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz",
"integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==",
"license": "MIT",
"engines": {
"node": ">=0.6"
}
},
"node_modules/type-is": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/type-is/-/type-is-2.0.1.tgz",
"integrity": "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==",
"license": "MIT",
"dependencies": {
"content-type": "^1.0.5",
"media-typer": "^1.1.0",
"mime-types": "^3.0.0"
},
"engines": {
"node": ">= 0.6"
}
},
"node_modules/uid-safe": {
"version": "2.1.5",
"resolved": "https://registry.npmjs.org/uid-safe/-/uid-safe-2.1.5.tgz",
"integrity": "sha512-KPHm4VL5dDXKz01UuEd88Df+KzynaohSL9fBh096KWAxSKZQDI2uBrVqtvRM4rwrIrRRKsdLNML/lnaaVSRioA==",
"license": "MIT",
"dependencies": {
"random-bytes": "~1.0.0"
},
"engines": {
"node": ">= 0.8"
}
},
"node_modules/unpipe": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
"integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==",
"license": "MIT",
"engines": {
"node": ">= 0.8"
}
},
"node_modules/vary": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
"integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==",
"license": "MIT",
"engines": {
"node": ">= 0.8"
}
},
"node_modules/wrappy": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
"integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
"license": "ISC"
},
"node_modules/ws": {
"version": "8.18.3",
"resolved": "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz",

View file

@ -10,6 +10,10 @@
"author": "",
"license": "ISC",
"dependencies": {
"cookie-parser": "^1.4.7",
"cors": "^2.8.5",
"express": "^5.2.1",
"express-session": "^1.18.2",
"pg": "^8.16.3",
"ws": "^8.18.3"
}

View file

@ -1,5 +1,6 @@
{ pkgs }: {
deps = [
pkgs.unzip
];
env = {
};