From e024187a18c10ad26666fcae258d8cbe35a34f16 Mon Sep 17 00:00:00 2001 From: ton1c Date: Wed, 22 Jan 2025 20:05:24 +0300 Subject: [PATCH] working bundling --- server/autoBundler.ts | 136 ++++- server/bundler.ts | 76 --- server/deno.json | 8 +- server/lock.json | 563 ++++++++++++++++++ server/main.ts | 25 +- server/public/js/login.js | 2 +- server/public/js/shared.bundle.js | 2 +- server/public/js/test.js | 2 +- server/src/client_js/test.ts | 3 - server/src/db.bkp/db.ts | 36 -- server/src/db.bkp/generateTypes.ts | 39 -- server/src/db.bkp/index.ts | 90 --- server/src/db.bkp/mariadbCon.ts | 72 --- server/src/db.bkp/migrations.ts | 222 ------- server/src/db.bkp/types/types.ts | 48 -- server/src/{client_js => js}/login.ts | 16 +- server/src/{client_js => js}/shared.bundle.ts | 0 server/src/js/test.js | 1 + server/src/{ => lib}/admin.ts | 17 +- server/src/{ => lib}/apiValidator.ts | 0 server/src/{ => lib}/context.ts | 7 +- server/src/{ => lib}/db/dbWrapper.ts | 8 +- server/src/{ => lib}/db/index.ts | 5 +- server/src/{ => lib}/db/migrations.ts | 0 server/src/{ => lib}/db/test.db | Bin server/src/{ => lib}/db/types/index.ts | 0 server/src/{ => lib}/errors.ts | 0 server/src/{ => lib}/router.ts | 4 +- server/src/{ => lib}/routerTree.ts | 0 server/src/{ => lib}/utils.ts | 0 server/src/middleware/auth.ts | 7 +- server/src/middleware/rateLimiter.ts | 3 +- server/src/requestHandler.ts | 49 -- server/src/views/index.html | 3 + server/src/views/layouts/basic.html | 15 + server/src/views/layouts/layout.html | 7 + server/src/views/login.html | 7 + server/src/views/test.html | 15 + server/src/wasm_example/Cargo.lock | 166 ------ server/src/wasm_example/Cargo.toml | 12 - server/src/wasm_example/src/lib.rs | 309 ---------- server/test.db | Bin 53248 -> 0 bytes server/views/index.html | 4 +- server/views/layouts/basic.html | 18 +- server/views/layouts/layout.html | 8 +- server/views/login.html | 12 +- 46 files changed, 792 insertions(+), 1225 deletions(-) delete mode 100644 server/bundler.ts create mode 100644 server/lock.json delete mode 100644 server/src/client_js/test.ts delete mode 100644 server/src/db.bkp/db.ts delete mode 100644 server/src/db.bkp/generateTypes.ts delete mode 100644 server/src/db.bkp/index.ts delete mode 100644 server/src/db.bkp/mariadbCon.ts delete mode 100644 server/src/db.bkp/migrations.ts delete mode 100644 server/src/db.bkp/types/types.ts rename server/src/{client_js => js}/login.ts (76%) rename server/src/{client_js => js}/shared.bundle.ts (100%) create mode 100644 server/src/js/test.js rename server/src/{ => lib}/admin.ts (95%) rename server/src/{ => lib}/apiValidator.ts (100%) rename server/src/{ => lib}/context.ts (96%) rename server/src/{ => lib}/db/dbWrapper.ts (93%) rename server/src/{ => lib}/db/index.ts (97%) rename server/src/{ => lib}/db/migrations.ts (100%) rename server/src/{ => lib}/db/test.db (100%) rename server/src/{ => lib}/db/types/index.ts (100%) rename server/src/{ => lib}/errors.ts (100%) rename server/src/{ => lib}/router.ts (97%) rename server/src/{ => lib}/routerTree.ts (100%) rename server/src/{ => lib}/utils.ts (100%) delete mode 100644 server/src/requestHandler.ts create mode 100644 server/src/views/index.html create mode 100644 server/src/views/layouts/basic.html create mode 100644 server/src/views/layouts/layout.html create mode 100644 server/src/views/login.html create mode 100644 server/src/views/test.html delete mode 100644 server/src/wasm_example/Cargo.lock delete mode 100644 server/src/wasm_example/Cargo.toml delete mode 100644 server/src/wasm_example/src/lib.rs delete mode 100644 server/test.db diff --git a/server/autoBundler.ts b/server/autoBundler.ts index 39a70fe..fe7feae 100644 --- a/server/autoBundler.ts +++ b/server/autoBundler.ts @@ -1,18 +1,130 @@ -import bundle from "./bundler.ts"; +import * as esbuild from "npm:esbuild"; +import { denoPlugins } from "jsr:@luca/esbuild-deno-loader"; +import init, { minify } from "npm:@minify-html/wasm"; +import { relative } from "jsr:@std/path@^1.0.8/relative"; -const directoryToWatch = "./src/client_js"; // Update this to your directory +await esbuild.build({ + plugins: [ + ...denoPlugins(), + ], + entryPoints: ["../shared/utils/index.ts"], + outfile: "./public/js/shared.bundle.js", + bundle: true, + minify: true, + format: "esm", +}); +esbuild.stop(); -const watcher = Deno.watchFs(directoryToWatch); +const JS_FROM_DIR = "./src/js/"; +const JS_TO_DIR = "./public/js/"; +const JS_EXCLUDE = ".bundle.ts"; +const watcherJS = Deno.watchFs(JS_FROM_DIR); -for await (const event of watcher) { - if ( - event.kind === "modify" || event.kind === "create" || - event.kind === "remove" - ) { - try { - await bundle(); - } catch (e) { - console.log(e); +const cwd = Deno.cwd(); + +function getPublic(paths: string[]): string[] { + return paths.map((path) => + path.replace(JS_FROM_DIR, JS_TO_DIR).replace(".ts", ".js") + ); +} + +(async () => { + for await (const event of watcherJS) { + const paths = event.paths.filter((path) => + !path.endsWith(JS_EXCLUDE) && + (path.endsWith(".ts") || path.endsWith(".js")) + ); + if (event.kind === "modify" || event.kind === "create") { + await esbuild.build({ + plugins: [ + ...denoPlugins(), + ], + entryPoints: paths, + outbase: JS_FROM_DIR, + outdir: JS_TO_DIR, + bundle: false, + minify: true, + format: "esm", + }); + esbuild.stop(); + + for ( + const path of getPublic(paths) + ) { + try { + const content = await Deno.readTextFile(path); + + const staticImportRegex = + /(import\s*[^'"]+from\s*['"])([^'"]+?)(\.ts)(['"])/g; + + const dynamicImportRegex = + /(import\(\s*['"])([^'"]+?)(\.ts)(['"]\s*\))/g; + + let updatedContent = content.replace( + staticImportRegex, + (_, p1, p2, p3, p4) => { + return `${p1}${p2}.js${p4}`; + }, + ); + + updatedContent = updatedContent.replace( + dynamicImportRegex, + (_, p1, p2, p3, p4) => { + return `${p1}${p2}.js${p4}`; + }, + ); + + await Deno.writeTextFile(path, updatedContent); + } catch (e) {} + } + } else if (event.kind === "remove") { + try { + for (const path of getPublic(paths)) { + await Deno.remove(path); + } + } catch (e) {} } } +})(); + +const HTML_FROM_DIR = "./src/views/"; +const HTML_TO_DIR = "./views/"; + +const watcherHTML = Deno.watchFs(HTML_FROM_DIR); + +function minifyHTML(html: Uint8Array) { + const data = minify(html, { + keep_spaces_between_attributes: true, + keep_comments: true, + preserve_brace_template_syntax: true, + }); + + return data; } + +function getHTMLToPath(path: string) { + return path.replace(HTML_FROM_DIR, HTML_TO_DIR); +} + +(async () => { + for await (const event of watcherHTML) { + const paths = event.paths.filter((path) => path.endsWith(".html")); + if (event.kind === "modify" || event.kind === "create") { + try { + for (const path of paths) { + const html = await Deno.readFile(path); + + const data = minifyHTML(html); + + await Deno.writeFile(getHTMLToPath(path), data); + } + } catch (_) {} + } else if (event.kind === "remove") { + try { + for (const path of paths) { + await Deno.remove(getHTMLToPath(path)); + } + } catch (_) {} + } + } +})(); diff --git a/server/bundler.ts b/server/bundler.ts deleted file mode 100644 index 5a106d2..0000000 --- a/server/bundler.ts +++ /dev/null @@ -1,76 +0,0 @@ -import * as esbuild from "npm:esbuild"; -import * as fg from "npm:fast-glob"; -import { denoPlugins } from "jsr:@luca/esbuild-deno-loader"; - -async function bundleSharedObject() { - await esbuild.build({ - plugins: [ - ...denoPlugins(), - ], - entryPoints: [ - "../shared/utils/index.ts", - ], - outfile: "./public/js/shared.bundle.js", - bundle: true, - format: "esm", - minify: true, - }); -} - -await bundleSharedObject(); - -async function bundle() { - const entryPoints = fg.default.sync([ - "./src/client_js/*.ts", - "!./src/client_js/shared.bundle.ts", - ]); - - async function bundleClientJS() { - await esbuild.build({ - plugins: [ - ...denoPlugins(), - ], - entryPoints: entryPoints, - outbase: "./src/client_js", - outdir: "./public/js", - bundle: false, - format: "esm", - minify: true, - }); - } - - await bundleClientJS(); - - const bundledFiles = fg.default.sync([ - "./public/js/*.js", - "!./public/js/shared.bundle.js", - ]); - - for (const file of bundledFiles) { - const content = await Deno.readTextFile(file); - - const staticImportRegex = - /(import\s*[^'"]+from\s*['"])([^'"]+?)(\.ts)(['"])/g; - - const dynamicImportRegex = - /(import\(\s*['"])([^'"]+?)(\.ts)(['"]\s*\))/g; - - let updatedContent = content.replace( - staticImportRegex, - (_, p1, p2, p3, p4) => { - return `${p1}${p2}.js${p4}`; - }, - ); - - updatedContent = updatedContent.replace( - dynamicImportRegex, - (_, p1, p2, p3, p4) => { - return `${p1}${p2}.js${p4}`; - }, - ); - - await Deno.writeTextFile(file, updatedContent); - } -} - -export default bundle; diff --git a/server/deno.json b/server/deno.json index fb05289..26126b6 100644 --- a/server/deno.json +++ b/server/deno.json @@ -6,15 +6,13 @@ "@db/sqlite": "jsr:@db/sqlite@^0.12.0", "@eta-dev/eta": "jsr:@eta-dev/eta@^3.5.0", "@felix/bcrypt": "jsr:@felix/bcrypt@^1.0.5", - "@minify-html/node": "npm:@minify-html/node@^0.15.0", - "@ryanflorence/sqlite-typegen": "npm:@ryanflorence/sqlite-typegen@^0.2.0", + "@minify-html/node": "npm:@minify-html/node-linux-x64@^0.15.0", "@std/assert": "jsr:@std/assert@1", "@std/crypto": "jsr:@std/crypto@^1.0.3", "@std/dotenv": "jsr:@std/dotenv@^0.225.3", "@std/http": "jsr:@std/http@^1.0.12", + "@lib/": "./src/lib/", "@src/": "./src/", - "better-sqlite3": "npm:better-sqlite3@^11.8.0", - "esbuild": "npm:esbuild@^0.24.2", - "mariadb": "npm:mariadb@^3.4.0" + "esbuild": "npm:esbuild@^0.24.2" } } diff --git a/server/lock.json b/server/lock.json new file mode 100644 index 0000000..cc5d650 --- /dev/null +++ b/server/lock.json @@ -0,0 +1,563 @@ +{ + "version": "4", + "specifiers": { + "jsr:@luca/esbuild-deno-loader@*": "0.11.1", + "jsr:@std/bytes@^1.0.2": "1.0.4", + "jsr:@std/encoding@^1.0.5": "1.0.6", + "jsr:@std/path@^1.0.6": "1.0.8", + "jsr:@std/path@^1.0.8": "1.0.8", + "npm:@tauri-apps/api@2": "2.2.0", + "npm:@tauri-apps/cli@2": "2.2.5", + "npm:@tauri-apps/plugin-shell@2": "2.2.0", + "npm:esbuild-plugin-tsc@0.4": "0.4.0_typescript@5.7.3", + "npm:esbuild@*": "0.24.2", + "npm:esbuild@~0.24.2": "0.24.2", + "npm:fast-glob@^3.3.3": "3.3.3", + "npm:typescript@^5.2.2": "5.7.3", + "npm:typescript@^5.7.3": "5.7.3", + "npm:vite@^5.3.1": "5.4.13" + }, + "jsr": { + "@luca/esbuild-deno-loader@0.11.1": { + "integrity": "dc020d16d75b591f679f6b9288b10f38bdb4f24345edb2f5732affa1d9885267", + "dependencies": [ + "jsr:@std/bytes", + "jsr:@std/encoding", + "jsr:@std/path@^1.0.6" + ] + }, + "@std/bytes@1.0.4": { + "integrity": "11a0debe522707c95c7b7ef89b478c13fb1583a7cfb9a85674cd2cc2e3a28abc" + }, + "@std/encoding@1.0.6": { + "integrity": "ca87122c196e8831737d9547acf001766618e78cd8c33920776c7f5885546069" + }, + "@std/path@1.0.8": { + "integrity": "548fa456bb6a04d3c1a1e7477986b6cffbce95102d0bb447c67c4ee70e0364be" + } + }, + "npm": { + "@esbuild/aix-ppc64@0.21.5": { + "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==" + }, + "@esbuild/aix-ppc64@0.24.2": { + "integrity": "sha512-thpVCb/rhxE/BnMLQ7GReQLLN8q9qbHmI55F4489/ByVg2aQaQ6kbcLb6FHkocZzQhxc4gx0sCk0tJkKBFzDhA==" + }, + "@esbuild/android-arm64@0.21.5": { + "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==" + }, + "@esbuild/android-arm64@0.24.2": { + "integrity": "sha512-cNLgeqCqV8WxfcTIOeL4OAtSmL8JjcN6m09XIgro1Wi7cF4t/THaWEa7eL5CMoMBdjoHOTh/vwTO/o2TRXIyzg==" + }, + "@esbuild/android-arm@0.21.5": { + "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==" + }, + "@esbuild/android-arm@0.24.2": { + "integrity": "sha512-tmwl4hJkCfNHwFB3nBa8z1Uy3ypZpxqxfTQOcHX+xRByyYgunVbZ9MzUUfb0RxaHIMnbHagwAxuTL+tnNM+1/Q==" + }, + "@esbuild/android-x64@0.21.5": { + "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==" + }, + "@esbuild/android-x64@0.24.2": { + "integrity": "sha512-B6Q0YQDqMx9D7rvIcsXfmJfvUYLoP722bgfBlO5cGvNVb5V/+Y7nhBE3mHV9OpxBf4eAS2S68KZztiPaWq4XYw==" + }, + "@esbuild/darwin-arm64@0.21.5": { + "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==" + }, + "@esbuild/darwin-arm64@0.24.2": { + "integrity": "sha512-kj3AnYWc+CekmZnS5IPu9D+HWtUI49hbnyqk0FLEJDbzCIQt7hg7ucF1SQAilhtYpIujfaHr6O0UHlzzSPdOeA==" + }, + "@esbuild/darwin-x64@0.21.5": { + "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==" + }, + "@esbuild/darwin-x64@0.24.2": { + "integrity": "sha512-WeSrmwwHaPkNR5H3yYfowhZcbriGqooyu3zI/3GGpF8AyUdsrrP0X6KumITGA9WOyiJavnGZUwPGvxvwfWPHIA==" + }, + "@esbuild/freebsd-arm64@0.21.5": { + "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==" + }, + "@esbuild/freebsd-arm64@0.24.2": { + "integrity": "sha512-UN8HXjtJ0k/Mj6a9+5u6+2eZ2ERD7Edt1Q9IZiB5UZAIdPnVKDoG7mdTVGhHJIeEml60JteamR3qhsr1r8gXvg==" + }, + "@esbuild/freebsd-x64@0.21.5": { + "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==" + }, + "@esbuild/freebsd-x64@0.24.2": { + "integrity": "sha512-TvW7wE/89PYW+IevEJXZ5sF6gJRDY/14hyIGFXdIucxCsbRmLUcjseQu1SyTko+2idmCw94TgyaEZi9HUSOe3Q==" + }, + "@esbuild/linux-arm64@0.21.5": { + "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==" + }, + "@esbuild/linux-arm64@0.24.2": { + "integrity": "sha512-7HnAD6074BW43YvvUmE/35Id9/NB7BeX5EoNkK9obndmZBUk8xmJJeU7DwmUeN7tkysslb2eSl6CTrYz6oEMQg==" + }, + "@esbuild/linux-arm@0.21.5": { + "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==" + }, + "@esbuild/linux-arm@0.24.2": { + "integrity": "sha512-n0WRM/gWIdU29J57hJyUdIsk0WarGd6To0s+Y+LwvlC55wt+GT/OgkwoXCXvIue1i1sSNWblHEig00GBWiJgfA==" + }, + "@esbuild/linux-ia32@0.21.5": { + "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==" + }, + "@esbuild/linux-ia32@0.24.2": { + "integrity": "sha512-sfv0tGPQhcZOgTKO3oBE9xpHuUqguHvSo4jl+wjnKwFpapx+vUDcawbwPNuBIAYdRAvIDBfZVvXprIj3HA+Ugw==" + }, + "@esbuild/linux-loong64@0.21.5": { + "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==" + }, + "@esbuild/linux-loong64@0.24.2": { + "integrity": "sha512-CN9AZr8kEndGooS35ntToZLTQLHEjtVB5n7dl8ZcTZMonJ7CCfStrYhrzF97eAecqVbVJ7APOEe18RPI4KLhwQ==" + }, + "@esbuild/linux-mips64el@0.21.5": { + "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==" + }, + "@esbuild/linux-mips64el@0.24.2": { + "integrity": "sha512-iMkk7qr/wl3exJATwkISxI7kTcmHKE+BlymIAbHO8xanq/TjHaaVThFF6ipWzPHryoFsesNQJPE/3wFJw4+huw==" + }, + "@esbuild/linux-ppc64@0.21.5": { + "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==" + }, + "@esbuild/linux-ppc64@0.24.2": { + "integrity": "sha512-shsVrgCZ57Vr2L8mm39kO5PPIb+843FStGt7sGGoqiiWYconSxwTiuswC1VJZLCjNiMLAMh34jg4VSEQb+iEbw==" + }, + "@esbuild/linux-riscv64@0.21.5": { + "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==" + }, + "@esbuild/linux-riscv64@0.24.2": { + "integrity": "sha512-4eSFWnU9Hhd68fW16GD0TINewo1L6dRrB+oLNNbYyMUAeOD2yCK5KXGK1GH4qD/kT+bTEXjsyTCiJGHPZ3eM9Q==" + }, + "@esbuild/linux-s390x@0.21.5": { + "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==" + }, + "@esbuild/linux-s390x@0.24.2": { + "integrity": "sha512-S0Bh0A53b0YHL2XEXC20bHLuGMOhFDO6GN4b3YjRLK//Ep3ql3erpNcPlEFed93hsQAjAQDNsvcK+hV90FubSw==" + }, + "@esbuild/linux-x64@0.21.5": { + "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==" + }, + "@esbuild/linux-x64@0.24.2": { + "integrity": "sha512-8Qi4nQcCTbLnK9WoMjdC9NiTG6/E38RNICU6sUNqK0QFxCYgoARqVqxdFmWkdonVsvGqWhmm7MO0jyTqLqwj0Q==" + }, + "@esbuild/netbsd-arm64@0.24.2": { + "integrity": "sha512-wuLK/VztRRpMt9zyHSazyCVdCXlpHkKm34WUyinD2lzK07FAHTq0KQvZZlXikNWkDGoT6x3TD51jKQ7gMVpopw==" + }, + "@esbuild/netbsd-x64@0.21.5": { + "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==" + }, + "@esbuild/netbsd-x64@0.24.2": { + "integrity": "sha512-VefFaQUc4FMmJuAxmIHgUmfNiLXY438XrL4GDNV1Y1H/RW3qow68xTwjZKfj/+Plp9NANmzbH5R40Meudu8mmw==" + }, + "@esbuild/openbsd-arm64@0.24.2": { + "integrity": "sha512-YQbi46SBct6iKnszhSvdluqDmxCJA+Pu280Av9WICNwQmMxV7nLRHZfjQzwbPs3jeWnuAhE9Jy0NrnJ12Oz+0A==" + }, + "@esbuild/openbsd-x64@0.21.5": { + "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==" + }, + "@esbuild/openbsd-x64@0.24.2": { + "integrity": "sha512-+iDS6zpNM6EnJyWv0bMGLWSWeXGN/HTaF/LXHXHwejGsVi+ooqDfMCCTerNFxEkM3wYVcExkeGXNqshc9iMaOA==" + }, + "@esbuild/sunos-x64@0.21.5": { + "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==" + }, + "@esbuild/sunos-x64@0.24.2": { + "integrity": "sha512-hTdsW27jcktEvpwNHJU4ZwWFGkz2zRJUz8pvddmXPtXDzVKTTINmlmga3ZzwcuMpUvLw7JkLy9QLKyGpD2Yxig==" + }, + "@esbuild/win32-arm64@0.21.5": { + "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==" + }, + "@esbuild/win32-arm64@0.24.2": { + "integrity": "sha512-LihEQ2BBKVFLOC9ZItT9iFprsE9tqjDjnbulhHoFxYQtQfai7qfluVODIYxt1PgdoyQkz23+01rzwNwYfutxUQ==" + }, + "@esbuild/win32-ia32@0.21.5": { + "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==" + }, + "@esbuild/win32-ia32@0.24.2": { + "integrity": "sha512-q+iGUwfs8tncmFC9pcnD5IvRHAzmbwQ3GPS5/ceCyHdjXubwQWI12MKWSNSMYLJMq23/IUCvJMS76PDqXe1fxA==" + }, + "@esbuild/win32-x64@0.21.5": { + "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==" + }, + "@esbuild/win32-x64@0.24.2": { + "integrity": "sha512-7VTgWzgMGvup6aSqDPLiW5zHaxYJGTO4OokMjIlrCtf+VpEL+cXKtCvg723iguPYI5oaUNdS+/V7OU2gvXVWEg==" + }, + "@nodelib/fs.scandir@2.1.5": { + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dependencies": [ + "@nodelib/fs.stat", + "run-parallel" + ] + }, + "@nodelib/fs.stat@2.0.5": { + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==" + }, + "@nodelib/fs.walk@1.2.8": { + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dependencies": [ + "@nodelib/fs.scandir", + "fastq" + ] + }, + "@rollup/rollup-android-arm-eabi@4.31.0": { + "integrity": "sha512-9NrR4033uCbUBRgvLcBrJofa2KY9DzxL2UKZ1/4xA/mnTNyhZCWBuD8X3tPm1n4KxcgaraOYgrFKSgwjASfmlA==" + }, + "@rollup/rollup-android-arm64@4.31.0": { + "integrity": "sha512-iBbODqT86YBFHajxxF8ebj2hwKm1k8PTBQSojSt3d1FFt1gN+xf4CowE47iN0vOSdnd+5ierMHBbu/rHc7nq5g==" + }, + "@rollup/rollup-darwin-arm64@4.31.0": { + "integrity": "sha512-WHIZfXgVBX30SWuTMhlHPXTyN20AXrLH4TEeH/D0Bolvx9PjgZnn4H677PlSGvU6MKNsjCQJYczkpvBbrBnG6g==" + }, + "@rollup/rollup-darwin-x64@4.31.0": { + "integrity": "sha512-hrWL7uQacTEF8gdrQAqcDy9xllQ0w0zuL1wk1HV8wKGSGbKPVjVUv/DEwT2+Asabf8Dh/As+IvfdU+H8hhzrQQ==" + }, + "@rollup/rollup-freebsd-arm64@4.31.0": { + "integrity": "sha512-S2oCsZ4hJviG1QjPY1h6sVJLBI6ekBeAEssYKad1soRFv3SocsQCzX6cwnk6fID6UQQACTjeIMB+hyYrFacRew==" + }, + "@rollup/rollup-freebsd-x64@4.31.0": { + "integrity": "sha512-pCANqpynRS4Jirn4IKZH4tnm2+2CqCNLKD7gAdEjzdLGbH1iO0zouHz4mxqg0uEMpO030ejJ0aA6e1PJo2xrPA==" + }, + "@rollup/rollup-linux-arm-gnueabihf@4.31.0": { + "integrity": "sha512-0O8ViX+QcBd3ZmGlcFTnYXZKGbFu09EhgD27tgTdGnkcYXLat4KIsBBQeKLR2xZDCXdIBAlWLkiXE1+rJpCxFw==" + }, + "@rollup/rollup-linux-arm-musleabihf@4.31.0": { + "integrity": "sha512-w5IzG0wTVv7B0/SwDnMYmbr2uERQp999q8FMkKG1I+j8hpPX2BYFjWe69xbhbP6J9h2gId/7ogesl9hwblFwwg==" + }, + "@rollup/rollup-linux-arm64-gnu@4.31.0": { + "integrity": "sha512-JyFFshbN5xwy6fulZ8B/8qOqENRmDdEkcIMF0Zz+RsfamEW+Zabl5jAb0IozP/8UKnJ7g2FtZZPEUIAlUSX8cA==" + }, + "@rollup/rollup-linux-arm64-musl@4.31.0": { + "integrity": "sha512-kpQXQ0UPFeMPmPYksiBL9WS/BDiQEjRGMfklVIsA0Sng347H8W2iexch+IEwaR7OVSKtr2ZFxggt11zVIlZ25g==" + }, + "@rollup/rollup-linux-loongarch64-gnu@4.31.0": { + "integrity": "sha512-pMlxLjt60iQTzt9iBb3jZphFIl55a70wexvo8p+vVFK+7ifTRookdoXX3bOsRdmfD+OKnMozKO6XM4zR0sHRrQ==" + }, + "@rollup/rollup-linux-powerpc64le-gnu@4.31.0": { + "integrity": "sha512-D7TXT7I/uKEuWiRkEFbed1UUYZwcJDU4vZQdPTcepK7ecPhzKOYk4Er2YR4uHKme4qDeIh6N3XrLfpuM7vzRWQ==" + }, + "@rollup/rollup-linux-riscv64-gnu@4.31.0": { + "integrity": "sha512-wal2Tc8O5lMBtoePLBYRKj2CImUCJ4UNGJlLwspx7QApYny7K1cUYlzQ/4IGQBLmm+y0RS7dwc3TDO/pmcneTw==" + }, + "@rollup/rollup-linux-s390x-gnu@4.31.0": { + "integrity": "sha512-O1o5EUI0+RRMkK9wiTVpk2tyzXdXefHtRTIjBbmFREmNMy7pFeYXCFGbhKFwISA3UOExlo5GGUuuj3oMKdK6JQ==" + }, + "@rollup/rollup-linux-x64-gnu@4.31.0": { + "integrity": "sha512-zSoHl356vKnNxwOWnLd60ixHNPRBglxpv2g7q0Cd3Pmr561gf0HiAcUBRL3S1vPqRC17Zo2CX/9cPkqTIiai1g==" + }, + "@rollup/rollup-linux-x64-musl@4.31.0": { + "integrity": "sha512-ypB/HMtcSGhKUQNiFwqgdclWNRrAYDH8iMYH4etw/ZlGwiTVxBz2tDrGRrPlfZu6QjXwtd+C3Zib5pFqID97ZA==" + }, + "@rollup/rollup-win32-arm64-msvc@4.31.0": { + "integrity": "sha512-JuhN2xdI/m8Hr+aVO3vspO7OQfUFO6bKLIRTAy0U15vmWjnZDLrEgCZ2s6+scAYaQVpYSh9tZtRijApw9IXyMw==" + }, + "@rollup/rollup-win32-ia32-msvc@4.31.0": { + "integrity": "sha512-U1xZZXYkvdf5MIWmftU8wrM5PPXzyaY1nGCI4KI4BFfoZxHamsIe+BtnPLIvvPykvQWlVbqUXdLa4aJUuilwLQ==" + }, + "@rollup/rollup-win32-x64-msvc@4.31.0": { + "integrity": "sha512-ul8rnCsUumNln5YWwz0ted2ZHFhzhRRnkpBZ+YRuHoRAlUji9KChpOUOndY7uykrPEPXVbHLlsdo6v5yXo/TXw==" + }, + "@tauri-apps/api@2.2.0": { + "integrity": "sha512-R8epOeZl1eJEl603aUMIGb4RXlhPjpgxbGVEaqY+0G5JG9vzV/clNlzTeqc+NLYXVqXcn8mb4c5b9pJIUDEyAg==" + }, + "@tauri-apps/cli-darwin-arm64@2.2.5": { + "integrity": "sha512-qdPmypQE7qj62UJy3Wl/ccCJZwsv5gyBByOrAaG7u5c/PB3QSxhNPegice2k4EHeIuApaVJOoe/CEYVgm/og2Q==" + }, + "@tauri-apps/cli-darwin-x64@2.2.5": { + "integrity": "sha512-8JVlCAb2c3n0EcGW7n/1kU4Rq831SsoLDD/0hNp85Um8HGIH2Mg/qos/MLOc8Qv2mOaoKcRKf4hd0I1y0Rl9Cg==" + }, + "@tauri-apps/cli-linux-arm-gnueabihf@2.2.5": { + "integrity": "sha512-mzxQCqZg7ljRVgekPpXQ5TOehCNgnXh/DNWU6kFjALaBvaw4fGzc369/hV94wOt29htNFyxf8ty2DaQaYljEHw==" + }, + "@tauri-apps/cli-linux-arm64-gnu@2.2.5": { + "integrity": "sha512-M9nkzx5jsSJSNpp7aSza0qv0/N13SUNzH8ysYSZ7IaCN8coGeMg2KgQ5qC6tqUVij2rbg8A/X1n0pPo/gtLx0A==" + }, + "@tauri-apps/cli-linux-arm64-musl@2.2.5": { + "integrity": "sha512-tFhZu950HNRLR1RM5Q9Xj5gAlA6AhyyiZgeoXGFAWto+s2jpWmmA3Qq2GUxnVDr7Xui8PF4UY5kANDIOschuwg==" + }, + "@tauri-apps/cli-linux-x64-gnu@2.2.5": { + "integrity": "sha512-eaGhTQLr3EKeksGsp2tK/Ndi7/oyo3P53Pye6kg0zqXiqu8LQjg1CgvDm1l+5oit04S60zR4AqlDFpoeEtDGgw==" + }, + "@tauri-apps/cli-linux-x64-musl@2.2.5": { + "integrity": "sha512-NLAO/SymDxeGuOWWQZCpwoED1K1jaHUvW+u9ip+rTetnxFPLvf3zXthx4QVKfCZLdj2WLQz4cLjHyQdMDXAM+w==" + }, + "@tauri-apps/cli-win32-arm64-msvc@2.2.5": { + "integrity": "sha512-yG5KFbqrHfGjkAQAaaCD4i7cJklBjmMxZ2C92DEnqCOujSsEuLxrwwoKxQ4+hqEHOmF3lyX0vfqhgZcS03H38w==" + }, + "@tauri-apps/cli-win32-ia32-msvc@2.2.5": { + "integrity": "sha512-G5lq+2EdxOc8ttg3uhME5t9U3hMGTxwaKz0X4DplTG2Iv4lcNWqw/AESIJVHa5a+EB+ZCC8I+yOfIykp/Cd5mQ==" + }, + "@tauri-apps/cli-win32-x64-msvc@2.2.5": { + "integrity": "sha512-vw4fPVOo0rIQIlqw6xUvK2nwiRFBHNgayDE2Z/SomJlQJAJ1q4VgpHOPl12ouuicmTjK1gWKm7RTouQe3Nig0Q==" + }, + "@tauri-apps/cli@2.2.5": { + "integrity": "sha512-PaefTQUCYYqvZWdH8EhXQkyJEjQwtoy/OHGoPcZx7Gk3D3K6AtGSxZ9OlHIz3Bu5LDGgVBk36vKtHW0WYsWnbw==", + "dependencies": [ + "@tauri-apps/cli-darwin-arm64", + "@tauri-apps/cli-darwin-x64", + "@tauri-apps/cli-linux-arm-gnueabihf", + "@tauri-apps/cli-linux-arm64-gnu", + "@tauri-apps/cli-linux-arm64-musl", + "@tauri-apps/cli-linux-x64-gnu", + "@tauri-apps/cli-linux-x64-musl", + "@tauri-apps/cli-win32-arm64-msvc", + "@tauri-apps/cli-win32-ia32-msvc", + "@tauri-apps/cli-win32-x64-msvc" + ] + }, + "@tauri-apps/plugin-shell@2.2.0": { + "integrity": "sha512-iC3Ic1hLmasoboG7BO+7p+AriSoqAwKrIk+Hpk+S/bjTQdXqbl2GbdclghI4gM32X0bls7xHzIFqhRdrlvJeaA==", + "dependencies": [ + "@tauri-apps/api" + ] + }, + "@types/estree@1.0.6": { + "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==" + }, + "braces@3.0.3": { + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dependencies": [ + "fill-range" + ] + }, + "esbuild-plugin-tsc@0.4.0_typescript@5.7.3": { + "integrity": "sha512-q9gWIovt1nkwchMLc2zhyksaiHOv3kDK4b0AUol8lkMCRhJ1zavgfb2fad6BKp7FT9rh/OHmEBXVjczLoi/0yw==", + "dependencies": [ + "strip-comments", + "typescript" + ] + }, + "esbuild@0.21.5": { + "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==", + "dependencies": [ + "@esbuild/aix-ppc64@0.21.5", + "@esbuild/android-arm@0.21.5", + "@esbuild/android-arm64@0.21.5", + "@esbuild/android-x64@0.21.5", + "@esbuild/darwin-arm64@0.21.5", + "@esbuild/darwin-x64@0.21.5", + "@esbuild/freebsd-arm64@0.21.5", + "@esbuild/freebsd-x64@0.21.5", + "@esbuild/linux-arm@0.21.5", + "@esbuild/linux-arm64@0.21.5", + "@esbuild/linux-ia32@0.21.5", + "@esbuild/linux-loong64@0.21.5", + "@esbuild/linux-mips64el@0.21.5", + "@esbuild/linux-ppc64@0.21.5", + "@esbuild/linux-riscv64@0.21.5", + "@esbuild/linux-s390x@0.21.5", + "@esbuild/linux-x64@0.21.5", + "@esbuild/netbsd-x64@0.21.5", + "@esbuild/openbsd-x64@0.21.5", + "@esbuild/sunos-x64@0.21.5", + "@esbuild/win32-arm64@0.21.5", + "@esbuild/win32-ia32@0.21.5", + "@esbuild/win32-x64@0.21.5" + ] + }, + "esbuild@0.24.2": { + "integrity": "sha512-+9egpBW8I3CD5XPe0n6BfT5fxLzxrlDzqydF3aviG+9ni1lDC/OvMHcxqEFV0+LANZG5R1bFMWfUrjVsdwxJvA==", + "dependencies": [ + "@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", + "@esbuild/netbsd-x64@0.24.2", + "@esbuild/openbsd-arm64", + "@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" + ] + }, + "fast-glob@3.3.3": { + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", + "dependencies": [ + "@nodelib/fs.stat", + "@nodelib/fs.walk", + "glob-parent", + "merge2", + "micromatch" + ] + }, + "fastq@1.18.0": { + "integrity": "sha512-QKHXPW0hD8g4UET03SdOdunzSouc9N4AuHdsX8XNcTsuz+yYFILVNIX4l9yHABMhiEI9Db0JTTIpu0wB+Y1QQw==", + "dependencies": [ + "reusify" + ] + }, + "fill-range@7.1.1": { + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dependencies": [ + "to-regex-range" + ] + }, + "fsevents@2.3.3": { + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==" + }, + "glob-parent@5.1.2": { + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dependencies": [ + "is-glob" + ] + }, + "is-extglob@2.1.1": { + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==" + }, + "is-glob@4.0.3": { + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dependencies": [ + "is-extglob" + ] + }, + "is-number@7.0.0": { + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==" + }, + "merge2@1.4.1": { + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==" + }, + "micromatch@4.0.8": { + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dependencies": [ + "braces", + "picomatch" + ] + }, + "nanoid@3.3.8": { + "integrity": "sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==" + }, + "picocolors@1.1.1": { + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==" + }, + "picomatch@2.3.1": { + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==" + }, + "postcss@8.5.1": { + "integrity": "sha512-6oz2beyjc5VMn/KV1pPw8fliQkhBXrVn1Z3TVyqZxU8kZpzEKhBdmCFqI6ZbmGtamQvQGuU1sgPTk8ZrXDD7jQ==", + "dependencies": [ + "nanoid", + "picocolors", + "source-map-js" + ] + }, + "queue-microtask@1.2.3": { + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==" + }, + "reusify@1.0.4": { + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==" + }, + "rollup@4.31.0": { + "integrity": "sha512-9cCE8P4rZLx9+PjoyqHLs31V9a9Vpvfo4qNcs6JCiGWYhw2gijSetFbH6SSy1whnkgcefnUwr8sad7tgqsGvnw==", + "dependencies": [ + "@rollup/rollup-android-arm-eabi", + "@rollup/rollup-android-arm64", + "@rollup/rollup-darwin-arm64", + "@rollup/rollup-darwin-x64", + "@rollup/rollup-freebsd-arm64", + "@rollup/rollup-freebsd-x64", + "@rollup/rollup-linux-arm-gnueabihf", + "@rollup/rollup-linux-arm-musleabihf", + "@rollup/rollup-linux-arm64-gnu", + "@rollup/rollup-linux-arm64-musl", + "@rollup/rollup-linux-loongarch64-gnu", + "@rollup/rollup-linux-powerpc64le-gnu", + "@rollup/rollup-linux-riscv64-gnu", + "@rollup/rollup-linux-s390x-gnu", + "@rollup/rollup-linux-x64-gnu", + "@rollup/rollup-linux-x64-musl", + "@rollup/rollup-win32-arm64-msvc", + "@rollup/rollup-win32-ia32-msvc", + "@rollup/rollup-win32-x64-msvc", + "@types/estree", + "fsevents" + ] + }, + "run-parallel@1.2.0": { + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dependencies": [ + "queue-microtask" + ] + }, + "source-map-js@1.2.1": { + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==" + }, + "strip-comments@2.0.1": { + "integrity": "sha512-ZprKx+bBLXv067WTCALv8SSz5l2+XhpYCsVtSqlMnkAXMWDq+/ekVbl1ghqP9rUHTzv6sm/DwCOiYutU/yp1fw==" + }, + "to-regex-range@5.0.1": { + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dependencies": [ + "is-number" + ] + }, + "typescript@5.7.3": { + "integrity": "sha512-84MVSjMEHP+FQRPy3pX9sTVV/INIex71s9TL2Gm5FG/WG1SqXeKyZ0k7/blY/4FdOzI12CBy1vGc4og/eus0fw==" + }, + "vite@5.4.13": { + "integrity": "sha512-7zp3N4YSjXOSAFfdBe9pPD3FrO398QlJ/5QpFGm3L8xDP1IxDn1XRxArPw4ZKk5394MM8rcTVPY4y1Hvo62bog==", + "dependencies": [ + "esbuild@0.21.5", + "fsevents", + "postcss", + "rollup" + ] + } + }, + "remote": { + "https://wilsonl.in/minify-html/deno/0.15.0/index.js": "8e7ee5067ca84fb5d5a1f33118cac4998de0b7d80b3f56cc5c6728b84e6bfb70" + }, + "workspace": { + "dependencies": [ + "jsr:@luca/esbuild-deno-loader@~0.11.1", + "jsr:@std/assert@1", + "npm:esbuild-plugin-tsc@0.4", + "npm:esbuild@~0.24.2", + "npm:fast-glob@^3.3.3", + "npm:typescript@^5.7.3" + ], + "members": { + "client": { + "packageJson": { + "dependencies": [ + "npm:@tauri-apps/api@2", + "npm:@tauri-apps/cli@2", + "npm:@tauri-apps/plugin-shell@2", + "npm:typescript@^5.2.2", + "npm:vite@^5.3.1" + ] + } + }, + "server": { + "dependencies": [ + "jsr:@db/sqlite@0.12", + "jsr:@eta-dev/eta@^3.5.0", + "jsr:@felix/bcrypt@^1.0.5", + "jsr:@std/assert@1", + "jsr:@std/crypto@^1.0.3", + "jsr:@std/dotenv@~0.225.3", + "jsr:@std/http@^1.0.12", + "npm:esbuild@~0.24.2" + ] + }, + "shared": { + "dependencies": [ + "jsr:@std/assert@1", + "jsr:@std/fmt@^1.0.3" + ] + } + } + } +} diff --git a/server/main.ts b/server/main.ts index 07e6130..e601cb2 100644 --- a/server/main.ts +++ b/server/main.ts @@ -1,8 +1,11 @@ -import HttpRouter from "@src/router.ts"; +import HttpRouter from "@lib/router.ts"; import { Eta } from "@eta-dev/eta"; import { serveFile } from "jsr:@std/http/file-server"; import rateLimitMiddleware from "@src/middleware/rateLimiter.ts"; import authMiddleware from "@src/middleware/auth.ts"; +import { ok, ResultFromJSON } from "@shared/utils/result.ts"; +import { ResultResponseFromJSON } from "@src/lib/context.ts"; +import admin from "@src/lib/admin.ts"; const router = new HttpRouter(); @@ -12,16 +15,20 @@ const eta = new Eta({ views }); router.use(rateLimitMiddleware); router.use(authMiddleware); -const filesToCache = new Set(["/public/js/shared.bundle.js"]); +const cache: Map = new Map(); router.get("/public/*", async (c) => { const filePath = "." + c.path; + const cached = cache.get(filePath); + + if (cached) { + return cached.clone(); + } + const res = await serveFile(c.req, filePath); - if (filesToCache.has(filePath)) { - res.headers.set("Cache-Control", "public max-age=31536000"); - } + cache.set(filePath, res.clone()); return res; }); @@ -34,11 +41,9 @@ router return c.html(eta.render("./login.html", {})); }) .post("/login", async (c) => { - const body = await c.req.text(); - - console.log(JSON.parse(body)); - - return c.json({ mes: "got you" }); + const r = await ResultFromJSON<{ password: string }>( + await c.req.text(), + ); }); export default { diff --git a/server/public/js/login.js b/server/public/js/login.js index 495a1e5..81624a0 100644 --- a/server/public/js/login.js +++ b/server/public/js/login.js @@ -1 +1 @@ -import{ok as e}from"./shared.bundle.js";const a=e("test");console.log(a);const c=document.getElementById("loginForm"),d=document.getElementById("passwordInput");c.addEventListener("submit",async t=>{t.preventDefault();const s=d.value,o=JSON.stringify(e({password:s}));console.log(o);const n=await(await fetch("/login",{method:"POST",headers:{accept:"application/json"},body:o})).json();console.log(n)}); +import{ok as n}from"./shared.bundle.js";const s=document.getElementById("loginForm"),a=document.getElementById("passwordInput");s.addEventListener("submit",async t=>{t.preventDefault();const o=a.value,e=JSON.stringify(n({password:o}).toJSON()),r=await(await fetch("/login",{method:"POST",headers:{accept:"application/json"},body:e})).json(),c=8}); diff --git a/server/public/js/shared.bundle.js b/server/public/js/shared.bundle.js index 983d593..8db5dd0 100644 --- a/server/public/js/shared.bundle.js +++ b/server/public/js/shared.bundle.js @@ -1 +1 @@ -var U=class r{constructor(e){this._promise=e;this._promise=e}static fromPromise(e,t){let n=e.then(p=>new s(p)).catch(p=>new i(t(p)));return new r(n)}static fromSafePromise(e){let t=e.then(n=>new s(n));return new r(t)}static fromThrowable(e,t){return(...n)=>{try{return v(e(n))}catch(p){return T(t?t(p):p)}}}async unwrap(){let e=await this._promise;if(e.isErr())throw e.error;return e.value}async match(e,t){let n=await this._promise;return n.isErr()?t(n.error):e(n.value)}map(e){return new r(this._promise.then(t=>t.isErr()?new i(t.error):new s(e(t.value))))}mapAsync(e){return new r(this._promise.then(async t=>t.isErr()?T(t.error):new s(await e(t.value))))}mapErr(e){return new r(this._promise.then(t=>t.isErr()?new i(e(t.error)):new s(t.value)))}mapErrAsync(e){return new r(this._promise.then(async t=>t.isErr()?T(await e(t.error)):new s(t.value)))}andThen(e){return new r(this._promise.then(t=>t.isErr()?T(t.error):e(t.value)))}nullableToOption(){return this.map(e=>e?E(e):f)}flatten(){return new r(this._promise.then(e=>e.flatten()))}flattenOption(e){return new r(this._promise.then(t=>t.flattenOption(e)))}flattenOptionOrDefault(e){return new r(this._promise.then(t=>t.flattenOptionOrDefault(e)))}matchOption(e,t){return new r(this._promise.then(n=>n.matchOption(e,t)))}matchOptionAndFlatten(e,t){return new r(this._promise.then(n=>n.matchOptionAndFlatten(e,t)))}then(e,t){return this._promise.then(e,t)}};function v(r){return new U(Promise.resolve(new s(r)))}function T(r){return new U(Promise.resolve(new i(r)))}var s=class r{constructor(e){this.value=e;this.value=e,Object.defineProperties(this,{tag:{writable:!1,enumerable:!1}})}tag="Ok";isErr(){return!1}ifErr(e){return this}isErrOrNone(){return this.value instanceof o}isOk(){return!0}ifOk(e){return e(this.value),this}unwrap(){return this.value}unwrapOr(e){return this.value}unwrapOrElse(e){return this.value}match(e,t){return e(this.value)}map(e){let t=e(this.value);return new r(t)}mapOption(e){return this.value instanceof o||this.value instanceof l?u(this.value.map(e)):u(E(e(this.value)))}andThen(e){return e(this.value)}mapErr(e){return new r(this.value)}flatten(){return c(this)}flattenOption(e){return this.value instanceof o||this.value instanceof l?this.value.okOrElse(e):new r(this.value)}flattenOptionOr(e){return this.value instanceof o||this.value instanceof l?this.value.unwrapOr(e):new r(this.value)}matchOption(e,t){return this.value instanceof o||this.value instanceof l?u(this.value.match(e,t)):u(e(this.value))}toNullable(){return this.value}toAsync(){return v(this.value)}void(){return u()}},i=class r{constructor(e){this.error=e;this.error=e,Object.defineProperties(this,{tag:{writable:!1,configurable:!1,enumerable:!1}})}tag="Err";isErr(){return!0}ifErr(e){return e(this.error),this}isOk(){return!1}ifOk(e){return this}isErrOrNone(){return!0}unwrap(){let e=`Tried to unwrap error: ${O(this.error)}`;throw new Error(e)}unwrapOr(e){return e}unwrapOrElse(e){return e()}match(e,t){return t(this.error)}map(e){return new r(this.error)}mapErr(e){let t=e(this.error);return new r(t)}mapOption(e){return a(this.error)}andThen(e){return new r(this.error)}flatten(){return c(this)}flattenOption(e){return new r(this.error)}flattenOptionOr(e){return new r(this.error)}matchOption(e,t){return a(this.error)}toNullable(){return null}toAsync(){return T(this.error)}void(){return a(this.error)}};function u(r){return new s(r)}function a(r){return new i(r)}function b(r,e){return(...t)=>{try{let n=r(...t);return u(n)}catch(n){return a(e?e(n):n)}}}function O(r){if(r instanceof Error)return r.message?r.message:"code"in r&&typeof r.code=="string"?r.code:"An unknown error occurred";if(typeof r=="string")return r;if(typeof r=="object"&&r!==null&&"message"in r){let e=r;return typeof e.message=="string"?e.message:String(e.message)}return"An unknown error occurred"}function c(r){let e=r;for(;e instanceof s;)e=e.value;return e}function g(r){let e=JSON.parse(r);if(obj.value)return u(obj.value);if(obj.error)return a(obj.error)}var l=class r{constructor(e){this.value=e;Object.defineProperties(this,{tag:{writable:!1,enumerable:!1}})}tag="Some";isSome(){return!0}ifSome(e){return e(this.value),this}isNone(){return!1}ifNone(e){return this}map(e){return new r(e(this.value))}flatMap(e){return e(this.value)}andThen(e){return e(this.value)}unwrap(){return this.value}unwrapOr(e){return this.value}unwrapOrElse(e){return this.value}or(e){return this}orElse(e){return this}match(e,t){return e(this.value)}toJSON(){return{value:this.value}}toString(){return`Some(${this.value})`}toNullable(){return this.value}toBoolean(){return!0}okOrElse(e){return u(this.value)}},o=class r{tag="None";constructor(){Object.defineProperties(this,{tag:{writable:!1,enumerable:!1}})}isSome(){return!1}ifSome(e){return this}isNone(){return!0}ifNone(e){return e(),this}map(e){return new r}andThen(e){return f}flatMap(e){return new r}unwrap(){throw new Error("Tried to unwrap a non-existent value")}unwrapOr(e){return e}unwrapOrElse(e){return e()}or(e){return e}orElse(e){return e()}match(e,t){return t()}toJSON(){return{_tag:this._tag}}toString(){return"None"}toNullable(){return null}toBoolean(){return!1}okOrElse(e){return a(e())}};function E(r){return new l(r)}var f=new o;function S(r){return r?E(r):f}export{i as Err,o as None,s as Ok,U as ResultAsync,g as ResultFromJSON,l as Some,a as err,T as errAsync,c as flattenResult,S as fromNullableVal,b as fromThrowable,O as getMessageFromError,f as none,u as ok,v as okAsync,E as some}; +var T=class r{constructor(e){this._promise=e;this._promise=e}static fromPromise(e,t){let n=e.then(E=>new s(E)).catch(E=>new a(t(E)));return new r(n)}static fromSafePromise(e){let t=e.then(n=>new s(n));return new r(t)}static fromThrowable(e,t){return(...n)=>{try{return f(e(n))}catch(E){return p(t?t(E):E)}}}async unwrap(){let e=await this._promise;if(e.isErr())throw e.error;return e.value}async match(e,t){let n=await this._promise;return n.isErr()?t(n.error):e(n.value)}map(e){return new r(this._promise.then(t=>t.isErr()?new a(t.error):new s(e(t.value))))}mapAsync(e){return new r(this._promise.then(async t=>t.isErr()?p(t.error):new s(await e(t.value))))}mapErr(e){return new r(this._promise.then(t=>t.isErr()?new a(e(t.error)):new s(t.value)))}mapErrAsync(e){return new r(this._promise.then(async t=>t.isErr()?p(await e(t.error)):new s(t.value)))}andThen(e){return new r(this._promise.then(t=>t.isErr()?p(t.error):e(t.value)))}nullableToOption(){return this.map(e=>e?U(e):v)}flatten(){return new r(this._promise.then(e=>e.flatten()))}flattenOption(e){return new r(this._promise.then(t=>t.flattenOption(e)))}flattenOptionOrDefault(e){return new r(this._promise.then(t=>t.flattenOptionOrDefault(e)))}matchOption(e,t){return new r(this._promise.then(n=>n.matchOption(e,t)))}matchOptionAndFlatten(e,t){return new r(this._promise.then(n=>n.matchOptionAndFlatten(e,t)))}then(e,t){return this._promise.then(e,t)}};function f(r){return new T(Promise.resolve(new s(r)))}function p(r){return new T(Promise.resolve(new a(r)))}var s=class r{constructor(e){this.value=e;this.value=e,Object.defineProperties(this,{tag:{writable:!1,enumerable:!1}})}tag="Ok";isErr(){return!1}ifErr(e){return this}isErrOrNone(){return this.value instanceof i}isOk(){return!0}ifOk(e){return e(this.value),this}unwrap(){return this.value}unwrapOr(e){return this.value}unwrapOrElse(e){return this.value}match(e,t){return e(this.value)}map(e){let t=e(this.value);return new r(t)}mapAsync(e){return T.fromSafePromise(e(this.value))}mapOption(e){return this.value instanceof i||this.value instanceof l?o(this.value.map(e)):o(U(e(this.value)))}andThen(e){return e(this.value)}andThenAsync(e){return e(this.value)}mapErr(e){return new r(this.value)}mapErrAsync(e){return f(this.value)}flatten(){return h(this)}flattenOption(e){return this.value instanceof i||this.value instanceof l?this.value.okOrElse(e):new r(this.value)}flattenOptionOr(e){return this.value instanceof i||this.value instanceof l?this.value.unwrapOr(e):new r(this.value)}matchOption(e,t){return this.value instanceof i||this.value instanceof l?o(this.value.match(e,t)):o(e(this.value))}toNullable(){return this.value}toAsync(){return f(this.value)}void(){return o()}toJSON(){return{tag:"ok",value:this.value}}},a=class r{constructor(e){this.error=e;this.error=e,Object.defineProperties(this,{tag:{writable:!1,configurable:!1,enumerable:!1}})}tag="Err";isErr(){return!0}ifErr(e){return e(this.error),this}isOk(){return!1}ifOk(e){return this}isErrOrNone(){return!0}unwrap(){let e=`Tried to unwrap error: ${O(this.error)}`;throw new Error(e)}unwrapOr(e){return e}unwrapOrElse(e){return e()}match(e,t){return t(this.error)}map(e){return new r(this.error)}mapAsync(e){return p(this.error)}mapErr(e){let t=e(this.error);return new r(t)}mapErrAsync(e){return T.fromPromise(new Promise(()=>{throw""}),()=>e(this.error))}mapOption(e){return u(this.error)}andThen(e){return new r(this.error)}flatten(){return h(this)}flattenOption(e){return new r(this.error)}flattenOptionOr(e){return new r(this.error)}matchOption(e,t){return u(this.error)}toNullable(){return null}toAsync(){return p(this.error)}void(){return u(this.error)}toJSON(){return{tag:"err",error:this.error}}};function o(r){return new s(r)}function u(r){return new a(r)}function b(r,e){return(...t)=>{try{let n=r(...t);return o(n)}catch(n){return u(e?e(n):n)}}}function O(r){if(r instanceof Error)return r.message?r.message:"code"in r&&typeof r.code=="string"?r.code:"An unknown error occurred";if(typeof r=="string")return r;if(typeof r=="object"&&r!==null&&"message"in r){let e=r;return typeof e.message=="string"?e.message:String(e.message)}return"An unknown error occurred"}function h(r){let e=r;for(;e instanceof s;)e=e.value;return e}var c=class extends Error{constructor(e){super(`Failed to parse ${e} as result`)}};function B(r){let e;if(typeof r=="string")try{e=JSON.parse(r)}catch(n){return u(new c(O(n)))}else e=r;if(typeof e!="object"||e===null)return u(new c("Expected an object but received type ${typeof data}."));let t=e;if("tag"in t)switch(t.tag){case"ok":{if("value"in t)return o(t.value);break}case"err":{if("error"in t)return u(t.error);break}}return u(new c("Object does not contain 'tag' and 'value' or 'error' property"))}var l=class r{constructor(e){this.value=e;Object.defineProperties(this,{tag:{writable:!1,enumerable:!1}})}tag="Some";isSome(){return!0}ifSome(e){return e(this.value),this}isNone(){return!1}ifNone(e){return this}map(e){return new r(e(this.value))}flatMap(e){return e(this.value)}andThen(e){return e(this.value)}unwrap(){return this.value}unwrapOr(e){return this.value}unwrapOrElse(e){return this.value}or(e){return this}orElse(e){return this}match(e,t){return e(this.value)}toJSON(){return{value:this.value}}toString(){return`Some(${this.value})`}toNullable(){return this.value}toBoolean(){return!0}okOrElse(e){return o(this.value)}},i=class r{tag="None";constructor(){Object.defineProperties(this,{tag:{writable:!1,enumerable:!1}})}isSome(){return!1}ifSome(e){return this}isNone(){return!0}ifNone(e){return e(),this}map(e){return new r}andThen(e){return v}flatMap(e){return new r}unwrap(){throw new Error("Tried to unwrap a non-existent value")}unwrapOr(e){return e}unwrapOrElse(e){return e()}or(e){return e}orElse(e){return e()}match(e,t){return t()}toJSON(){return{_tag:this._tag}}toString(){return"None"}toNullable(){return null}toBoolean(){return!1}okOrElse(e){return u(e())}};function U(r){return new l(r)}var v=new i;function S(r){return r?U(r):v}export{a as Err,i as None,s as Ok,T as ResultAsync,B as ResultFromJSON,l as Some,u as err,p as errAsync,h as flattenResult,S as fromNullableVal,b as fromThrowable,O as getMessageFromError,v as none,o as ok,f as okAsync,U as some}; diff --git a/server/public/js/test.js b/server/public/js/test.js index 39a479a..27fa898 100644 --- a/server/public/js/test.js +++ b/server/public/js/test.js @@ -1 +1 @@ -import{none as o}from"./shared.bundle.js";console.log(o); +const a=5; diff --git a/server/src/client_js/test.ts b/server/src/client_js/test.ts deleted file mode 100644 index cb3d709..0000000 --- a/server/src/client_js/test.ts +++ /dev/null @@ -1,3 +0,0 @@ -import { none, some } from "./shared.bundle.ts"; - -console.log(none); diff --git a/server/src/db.bkp/db.ts b/server/src/db.bkp/db.ts deleted file mode 100644 index c5f123b..0000000 --- a/server/src/db.bkp/db.ts +++ /dev/null @@ -1,36 +0,0 @@ -import mariadb from "npm:mariadb"; -import "jsr:@std/dotenv/load"; -import { getMessageFromError } from "@shared/utils/result.ts"; -import { QueryExecutionError } from "@src/errors.ts"; -import { none, some } from "@shared/utils/option.ts"; -import { ResultAsync } from "@shared/utils/resultasync.ts"; -import OkPacket from "../../../../../.cache/deno/npm/registry.npmjs.org/mariadb/3.4.0/lib/cmd/class/ok-packet.js"; - -export class DB { - constructor(private readonly pool: mariadb.Pool) {} - - public query(sql: string, values?: (string | number)[]) { - return ResultAsync.fromPromise(this.pool.query(sql, values), (e) => { - const errorMessage = getMessageFromError(e); - return new QueryExecutionError(errorMessage); - }); - } - - public getAll(sql: string, values?: (string | number)[]) { - return this.query(sql, values).nullableToOption(); - } - - public getFirst(sql: string, values?: (string | number)[]) { - return this.query(sql, values).map((v) => - v[0] ? some(v[0]) : none, - ); - } - - public insert(sql: string, values?: (string | number)[]) { - return this.query(sql, values); - } - - public update(sql: string, values?: (string | number)[]) { - return this.query(sql, values); - } -} diff --git a/server/src/db.bkp/generateTypes.ts b/server/src/db.bkp/generateTypes.ts deleted file mode 100644 index e363a28..0000000 --- a/server/src/db.bkp/generateTypes.ts +++ /dev/null @@ -1,39 +0,0 @@ -import { generateMysqlTypes } from "npm:mysql-types-generator"; -import { loadMariaDBConfigFromEnv } from "@lib/db/db.ts"; - -const { host, user, password, port, database } = loadMariaDBConfigFromEnv(); - -const dbConfig = { - host, - port, - user, - password, - database, - //ssl: { - // rejectUnauthorized: true, - //}, -}; - -generateMysqlTypes({ - db: dbConfig, - output: { - // Specify only one of the following 2 options: - file: "types/types.ts", - }, - //suffix: "PO", - ignoreTables: ["_keyborgMigrations"], - //overrides: [ - // { - // tableName: "my_table", - // columnName: "my_actual_tinyint_column", - // columnType: "int", - // }, - // { - // tableName: "my_table", - // columnName: "my_column", - // columnType: "enum", - // enumString: `enum('a','b','c')`, - // }, - //], - tinyintIsBoolean: true, -}); diff --git a/server/src/db.bkp/index.ts b/server/src/db.bkp/index.ts deleted file mode 100644 index 73feac6..0000000 --- a/server/src/db.bkp/index.ts +++ /dev/null @@ -1,90 +0,0 @@ -import { - createAndTestMariaDBPool, - loadMariaDBConfigFromEnv, -} from "@src/db/mariadbCon.ts"; -import { Migration, MigrationManager } from "@src/db/migrations.ts"; -import "jsr:@std/dotenv/load"; -import { DB } from "@src/db/db.ts"; - -// ── Establish connection to mariadb ───────────────────────────────── - -const config = loadMariaDBConfigFromEnv(); -const pool = await createAndTestMariaDBPool(config); - -// ── Migrate database to the latest version ────────────────────────── -//#region Migrations -const migrations = [ - Migration.create( - "initialize-database", - "Creates tables that are essential for the app", - [ - `CREATE TABLE IF NOT EXISTS admin ( - id INT PRIMARY KEY AUTO_INCREMENT, - passwordHash CHAR(60) NOT NULL - );`, - - `CREATE TABLE IF NOT EXISTS adminSessions ( - id INT PRIMARY KEY AUTO_INCREMENT, - sessionId CHAR(255) NOT NULL, - expiresAt DATETIME NOT NULL - );`, - - `CREATE TABLE IF NOT EXISTS users ( - id INT PRIMARY KEY AUTO_INCREMENT, - login VARCHAR(255) NOT NULL UNIQUE, - name VARCHAR(512), - telegramId VARCHAR(32), - passwordHash CHAR(60) NOT NULL, - createdAt DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, - updatedAt DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - deleted TINYINT(1) NOT NULL DEFAULT 0 - );`, - - `CREATE TABLE IF NOT EXISTS userSessions ( - id INT PRIMARY KEY AUTO_INCREMENT, - userId INT NOT NULL, - sessionId CHAR(255) NOT NULL, - expiresAt DATETIME NOT NULL DEFAULT (CURRENT_TIMESTAMP + INTERVAL 2 WEEK), - FOREIGN KEY (userId) REFERENCES users(id) ON DELETE CASCADE - );`, - - `CREATE TABLE IF NOT EXISTS devices ( - uuid CHAR(36) PRIMARY KEY, - busid VARCHAR(512) NOT NULL, - usbid CHAR(9) NOT NULL, - vendor VARCHAR(1024) NOT NULL DEFAULT 'unknown vendor', - deviceName VARCHAR(1024) NOT NULL DEFAULT 'unknown device', - displayName VARCHAR(255), - description TEXT, - connectedAt DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, - updatedAt DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP - );`, - - `CREATE TABLE IF NOT EXISTS deviceEvents ( - id INT PRIMARY KEY AUTO_INCREMENT, - userId INT, - targetUuid CHAR(36), - event ENUM('attached', 'detached'), - occuredAt DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, - FOREIGN KEY(targetUuid) REFERENCES devices(uuid) ON DELETE CASCADE, - FOREIGN KEY(userId) REFERENCES users(id) ON DELETE CASCADE - );`, - ], - [ - `DROP TABLE IF EXISTS adminSessions;`, - `DROP TABLE IF EXISTS admin;`, - `DROP TABLE IF EXISTS deviceEvents;`, - `DROP TABLE IF EXISTS devices;`, - `DROP TABLE IF EXISTS userSessions;`, - `DROP TABLE IF EXISTS users;`, - ], - ), -]; -//#endregion - -await MigrationManager.initialize(pool, config.database, migrations); - -// ── initialize db helper ──────────────────────────────────────────── -const db = new DB(pool); - -export default db; diff --git a/server/src/db.bkp/mariadbCon.ts b/server/src/db.bkp/mariadbCon.ts deleted file mode 100644 index d425af7..0000000 --- a/server/src/db.bkp/mariadbCon.ts +++ /dev/null @@ -1,72 +0,0 @@ -import log from "@shared/utils/logger.ts"; -import * as mariadb from "npm:mariadb"; -import { getMessageFromError } from "@shared/utils/result.ts"; - -interface MariaDBConfig { - user: string; - password: string; - host: string; - port: number; - database: string; -} - -export function loadMariaDBConfigFromEnv(): MariaDBConfig { - const requiredVars = [ - "KBRG_MYSQL_USER", - "KBRG_MYSQL_PASSWORD", - "KBRG_MYSQL_DATABASE", - ]; - - const missingVars = requiredVars.filter( - (varName) => !Deno.env.has(varName), - ); - - if (missingVars.length > 0) { - log.critical( - `Missing required environment variables: ${missingVars.join(", ")}`, - ); - Deno.exit(1); - } - - const host = Deno.env.get("KBRG_MYSQL_HOST") || "localhost"; - const user = Deno.env.get("KBRG_MYSQL_USER")!; - const password = Deno.env.get("KBRG_MYSQL_PASSWORD")!; - const database = Deno.env.get("KBRG_MYSQL_DATABASE")!; - const port = parseInt(Deno.env.get("KBRG_MYSQL_PORT") || "3306"); - - if (isNaN(port)) { - log.critical(`Invalid port number for KBRG_MYSQL_PORT: ${port}`); - Deno.exit(1); - } - - return { user, password, host, port, database }; -} - -export async function createAndTestMariaDBPool( - config: MariaDBConfig, -): Promise { - const { user, password, host, port, database } = config; - - try { - const pool = mariadb.createPool({ - host, - port, - user, - password, - database, - connectionLimit: 10, - }); - - // Test Connection - const connection = await pool.getConnection(); - connection.release(); - - log.info("Successfully connected to MySQL"); - - return pool; - } catch (e) { - const errorMessage = getMessageFromError(e); - log.critical(`Failed to create MySQL pool: ${errorMessage}`); - Deno.exit(1); - } -} diff --git a/server/src/db.bkp/migrations.ts b/server/src/db.bkp/migrations.ts deleted file mode 100644 index 09a64f7..0000000 --- a/server/src/db.bkp/migrations.ts +++ /dev/null @@ -1,222 +0,0 @@ -import { Pool } from "mariadb"; -import log from "@shared/utils/logger.ts"; -import { none, Option, some } from "@shared/utils/option.ts"; -import { getMessageFromError } from "@shared/utils/result.ts"; - -interface MigrationTableEntry { - name: string; - description: string; - step: number; -} - -export class Migration { - constructor( - public readonly name: string, - public readonly description: string, - public readonly up: string[], - public readonly down: string[], - public readonly step: number, - ) {} - - static create = (() => { - let step = 0; - - return ( - name: string, - description: string, - up: string[], - down: string[], - ) => new Migration(name, description, up, down, step++); - })(); -} - -interface Exists { - table_exists: 0 | 1; -} - -interface Count { - count: number; -} - -const MIGRATION_TABLE = "_keyborgMigrations"; - -export class MigrationManager { - constructor( - private readonly pool: Pool, - private readonly databaseName: string, - private readonly migrations: Migration[], - ) {} - - static async initialize( - pool: Pool, - databaseName: string, - migrations: Migration[], - ) { - const manager = new MigrationManager(pool, databaseName, migrations); - - if (await manager.doesMigrationTableExist()) { - if (!(await manager.areMigrationsAppliedInOrder())) { - log.critical("Migrations are not applied in order"); - Deno.exit(1); - } - } else { - if (await manager.hasOtherTables()) { - log.critical( - "Migration table not found, but the database is not empty. Clean the database or create a new one.", - ); - Deno.exit(1); - } - await manager.createMigrationTable(); - } - - await manager.migrateToLatest(); - //await manager.migrateDown(); - } - - private async executeOrExit( - query: string, - params?: string[], - ): Promise { - try { - const rows = await this.pool.query(query, params); - return rows; - } catch (e) { - const errMsg = getMessageFromError(e); - log.critical(`Database query failed: ${errMsg}`); - Deno.exit(1); - } - } - - private async doesMigrationTableExist(): Promise { - const query = ` - SELECT EXISTS ( - SELECT 1 - FROM INFORMATION_SCHEMA.TABLES - WHERE TABLE_SCHEMA = ? - AND TABLE_NAME = ? - ) AS table_exists; - `; - const params = [this.databaseName, MIGRATION_TABLE]; - const [result] = await this.executeOrExit(query, params); - return result.table_exists === 1; - } - - async hasOtherTables(): Promise { - const query = ` - SELECT COUNT(*) AS count - FROM INFORMATION_SCHEMA.TABLES - WHERE TABLE_SCHEMA = ? - AND TABLE_NAME != ?; - `; - const params = [this.databaseName, MIGRATION_TABLE]; - const [result] = await this.executeOrExit(query, params); - return result.count > 0; - } - - private async getAppliedMigrations(): Promise< - Option - > { - const query = `SELECT * FROM ${MIGRATION_TABLE} ORDER BY step ASC;`; - const migrations = await this.executeOrExit(query); - - return migrations ? none : some(migrations); - } - - private async getLastAppliedMigration(): Promise< - Option - > { - const query = ` - SELECT * FROM ${MIGRATION_TABLE} - ORDER BY step DESC - LIMIT 1; - `; - const [migration] = - await this.executeOrExit(query); - return migration ? some(migration) : none; - } - - async areMigrationsAppliedInOrder(): Promise { - const migrations = await this.getAppliedMigrations(); - - if (migrations.isSome()) { - let step = 0; - for (const migration of migrations.value) { - if (migration.step !== step++) { - return false; - } - } - } - - return true; - } - - async createMigrationTable() { - const query = ` - CREATE TABLE ? ( - id INT PRIMARY KEY AUTO_INCREMENT, - name VARCHAR(256) NOT NULL, - description TEXT, - step INT NOT NULL, - appliedAt DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP - ); - `; - - await this.executeOrExit(query, { MIGRATION_TABLE }); - } - - /** - * Return an Some(number) where number represents the step of an applied migration or none if no migrations were applied - */ - private async applyMigration(migration: Migration) { - for (const statement of migration.up) { - await this.executeOrExit(statement); - } - const query = ` - INSERT INTO ${MIGRATION_TABLE} (name, description, step) - VALUES (?, ?, ?); - `; - await this.executeOrExit(query, [ - migration.name, - migration.description, - migration.step.toString(), - ]); - } - - private async destroyMigration(migration: Migration) { - for (const statement of migration.down) { - await this.executeOrExit(statement); - } - const query = ` - DELETE FROM ${MIGRATION_TABLE} WHERE step = ?; - `; - await this.executeOrExit(query, [migration.step.toString()]); - } - - async migrateToLatest() { - const lastAppliedMigration = ( - await this.getLastAppliedMigration() - ).toNullable(); - let nextStep = lastAppliedMigration ? lastAppliedMigration.step + 1 : 0; - - while (nextStep < this.migrations.length) { - const migration = this.migrations[nextStep]; - await this.applyMigration(migration); - nextStep++; - } - - log.info("All migrations have been applied."); - } - - async migrateDown() { - const lastAppliedMigration = await this.getLastAppliedMigration(); - - if (lastAppliedMigration.isNone()) { - log.warn( - "Cannot migrate down because no migrations has been applied", - ); - } else { - const step = lastAppliedMigration.value.step; - this.destroyMigration(this.migrations[step]); - } - } -} diff --git a/server/src/db.bkp/types/types.ts b/server/src/db.bkp/types/types.ts deleted file mode 100644 index efb4e87..0000000 --- a/server/src/db.bkp/types/types.ts +++ /dev/null @@ -1,48 +0,0 @@ -/** - * This file is auto-generated and should not be edited. - * It will be overwritten the next time types are generated. - */ - -export type Admin = { - id: number; - passwordHash: string; -}; -export type AdminSessions = { - id: number; - sessionId: string; - expiresAt: Date; -}; -export type Users = { - id: number; - login: string; - name: string | null; - telegramId: string | null; - passwordHash: string; - createdAt: Date; - updatedAt: Date; - deleted: boolean; -}; -export type UserSessions = { - id: number; - userId: number; - sessionId: string; - expiresAt: Date; -}; -export type Devices = { - uuid: string; - busid: string; - usbid: string; - vendor: string; - deviceName: string; - displayName: string | null; - description: string | null; - connectedAt: Date; - updatedAt: Date; -}; -export type DeviceEvents = { - id: number; - userId: number | null; - targetUuid: string | null; - event: "attached" | "detached" | null; - occuredAt: Date; -}; diff --git a/server/src/client_js/login.ts b/server/src/js/login.ts similarity index 76% rename from server/src/client_js/login.ts rename to server/src/js/login.ts index 1db5078..4ddf6c3 100644 --- a/server/src/client_js/login.ts +++ b/server/src/js/login.ts @@ -2,10 +2,6 @@ import { ok } from "./shared.bundle.ts"; -const a = ok("test"); - -console.log(a); - const form = document.getElementById("loginForm") as HTMLFormElement; const passwordInput = document.getElementById( "passwordInput", @@ -16,11 +12,11 @@ form.addEventListener("submit", async (e) => { const password = passwordInput.value; - const bodyReq = JSON.stringify(ok({ - password: password, - })); - - console.log(bodyReq); + const bodyReq = JSON.stringify( + ok({ + password: password, + }).toJSON(), + ); const response = await fetch("/login", { method: "POST", @@ -30,5 +26,5 @@ form.addEventListener("submit", async (e) => { const body = await response.json(); - console.log(body); + const a = 8; }); diff --git a/server/src/client_js/shared.bundle.ts b/server/src/js/shared.bundle.ts similarity index 100% rename from server/src/client_js/shared.bundle.ts rename to server/src/js/shared.bundle.ts diff --git a/server/src/js/test.js b/server/src/js/test.js new file mode 100644 index 0000000..ef74d34 --- /dev/null +++ b/server/src/js/test.js @@ -0,0 +1 @@ +const a = 5; diff --git a/server/src/admin.ts b/server/src/lib/admin.ts similarity index 95% rename from server/src/admin.ts rename to server/src/lib/admin.ts index 4c0e760..e4c221f 100644 --- a/server/src/admin.ts +++ b/server/src/lib/admin.ts @@ -1,9 +1,9 @@ import { Option, some } from "@shared/utils/option.ts"; -import db from "@src/db/index.ts"; +import db from "@lib/db/index.ts"; import { ok, Result } from "@shared/utils/result.ts"; -import { AdminPasswordNotSetError, QueryExecutionError } from "@src/errors.ts"; -import { AdminRaw, AdminSessionRaw } from "@src/db/types/index.ts"; -import { generateRandomString, passwd } from "@src/utils.ts"; +import { AdminPasswordNotSetError, QueryExecutionError } from "@lib/errors.ts"; +import { AdminRaw, AdminSessionRaw } from "@lib/db/types/index.ts"; +import { generateRandomString, passwd } from "@lib/utils.ts"; import { errAsync, ResultAsync } from "@shared/utils/resultasync.ts"; const TOKEN_LENGTH = 128; @@ -113,10 +113,13 @@ class AdminSessions { setInterval(() => { this.clearExpiredSessions().match( (clearedCount) => { - if (clearedCount > 0) + if (clearedCount > 0) { console.info( - `cleared ${clearedCount} expired token${clearedCount === 1 ? "" : "s"}`, + `cleared ${clearedCount} expired token${ + clearedCount === 1 ? "" : "s" + }`, ); + } }, (error) => console.error(`failed to clear expired tokens: ${error}`), @@ -164,7 +167,7 @@ class AdminSessions { return this.deleteSessionById(session.id).map(() => false); }, () => ok(false), - ), + ) ); } diff --git a/server/src/apiValidator.ts b/server/src/lib/apiValidator.ts similarity index 100% rename from server/src/apiValidator.ts rename to server/src/lib/apiValidator.ts diff --git a/server/src/context.ts b/server/src/lib/context.ts similarity index 96% rename from server/src/context.ts rename to server/src/lib/context.ts index d61326d..24b79d6 100644 --- a/server/src/context.ts +++ b/server/src/lib/context.ts @@ -1,8 +1,9 @@ -import { type Params } from "@src/router.ts"; -import { ExtractRouteParams } from "@src/router.ts"; +import { type Params } from "@lib/router.ts"; +import { type ExtractRouteParams } from "@lib/router.ts"; import { fromNullableVal, none, Option, some } from "@shared/utils/option.ts"; import { deleteCookie, getCookies, setCookie } from "@std/http/cookie"; import { type Cookie } from "@std/http/cookie"; +import { Err, Ok, type Result, ResultFromJSON } from "@shared/utils/result.ts"; // https://cheatsheetseries.owasp.org/cheatsheets/HTTP_Headers_Cheat_Sheet.html const SECURITY_HEADERS: Headers = new Headers({ @@ -120,7 +121,7 @@ export class Context { responseBody = JSON.stringify({ err: "Internal Server Error", }); - status = 400; + status = 500; } } diff --git a/server/src/db/dbWrapper.ts b/server/src/lib/db/dbWrapper.ts similarity index 93% rename from server/src/db/dbWrapper.ts rename to server/src/lib/db/dbWrapper.ts index ed1b136..393433f 100644 --- a/server/src/db/dbWrapper.ts +++ b/server/src/lib/db/dbWrapper.ts @@ -1,6 +1,6 @@ -import { Database, RestBindParameters, Statement } from "@db/sqlite"; +import { Database, RestBindParameters } from "@db/sqlite"; import { err, getMessageFromError, ok, Result } from "@shared/utils/result.ts"; -import { QueryExecutionError } from "@src/errors.ts"; +import { QueryExecutionError } from "@lib/errors.ts"; import { fromNullableVal, none, Option, some } from "@shared/utils/option.ts"; export class DatabaseClient { @@ -27,7 +27,7 @@ export class DatabaseClient { ...params: RestBindParameters ): Result, QueryExecutionError> { return this.safeExecute(() => - fromNullableVal(this.db.prepare(sql).get(params)), + fromNullableVal(this.db.prepare(sql).get(params)) ); } @@ -55,7 +55,7 @@ export class DatabaseClient { ...params: P ): Result>, QueryExecutionError> => this.safeExecute(() => stmt.all(params)).map((result) => - result.length > 0 ? some(result) : none, + result.length > 0 ? some(result) : none ); return { get, all }; diff --git a/server/src/db/index.ts b/server/src/lib/db/index.ts similarity index 97% rename from server/src/db/index.ts rename to server/src/lib/db/index.ts index e00f716..78f296e 100644 --- a/server/src/db/index.ts +++ b/server/src/lib/db/index.ts @@ -1,6 +1,6 @@ -import { Migration, MigrationManager } from "@src/db/migrations.ts"; +import { Migration, MigrationManager } from "@lib/db/migrations.ts"; +import { DatabaseClient } from "@lib/db/dbWrapper.ts"; import { Database } from "@db/sqlite"; -import { DatabaseClient } from "@src/db/dbWrapper.ts"; const sqliteDbPath = Deno.env.get("KBRG_SQLITE_DB_PATH") || "./test.db"; @@ -107,7 +107,6 @@ const migrationManager = new MigrationManager( WHERE id = OLD.id; END;`, ], - [ `DELETE TABLE admin;`, `DELETE TABLE adminSessions;`, diff --git a/server/src/db/migrations.ts b/server/src/lib/db/migrations.ts similarity index 100% rename from server/src/db/migrations.ts rename to server/src/lib/db/migrations.ts diff --git a/server/src/db/test.db b/server/src/lib/db/test.db similarity index 100% rename from server/src/db/test.db rename to server/src/lib/db/test.db diff --git a/server/src/db/types/index.ts b/server/src/lib/db/types/index.ts similarity index 100% rename from server/src/db/types/index.ts rename to server/src/lib/db/types/index.ts diff --git a/server/src/errors.ts b/server/src/lib/errors.ts similarity index 100% rename from server/src/errors.ts rename to server/src/lib/errors.ts diff --git a/server/src/router.ts b/server/src/lib/router.ts similarity index 97% rename from server/src/router.ts rename to server/src/lib/router.ts index 30b1578..2209179 100644 --- a/server/src/router.ts +++ b/server/src/lib/router.ts @@ -1,6 +1,6 @@ -import { RouterTree } from "@src/routerTree.ts"; +import { RouterTree } from "@lib/routerTree.ts"; import { none, Option, some } from "@shared/utils/option.ts"; -import { Context } from "@src/context.ts"; +import { Context } from "@lib/context.ts"; type RequestHandler = ( c: Context, diff --git a/server/src/routerTree.ts b/server/src/lib/routerTree.ts similarity index 100% rename from server/src/routerTree.ts rename to server/src/lib/routerTree.ts diff --git a/server/src/utils.ts b/server/src/lib/utils.ts similarity index 100% rename from server/src/utils.ts rename to server/src/lib/utils.ts diff --git a/server/src/middleware/auth.ts b/server/src/middleware/auth.ts index b413a0c..b97d252 100644 --- a/server/src/middleware/auth.ts +++ b/server/src/middleware/auth.ts @@ -1,10 +1,9 @@ -import { Middleware } from "@src/router.ts"; -import { none } from "@shared/utils/option.ts"; -import admin from "@src/admin.ts"; +import { Middleware } from "@lib/router.ts"; +import admin from "@lib/admin.ts"; const LOGIN_PATH = "/login"; -const authMiddleware: Middleware = async (c) => { +const authMiddleware: Middleware = (c) => { const token = c.cookies.get("token"); const isValid = token .map((token) => admin.sessions.verifyToken(token)) diff --git a/server/src/middleware/rateLimiter.ts b/server/src/middleware/rateLimiter.ts index 129f0ea..3f3aacd 100644 --- a/server/src/middleware/rateLimiter.ts +++ b/server/src/middleware/rateLimiter.ts @@ -1,5 +1,4 @@ -import { Middleware } from "@src/router.ts"; -import { none, some } from "@shared/utils/option.ts"; +import { Middleware } from "@lib/router.ts"; const requestCounts: Partial< Record diff --git a/server/src/requestHandler.ts b/server/src/requestHandler.ts deleted file mode 100644 index cfcdba4..0000000 --- a/server/src/requestHandler.ts +++ /dev/null @@ -1,49 +0,0 @@ -type Methods = "GET" | "POST"; - -class RequestHandler { - constructor( - public readonly method: Methods, - public readonly handler: (req: Request) => Response, - ) {} -} - -class Router { - handlers: { - [x in string]: { - [method in Methods]?: RequestHandler; - }; - } = {}; - - add( - route: T, - method: "GET" | "POST", - handler: (req: Request, params: Params>) => Response, - ) {} - - handleRequest(req: Request) { - const url = new URL(req.url); - const pathname = url.pathname; - } -} - -const router = new Router(); - -router.add("/users/:slug", "POST", (req, params) => { - const _url = req.url; - - return new Response(_url); -}); - -type Params = Keys extends never - ? never - : { - [K in Keys]: string; - }; - -type ExtractParams = T extends string - ? T extends `${infer _Start}:${infer Param}/${infer Rest}` - ? Param | ExtractParams - : T extends `${infer _Start}:${infer Param}` - ? Param - : never - : never; diff --git a/server/src/views/index.html b/server/src/views/index.html new file mode 100644 index 0000000..87f29a7 --- /dev/null +++ b/server/src/views/index.html @@ -0,0 +1,3 @@ +<% layout("./layouts/layout.html") %> + + this is an index.html diff --git a/server/src/views/layouts/basic.html b/server/src/views/layouts/basic.html new file mode 100644 index 0000000..20089f1 --- /dev/null +++ b/server/src/views/layouts/basic.html @@ -0,0 +1,15 @@ + + + + + + + Keyborg + + + + + <%~ it.body %> + + + diff --git a/server/src/views/layouts/layout.html b/server/src/views/layouts/layout.html new file mode 100644 index 0000000..7f9c13d --- /dev/null +++ b/server/src/views/layouts/layout.html @@ -0,0 +1,7 @@ +<% layout("./basic.html") %> + +
+
+ <%~ it.body %> +
+
diff --git a/server/src/views/login.html b/server/src/views/login.html new file mode 100644 index 0000000..efb87f2 --- /dev/null +++ b/server/src/views/login.html @@ -0,0 +1,7 @@ +<% layout("./layouts/basic.html") %> +
+
+

password

+
+
+ diff --git a/server/src/views/test.html b/server/src/views/test.html new file mode 100644 index 0000000..d81edef --- /dev/null +++ b/server/src/views/test.html @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/server/src/wasm_example/Cargo.lock b/server/src/wasm_example/Cargo.lock deleted file mode 100644 index 54af417..0000000 --- a/server/src/wasm_example/Cargo.lock +++ /dev/null @@ -1,166 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 4 - -[[package]] -name = "bumpalo" -version = "3.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" - -[[package]] -name = "cfg-if" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" - -[[package]] -name = "js-sys" -version = "0.3.76" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6717b6b5b077764fb5966237269cb3c64edddde4b14ce42647430a78ced9e7b7" -dependencies = [ - "once_cell", - "wasm-bindgen", -] - -[[package]] -name = "log" -version = "0.4.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" - -[[package]] -name = "once_cell" -version = "1.20.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" - -[[package]] -name = "proc-macro2" -version = "1.0.92" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "quote" -version = "1.0.38" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc" -dependencies = [ - "proc-macro2", -] - -[[package]] -name = "serde" -version = "1.0.217" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02fc4265df13d6fa1d00ecff087228cc0a2b5f3c0e87e258d8b94a156e984c70" -dependencies = [ - "serde_derive", -] - -[[package]] -name = "serde-wasm-bindgen" -version = "0.4.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3b4c031cd0d9014307d82b8abf653c0290fbdaeb4c02d00c63cf52f728628bf" -dependencies = [ - "js-sys", - "serde", - "wasm-bindgen", -] - -[[package]] -name = "serde_derive" -version = "1.0.217" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a9bf7cf98d04a2b28aead066b7496853d4779c9cc183c440dbac457641e19a0" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "syn" -version = "2.0.95" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46f71c0377baf4ef1cc3e3402ded576dccc315800fbc62dfc7fe04b009773b4a" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "unicode-ident" -version = "1.0.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83" - -[[package]] -name = "wasm-bindgen" -version = "0.2.99" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a474f6281d1d70c17ae7aa6a613c87fce69a127e2624002df63dcb39d6cf6396" -dependencies = [ - "cfg-if", - "once_cell", - "wasm-bindgen-macro", -] - -[[package]] -name = "wasm-bindgen-backend" -version = "0.2.99" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f89bb38646b4f81674e8f5c3fb81b562be1fd936d84320f3264486418519c79" -dependencies = [ - "bumpalo", - "log", - "proc-macro2", - "quote", - "syn", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-macro" -version = "0.2.99" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2cc6181fd9a7492eef6fef1f33961e3695e4579b9872a6f7c83aee556666d4fe" -dependencies = [ - "quote", - "wasm-bindgen-macro-support", -] - -[[package]] -name = "wasm-bindgen-macro-support" -version = "0.2.99" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30d7a95b763d3c45903ed6c81f156801839e5ee968bb07e534c44df0fcd330c2" -dependencies = [ - "proc-macro2", - "quote", - "syn", - "wasm-bindgen-backend", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-shared" -version = "0.2.99" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "943aab3fdaaa029a6e0271b35ea10b72b943135afe9bffca82384098ad0e06a6" - -[[package]] -name = "wasm_example" -version = "0.1.0" -dependencies = [ - "serde", - "serde-wasm-bindgen", - "wasm-bindgen", -] diff --git a/server/src/wasm_example/Cargo.toml b/server/src/wasm_example/Cargo.toml deleted file mode 100644 index da63bee..0000000 --- a/server/src/wasm_example/Cargo.toml +++ /dev/null @@ -1,12 +0,0 @@ -[package] -name = "wasm_example" -version = "0.1.0" -edition = "2021" - -[dependencies] -wasm-bindgen = "0.2.99" -serde = { version = "1.0", features = ["derive"] } -serde-wasm-bindgen = "0.4" - -[lib] -crate-type = ["cdylib"] diff --git a/server/src/wasm_example/src/lib.rs b/server/src/wasm_example/src/lib.rs deleted file mode 100644 index ca92307..0000000 --- a/server/src/wasm_example/src/lib.rs +++ /dev/null @@ -1,309 +0,0 @@ -use std::collections::HashMap; - -use wasm_bindgen::prelude::*; - -type Handler = Option; -type Params = HashMap; - -const PARAM_PREFIX_DEFAULT: &str = ":"; -const PATH_SEPARATOR_DEFAULT: &str = "/"; -const WILDCARD_SYMBOL_DEFAULT: char = '*'; - -enum TreeNode<'a> { - Static(&'a StaticTreeNode), - Dynamic(&'a DynamicTreeNode), -} - -impl<'a> TreeNode<'a> { - pub fn extract_static_node(&self) -> &StaticTreeNode { - match self { - Self::Static(n) => n, - Self::Dynamic(n) => &n.node, - } - } -} - -enum TreeNodeMut<'a> { - Static(&'a mut StaticTreeNode), - Dynamic(&'a mut DynamicTreeNode), -} - -impl TreeNodeMut<'_> { - pub fn extract_static_node(&mut self) -> &mut StaticTreeNode { - match self { - Self::Static(n) => n, - Self::Dynamic(n) => &mut n.node, - } - } -} - -#[derive(Default)] -struct StaticTreeNode { - handler: Handler, - wildcard_handler: Handler, - - static_children: HashMap, - dynamic_child: Option>, -} - -struct DynamicTreeNode { - node: StaticTreeNode, - param_name: String, -} - -impl DynamicTreeNode { - pub fn new(handler: Handler, param_name: &str) -> Self { - DynamicTreeNode { - node: StaticTreeNode::new(handler), - param_name: param_name.to_string(), - } - } -} - -impl StaticTreeNode { - pub fn new(handler: Handler) -> Self { - StaticTreeNode { - handler, - ..Default::default() - } - } - - pub fn add_static_child(&mut self, segment: &str, handler: Handler) { - let child = StaticTreeNode::new(handler); - - self.static_children.insert(segment.to_string(), child); - } - - pub fn delete_static_child(&mut self, segment: &str) -> Option { - self.static_children.remove(segment) - } - - pub fn set_dynamic_child(&mut self, param_name: &str, handler: Handler) { - let child = DynamicTreeNode::new(handler, param_name); - - self.dynamic_child = Some(Box::new(child)); - } - - pub fn delete_dynamic_child(&mut self) { - self.dynamic_child = None - } - - pub fn set_wildcard_handler(&mut self, handler: Handler) { - self.wildcard_handler = handler - } - - pub fn delete_wildcard_handler(&mut self) { - self.wildcard_handler = None - } - - pub fn get_static_child(&self, segment: &str) -> Option<&StaticTreeNode> { - self.static_children.get(segment) - } - - pub fn has_static_child(&self, segment: &str) -> bool { - self.static_children.contains_key(segment) - } - - pub fn get_dynamic_child(&self) -> Option<&DynamicTreeNode> { - self.dynamic_child.as_ref().map(|n| n.as_ref()) - } - - pub fn has_dynamic_child(&self) -> bool { - match self.dynamic_child { - Some(_) => true, - None => false, - } - } - - pub fn get_child(&self, segment: &str) -> Option { - self.get_static_child(segment) - .map(|n| TreeNode::Static(&n)) - .or_else(|| self.get_dynamic_child().map(|n| TreeNode::Dynamic(n))) - } - - pub fn get_static_child_mut(&mut self, segment: &str) -> Option<&mut StaticTreeNode> { - self.static_children.get_mut(segment) - } - - pub fn get_dynamic_child_mut(&mut self) -> Option<&mut DynamicTreeNode> { - self.dynamic_child.as_mut().map(|n| n.as_mut()) - } - - pub fn get_child_mut(&mut self, segment: &str) -> Option { - let static_child = self - .static_children - .get_mut(segment) - .map(|c| TreeNodeMut::Static(c)); - - if let Some(static_child) = static_child { - Some(static_child) - } else { - self.dynamic_child.as_mut().map(|n| TreeNodeMut::Dynamic(n)) - } - } -} - -struct TraversePathReturn<'a> { - node: &'a StaticTreeNode, - params: Params, -} - -impl TraversePathReturn<'_> { - pub fn extract_handler(&self) -> Option { - self.node.handler.as_ref().map(|handler| HandlerAndParams { - handler: handler.clone(), - params: serde_wasm_bindgen::to_value(&self.params).unwrap(), - }) - } -} - -fn js_value_to_option(js_value: JsValue) -> Handler { - if js_value.is_undefined() || js_value.is_null() { - None - } else { - Some(js_value) - } -} - -#[wasm_bindgen] -struct HandlerAndParams { - handler: JsValue, - params: JsValue, -} - -#[wasm_bindgen] -impl HandlerAndParams { - #[wasm_bindgen(getter)] - pub fn handler(&self) -> JsValue { - self.handler.clone() - } - - #[wasm_bindgen(getter)] - pub fn params(&self) -> JsValue { - self.params.clone() - } -} - -#[wasm_bindgen] -struct RouterTree { - root: StaticTreeNode, - path_separator: String, - param_prefix: String, - wildcard_symbol: String, -} - -#[wasm_bindgen] -impl RouterTree { - #[wasm_bindgen(constructor)] - pub fn new( - handler: JsValue, - param_prefix: Option, - path_separator: Option, - wildcard_symbol: Option, - ) -> Self { - let root = StaticTreeNode::new(js_value_to_option(handler)); - - RouterTree { - root, - path_separator: path_separator.unwrap_or(PATH_SEPARATOR_DEFAULT.to_string()), - param_prefix: param_prefix.unwrap_or(PARAM_PREFIX_DEFAULT.to_string()), - wildcard_symbol: wildcard_symbol.unwrap_or(WILDCARD_SYMBOL_DEFAULT.to_string()), - } - } - - #[wasm_bindgen] - pub fn add(&mut self, path: String, handler: JsValue) { - let segments = self.parse_path(&path); - let param_prefix = self.param_prefix.as_str(); - let mut current_node = &mut self.root; - - for segment in segments { - current_node = if RouterTree::is_dynamic_segment(segment, param_prefix) { - let param_name = RouterTree::strip_param_prefix(segment, param_prefix); - - if !current_node.has_dynamic_child() { - current_node.set_dynamic_child(param_name, None); - } - &mut current_node.get_dynamic_child_mut().unwrap().node - } else { - if !current_node.has_static_child(segment) { - current_node.add_static_child(segment, None); - } - current_node.get_static_child_mut(segment).unwrap() - } - } - - current_node.handler = js_value_to_option(handler); - } - - #[wasm_bindgen] - pub fn get(&self, path: String) -> Option { - self.traverse_path(&path) - .map(|v| v.extract_handler()) - .flatten() - } - - fn traverse_path(&self, path: &String) -> Option { - let segments = self.parse_path(path); - let mut params: Params = HashMap::new(); - let mut current_node = &self.root; - - for segment in segments { - if current_node.wildcard_handler.is_some() { - break; - } - if let Some(child) = current_node.get_child(segment) { - match child { - TreeNode::Static(node) => current_node = node, - TreeNode::Dynamic(node) => { - params.insert(node.param_name.clone(), segment.to_string()); - current_node = &node.node; - } - } - } else { - return None; - } - } - - return Some(TraversePathReturn { - node: current_node, - params, - }); - } - - fn parse_path<'a>(&self, path: &'a String) -> Vec<&'a str> { - path.trim_start_matches(&self.path_separator) - .split(&self.path_separator) - .collect() - } - - fn get_root_node(&self) -> TreeNode { - TreeNode::Static(&self.root) - } - - fn is_dynamic_segment(segment: &str, param_prefix: &str) -> bool { - segment.starts_with(param_prefix) - } - - fn strip_param_prefix<'a>(segment: &'a str, param_prefix: &str) -> &'a str { - segment.strip_prefix(param_prefix).unwrap_or("") - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn it_works() { - let mut router = RouterTree::new(JsValue::null(), None, None, None); - - router.add("/user/:id".to_string(), JsValue::null()); - - let result = router.get("/user/123".to_string()); - - dbg!("I am here!!!"); - - assert_eq!(1, 1) - } -} diff --git a/server/test.db b/server/test.db deleted file mode 100644 index f52513dadac47f54590966adf2af76183d6fef4c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 53248 zcmeI5TW{OQ701Q+LSJ&(mx2Y11qN6Il3;Hd#W0Ml>rH8SY!i_wN2cI4P(Yx?iOohL z84i_8AH4!L`_{Jt1^QB;AEW&qMIVbk>_b1n_MtPpNFEKX*ntzI_93i@h|t{tJ}!WRWaDf|YZLZL9vK6ltB+veELWOl&5l{5S2?amh-{63iC z#`k{aHh1p*eEZ+?f0|vL_tnkGf8P4D^4s$+{r^9I`t7{B@K~GauSTJJd`!Y#;Mfsq z*-qE>T9Ml&G_t!vd{}K@-NMLf7~9*pf$}Zq(3wr&vY26WZ%N+v4|QNxlS5~>#PYT*xc-|&8gb*vhwcB$Uf>2hrDvzM2{l7eL@`kig;1Zsrd}0 zzEQ&{#{iXtdU)t82*b26C%@M)cJ;;~dWsK`-n8n5$^7kN(^`_Q>QNFJgDd6or0#JR zgtq}%_&W=kb@pl2YN)z{t4~WwiD9Brk-Q1~Fd|NI2^B-qAvR_Eec$yC+%tNJFE+^1G)5}0+QS1qG%j*laQ z@JS-!5oTMt|9n=}78aBrzdDmWG>M1KS*Y0aUa+y{p~R{v*O9c=w7cYeE)vT;7cLDt_S>P4tahHQc{S2@=mwGNdr6h! z&D*}`kv7Y2rbm{si}x*kcW*ctvWrV2V#2aN>*ws-acRD@%twB_LpkI^%0sw7?Ly_{ z?}HgtyMJH#AxSho%=QVTi~v2mKP#l%*^(>EJMJ{mn?WOFP?7(Fq~0RBmw@o%p?wepV`%P1BTjMyxHId)(Jll{)$ARHfq| zyCWTknx^rriDmGCP3dbtbarfdA`X4omW`atjPQufLC2xpWr-Fm-1zYoocLZUKI08Z z*LFJ>@#wJnS!$w+CZEQkcN0pq^ zyUcjJP?}y~M$Ac5d8~}j*S(2pZE;aKjJc)xO3BL}@!DkMa8h77QdsiX5@d;8Sw4ND zs9L3>^vki&)(i6X$PbTqT_!1f(Lm)hw*=2}`3H8HJg#|=u>#3O4iT3%vIUJuFu02t za6V6;?I7s5BdeZFLnH+fv$=X}T05wmry(+)Rz*HHk=$M1p5Zt6%Dul8*bi(#00;m9 zAOHk_01yBIKmZ5;0U!VbfWYTSKv$*MQsDTVOx10RbQY1b_e#00KY&2mk>f00e*l z5C8(#kif*m%xv=h|EI#epRQp8p-Dgh2mk>f00e*l5C8%|00;m9AOHkz8i8LZ6O~!{ z7Xjj5@c-hb!GQJx0U!VbfB+Bx0zd!=00AHX1b_e#xLE|?{{Lq6H?$TA00AHX1b_e# z00KY&2mk>f00eF(0l5Fanf(r}1_D3;2mk>f00e*l5C8%|00;nqn?->C|9|X{{=o(W zfB+Bx0zd!=00AHX1b_e#00KY&2wYf00e*l5C8%|00;m9AOHk_ zKuQ4a{~;BC01yBIKmZ5;0U!VbfB+Bx0zd!=Tz>*^|9}1a7+MGffB+Bx0zd!=00AHX z1b_e#00KaO-~Z33wZh%M-+g6!|f0_N=>=U&%`LA1lRh+{8n9%93Z>tNd+Dv~X z3f<#l67~Yej!3IVN!W_qE}@a#4fqkQHn47CWHpTKZQMY)hI8o5rf*ryuDQ3#FRPm< z_99qp)f))w)g9ERAE1LBY$6jMJV-n{dW6)#$yM>feQc#+ zC(e0Gc^gYb&wpJkOU_tC4UAkTjl*@;1QTp-o_;&8E<9%VRz-Za({;TezxXhp--xA9 z!6?AkRDv2F6$OG$8L z)WBOTPt7XcPli~@b>v*;4A>al#5K%DV^!a;>YG?{pIW7($gCs1Y8iENd>k2sPZ9}_ zFx$%g=d-G|u%P_-)tT&}Nj!8eCrjv+WyVpDvXm0~RZfo{J$kANlbP=Mz2?@enRhyHI)g`(Q@Z z?%!8_ND_^YynR9`BS6pY&k8Abw&cq4E*bP(%La{*K}G%xlJ-p|IbaG zvKyvdZg@F2Oyw4r(TU#+>1U;KS)TFla#@|$)-|QzdJcI*Uv^x!DzbZ#A0Nl-BigF) z-k@ypG_7iyro1y^8H!KF|EN+vT%7=Q{9|{dk5SV!o;9)DM}bZ0Yd>^$YnWfp%|R_UaC{$4N2E_I~Vcju=-gRqm6pKhIO`p%qH2)f9bj1 zp(IC5hWS56m8#7Kn_8{bz(fX?BQJxxM>S1UmD8OmRVx;iem%~cc+Hi`if}M3yF-*QrzsiT2O>+CZEQkcN0pp>y38QE zQ0`q|M$E}Qc>s)0!o7)UZE;aKjJc&cN6E_`@!DkMa8h77QdsiX5@d;8S+3ri)($G? zDX090>RT20+{EbcVy|5sf(md;*j9?|5NH83U~fEe>nHcnSZcd*nj{K00KY&2mpakOyG3u zj=He6Fw@t?XT`~5nc>6Y^zl`;m=Pbhj~WY8{^;}K)RSQI(ViIRRmN|mKf*(IcxM6c zEO=^VTl%;_nNG#!< J#Rt5z_#f_1H-P{E diff --git a/server/views/index.html b/server/views/index.html index 87f29a7..c62e141 100644 --- a/server/views/index.html +++ b/server/views/index.html @@ -1,3 +1 @@ -<% layout("./layouts/layout.html") %> - - this is an index.html +<% layout("./layouts/layout.html") %> this is an index.html \ No newline at end of file diff --git a/server/views/layouts/basic.html b/server/views/layouts/basic.html index 57cb5c8..e94a699 100644 --- a/server/views/layouts/basic.html +++ b/server/views/layouts/basic.html @@ -1,17 +1 @@ - - - - - - - Keyborg - - - - - - <%~ it.body %> - - - - +Keyborg<%~ it.body %> \ No newline at end of file diff --git a/server/views/layouts/layout.html b/server/views/layouts/layout.html index 7f9c13d..35d9a6b 100644 --- a/server/views/layouts/layout.html +++ b/server/views/layouts/layout.html @@ -1,7 +1 @@ -<% layout("./basic.html") %> - -
-
- <%~ it.body %> -
-
+<% layout("./basic.html") %>
<%~ it.body %>
\ No newline at end of file diff --git a/server/views/login.html b/server/views/login.html index ad7bf9b..c2d8fd0 100644 --- a/server/views/login.html +++ b/server/views/login.html @@ -1,11 +1 @@ -<% layout("./layouts/basic.html") %> - -
-
-

password

- - -
-
- - +<% layout("./layouts/basic.html") %>

password

\ No newline at end of file