server(utils): config, logger, startup

This commit is contained in:
Sam Chau
2025-10-18 03:55:11 +10:30
parent 72415af8a5
commit f3379b9ea4
20 changed files with 508 additions and 97 deletions

3
.gitignore vendored
View File

@@ -8,6 +8,9 @@ node_modules
/.svelte-kit
/build
# Application
/temp
# OS
.DS_Store
Thumbs.db

3
.vscode/settings.json vendored Normal file
View File

@@ -0,0 +1,3 @@
{
"css.lint.unknownAtRules": "ignore"
}

View File

@@ -1,29 +1,28 @@
{
"tasks": {
"dev": "vite dev",
"build": "vite build",
"preview": "vite preview",
"prepare": "svelte-kit sync || echo ''",
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
"format": "prettier --write .",
"lint": "prettier --check . && eslint ."
},
"compilerOptions": {
"lib": ["deno.window", "dom"],
"strict": true
},
"exclude": [
"build/",
".svelte-kit/",
"node_modules/"
],
"fmt": {
"exclude": ["build/", ".svelte-kit/", "node_modules/"],
"indentWidth": 2,
"useTabs": false
},
"lint": {
"exclude": ["build/", ".svelte-kit/", "node_modules/"]
}
}
"imports": {
"$config": "./src/utils/config/config.ts"
},
"tasks": {
"dev": "APP_BASE_PATH=./temp vite dev",
"build": "vite build",
"preview": "vite preview",
"prepare": "svelte-kit sync || echo ''",
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
"format": "prettier --write .",
"lint": "prettier --check . && eslint ."
},
"compilerOptions": {
"lib": ["deno.window", "dom"],
"strict": true
},
"exclude": ["build/", ".svelte-kit/", "node_modules/"],
"fmt": {
"exclude": ["build/", ".svelte-kit/", "node_modules/"],
"indentWidth": 2,
"useTabs": false
},
"lint": {
"exclude": ["build/", ".svelte-kit/", "node_modules/"]
}
}

242
deno.lock generated
View File

@@ -1,10 +1,8 @@
{
"version": "5",
"specifiers": {
"npm:@deno/svelte-adapter@0.1": "0.1.0_@sveltejs+kit@2.47.1__@sveltejs+vite-plugin-svelte@6.2.1___svelte@5.40.2____acorn@8.15.0___vite@7.1.10____@types+node@22.18.11____picomatch@4.0.3___@types+node@22.18.11__svelte@5.40.2___acorn@8.15.0__vite@7.1.10___@types+node@22.18.11___picomatch@4.0.3__acorn@8.15.0__@types+node@22.18.11_@sveltejs+vite-plugin-svelte@6.2.1__svelte@5.40.2___acorn@8.15.0__vite@7.1.10___@types+node@22.18.11___picomatch@4.0.3__@types+node@22.18.11_svelte@5.40.2__acorn@8.15.0_vite@7.1.10__@types+node@22.18.11__picomatch@4.0.3_@types+node@22.18.11",
"npm:@eslint/compat@^1.4.0": "1.4.0_eslint@9.37.0",
"npm:@eslint/js@^9.36.0": "9.37.0",
"npm:@sveltejs/adapter-auto@^6.1.0": "6.1.1_@sveltejs+kit@2.47.1__@sveltejs+vite-plugin-svelte@6.2.1___svelte@5.40.2____acorn@8.15.0___vite@7.1.10____@types+node@22.18.11____picomatch@4.0.3___@types+node@22.18.11__svelte@5.40.2___acorn@8.15.0__vite@7.1.10___@types+node@22.18.11___picomatch@4.0.3__acorn@8.15.0__@types+node@22.18.11_@sveltejs+vite-plugin-svelte@6.2.1__svelte@5.40.2___acorn@8.15.0__vite@7.1.10___@types+node@22.18.11___picomatch@4.0.3__@types+node@22.18.11_svelte@5.40.2__acorn@8.15.0_vite@7.1.10__@types+node@22.18.11__picomatch@4.0.3_@types+node@22.18.11",
"npm:@sveltejs/kit@^2.43.2": "2.47.1_@sveltejs+vite-plugin-svelte@6.2.1__svelte@5.40.2___acorn@8.15.0__vite@7.1.10___@types+node@22.18.11___picomatch@4.0.3__@types+node@22.18.11_svelte@5.40.2__acorn@8.15.0_vite@7.1.10__@types+node@22.18.11__picomatch@4.0.3_acorn@8.15.0_@types+node@22.18.11",
"npm:@sveltejs/vite-plugin-svelte@^6.2.0": "6.2.1_svelte@5.40.2__acorn@8.15.0_vite@7.1.10__@types+node@22.18.11__picomatch@4.0.3_@types+node@22.18.11",
"npm:@tailwindcss/forms@~0.5.10": "0.5.10_tailwindcss@4.1.14",
@@ -19,125 +17,218 @@
"npm:prettier@^3.6.2": "3.6.2",
"npm:svelte-check@^4.3.2": "4.3.3_svelte@5.40.2__acorn@8.15.0_typescript@5.9.3",
"npm:svelte@^5.39.5": "5.40.2_acorn@8.15.0",
"npm:sveltekit-adapter-deno@~0.16.1": "0.16.1_@sveltejs+kit@2.47.1__@sveltejs+vite-plugin-svelte@6.2.1___svelte@5.40.2____acorn@8.15.0___vite@7.1.10____@types+node@22.18.11____picomatch@4.0.3___@types+node@22.18.11__svelte@5.40.2___acorn@8.15.0__vite@7.1.10___@types+node@22.18.11___picomatch@4.0.3__acorn@8.15.0__@types+node@22.18.11_@sveltejs+vite-plugin-svelte@6.2.1__svelte@5.40.2___acorn@8.15.0__vite@7.1.10___@types+node@22.18.11___picomatch@4.0.3__@types+node@22.18.11_svelte@5.40.2__acorn@8.15.0_vite@7.1.10__@types+node@22.18.11__picomatch@4.0.3_@types+node@22.18.11",
"npm:tailwindcss@^4.1.13": "4.1.14",
"npm:typescript-eslint@^8.44.1": "8.46.1_eslint@9.37.0_typescript@5.9.3_@typescript-eslint+parser@8.46.1__eslint@9.37.0__typescript@5.9.3",
"npm:typescript@^5.9.2": "5.9.3",
"npm:vite@^7.1.7": "7.1.10_@types+node@22.18.11_picomatch@4.0.3"
},
"npm": {
"@deno/experimental-route-config@0.0.5": {
"integrity": "sha512-0PN4qij3sC3Qm8WbiOBGlOQz8WtB0AENGkzsTHOYyPenf40iW7OGFid8QT3L8lGApnz3t6ufET0c2XgagV8Jjw==",
"dependencies": [
"urlpattern-polyfill"
]
},
"@deno/svelte-adapter@0.1.0_@sveltejs+kit@2.47.1__@sveltejs+vite-plugin-svelte@6.2.1___svelte@5.40.2____acorn@8.15.0___vite@7.1.10____@types+node@22.18.11____picomatch@4.0.3___@types+node@22.18.11__svelte@5.40.2___acorn@8.15.0__vite@7.1.10___@types+node@22.18.11___picomatch@4.0.3__acorn@8.15.0__@types+node@22.18.11_@sveltejs+vite-plugin-svelte@6.2.1__svelte@5.40.2___acorn@8.15.0__vite@7.1.10___@types+node@22.18.11___picomatch@4.0.3__@types+node@22.18.11_svelte@5.40.2__acorn@8.15.0_vite@7.1.10__@types+node@22.18.11__picomatch@4.0.3_@types+node@22.18.11": {
"integrity": "sha512-fx4Kj1lSx1rJvjtPr3cXO7qKveI1vUyGW8jX+clJHQEqEeIDnEcBW0FpMZEVORPlay2SGydQWSGbwh4j2iENzg==",
"dependencies": [
"@deno/experimental-route-config",
"@sveltejs/kit"
]
"@esbuild/aix-ppc64@0.24.2": {
"integrity": "sha512-thpVCb/rhxE/BnMLQ7GReQLLN8q9qbHmI55F4489/ByVg2aQaQ6kbcLb6FHkocZzQhxc4gx0sCk0tJkKBFzDhA==",
"os": ["aix"],
"cpu": ["ppc64"]
},
"@esbuild/aix-ppc64@0.25.11": {
"integrity": "sha512-Xt1dOL13m8u0WE8iplx9Ibbm+hFAO0GsU2P34UNoDGvZYkY8ifSiy6Zuc1lYxfG7svWE2fzqCUmFp5HCn51gJg==",
"os": ["aix"],
"cpu": ["ppc64"]
},
"@esbuild/android-arm64@0.24.2": {
"integrity": "sha512-cNLgeqCqV8WxfcTIOeL4OAtSmL8JjcN6m09XIgro1Wi7cF4t/THaWEa7eL5CMoMBdjoHOTh/vwTO/o2TRXIyzg==",
"os": ["android"],
"cpu": ["arm64"]
},
"@esbuild/android-arm64@0.25.11": {
"integrity": "sha512-9slpyFBc4FPPz48+f6jyiXOx/Y4v34TUeDDXJpZqAWQn/08lKGeD8aDp9TMn9jDz2CiEuHwfhRmGBvpnd/PWIQ==",
"os": ["android"],
"cpu": ["arm64"]
},
"@esbuild/android-arm@0.24.2": {
"integrity": "sha512-tmwl4hJkCfNHwFB3nBa8z1Uy3ypZpxqxfTQOcHX+xRByyYgunVbZ9MzUUfb0RxaHIMnbHagwAxuTL+tnNM+1/Q==",
"os": ["android"],
"cpu": ["arm"]
},
"@esbuild/android-arm@0.25.11": {
"integrity": "sha512-uoa7dU+Dt3HYsethkJ1k6Z9YdcHjTrSb5NUy66ZfZaSV8hEYGD5ZHbEMXnqLFlbBflLsl89Zke7CAdDJ4JI+Gg==",
"os": ["android"],
"cpu": ["arm"]
},
"@esbuild/android-x64@0.24.2": {
"integrity": "sha512-B6Q0YQDqMx9D7rvIcsXfmJfvUYLoP722bgfBlO5cGvNVb5V/+Y7nhBE3mHV9OpxBf4eAS2S68KZztiPaWq4XYw==",
"os": ["android"],
"cpu": ["x64"]
},
"@esbuild/android-x64@0.25.11": {
"integrity": "sha512-Sgiab4xBjPU1QoPEIqS3Xx+R2lezu0LKIEcYe6pftr56PqPygbB7+szVnzoShbx64MUupqoE0KyRlN7gezbl8g==",
"os": ["android"],
"cpu": ["x64"]
},
"@esbuild/darwin-arm64@0.24.2": {
"integrity": "sha512-kj3AnYWc+CekmZnS5IPu9D+HWtUI49hbnyqk0FLEJDbzCIQt7hg7ucF1SQAilhtYpIujfaHr6O0UHlzzSPdOeA==",
"os": ["darwin"],
"cpu": ["arm64"]
},
"@esbuild/darwin-arm64@0.25.11": {
"integrity": "sha512-VekY0PBCukppoQrycFxUqkCojnTQhdec0vevUL/EDOCnXd9LKWqD/bHwMPzigIJXPhC59Vd1WFIL57SKs2mg4w==",
"os": ["darwin"],
"cpu": ["arm64"]
},
"@esbuild/darwin-x64@0.24.2": {
"integrity": "sha512-WeSrmwwHaPkNR5H3yYfowhZcbriGqooyu3zI/3GGpF8AyUdsrrP0X6KumITGA9WOyiJavnGZUwPGvxvwfWPHIA==",
"os": ["darwin"],
"cpu": ["x64"]
},
"@esbuild/darwin-x64@0.25.11": {
"integrity": "sha512-+hfp3yfBalNEpTGp9loYgbknjR695HkqtY3d3/JjSRUyPg/xd6q+mQqIb5qdywnDxRZykIHs3axEqU6l1+oWEQ==",
"os": ["darwin"],
"cpu": ["x64"]
},
"@esbuild/freebsd-arm64@0.24.2": {
"integrity": "sha512-UN8HXjtJ0k/Mj6a9+5u6+2eZ2ERD7Edt1Q9IZiB5UZAIdPnVKDoG7mdTVGhHJIeEml60JteamR3qhsr1r8gXvg==",
"os": ["freebsd"],
"cpu": ["arm64"]
},
"@esbuild/freebsd-arm64@0.25.11": {
"integrity": "sha512-CmKjrnayyTJF2eVuO//uSjl/K3KsMIeYeyN7FyDBjsR3lnSJHaXlVoAK8DZa7lXWChbuOk7NjAc7ygAwrnPBhA==",
"os": ["freebsd"],
"cpu": ["arm64"]
},
"@esbuild/freebsd-x64@0.24.2": {
"integrity": "sha512-TvW7wE/89PYW+IevEJXZ5sF6gJRDY/14hyIGFXdIucxCsbRmLUcjseQu1SyTko+2idmCw94TgyaEZi9HUSOe3Q==",
"os": ["freebsd"],
"cpu": ["x64"]
},
"@esbuild/freebsd-x64@0.25.11": {
"integrity": "sha512-Dyq+5oscTJvMaYPvW3x3FLpi2+gSZTCE/1ffdwuM6G1ARang/mb3jvjxs0mw6n3Lsw84ocfo9CrNMqc5lTfGOw==",
"os": ["freebsd"],
"cpu": ["x64"]
},
"@esbuild/linux-arm64@0.24.2": {
"integrity": "sha512-7HnAD6074BW43YvvUmE/35Id9/NB7BeX5EoNkK9obndmZBUk8xmJJeU7DwmUeN7tkysslb2eSl6CTrYz6oEMQg==",
"os": ["linux"],
"cpu": ["arm64"]
},
"@esbuild/linux-arm64@0.25.11": {
"integrity": "sha512-Qr8AzcplUhGvdyUF08A1kHU3Vr2O88xxP0Tm8GcdVOUm25XYcMPp2YqSVHbLuXzYQMf9Bh/iKx7YPqECs6ffLA==",
"os": ["linux"],
"cpu": ["arm64"]
},
"@esbuild/linux-arm@0.24.2": {
"integrity": "sha512-n0WRM/gWIdU29J57hJyUdIsk0WarGd6To0s+Y+LwvlC55wt+GT/OgkwoXCXvIue1i1sSNWblHEig00GBWiJgfA==",
"os": ["linux"],
"cpu": ["arm"]
},
"@esbuild/linux-arm@0.25.11": {
"integrity": "sha512-TBMv6B4kCfrGJ8cUPo7vd6NECZH/8hPpBHHlYI3qzoYFvWu2AdTvZNuU/7hsbKWqu/COU7NIK12dHAAqBLLXgw==",
"os": ["linux"],
"cpu": ["arm"]
},
"@esbuild/linux-ia32@0.24.2": {
"integrity": "sha512-sfv0tGPQhcZOgTKO3oBE9xpHuUqguHvSo4jl+wjnKwFpapx+vUDcawbwPNuBIAYdRAvIDBfZVvXprIj3HA+Ugw==",
"os": ["linux"],
"cpu": ["ia32"]
},
"@esbuild/linux-ia32@0.25.11": {
"integrity": "sha512-TmnJg8BMGPehs5JKrCLqyWTVAvielc615jbkOirATQvWWB1NMXY77oLMzsUjRLa0+ngecEmDGqt5jiDC6bfvOw==",
"os": ["linux"],
"cpu": ["ia32"]
},
"@esbuild/linux-loong64@0.24.2": {
"integrity": "sha512-CN9AZr8kEndGooS35ntToZLTQLHEjtVB5n7dl8ZcTZMonJ7CCfStrYhrzF97eAecqVbVJ7APOEe18RPI4KLhwQ==",
"os": ["linux"],
"cpu": ["loong64"]
},
"@esbuild/linux-loong64@0.25.11": {
"integrity": "sha512-DIGXL2+gvDaXlaq8xruNXUJdT5tF+SBbJQKbWy/0J7OhU8gOHOzKmGIlfTTl6nHaCOoipxQbuJi7O++ldrxgMw==",
"os": ["linux"],
"cpu": ["loong64"]
},
"@esbuild/linux-mips64el@0.24.2": {
"integrity": "sha512-iMkk7qr/wl3exJATwkISxI7kTcmHKE+BlymIAbHO8xanq/TjHaaVThFF6ipWzPHryoFsesNQJPE/3wFJw4+huw==",
"os": ["linux"],
"cpu": ["mips64el"]
},
"@esbuild/linux-mips64el@0.25.11": {
"integrity": "sha512-Osx1nALUJu4pU43o9OyjSCXokFkFbyzjXb6VhGIJZQ5JZi8ylCQ9/LFagolPsHtgw6himDSyb5ETSfmp4rpiKQ==",
"os": ["linux"],
"cpu": ["mips64el"]
},
"@esbuild/linux-ppc64@0.24.2": {
"integrity": "sha512-shsVrgCZ57Vr2L8mm39kO5PPIb+843FStGt7sGGoqiiWYconSxwTiuswC1VJZLCjNiMLAMh34jg4VSEQb+iEbw==",
"os": ["linux"],
"cpu": ["ppc64"]
},
"@esbuild/linux-ppc64@0.25.11": {
"integrity": "sha512-nbLFgsQQEsBa8XSgSTSlrnBSrpoWh7ioFDUmwo158gIm5NNP+17IYmNWzaIzWmgCxq56vfr34xGkOcZ7jX6CPw==",
"os": ["linux"],
"cpu": ["ppc64"]
},
"@esbuild/linux-riscv64@0.24.2": {
"integrity": "sha512-4eSFWnU9Hhd68fW16GD0TINewo1L6dRrB+oLNNbYyMUAeOD2yCK5KXGK1GH4qD/kT+bTEXjsyTCiJGHPZ3eM9Q==",
"os": ["linux"],
"cpu": ["riscv64"]
},
"@esbuild/linux-riscv64@0.25.11": {
"integrity": "sha512-HfyAmqZi9uBAbgKYP1yGuI7tSREXwIb438q0nqvlpxAOs3XnZ8RsisRfmVsgV486NdjD7Mw2UrFSw51lzUk1ww==",
"os": ["linux"],
"cpu": ["riscv64"]
},
"@esbuild/linux-s390x@0.24.2": {
"integrity": "sha512-S0Bh0A53b0YHL2XEXC20bHLuGMOhFDO6GN4b3YjRLK//Ep3ql3erpNcPlEFed93hsQAjAQDNsvcK+hV90FubSw==",
"os": ["linux"],
"cpu": ["s390x"]
},
"@esbuild/linux-s390x@0.25.11": {
"integrity": "sha512-HjLqVgSSYnVXRisyfmzsH6mXqyvj0SA7pG5g+9W7ESgwA70AXYNpfKBqh1KbTxmQVaYxpzA/SvlB9oclGPbApw==",
"os": ["linux"],
"cpu": ["s390x"]
},
"@esbuild/linux-x64@0.24.2": {
"integrity": "sha512-8Qi4nQcCTbLnK9WoMjdC9NiTG6/E38RNICU6sUNqK0QFxCYgoARqVqxdFmWkdonVsvGqWhmm7MO0jyTqLqwj0Q==",
"os": ["linux"],
"cpu": ["x64"]
},
"@esbuild/linux-x64@0.25.11": {
"integrity": "sha512-HSFAT4+WYjIhrHxKBwGmOOSpphjYkcswF449j6EjsjbinTZbp8PJtjsVK1XFJStdzXdy/jaddAep2FGY+wyFAQ==",
"os": ["linux"],
"cpu": ["x64"]
},
"@esbuild/netbsd-arm64@0.24.2": {
"integrity": "sha512-wuLK/VztRRpMt9zyHSazyCVdCXlpHkKm34WUyinD2lzK07FAHTq0KQvZZlXikNWkDGoT6x3TD51jKQ7gMVpopw==",
"os": ["netbsd"],
"cpu": ["arm64"]
},
"@esbuild/netbsd-arm64@0.25.11": {
"integrity": "sha512-hr9Oxj1Fa4r04dNpWr3P8QKVVsjQhqrMSUzZzf+LZcYjZNqhA3IAfPQdEh1FLVUJSiu6sgAwp3OmwBfbFgG2Xg==",
"os": ["netbsd"],
"cpu": ["arm64"]
},
"@esbuild/netbsd-x64@0.24.2": {
"integrity": "sha512-VefFaQUc4FMmJuAxmIHgUmfNiLXY438XrL4GDNV1Y1H/RW3qow68xTwjZKfj/+Plp9NANmzbH5R40Meudu8mmw==",
"os": ["netbsd"],
"cpu": ["x64"]
},
"@esbuild/netbsd-x64@0.25.11": {
"integrity": "sha512-u7tKA+qbzBydyj0vgpu+5h5AeudxOAGncb8N6C9Kh1N4n7wU1Xw1JDApsRjpShRpXRQlJLb9wY28ELpwdPcZ7A==",
"os": ["netbsd"],
"cpu": ["x64"]
},
"@esbuild/openbsd-arm64@0.24.2": {
"integrity": "sha512-YQbi46SBct6iKnszhSvdluqDmxCJA+Pu280Av9WICNwQmMxV7nLRHZfjQzwbPs3jeWnuAhE9Jy0NrnJ12Oz+0A==",
"os": ["openbsd"],
"cpu": ["arm64"]
},
"@esbuild/openbsd-arm64@0.25.11": {
"integrity": "sha512-Qq6YHhayieor3DxFOoYM1q0q1uMFYb7cSpLD2qzDSvK1NAvqFi8Xgivv0cFC6J+hWVw2teCYltyy9/m/14ryHg==",
"os": ["openbsd"],
"cpu": ["arm64"]
},
"@esbuild/openbsd-x64@0.24.2": {
"integrity": "sha512-+iDS6zpNM6EnJyWv0bMGLWSWeXGN/HTaF/LXHXHwejGsVi+ooqDfMCCTerNFxEkM3wYVcExkeGXNqshc9iMaOA==",
"os": ["openbsd"],
"cpu": ["x64"]
},
"@esbuild/openbsd-x64@0.25.11": {
"integrity": "sha512-CN+7c++kkbrckTOz5hrehxWN7uIhFFlmS/hqziSFVWpAzpWrQoAG4chH+nN3Be+Kzv/uuo7zhX716x3Sn2Jduw==",
"os": ["openbsd"],
@@ -148,21 +239,41 @@
"os": ["openharmony"],
"cpu": ["arm64"]
},
"@esbuild/sunos-x64@0.24.2": {
"integrity": "sha512-hTdsW27jcktEvpwNHJU4ZwWFGkz2zRJUz8pvddmXPtXDzVKTTINmlmga3ZzwcuMpUvLw7JkLy9QLKyGpD2Yxig==",
"os": ["sunos"],
"cpu": ["x64"]
},
"@esbuild/sunos-x64@0.25.11": {
"integrity": "sha512-nq2xdYaWxyg9DcIyXkZhcYulC6pQ2FuCgem3LI92IwMgIZ69KHeY8T4Y88pcwoLIjbed8n36CyKoYRDygNSGhA==",
"os": ["sunos"],
"cpu": ["x64"]
},
"@esbuild/win32-arm64@0.24.2": {
"integrity": "sha512-LihEQ2BBKVFLOC9ZItT9iFprsE9tqjDjnbulhHoFxYQtQfai7qfluVODIYxt1PgdoyQkz23+01rzwNwYfutxUQ==",
"os": ["win32"],
"cpu": ["arm64"]
},
"@esbuild/win32-arm64@0.25.11": {
"integrity": "sha512-3XxECOWJq1qMZ3MN8srCJ/QfoLpL+VaxD/WfNRm1O3B4+AZ/BnLVgFbUV3eiRYDMXetciH16dwPbbHqwe1uU0Q==",
"os": ["win32"],
"cpu": ["arm64"]
},
"@esbuild/win32-ia32@0.24.2": {
"integrity": "sha512-q+iGUwfs8tncmFC9pcnD5IvRHAzmbwQ3GPS5/ceCyHdjXubwQWI12MKWSNSMYLJMq23/IUCvJMS76PDqXe1fxA==",
"os": ["win32"],
"cpu": ["ia32"]
},
"@esbuild/win32-ia32@0.25.11": {
"integrity": "sha512-3ukss6gb9XZ8TlRyJlgLn17ecsK4NSQTmdIXRASVsiS2sQ6zPPZklNJT5GR5tE/MUarymmy8kCEf5xPCNCqVOA==",
"os": ["win32"],
"cpu": ["ia32"]
},
"@esbuild/win32-x64@0.24.2": {
"integrity": "sha512-7VTgWzgMGvup6aSqDPLiW5zHaxYJGTO4OokMjIlrCtf+VpEL+cXKtCvg723iguPYI5oaUNdS+/V7OU2gvXVWEg==",
"os": ["win32"],
"cpu": ["x64"]
},
"@esbuild/win32-x64@0.25.11": {
"integrity": "sha512-D7Hpz6A2L4hzsRpPaCYkQnGOotdUpDzSGRIv9I+1ITdHROSFUWW95ZPZWQmGka1Fg7W3zFJowyn9WGwMJ0+KPA==",
"os": ["win32"],
@@ -423,12 +534,6 @@
"acorn"
]
},
"@sveltejs/adapter-auto@6.1.1_@sveltejs+kit@2.47.1__@sveltejs+vite-plugin-svelte@6.2.1___svelte@5.40.2____acorn@8.15.0___vite@7.1.10____@types+node@22.18.11____picomatch@4.0.3___@types+node@22.18.11__svelte@5.40.2___acorn@8.15.0__vite@7.1.10___@types+node@22.18.11___picomatch@4.0.3__acorn@8.15.0__@types+node@22.18.11_@sveltejs+vite-plugin-svelte@6.2.1__svelte@5.40.2___acorn@8.15.0__vite@7.1.10___@types+node@22.18.11___picomatch@4.0.3__@types+node@22.18.11_svelte@5.40.2__acorn@8.15.0_vite@7.1.10__@types+node@22.18.11__picomatch@4.0.3_@types+node@22.18.11": {
"integrity": "sha512-cBNt4jgH4KuaNO5gRSB2CZKkGtz+OCZ8lPjRQGjhvVUD4akotnj2weUia6imLl2v07K3IgsQRyM36909miSwoQ==",
"dependencies": [
"@sveltejs/kit"
]
},
"@sveltejs/kit@2.47.1_@sveltejs+vite-plugin-svelte@6.2.1__svelte@5.40.2___acorn@8.15.0__vite@7.1.10___@types+node@22.18.11___picomatch@4.0.3__@types+node@22.18.11_svelte@5.40.2__acorn@8.15.0_vite@7.1.10__@types+node@22.18.11__picomatch@4.0.3_acorn@8.15.0_@types+node@22.18.11": {
"integrity": "sha512-1v+MbMHxTi6ctQyxmz3owLKqZGaBHyx4EQqTdq/PvDswPFzw3WlqhrOKOh2ZzH23+XpQGEF9G+KDIgYJE+byvg==",
"dependencies": [
@@ -826,35 +931,67 @@
"tapable"
]
},
"esbuild@0.24.2": {
"integrity": "sha512-+9egpBW8I3CD5XPe0n6BfT5fxLzxrlDzqydF3aviG+9ni1lDC/OvMHcxqEFV0+LANZG5R1bFMWfUrjVsdwxJvA==",
"optionalDependencies": [
"@esbuild/aix-ppc64@0.24.2",
"@esbuild/android-arm@0.24.2",
"@esbuild/android-arm64@0.24.2",
"@esbuild/android-x64@0.24.2",
"@esbuild/darwin-arm64@0.24.2",
"@esbuild/darwin-x64@0.24.2",
"@esbuild/freebsd-arm64@0.24.2",
"@esbuild/freebsd-x64@0.24.2",
"@esbuild/linux-arm@0.24.2",
"@esbuild/linux-arm64@0.24.2",
"@esbuild/linux-ia32@0.24.2",
"@esbuild/linux-loong64@0.24.2",
"@esbuild/linux-mips64el@0.24.2",
"@esbuild/linux-ppc64@0.24.2",
"@esbuild/linux-riscv64@0.24.2",
"@esbuild/linux-s390x@0.24.2",
"@esbuild/linux-x64@0.24.2",
"@esbuild/netbsd-arm64@0.24.2",
"@esbuild/netbsd-x64@0.24.2",
"@esbuild/openbsd-arm64@0.24.2",
"@esbuild/openbsd-x64@0.24.2",
"@esbuild/sunos-x64@0.24.2",
"@esbuild/win32-arm64@0.24.2",
"@esbuild/win32-ia32@0.24.2",
"@esbuild/win32-x64@0.24.2"
],
"scripts": true,
"bin": true
},
"esbuild@0.25.11": {
"integrity": "sha512-KohQwyzrKTQmhXDW1PjCv3Tyspn9n5GcY2RTDqeORIdIJY8yKIF7sTSopFmn/wpMPW4rdPXI0UE5LJLuq3bx0Q==",
"optionalDependencies": [
"@esbuild/aix-ppc64",
"@esbuild/android-arm",
"@esbuild/android-arm64",
"@esbuild/android-x64",
"@esbuild/darwin-arm64",
"@esbuild/darwin-x64",
"@esbuild/freebsd-arm64",
"@esbuild/freebsd-x64",
"@esbuild/linux-arm",
"@esbuild/linux-arm64",
"@esbuild/linux-ia32",
"@esbuild/linux-loong64",
"@esbuild/linux-mips64el",
"@esbuild/linux-ppc64",
"@esbuild/linux-riscv64",
"@esbuild/linux-s390x",
"@esbuild/linux-x64",
"@esbuild/netbsd-arm64",
"@esbuild/netbsd-x64",
"@esbuild/openbsd-arm64",
"@esbuild/openbsd-x64",
"@esbuild/aix-ppc64@0.25.11",
"@esbuild/android-arm@0.25.11",
"@esbuild/android-arm64@0.25.11",
"@esbuild/android-x64@0.25.11",
"@esbuild/darwin-arm64@0.25.11",
"@esbuild/darwin-x64@0.25.11",
"@esbuild/freebsd-arm64@0.25.11",
"@esbuild/freebsd-x64@0.25.11",
"@esbuild/linux-arm@0.25.11",
"@esbuild/linux-arm64@0.25.11",
"@esbuild/linux-ia32@0.25.11",
"@esbuild/linux-loong64@0.25.11",
"@esbuild/linux-mips64el@0.25.11",
"@esbuild/linux-ppc64@0.25.11",
"@esbuild/linux-riscv64@0.25.11",
"@esbuild/linux-s390x@0.25.11",
"@esbuild/linux-x64@0.25.11",
"@esbuild/netbsd-arm64@0.25.11",
"@esbuild/netbsd-x64@0.25.11",
"@esbuild/openbsd-arm64@0.25.11",
"@esbuild/openbsd-x64@0.25.11",
"@esbuild/openharmony-arm64",
"@esbuild/sunos-x64",
"@esbuild/win32-arm64",
"@esbuild/win32-ia32",
"@esbuild/win32-x64"
"@esbuild/sunos-x64@0.25.11",
"@esbuild/win32-arm64@0.25.11",
"@esbuild/win32-ia32@0.25.11",
"@esbuild/win32-x64@0.25.11"
],
"scripts": true,
"bin": true
@@ -1537,6 +1674,13 @@
"zimmerframe"
]
},
"sveltekit-adapter-deno@0.16.1_@sveltejs+kit@2.47.1__@sveltejs+vite-plugin-svelte@6.2.1___svelte@5.40.2____acorn@8.15.0___vite@7.1.10____@types+node@22.18.11____picomatch@4.0.3___@types+node@22.18.11__svelte@5.40.2___acorn@8.15.0__vite@7.1.10___@types+node@22.18.11___picomatch@4.0.3__acorn@8.15.0__@types+node@22.18.11_@sveltejs+vite-plugin-svelte@6.2.1__svelte@5.40.2___acorn@8.15.0__vite@7.1.10___@types+node@22.18.11___picomatch@4.0.3__@types+node@22.18.11_svelte@5.40.2__acorn@8.15.0_vite@7.1.10__@types+node@22.18.11__picomatch@4.0.3_@types+node@22.18.11": {
"integrity": "sha512-AwJj5Y9yoJ5BQVhfFURrVex5GrQmU/v06pW8vs6tyWaJxEYrnF6o3gbELVtCOI/YRl4G02I7n8fBDMY+/cgBdg==",
"dependencies": [
"@sveltejs/kit",
"esbuild@0.24.2"
]
},
"tailwindcss@4.1.14": {
"integrity": "sha512-b7pCxjGO98LnxVkKjaZSDeNuljC4ueKUddjENJOADtubtdo8llTaJy7HwBMeLNSSo2N5QIAgklslK1+Ir8r6CA=="
},
@@ -1605,9 +1749,6 @@
"punycode"
]
},
"urlpattern-polyfill@10.1.0": {
"integrity": "sha512-IGjKp/o0NL3Bso1PymYURCJxMPNAf/ILOpendP9f5B6e1rTJgdgiOvgfoT8VxCAdY+Wisb9uhGaJJf3yZ2V9nw=="
},
"util-deprecate@1.0.2": {
"integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="
},
@@ -1615,7 +1756,7 @@
"integrity": "sha512-CmuvUBzVJ/e3HGxhg6cYk88NGgTnBoOo7ogtfJJ0fefUWAxN/WDSUa50o+oVBxuIhO8FoEZW0j2eW7sfjs5EtA==",
"dependencies": [
"@types/node",
"esbuild",
"esbuild@0.25.11",
"fdir",
"picomatch@4.0.3",
"postcss",
@@ -1665,10 +1806,8 @@
"workspace": {
"packageJson": {
"dependencies": [
"npm:@deno/svelte-adapter@0.1",
"npm:@eslint/compat@^1.4.0",
"npm:@eslint/js@^9.36.0",
"npm:@sveltejs/adapter-auto@^6.1.0",
"npm:@sveltejs/kit@^2.43.2",
"npm:@sveltejs/vite-plugin-svelte@^6.2.0",
"npm:@tailwindcss/forms@~0.5.10",
@@ -1683,6 +1822,7 @@
"npm:prettier@^3.6.2",
"npm:svelte-check@^4.3.2",
"npm:svelte@^5.39.5",
"npm:sveltekit-adapter-deno@~0.16.1",
"npm:tailwindcss@^4.1.13",
"npm:typescript-eslint@^8.44.1",
"npm:typescript@^5.9.2",

View File

@@ -3,11 +3,12 @@
"private": true,
"version": "2.0.0",
"type": "module",
"dependencies": {
"sveltekit-adapter-deno": "^0.16.1"
},
"devDependencies": {
"@deno/svelte-adapter": "^0.1.0",
"@eslint/compat": "^1.4.0",
"@eslint/js": "^9.36.0",
"@sveltejs/adapter-auto": "^6.1.0",
"@sveltejs/kit": "^2.43.2",
"@sveltejs/vite-plugin-svelte": "^6.2.0",
"@tailwindcss/forms": "^0.5.10",

20
src/deno.d.ts vendored Normal file
View File

@@ -0,0 +1,20 @@
/**
* Deno global type declarations for SvelteKit project
*/
declare namespace Deno {
export const env: {
get(key: string): string | undefined;
};
export function mkdir(
path: string,
options?: { recursive?: boolean }
): Promise<void>;
export function writeTextFile(
path: string,
data: string,
options?: { append?: boolean }
): Promise<void>;
}

8
src/hooks.server.ts Normal file
View File

@@ -0,0 +1,8 @@
import { config } from '$config';
import { logStartup } from './utils/logger/startup.ts';
// Initialize configuration on server startup
await config.init();
// Log startup banner
await logStartup();

View File

@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="107" height="128" viewBox="0 0 107 128"><title>svelte-logo</title><path d="M94.157 22.819c-10.4-14.885-30.94-19.297-45.792-9.835L22.282 29.608A29.92 29.92 0 0 0 8.764 49.65a31.5 31.5 0 0 0 3.108 20.231 30 30 0 0 0-4.477 11.183 31.9 31.9 0 0 0 5.448 24.116c10.402 14.887 30.942 19.297 45.791 9.835l26.083-16.624A29.92 29.92 0 0 0 98.235 78.35a31.53 31.53 0 0 0-3.105-20.232 30 30 0 0 0 4.474-11.182 31.88 31.88 0 0 0-5.447-24.116" style="fill:#ff3e00"/><path d="M45.817 106.582a20.72 20.72 0 0 1-22.237-8.243 19.17 19.17 0 0 1-3.277-14.503 18 18 0 0 1 .624-2.435l.49-1.498 1.337.981a33.6 33.6 0 0 0 10.203 5.098l.97.294-.09.968a5.85 5.85 0 0 0 1.052 3.878 6.24 6.24 0 0 0 6.695 2.485 5.8 5.8 0 0 0 1.603-.704L69.27 76.28a5.43 5.43 0 0 0 2.45-3.631 5.8 5.8 0 0 0-.987-4.371 6.24 6.24 0 0 0-6.698-2.487 5.7 5.7 0 0 0-1.6.704l-9.953 6.345a19 19 0 0 1-5.296 2.326 20.72 20.72 0 0 1-22.237-8.243 19.17 19.17 0 0 1-3.277-14.502 17.99 17.99 0 0 1 8.13-12.052l26.081-16.623a19 19 0 0 1 5.3-2.329 20.72 20.72 0 0 1 22.237 8.243 19.17 19.17 0 0 1 3.277 14.503 18 18 0 0 1-.624 2.435l-.49 1.498-1.337-.98a33.6 33.6 0 0 0-10.203-5.1l-.97-.294.09-.968a5.86 5.86 0 0 0-1.052-3.878 6.24 6.24 0 0 0-6.696-2.485 5.8 5.8 0 0 0-1.602.704L37.73 51.72a5.42 5.42 0 0 0-2.449 3.63 5.79 5.79 0 0 0 .986 4.372 6.24 6.24 0 0 0 6.698 2.486 5.8 5.8 0 0 0 1.602-.704l9.952-6.342a19 19 0 0 1 5.295-2.328 20.72 20.72 0 0 1 22.237 8.242 19.17 19.17 0 0 1 3.277 14.503 18 18 0 0 1-8.13 12.053l-26.081 16.622a19 19 0 0 1-5.3 2.328" style="fill:#fff"/></svg>

Before

Width:  |  Height:  |  Size: 1.5 KiB

View File

@@ -1 +0,0 @@
// place files you want to import through the `$lib` alias in this folder.

View File

@@ -1,12 +1,13 @@
<script lang="ts">
import '../app.css';
import favicon from '$lib/assets/favicon.svg';
import logo from '../static/logo.svg';
let { children } = $props();
</script>
<svelte:head>
<link rel="icon" href={favicon} />
<link rel="icon" href={logo} />
<title>Profilarr</title>
</svelte:head>
{@render children?.()}

17
src/static/logo.svg Normal file
View File

@@ -0,0 +1,17 @@
<svg width="1024" height="1024" viewBox="0 0 1024 1024" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_27_43)">
<path d="M200 0C310.457 0 400 89.5431 400 200C400 293.192 336.261 371.496 250 393.698V630.301C320.28 648.39 375.61 703.72 393.699 774H674C728.797 774 773.3 729.926 773.992 675.293L774 674V393.698C687.739 371.496 624 293.192 624 200C624 89.5431 713.543 0 824 0C934.457 0 1024 89.5431 1024 200C1024 293.192 960.261 371.496 874 393.698V674C874 784.457 784.457 874 674 874H393.699C371.497 960.261 293.192 1024 200 1024C89.5431 1024 0 934.457 0 824C0 730.808 63.7386 652.503 150 630.301V393.698C63.7387 371.496 0 293.192 0 200C0 89.5431 89.5431 0 200 0ZM200 724C144.772 724 100 768.772 100 824C100 879.228 144.772 924 200 924C255.228 924 300 879.228 300 824C300 768.772 255.228 724 200 724ZM200 100C144.772 100 100 144.772 100 200C100 255.228 144.772 300 200 300C255.228 300 300 255.228 300 200C300 144.772 255.228 100 200 100ZM824 100C768.772 100 724 144.772 724 200C724 255.228 768.772 300 824 300C879.228 300 924 255.228 924 200C924 144.772 879.228 100 824 100Z" fill="url(#paint0_linear_27_43)"/>
<circle cx="200" cy="200" r="100" fill="#4A90E2"/>
<circle cx="200" cy="824" r="100" fill="#4A90E2"/>
<circle cx="824" cy="200" r="100" fill="#4A90E2"/>
</g>
<defs>
<linearGradient id="paint0_linear_27_43" x1="888.5" y1="956.5" x2="47" y2="11" gradientUnits="userSpaceOnUse">
<stop stop-color="#374151"/>
<stop offset="1" stop-color="#1F2937"/>
</linearGradient>
<clipPath id="clip0_27_43">
<rect width="1024" height="1024" fill="white"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

@@ -0,0 +1,46 @@
/**
* Application configuration singleton
*/
class Config {
private basePath: string;
constructor() {
// Default base path logic:
// 1. Check environment variable
// 2. Fall back to /app (Docker default)
this.basePath = Deno.env.get('APP_BASE_PATH') || '/app';
}
/**
* Initialize the configuration (create directories)
* Must be called before using the config
*/
async init(): Promise<void> {
await Deno.mkdir(this.paths.logs, { recursive: true });
}
/**
* Set the base path for the application
*/
setBasePath(path: string): void {
this.basePath = path;
}
/**
* Application paths (relative to base)
*/
readonly paths = {
get base(): string {
return config.basePath;
},
get logs(): string {
return `${config.basePath}/logs`;
},
get logFile(): string {
return `${config.basePath}/logs/app.log`;
}
};
}
export const config = new Config();

View File

@@ -0,0 +1,12 @@
/**
* ANSI color codes for terminal output
*/
export const colors = {
reset: '\x1b[0m',
grey: '\x1b[90m',
cyan: '\x1b[36m',
green: '\x1b[32m',
yellow: '\x1b[33m',
red: '\x1b[31m'
};

111
src/utils/logger/logger.ts Normal file
View File

@@ -0,0 +1,111 @@
/**
* Logger singleton with console and file output
*/
import { config } from '$config';
import { colors } from './colors.ts';
import type { LogOptions, LogEntry } from './types.ts';
class Logger {
private formatTimestamp(): string {
const timestamp = new Date().toISOString();
return `${colors.grey}${timestamp}${colors.reset}`;
}
private formatLevel(level: string, color: string): string {
return `${color}${level.padEnd(5)}${colors.reset}`;
}
private formatSource(source?: string): string {
if (!source) return '';
return `${colors.grey}[${source}]${colors.reset}`;
}
private formatMeta(meta?: unknown): string {
if (!meta) return '';
return `${colors.grey}${JSON.stringify(meta)}${colors.reset}`;
}
private async log(
level: string,
color: string,
message: string,
options?: LogOptions
): Promise<void> {
const timestamp = new Date().toISOString();
// Console output (colored)
const consoleParts = [
this.formatTimestamp(),
this.formatLevel(level, color),
message,
options?.source ? this.formatSource(options.source) : '',
options?.meta ? this.formatMeta(options.meta) : ''
].filter(Boolean);
console.log(consoleParts.join(' | '));
// File output (JSON)
const logEntry: LogEntry = {
timestamp,
level,
message,
...(options?.source ? { source: options.source } : {}),
...(options?.meta ? { meta: options.meta } : {})
};
try {
await Deno.writeTextFile(config.paths.logFile, JSON.stringify(logEntry) + '\n', {
append: true
});
} catch (error) {
// If file write fails, at least we have console output
console.error('Failed to write to log file:', error);
}
}
async debug(message: string, options?: LogOptions): Promise<void> {
await this.log('DEBUG', colors.cyan, message, options);
}
async info(message: string, options?: LogOptions): Promise<void> {
await this.log('INFO', colors.green, message, options);
}
async warn(message: string, options?: LogOptions): Promise<void> {
await this.log('WARN', colors.yellow, message, options);
}
async error(message: string, options?: LogOptions): Promise<void> {
await this.log('ERROR', colors.red, message, options);
}
async errorWithTrace(message: string, error?: Error, options?: LogOptions): Promise<void> {
await this.log('ERROR', colors.red, message, options);
// Print stack trace to console
if (error?.stack) {
console.log(`${colors.grey}${error.stack}${colors.reset}`);
}
// Write stack trace to file
if (error?.stack) {
const traceEntry: LogEntry = {
timestamp: new Date().toISOString(),
level: 'ERROR',
message: 'Stack trace',
meta: { stack: error.stack }
};
try {
await Deno.writeTextFile(config.paths.logFile, JSON.stringify(traceEntry) + '\n', {
append: true
});
} catch (writeError) {
console.error('Failed to write stack trace to log file:', writeError);
}
}
}
}
export const logger = new Logger();

View File

@@ -0,0 +1,26 @@
/**
* Startup banner and logging
*/
import { logger } from './logger.ts';
const BANNER = String.raw`
_____.__.__
_____________ _____/ ____\__| | _____ ______________
\____ \_ __ \/ _ \ __\| | | \__ \\_ __ \_ __ \
| |_> > | \( <_> ) | | | |__/ __ \| | \/| | \/
| __/|__| \____/|__| |__|____(____ /__| |__|
|__| \/
`;
export async function logStartup(): Promise<void> {
// Print banner (not logged to file, just console)
console.log(BANNER);
// Log startup info
await logger.info('Server started');
// Log environment
const env = Deno.env.get('NODE_ENV') || Deno.env.get('DENO_ENV') || 'development';
await logger.info(`Environment: ${env}`);
}

18
src/utils/logger/types.ts Normal file
View File

@@ -0,0 +1,18 @@
/**
* Logger types and interfaces
*/
export interface LogOptions {
/** Optional metadata to include with the log */
meta?: unknown;
/** Optional source/context tag (e.g., "database", "api") */
source?: string;
}
export interface LogEntry {
timestamp: string;
level: string;
message: string;
source?: string;
meta?: unknown;
}

View File

@@ -1,3 +0,0 @@
# allow crawling everything by default
User-agent: *
Disallow:

View File

@@ -1,13 +1,18 @@
import adapter from '@deno/svelte-adapter';
import adapter from 'sveltekit-adapter-deno';
import { vitePreprocess } from '@sveltejs/vite-plugin-svelte';
/** @type {import('@sveltejs/kit').Config} */
const config = {
preprocess: vitePreprocess(),
kit: {
adapter: adapter()
}
preprocess: vitePreprocess(),
kit: {
adapter: adapter({
usage: 'deno-compile'
}),
alias: {
$config: './src/utils/config/config.ts'
}
}
};
export default config;
export default config;

View File

@@ -9,7 +9,10 @@
"skipLibCheck": true,
"sourceMap": true,
"strict": true,
"moduleResolution": "bundler"
"moduleResolution": "bundler",
"lib": ["ES2022", "DOM"],
"allowImportingTsExtensions": true,
"noEmit": true
}
// Path aliases are handled by https://svelte.dev/docs/kit/configuration#alias
// except $lib which is handled by https://svelte.dev/docs/kit/configuration#files

View File

@@ -3,5 +3,8 @@ import { sveltekit } from '@sveltejs/kit/vite';
import { defineConfig } from 'vite';
export default defineConfig({
plugins: [tailwindcss(), sveltekit()]
plugins: [tailwindcss(), sveltekit()],
server: {
port: 6969
}
});