Compare commits
5 Commits
feature/ap
...
feature/pr
Author | SHA1 | Date | |
---|---|---|---|
1f7dded292
|
|||
c3e64c8b98
|
|||
7b76726e6e
|
|||
24ded15ac9
|
|||
bacba95adc
|
@@ -1,10 +0,0 @@
|
|||||||
[*]
|
|
||||||
charset = utf-8
|
|
||||||
insert_final_newline = true
|
|
||||||
end_of_line = lf
|
|
||||||
|
|
||||||
indent_style = tab
|
|
||||||
indent_size = 2
|
|
||||||
max_line_length = 100
|
|
||||||
quote_type = single
|
|
||||||
trim_trailing_whitespace = true
|
|
@@ -1,7 +0,0 @@
|
|||||||
// Folder-specific settings
|
|
||||||
//
|
|
||||||
// For a full list of overridable settings, and general information on folder-specific settings,
|
|
||||||
// see the documentation: https://zed.dev/docs/configuring-zed#settings-files
|
|
||||||
{
|
|
||||||
"formatter": "prettier"
|
|
||||||
}
|
|
@@ -27,10 +27,3 @@ bun install
|
|||||||
bun run prepare
|
bun run prepare
|
||||||
bun run dev
|
bun run dev
|
||||||
```
|
```
|
||||||
|
|
||||||
## To Do
|
|
||||||
|
|
||||||
- [ ] Proper invite page
|
|
||||||
- [ ] Proper error page for login without invite
|
|
||||||
- [ ] Support file provider (for wg-quick)
|
|
||||||
- [ ] wg-quick scripts (maybe?)
|
|
||||||
|
32
bun.lock
32
bun.lock
@@ -9,7 +9,6 @@
|
|||||||
"drizzle-orm": "^0.38.4",
|
"drizzle-orm": "^0.38.4",
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@lucide/svelte": "^0.487.0",
|
|
||||||
"@oslojs/crypto": "^1.0.1",
|
"@oslojs/crypto": "^1.0.1",
|
||||||
"@oslojs/encoding": "^1.1.0",
|
"@oslojs/encoding": "^1.1.0",
|
||||||
"@ryoppippi/unplugin-typia": "npm:@jsr/ryoppippi__unplugin-typia",
|
"@ryoppippi/unplugin-typia": "npm:@jsr/ryoppippi__unplugin-typia",
|
||||||
@@ -25,13 +24,14 @@
|
|||||||
"@types/qrcode-svg": "^1.1.5",
|
"@types/qrcode-svg": "^1.1.5",
|
||||||
"arctic": "^2.3.4",
|
"arctic": "^2.3.4",
|
||||||
"autoprefixer": "^10.4.21",
|
"autoprefixer": "^10.4.21",
|
||||||
"bits-ui": "^1.3.18",
|
"bits-ui": "^0.22.0",
|
||||||
"clsx": "^2.1.1",
|
"clsx": "^2.1.1",
|
||||||
"eslint": "^9.22.0",
|
"eslint": "^9.22.0",
|
||||||
"eslint-config-prettier": "^9.1.0",
|
"eslint-config-prettier": "^9.1.0",
|
||||||
"eslint-plugin-svelte": "^2.46.1",
|
"eslint-plugin-svelte": "^2.46.1",
|
||||||
"globals": "^15.15.0",
|
"globals": "^15.15.0",
|
||||||
"ip-address": "^10.0.1",
|
"ip-address": "^10.0.1",
|
||||||
|
"lucide-svelte": "^0.469.0",
|
||||||
"prettier": "^3.5.3",
|
"prettier": "^3.5.3",
|
||||||
"prettier-plugin-svelte": "^3.3.3",
|
"prettier-plugin-svelte": "^3.3.3",
|
||||||
"prettier-plugin-tailwindcss": "^0.6.11",
|
"prettier-plugin-tailwindcss": "^0.6.11",
|
||||||
@@ -181,7 +181,7 @@
|
|||||||
|
|
||||||
"@libsql/win32-x64-msvc": ["@libsql/win32-x64-msvc@0.4.7", "", { "os": "win32", "cpu": "x64" }, "sha512-7pJzOWzPm6oJUxml+PCDRzYQ4A1hTMHAciTAHfFK4fkbDZX33nWPVG7Y3vqdKtslcwAzwmrNDc6sXy2nwWnbiw=="],
|
"@libsql/win32-x64-msvc": ["@libsql/win32-x64-msvc@0.4.7", "", { "os": "win32", "cpu": "x64" }, "sha512-7pJzOWzPm6oJUxml+PCDRzYQ4A1hTMHAciTAHfFK4fkbDZX33nWPVG7Y3vqdKtslcwAzwmrNDc6sXy2nwWnbiw=="],
|
||||||
|
|
||||||
"@lucide/svelte": ["@lucide/svelte@0.487.0", "", { "peerDependencies": { "svelte": "^5" } }, "sha512-27b/wUzWrqDJu97+1iSV2X8L2JGRWH/mAWAjHgazWxhGxVu/kS0p3SbNu6w3skNmQNEku33EKU1v44IVwULzbw=="],
|
"@melt-ui/svelte": ["@melt-ui/svelte@0.76.2", "", { "dependencies": { "@floating-ui/core": "^1.3.1", "@floating-ui/dom": "^1.4.5", "@internationalized/date": "^3.5.0", "dequal": "^2.0.3", "focus-trap": "^7.5.2", "nanoid": "^5.0.4" }, "peerDependencies": { "svelte": ">=3 <5" } }, "sha512-7SbOa11tXUS95T3fReL+dwDs5FyJtCEqrqG3inRziDws346SYLsxOQ6HmX+4BkIsQh1R8U3XNa+EMmdMt38lMA=="],
|
||||||
|
|
||||||
"@neon-rs/load": ["@neon-rs/load@0.0.4", "", {}, "sha512-kTPhdZyTQxB+2wpiRcFWrDcejc4JI6tkPuS7UZCG4l6Zvc5kU/gGQ/ozvHTh1XR5tS+UlfAfGuPajjzQjCiHCw=="],
|
"@neon-rs/load": ["@neon-rs/load@0.0.4", "", {}, "sha512-kTPhdZyTQxB+2wpiRcFWrDcejc4JI6tkPuS7UZCG4l6Zvc5kU/gGQ/ozvHTh1XR5tS+UlfAfGuPajjzQjCiHCw=="],
|
||||||
|
|
||||||
@@ -347,7 +347,7 @@
|
|||||||
|
|
||||||
"binary-extensions": ["binary-extensions@2.3.0", "", {}, "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw=="],
|
"binary-extensions": ["binary-extensions@2.3.0", "", {}, "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw=="],
|
||||||
|
|
||||||
"bits-ui": ["bits-ui@1.3.18", "", { "dependencies": { "@floating-ui/core": "^1.6.4", "@floating-ui/dom": "^1.6.7", "@internationalized/date": "^3.5.6", "esm-env": "^1.1.2", "runed": "^0.23.2", "svelte-toolbelt": "^0.7.1", "tabbable": "^6.2.0" }, "peerDependencies": { "svelte": "^5.11.0" } }, "sha512-4ZsJqMZUpCEGYMHfVSNZDbOTg3Haxwe01I5LMs4ZImjY7huDac7Dkh2KWTtOdc7jhj2vJprE9bJI4Ayg8gexXQ=="],
|
"bits-ui": ["bits-ui@0.22.0", "", { "dependencies": { "@internationalized/date": "^3.5.1", "@melt-ui/svelte": "0.76.2", "nanoid": "^5.0.5" }, "peerDependencies": { "svelte": "^4.0.0 || ^5.0.0" } }, "sha512-r7Fw1HNgA4YxZBRcozl7oP0bheQ8EHh+kfMBZJgyFISix8t4p/nqDcHLmBgIiJ3T5XjYnJRorYDjIWaCfhb5fw=="],
|
||||||
|
|
||||||
"bl": ["bl@4.1.0", "", { "dependencies": { "buffer": "^5.5.0", "inherits": "^2.0.4", "readable-stream": "^3.4.0" } }, "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w=="],
|
"bl": ["bl@4.1.0", "", { "dependencies": { "buffer": "^5.5.0", "inherits": "^2.0.4", "readable-stream": "^3.4.0" } }, "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w=="],
|
||||||
|
|
||||||
@@ -421,6 +421,8 @@
|
|||||||
|
|
||||||
"defu": ["defu@6.1.4", "", {}, "sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg=="],
|
"defu": ["defu@6.1.4", "", {}, "sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg=="],
|
||||||
|
|
||||||
|
"dequal": ["dequal@2.0.3", "", {}, "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA=="],
|
||||||
|
|
||||||
"detect-libc": ["detect-libc@2.0.2", "", {}, "sha512-UX6sGumvvqSaXgdKGUsgZWqcUyIXZ/vZTrlRT/iobiKhGL0zL4d3osHj3uqllWJK+i+sixDS/3COVEOFbupFyw=="],
|
"detect-libc": ["detect-libc@2.0.2", "", {}, "sha512-UX6sGumvvqSaXgdKGUsgZWqcUyIXZ/vZTrlRT/iobiKhGL0zL4d3osHj3uqllWJK+i+sixDS/3COVEOFbupFyw=="],
|
||||||
|
|
||||||
"devalue": ["devalue@5.1.1", "", {}, "sha512-maua5KUiapvEwiEAe+XnlZ3Rh0GD+qI1J/nb9vrJc3muPXvcF/8gXYTWF76+5DAqHyDUtOIImEuo0YKE9mshVw=="],
|
"devalue": ["devalue@5.1.1", "", {}, "sha512-maua5KUiapvEwiEAe+XnlZ3Rh0GD+qI1J/nb9vrJc3muPXvcF/8gXYTWF76+5DAqHyDUtOIImEuo0YKE9mshVw=="],
|
||||||
@@ -513,6 +515,8 @@
|
|||||||
|
|
||||||
"flatted": ["flatted@3.3.2", "", {}, "sha512-AiwGJM8YcNOaobumgtng+6NHuOqC3A7MixFeDafM3X9cIUM+xUXoS5Vfgf+OihAYe20fxqNM9yPBXJzRtZ/4eA=="],
|
"flatted": ["flatted@3.3.2", "", {}, "sha512-AiwGJM8YcNOaobumgtng+6NHuOqC3A7MixFeDafM3X9cIUM+xUXoS5Vfgf+OihAYe20fxqNM9yPBXJzRtZ/4eA=="],
|
||||||
|
|
||||||
|
"focus-trap": ["focus-trap@7.6.2", "", { "dependencies": { "tabbable": "^6.2.0" } }, "sha512-9FhUxK1hVju2+AiQIDJ5Dd//9R2n2RAfJ0qfhF4IHGHgcoEUTMpbTeG/zbEuwaiYXfuAH6XE0/aCyxDdRM+W5w=="],
|
||||||
|
|
||||||
"foreground-child": ["foreground-child@3.3.0", "", { "dependencies": { "cross-spawn": "^7.0.0", "signal-exit": "^4.0.1" } }, "sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg=="],
|
"foreground-child": ["foreground-child@3.3.0", "", { "dependencies": { "cross-spawn": "^7.0.0", "signal-exit": "^4.0.1" } }, "sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg=="],
|
||||||
|
|
||||||
"formdata-polyfill": ["formdata-polyfill@4.0.10", "", { "dependencies": { "fetch-blob": "^3.1.2" } }, "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g=="],
|
"formdata-polyfill": ["formdata-polyfill@4.0.10", "", { "dependencies": { "fetch-blob": "^3.1.2" } }, "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g=="],
|
||||||
@@ -559,8 +563,6 @@
|
|||||||
|
|
||||||
"ini": ["ini@4.1.3", "", {}, "sha512-X7rqawQBvfdjS10YU1y1YVreA3SsLrW9dX2CewP2EbBJM4ypVNLDkO5y04gejPwKIY9lR+7r9gn3rFPt/kmWFg=="],
|
"ini": ["ini@4.1.3", "", {}, "sha512-X7rqawQBvfdjS10YU1y1YVreA3SsLrW9dX2CewP2EbBJM4ypVNLDkO5y04gejPwKIY9lR+7r9gn3rFPt/kmWFg=="],
|
||||||
|
|
||||||
"inline-style-parser": ["inline-style-parser@0.2.4", "", {}, "sha512-0aO8FkhNZlj/ZIbNi7Lxxr12obT7cL1moPfE4tg1LkX7LlLfC6DeX4l2ZEud1ukP9jNQyNnfzQVqwbwmAATY4Q=="],
|
|
||||||
|
|
||||||
"inquirer": ["inquirer@8.2.6", "", { "dependencies": { "ansi-escapes": "^4.2.1", "chalk": "^4.1.1", "cli-cursor": "^3.1.0", "cli-width": "^3.0.0", "external-editor": "^3.0.3", "figures": "^3.0.0", "lodash": "^4.17.21", "mute-stream": "0.0.8", "ora": "^5.4.1", "run-async": "^2.4.0", "rxjs": "^7.5.5", "string-width": "^4.1.0", "strip-ansi": "^6.0.0", "through": "^2.3.6", "wrap-ansi": "^6.0.1" } }, "sha512-M1WuAmb7pn9zdFRtQYk26ZBoY043Sse0wVDdk4Bppr+JOXyQYybdtvK+l9wUibhtjdjvtoiNy8tk+EgsYIUqKg=="],
|
"inquirer": ["inquirer@8.2.6", "", { "dependencies": { "ansi-escapes": "^4.2.1", "chalk": "^4.1.1", "cli-cursor": "^3.1.0", "cli-width": "^3.0.0", "external-editor": "^3.0.3", "figures": "^3.0.0", "lodash": "^4.17.21", "mute-stream": "0.0.8", "ora": "^5.4.1", "run-async": "^2.4.0", "rxjs": "^7.5.5", "string-width": "^4.1.0", "strip-ansi": "^6.0.0", "through": "^2.3.6", "wrap-ansi": "^6.0.1" } }, "sha512-M1WuAmb7pn9zdFRtQYk26ZBoY043Sse0wVDdk4Bppr+JOXyQYybdtvK+l9wUibhtjdjvtoiNy8tk+EgsYIUqKg=="],
|
||||||
|
|
||||||
"ip-address": ["ip-address@10.0.1", "", {}, "sha512-NWv9YLW4PoW2B7xtzaS3NCot75m6nK7Icdv0o3lfMceJVRfSoQwqD4wEH5rLwoKJwUiZ/rfpiVBhnaF0FK4HoA=="],
|
"ip-address": ["ip-address@10.0.1", "", {}, "sha512-NWv9YLW4PoW2B7xtzaS3NCot75m6nK7Icdv0o3lfMceJVRfSoQwqD4wEH5rLwoKJwUiZ/rfpiVBhnaF0FK4HoA=="],
|
||||||
@@ -633,6 +635,8 @@
|
|||||||
|
|
||||||
"lru-cache": ["lru-cache@10.4.3", "", {}, "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ=="],
|
"lru-cache": ["lru-cache@10.4.3", "", {}, "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ=="],
|
||||||
|
|
||||||
|
"lucide-svelte": ["lucide-svelte@0.469.0", "", { "peerDependencies": { "svelte": "^3 || ^4 || ^5.0.0-next.42" } }, "sha512-PMIJ8jrFqVUsXJz4d1yfAQplaGhNOahwwkzbunha8DhpiD73xqX24n8dE1dPpUk3vcrdWVsHc1y/liHHotOnGQ=="],
|
||||||
|
|
||||||
"magic-string": ["magic-string@0.30.17", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0" } }, "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA=="],
|
"magic-string": ["magic-string@0.30.17", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0" } }, "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA=="],
|
||||||
|
|
||||||
"merge2": ["merge2@1.4.1", "", {}, "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg=="],
|
"merge2": ["merge2@1.4.1", "", {}, "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg=="],
|
||||||
@@ -661,7 +665,7 @@
|
|||||||
|
|
||||||
"mz": ["mz@2.7.0", "", { "dependencies": { "any-promise": "^1.0.0", "object-assign": "^4.0.1", "thenify-all": "^1.0.0" } }, "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q=="],
|
"mz": ["mz@2.7.0", "", { "dependencies": { "any-promise": "^1.0.0", "object-assign": "^4.0.1", "thenify-all": "^1.0.0" } }, "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q=="],
|
||||||
|
|
||||||
"nanoid": ["nanoid@3.3.8", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w=="],
|
"nanoid": ["nanoid@5.0.9", "", { "bin": { "nanoid": "bin/nanoid.js" } }, "sha512-Aooyr6MXU6HpvvWXKoVoXwKMs/KyVakWwg7xQfv5/S/RIgJMy0Ifa45H9qqYy7pTCszrHzP21Uk4PZq2HpEM8Q=="],
|
||||||
|
|
||||||
"natural-compare": ["natural-compare@1.4.0", "", {}, "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw=="],
|
"natural-compare": ["natural-compare@1.4.0", "", {}, "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw=="],
|
||||||
|
|
||||||
@@ -783,8 +787,6 @@
|
|||||||
|
|
||||||
"run-parallel": ["run-parallel@1.2.0", "", { "dependencies": { "queue-microtask": "^1.2.2" } }, "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA=="],
|
"run-parallel": ["run-parallel@1.2.0", "", { "dependencies": { "queue-microtask": "^1.2.2" } }, "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA=="],
|
||||||
|
|
||||||
"runed": ["runed@0.23.4", "", { "dependencies": { "esm-env": "^1.0.0" }, "peerDependencies": { "svelte": "^5.7.0" } }, "sha512-9q8oUiBYeXIDLWNK5DfCWlkL0EW3oGbk845VdKlPeia28l751VpfesaB/+7pI6rnbx1I6rqoZ2fZxptOJLxILA=="],
|
|
||||||
|
|
||||||
"rxjs": ["rxjs@7.8.2", "", { "dependencies": { "tslib": "^2.1.0" } }, "sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA=="],
|
"rxjs": ["rxjs@7.8.2", "", { "dependencies": { "tslib": "^2.1.0" } }, "sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA=="],
|
||||||
|
|
||||||
"sade": ["sade@1.8.1", "", { "dependencies": { "mri": "^1.1.0" } }, "sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A=="],
|
"sade": ["sade@1.8.1", "", { "dependencies": { "mri": "^1.1.0" } }, "sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A=="],
|
||||||
@@ -825,8 +827,6 @@
|
|||||||
|
|
||||||
"strip-json-comments": ["strip-json-comments@3.1.1", "", {}, "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig=="],
|
"strip-json-comments": ["strip-json-comments@3.1.1", "", {}, "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig=="],
|
||||||
|
|
||||||
"style-to-object": ["style-to-object@1.0.8", "", { "dependencies": { "inline-style-parser": "0.2.4" } }, "sha512-xT47I/Eo0rwJmaXC4oilDGDWLohVhR6o/xAQcPQN8q6QBuZVL8qMYL85kLmST5cPjAorwvqIA4qXTRQoYHaL6g=="],
|
|
||||||
|
|
||||||
"sucrase": ["sucrase@3.35.0", "", { "dependencies": { "@jridgewell/gen-mapping": "^0.3.2", "commander": "^4.0.0", "glob": "^10.3.10", "lines-and-columns": "^1.1.6", "mz": "^2.7.0", "pirates": "^4.0.1", "ts-interface-checker": "^0.1.9" }, "bin": { "sucrase": "bin/sucrase", "sucrase-node": "bin/sucrase-node" } }, "sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA=="],
|
"sucrase": ["sucrase@3.35.0", "", { "dependencies": { "@jridgewell/gen-mapping": "^0.3.2", "commander": "^4.0.0", "glob": "^10.3.10", "lines-and-columns": "^1.1.6", "mz": "^2.7.0", "pirates": "^4.0.1", "ts-interface-checker": "^0.1.9" }, "bin": { "sucrase": "bin/sucrase", "sucrase-node": "bin/sucrase-node" } }, "sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA=="],
|
||||||
|
|
||||||
"supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="],
|
"supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="],
|
||||||
@@ -839,8 +839,6 @@
|
|||||||
|
|
||||||
"svelte-eslint-parser": ["svelte-eslint-parser@0.43.0", "", { "dependencies": { "eslint-scope": "^7.2.2", "eslint-visitor-keys": "^3.4.3", "espree": "^9.6.1", "postcss": "^8.4.39", "postcss-scss": "^4.0.9" }, "peerDependencies": { "svelte": "^3.37.0 || ^4.0.0 || ^5.0.0" }, "optionalPeers": ["svelte"] }, "sha512-GpU52uPKKcVnh8tKN5P4UZpJ/fUDndmq7wfsvoVXsyP+aY0anol7Yqo01fyrlaWGMFfm4av5DyrjlaXdLRJvGA=="],
|
"svelte-eslint-parser": ["svelte-eslint-parser@0.43.0", "", { "dependencies": { "eslint-scope": "^7.2.2", "eslint-visitor-keys": "^3.4.3", "espree": "^9.6.1", "postcss": "^8.4.39", "postcss-scss": "^4.0.9" }, "peerDependencies": { "svelte": "^3.37.0 || ^4.0.0 || ^5.0.0" }, "optionalPeers": ["svelte"] }, "sha512-GpU52uPKKcVnh8tKN5P4UZpJ/fUDndmq7wfsvoVXsyP+aY0anol7Yqo01fyrlaWGMFfm4av5DyrjlaXdLRJvGA=="],
|
||||||
|
|
||||||
"svelte-toolbelt": ["svelte-toolbelt@0.7.1", "", { "dependencies": { "clsx": "^2.1.1", "runed": "^0.23.2", "style-to-object": "^1.0.8" }, "peerDependencies": { "svelte": "^5.0.0" } }, "sha512-HcBOcR17Vx9bjaOceUvxkY3nGmbBmCBBbuWLLEWO6jtmWH8f/QoWmbyUfQZrpDINH39en1b8mptfPQT9VKQ1xQ=="],
|
|
||||||
|
|
||||||
"tabbable": ["tabbable@6.2.0", "", {}, "sha512-Cat63mxsVJlzYvN51JmVXIgNoUokrIaT2zLclCXjRd8boZ0004U4KCs/sToJ75C6sdlByWxpYnb5Boif1VSFew=="],
|
"tabbable": ["tabbable@6.2.0", "", {}, "sha512-Cat63mxsVJlzYvN51JmVXIgNoUokrIaT2zLclCXjRd8boZ0004U4KCs/sToJ75C6sdlByWxpYnb5Boif1VSFew=="],
|
||||||
|
|
||||||
"tailwind-merge": ["tailwind-merge@2.6.0", "", {}, "sha512-P+Vu1qXfzediirmHOC3xKGAYeZtPcV9g76X+xg2FD4tYgR71ewMA35Y3sCz3zhiN/dwefRpJX0yBcgwi1fXNQA=="],
|
"tailwind-merge": ["tailwind-merge@2.6.0", "", {}, "sha512-P+Vu1qXfzediirmHOC3xKGAYeZtPcV9g76X+xg2FD4tYgR71ewMA35Y3sCz3zhiN/dwefRpJX0yBcgwi1fXNQA=="],
|
||||||
@@ -971,6 +969,8 @@
|
|||||||
|
|
||||||
"pkg-types/pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="],
|
"pkg-types/pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="],
|
||||||
|
|
||||||
|
"postcss/nanoid": ["nanoid@3.3.8", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w=="],
|
||||||
|
|
||||||
"postcss-load-config/lilconfig": ["lilconfig@2.1.0", "", {}, "sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ=="],
|
"postcss-load-config/lilconfig": ["lilconfig@2.1.0", "", {}, "sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ=="],
|
||||||
|
|
||||||
"postcss-nested/postcss-selector-parser": ["postcss-selector-parser@6.1.2", "", { "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" } }, "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg=="],
|
"postcss-nested/postcss-selector-parser": ["postcss-selector-parser@6.1.2", "", { "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" } }, "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg=="],
|
||||||
@@ -1099,6 +1099,8 @@
|
|||||||
|
|
||||||
"drizzle-kit/esbuild/@esbuild/win32-x64": ["@esbuild/win32-x64@0.19.12", "", { "os": "win32", "cpu": "x64" }, "sha512-T1QyPSDCyMXaO3pzBkF96E8xMkiRYbUEZADd29SyPGabqxMViNoii+NcK7eWJAEoU6RZyEm5lVSIjTmcdoB9HA=="],
|
"drizzle-kit/esbuild/@esbuild/win32-x64": ["@esbuild/win32-x64@0.19.12", "", { "os": "win32", "cpu": "x64" }, "sha512-T1QyPSDCyMXaO3pzBkF96E8xMkiRYbUEZADd29SyPGabqxMViNoii+NcK7eWJAEoU6RZyEm5lVSIjTmcdoB9HA=="],
|
||||||
|
|
||||||
|
"eslint-plugin-svelte/postcss/nanoid": ["nanoid@3.3.8", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w=="],
|
||||||
|
|
||||||
"gel/which/isexe": ["isexe@3.1.1", "", {}, "sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ=="],
|
"gel/which/isexe": ["isexe@3.1.1", "", {}, "sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ=="],
|
||||||
|
|
||||||
"glob/minimatch/brace-expansion": ["brace-expansion@2.0.1", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA=="],
|
"glob/minimatch/brace-expansion": ["brace-expansion@2.0.1", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA=="],
|
||||||
@@ -1109,10 +1111,14 @@
|
|||||||
|
|
||||||
"pkg-dir/find-up/path-exists": ["path-exists@5.0.0", "", {}, "sha512-RjhtfwJOxzcFmNOi6ltcbcu4Iu+FL3zEj83dk4kAS+fVpTxXLO1b38RvJgT/0QwvV/L3aY9TAnyv0EOqW4GoMQ=="],
|
"pkg-dir/find-up/path-exists": ["path-exists@5.0.0", "", {}, "sha512-RjhtfwJOxzcFmNOi6ltcbcu4Iu+FL3zEj83dk4kAS+fVpTxXLO1b38RvJgT/0QwvV/L3aY9TAnyv0EOqW4GoMQ=="],
|
||||||
|
|
||||||
|
"svelte-eslint-parser/postcss/nanoid": ["nanoid@3.3.8", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w=="],
|
||||||
|
|
||||||
"tailwindcss/chokidar/glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="],
|
"tailwindcss/chokidar/glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="],
|
||||||
|
|
||||||
"tailwindcss/chokidar/readdirp": ["readdirp@3.6.0", "", { "dependencies": { "picomatch": "^2.2.1" } }, "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA=="],
|
"tailwindcss/chokidar/readdirp": ["readdirp@3.6.0", "", { "dependencies": { "picomatch": "^2.2.1" } }, "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA=="],
|
||||||
|
|
||||||
|
"tailwindcss/postcss/nanoid": ["nanoid@3.3.8", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w=="],
|
||||||
|
|
||||||
"tailwindcss/postcss-load-config/yaml": ["yaml@2.6.1", "", { "bin": { "yaml": "bin.mjs" } }, "sha512-7r0XPzioN/Q9kXBro/XPnA6kznR73DHq+GXh5ON7ZozRO6aMjbmiBuKste2wslTFkC5d1dw0GooOCepZXJ2SAg=="],
|
"tailwindcss/postcss-load-config/yaml": ["yaml@2.6.1", "", { "bin": { "yaml": "bin.mjs" } }, "sha512-7r0XPzioN/Q9kXBro/XPnA6kznR73DHq+GXh5ON7ZozRO6aMjbmiBuKste2wslTFkC5d1dw0GooOCepZXJ2SAg=="],
|
||||||
|
|
||||||
"vite/rollup/@rollup/rollup-android-arm-eabi": ["@rollup/rollup-android-arm-eabi@4.34.8", "", { "os": "android", "cpu": "arm" }, "sha512-q217OSE8DTp8AFHuNHXo0Y86e1wtlfVrXiAlwkIvGRQv9zbc6mE3sjIVfwI8sYUyNxwOg0j/Vm1RKM04JcWLJw=="],
|
"vite/rollup/@rollup/rollup-android-arm-eabi": ["@rollup/rollup-android-arm-eabi@4.34.8", "", { "os": "android", "cpu": "arm" }, "sha512-q217OSE8DTp8AFHuNHXo0Y86e1wtlfVrXiAlwkIvGRQv9zbc6mE3sjIVfwI8sYUyNxwOg0j/Vm1RKM04JcWLJw=="],
|
||||||
|
@@ -19,7 +19,6 @@
|
|||||||
"prepare": "ts-patch install"
|
"prepare": "ts-patch install"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@lucide/svelte": "^0.487.0",
|
|
||||||
"@oslojs/crypto": "^1.0.1",
|
"@oslojs/crypto": "^1.0.1",
|
||||||
"@oslojs/encoding": "^1.1.0",
|
"@oslojs/encoding": "^1.1.0",
|
||||||
"@ryoppippi/unplugin-typia": "npm:@jsr/ryoppippi__unplugin-typia",
|
"@ryoppippi/unplugin-typia": "npm:@jsr/ryoppippi__unplugin-typia",
|
||||||
@@ -35,13 +34,14 @@
|
|||||||
"@types/qrcode-svg": "^1.1.5",
|
"@types/qrcode-svg": "^1.1.5",
|
||||||
"arctic": "^2.3.4",
|
"arctic": "^2.3.4",
|
||||||
"autoprefixer": "^10.4.21",
|
"autoprefixer": "^10.4.21",
|
||||||
"bits-ui": "^1.3.18",
|
"bits-ui": "^0.22.0",
|
||||||
"clsx": "^2.1.1",
|
"clsx": "^2.1.1",
|
||||||
"eslint": "^9.22.0",
|
"eslint": "^9.22.0",
|
||||||
"eslint-config-prettier": "^9.1.0",
|
"eslint-config-prettier": "^9.1.0",
|
||||||
"eslint-plugin-svelte": "^2.46.1",
|
"eslint-plugin-svelte": "^2.46.1",
|
||||||
"globals": "^15.15.0",
|
"globals": "^15.15.0",
|
||||||
"ip-address": "^10.0.1",
|
"ip-address": "^10.0.1",
|
||||||
|
"lucide-svelte": "^0.469.0",
|
||||||
"prettier": "^3.5.3",
|
"prettier": "^3.5.3",
|
||||||
"prettier-plugin-svelte": "^3.3.3",
|
"prettier-plugin-svelte": "^3.3.3",
|
||||||
"prettier-plugin-tailwindcss": "^0.6.11",
|
"prettier-plugin-tailwindcss": "^0.6.11",
|
||||||
|
@@ -1,24 +1,24 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { LucideLoaderCircle } from '@lucide/svelte';
|
import { LucideLoaderCircle } from 'lucide-svelte';
|
||||||
import { Button } from '$lib/components/ui/button';
|
import { Button } from '$lib/components/ui/button';
|
||||||
import { cn } from '$lib/utils.js';
|
import { cn } from '$lib/utils.js';
|
||||||
import googleIcon from '$lib/assets/google.svg';
|
import googleIcon from '$lib/assets/google.svg';
|
||||||
|
|
||||||
let { inviteToken, class: className, ...rest }: {
|
let { inviteToken, class: className, ...rest }: { inviteToken?: string; class?: string; rest?: { [p: string]: unknown } } = $props();
|
||||||
inviteToken?: string;
|
|
||||||
class?: string;
|
|
||||||
rest?: { [p: string]: unknown }
|
|
||||||
} = $props();
|
|
||||||
|
|
||||||
let submitted = $state(false);
|
let isLoading = $state(false);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class={cn('flex gap-6', className)} {...rest}>
|
<div class={cn('flex gap-6', className)} {...rest}>
|
||||||
<form method="get" onsubmit={() => submitted = true}
|
<form method="get" action="/auth/authentik{inviteToken ? `?invite=${inviteToken}` : ''}">
|
||||||
action="/auth/authentik{inviteToken ? `?invite=${inviteToken}` : ''}">
|
|
||||||
<input type="hidden" value={inviteToken} name="invite" />
|
<input type="hidden" value={inviteToken} name="invite" />
|
||||||
<Button type="submit" disabled={submitted}>
|
<Button
|
||||||
{#if submitted}
|
type="submit"
|
||||||
|
onclick={() => {
|
||||||
|
isLoading = true;
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{#if isLoading}
|
||||||
<LucideLoaderCircle class="mr-2 h-4 w-4 animate-spin" />
|
<LucideLoaderCircle class="mr-2 h-4 w-4 animate-spin" />
|
||||||
{:else}
|
{:else}
|
||||||
<img
|
<img
|
||||||
@@ -30,11 +30,15 @@
|
|||||||
Sign in with Authentik
|
Sign in with Authentik
|
||||||
</Button>
|
</Button>
|
||||||
</form>
|
</form>
|
||||||
<form method="get" onsubmit={() => submitted = true}
|
<form method="get" action="/auth/google">
|
||||||
action="/auth/google{inviteToken ? `?invite=${inviteToken}` : ''}">
|
|
||||||
<input type="hidden" value={inviteToken} name="invite" />
|
<input type="hidden" value={inviteToken} name="invite" />
|
||||||
<Button type="submit" disabled={submitted}>
|
<Button
|
||||||
{#if submitted}
|
type="submit"
|
||||||
|
onclick={() => {
|
||||||
|
isLoading = true;
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{#if isLoading}
|
||||||
<LucideLoaderCircle class="mr-2 h-4 w-4 animate-spin" />
|
<LucideLoaderCircle class="mr-2 h-4 w-4 animate-spin" />
|
||||||
{:else}
|
{:else}
|
||||||
<img
|
<img
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { Button } from '$lib/components/ui/button';
|
import { Button } from '$lib/components/ui/button';
|
||||||
import { LucideClipboardCopy, LucideDownload } from '@lucide/svelte';
|
import { LucideClipboardCopy, LucideDownload } from 'lucide-svelte';
|
||||||
|
|
||||||
const {
|
const {
|
||||||
data,
|
data,
|
||||||
|
@@ -43,7 +43,7 @@
|
|||||||
this={href ? "a" : "span"}
|
this={href ? "a" : "span"}
|
||||||
bind:this={ref}
|
bind:this={ref}
|
||||||
{href}
|
{href}
|
||||||
class={cn(badgeVariants({ variant }), className)}
|
class={cn(badgeVariants({ variant, className }))}
|
||||||
{...restProps}
|
{...restProps}
|
||||||
>
|
>
|
||||||
{@render children?.()}
|
{@render children?.()}
|
||||||
|
@@ -56,7 +56,7 @@
|
|||||||
{#if href}
|
{#if href}
|
||||||
<a
|
<a
|
||||||
bind:this={ref}
|
bind:this={ref}
|
||||||
class={cn(buttonVariants({ variant, size }), className)}
|
class={cn(buttonVariants({ variant, size, className }))}
|
||||||
{href}
|
{href}
|
||||||
{...restProps}
|
{...restProps}
|
||||||
>
|
>
|
||||||
@@ -65,7 +65,7 @@
|
|||||||
{:else}
|
{:else}
|
||||||
<button
|
<button
|
||||||
bind:this={ref}
|
bind:this={ref}
|
||||||
class={cn(buttonVariants({ variant, size }), className)}
|
class={cn(buttonVariants({ variant, size, className }))}
|
||||||
{type}
|
{type}
|
||||||
{...restProps}
|
{...restProps}
|
||||||
>
|
>
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { Checkbox as CheckboxPrimitive, type WithoutChildrenOrChild } from "bits-ui";
|
import { Checkbox as CheckboxPrimitive, type WithoutChildrenOrChild } from "bits-ui";
|
||||||
import Check from "@lucide/svelte/icons/check";
|
import Check from "lucide-svelte/icons/check";
|
||||||
import Minus from "@lucide/svelte/icons/minus";
|
import Minus from "lucide-svelte/icons/minus";
|
||||||
import { cn } from "$lib/utils.js";
|
import { cn } from "$lib/utils.js";
|
||||||
|
|
||||||
let {
|
let {
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { Dialog as DialogPrimitive, type WithoutChildrenOrChild } from "bits-ui";
|
import { Dialog as DialogPrimitive, type WithoutChildrenOrChild } from "bits-ui";
|
||||||
import X from "@lucide/svelte/icons/x";
|
import X from "lucide-svelte/icons/x";
|
||||||
import type { Snippet } from "svelte";
|
import type { Snippet } from "svelte";
|
||||||
import * as Dialog from "./index.js";
|
import * as Dialog from "./index.js";
|
||||||
import { cn } from "$lib/utils.js";
|
import { cn } from "$lib/utils.js";
|
||||||
@@ -17,7 +17,7 @@
|
|||||||
} = $props();
|
} = $props();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<Dialog.Portal {...portalProps}>
|
<Dialog.Portal class="absolute" {...portalProps}>
|
||||||
<Dialog.Overlay />
|
<Dialog.Overlay />
|
||||||
<DialogPrimitive.Content
|
<DialogPrimitive.Content
|
||||||
bind:ref
|
bind:ref
|
||||||
|
@@ -5,6 +5,7 @@
|
|||||||
let {
|
let {
|
||||||
ref = $bindable(null),
|
ref = $bindable(null),
|
||||||
class: className,
|
class: className,
|
||||||
|
children,
|
||||||
...restProps
|
...restProps
|
||||||
}: DialogPrimitive.DescriptionProps = $props();
|
}: DialogPrimitive.DescriptionProps = $props();
|
||||||
</script>
|
</script>
|
||||||
@@ -13,4 +14,6 @@
|
|||||||
bind:ref
|
bind:ref
|
||||||
class={cn("text-muted-foreground text-sm", className)}
|
class={cn("text-muted-foreground text-sm", className)}
|
||||||
{...restProps}
|
{...restProps}
|
||||||
/>
|
>
|
||||||
|
{@render children?.()}
|
||||||
|
</DialogPrimitive.Description>
|
||||||
|
@@ -5,6 +5,7 @@
|
|||||||
let {
|
let {
|
||||||
ref = $bindable(null),
|
ref = $bindable(null),
|
||||||
class: className,
|
class: className,
|
||||||
|
children,
|
||||||
...restProps
|
...restProps
|
||||||
}: DialogPrimitive.OverlayProps = $props();
|
}: DialogPrimitive.OverlayProps = $props();
|
||||||
</script>
|
</script>
|
||||||
@@ -16,4 +17,6 @@
|
|||||||
className
|
className
|
||||||
)}
|
)}
|
||||||
{...restProps}
|
{...restProps}
|
||||||
/>
|
>
|
||||||
|
{@render children?.()}
|
||||||
|
</DialogPrimitive.Overlay>
|
||||||
|
@@ -5,6 +5,7 @@
|
|||||||
let {
|
let {
|
||||||
ref = $bindable(null),
|
ref = $bindable(null),
|
||||||
class: className,
|
class: className,
|
||||||
|
children,
|
||||||
...restProps
|
...restProps
|
||||||
}: DialogPrimitive.TitleProps = $props();
|
}: DialogPrimitive.TitleProps = $props();
|
||||||
</script>
|
</script>
|
||||||
@@ -13,4 +14,6 @@
|
|||||||
bind:ref
|
bind:ref
|
||||||
class={cn("text-lg font-semibold leading-none tracking-tight", className)}
|
class={cn("text-lg font-semibold leading-none tracking-tight", className)}
|
||||||
{...restProps}
|
{...restProps}
|
||||||
/>
|
>
|
||||||
|
{@render children?.()}
|
||||||
|
</DialogPrimitive.Title>
|
||||||
|
@@ -1,46 +1,22 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import type { HTMLInputAttributes, HTMLInputTypeAttribute } from "svelte/elements";
|
import type { HTMLInputAttributes } from "svelte/elements";
|
||||||
import type { WithElementRef } from "bits-ui";
|
import type { WithElementRef } from "bits-ui";
|
||||||
import { cn } from "$lib/utils.js";
|
import { cn } from "$lib/utils.js";
|
||||||
|
|
||||||
type InputType = Exclude<HTMLInputTypeAttribute, "file">;
|
|
||||||
|
|
||||||
type Props = WithElementRef<
|
|
||||||
Omit<HTMLInputAttributes, "type"> &
|
|
||||||
({ type: "file"; files?: FileList } | { type?: InputType; files?: undefined })
|
|
||||||
>;
|
|
||||||
|
|
||||||
let {
|
let {
|
||||||
ref = $bindable(null),
|
ref = $bindable(null),
|
||||||
value = $bindable(),
|
value = $bindable(),
|
||||||
type,
|
|
||||||
files = $bindable(),
|
|
||||||
class: className,
|
class: className,
|
||||||
...restProps
|
...restProps
|
||||||
}: Props = $props();
|
}: WithElementRef<HTMLInputAttributes> = $props();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#if type === "file"}
|
<input
|
||||||
<input
|
|
||||||
bind:this={ref}
|
bind:this={ref}
|
||||||
class={cn(
|
class={cn(
|
||||||
"border-input bg-background ring-offset-background placeholder:text-muted-foreground focus-visible:ring-ring flex h-10 w-full rounded-md border px-3 py-2 text-base file:border-0 file:bg-transparent file:text-sm file:font-medium focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",
|
"border-input bg-background ring-offset-background placeholder:text-muted-foreground focus-visible:ring-ring flex h-10 w-full rounded-md border px-3 py-2 text-base file:border-0 file:bg-transparent file:text-sm file:font-medium focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",
|
||||||
className
|
className
|
||||||
)}
|
)}
|
||||||
type="file"
|
|
||||||
bind:files
|
|
||||||
bind:value
|
bind:value
|
||||||
{...restProps}
|
{...restProps}
|
||||||
/>
|
/>
|
||||||
{:else}
|
|
||||||
<input
|
|
||||||
bind:this={ref}
|
|
||||||
class={cn(
|
|
||||||
"border-input bg-background ring-offset-background placeholder:text-muted-foreground focus-visible:ring-ring flex h-10 w-full rounded-md border px-3 py-2 text-base file:border-0 file:bg-transparent file:text-sm file:font-medium focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",
|
|
||||||
className
|
|
||||||
)}
|
|
||||||
{type}
|
|
||||||
bind:value
|
|
||||||
{...restProps}
|
|
||||||
/>
|
|
||||||
{/if}
|
|
||||||
|
@@ -5,6 +5,7 @@
|
|||||||
let {
|
let {
|
||||||
ref = $bindable(null),
|
ref = $bindable(null),
|
||||||
class: className,
|
class: className,
|
||||||
|
children,
|
||||||
...restProps
|
...restProps
|
||||||
}: LabelPrimitive.RootProps = $props();
|
}: LabelPrimitive.RootProps = $props();
|
||||||
</script>
|
</script>
|
||||||
@@ -16,4 +17,6 @@
|
|||||||
className
|
className
|
||||||
)}
|
)}
|
||||||
{...restProps}
|
{...restProps}
|
||||||
/>
|
>
|
||||||
|
{@render children?.()}
|
||||||
|
</LabelPrimitive.Root>
|
||||||
|
@@ -5,6 +5,7 @@
|
|||||||
let {
|
let {
|
||||||
ref = $bindable(null),
|
ref = $bindable(null),
|
||||||
class: className,
|
class: className,
|
||||||
|
children,
|
||||||
...restProps
|
...restProps
|
||||||
}: TabsPrimitive.ContentProps = $props();
|
}: TabsPrimitive.ContentProps = $props();
|
||||||
</script>
|
</script>
|
||||||
@@ -16,4 +17,6 @@
|
|||||||
className
|
className
|
||||||
)}
|
)}
|
||||||
{...restProps}
|
{...restProps}
|
||||||
/>
|
>
|
||||||
|
{@render children?.()}
|
||||||
|
</TabsPrimitive.Content>
|
||||||
|
@@ -5,6 +5,7 @@
|
|||||||
let {
|
let {
|
||||||
ref = $bindable(null),
|
ref = $bindable(null),
|
||||||
class: className,
|
class: className,
|
||||||
|
children,
|
||||||
...restProps
|
...restProps
|
||||||
}: TabsPrimitive.ListProps = $props();
|
}: TabsPrimitive.ListProps = $props();
|
||||||
</script>
|
</script>
|
||||||
@@ -16,4 +17,6 @@
|
|||||||
className
|
className
|
||||||
)}
|
)}
|
||||||
{...restProps}
|
{...restProps}
|
||||||
/>
|
>
|
||||||
|
{@render children?.()}
|
||||||
|
</TabsPrimitive.List>
|
||||||
|
@@ -5,6 +5,7 @@
|
|||||||
let {
|
let {
|
||||||
ref = $bindable(null),
|
ref = $bindable(null),
|
||||||
class: className,
|
class: className,
|
||||||
|
children,
|
||||||
...restProps
|
...restProps
|
||||||
}: TabsPrimitive.TriggerProps = $props();
|
}: TabsPrimitive.TriggerProps = $props();
|
||||||
</script>
|
</script>
|
||||||
@@ -16,4 +17,6 @@
|
|||||||
className
|
className
|
||||||
)}
|
)}
|
||||||
{...restProps}
|
{...restProps}
|
||||||
/>
|
>
|
||||||
|
{@render children?.()}
|
||||||
|
</TabsPrimitive.Trigger>
|
||||||
|
@@ -8,7 +8,7 @@ export const GET: RequestHandler = ({ url, cookies }) => {
|
|||||||
const state = generateState();
|
const state = generateState();
|
||||||
const codeVerifier = generateCodeVerifier();
|
const codeVerifier = generateCodeVerifier();
|
||||||
const scopes = ['openid', 'profile', 'email'];
|
const scopes = ['openid', 'profile', 'email'];
|
||||||
const authUrl = google.createAuthorizationURL(state + inviteToken, codeVerifier, scopes);
|
const authUrl = google.createAuthorizationURL(state, codeVerifier, scopes);
|
||||||
|
|
||||||
cookies.set('google_oauth_state', state, {
|
cookies.set('google_oauth_state', state, {
|
||||||
path: '/',
|
path: '/',
|
||||||
@@ -22,6 +22,12 @@ export const GET: RequestHandler = ({ url, cookies }) => {
|
|||||||
maxAge: 60 * 10, // 10 minutes
|
maxAge: 60 * 10, // 10 minutes
|
||||||
sameSite: 'lax',
|
sameSite: 'lax',
|
||||||
});
|
});
|
||||||
|
if (inviteToken !== null) cookies.set('invite_token', inviteToken, {
|
||||||
|
path: '/',
|
||||||
|
httpOnly: true,
|
||||||
|
maxAge: 60 * 10, // 10 minutes
|
||||||
|
sameSite: 'lax',
|
||||||
|
});
|
||||||
|
|
||||||
return new Response(null, {
|
return new Response(null, {
|
||||||
status: 302,
|
status: 302,
|
||||||
|
@@ -1,108 +0,0 @@
|
|||||||
import { error } from '@sveltejs/kit';
|
|
||||||
import * as arctic from 'arctic';
|
|
||||||
import { google } from '$lib/server/oauth';
|
|
||||||
import { db } from '$lib/server/db';
|
|
||||||
import { eq } from 'drizzle-orm';
|
|
||||||
import * as table from '$lib/server/db/schema';
|
|
||||||
import { createSession, isValidInviteToken, setSessionTokenCookie } from '$lib/server/auth';
|
|
||||||
import type { OAuth2Tokens } from 'arctic';
|
|
||||||
import { assertGuard } from 'typia';
|
|
||||||
import type { PageServerLoad } from './$types';
|
|
||||||
|
|
||||||
export const load: PageServerLoad = async ({ url, cookies }) => {
|
|
||||||
const code = url.searchParams.get('code');
|
|
||||||
const state = url.searchParams.get('state');
|
|
||||||
const storedState = cookies.get('google_oauth_state') ?? null;
|
|
||||||
const codeVerifier = cookies.get('google_code_verifier') ?? null;
|
|
||||||
|
|
||||||
if (code === null || state === null || storedState === null || codeVerifier === null) {
|
|
||||||
error(400, 'Missing url parameters');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const stateGeneratedToken = state.slice(0, storedState.length);
|
|
||||||
const stateInviteToken = state.slice(storedState.length);
|
|
||||||
if (stateGeneratedToken !== storedState) {
|
|
||||||
return new Response(null, {
|
|
||||||
status: 400,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
let tokens: OAuth2Tokens;
|
|
||||||
try {
|
|
||||||
tokens = await google.validateAuthorizationCode(code, codeVerifier);
|
|
||||||
} catch (e) {
|
|
||||||
if (e instanceof arctic.OAuth2RequestError) {
|
|
||||||
console.debug('Arctic: OAuth: invalid authorization code, credentials, or redirect URI', e);
|
|
||||||
return new Response(null, {
|
|
||||||
status: 400,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if (e instanceof arctic.ArcticFetchError) {
|
|
||||||
console.debug('Arctic: failed to call `fetch()`', e);
|
|
||||||
return new Response(null, {
|
|
||||||
status: 400,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return new Response(null, {
|
|
||||||
status: 500,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const idToken = tokens.idToken();
|
|
||||||
const claims = arctic.decodeIdToken(idToken);
|
|
||||||
|
|
||||||
console.log('claims', claims);
|
|
||||||
|
|
||||||
assertGuard<{
|
|
||||||
sub: string;
|
|
||||||
email: string;
|
|
||||||
name: string;
|
|
||||||
}>(claims);
|
|
||||||
|
|
||||||
const userId = claims.sub;
|
|
||||||
const existingUser = await db.query.users.findFirst({ where: eq(table.users.id, userId) });
|
|
||||||
|
|
||||||
if (existingUser) {
|
|
||||||
const session = await createSession(existingUser.id);
|
|
||||||
setSessionTokenCookie(event, session.id, session.expiresAt);
|
|
||||||
return new Response(null, {
|
|
||||||
status: 302,
|
|
||||||
headers: {
|
|
||||||
Location: '/',
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!isValidInviteToken(stateInviteToken)) {
|
|
||||||
const message =
|
|
||||||
stateInviteToken.length === 0 ? 'sign up with an invite link first' : 'invalid invite link';
|
|
||||||
|
|
||||||
return new Response('Not Authorized: ' + message, {
|
|
||||||
status: 403,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const user: table.User = {
|
|
||||||
id: userId,
|
|
||||||
authSource: 'google',
|
|
||||||
username: claims.email,
|
|
||||||
name: claims.name,
|
|
||||||
};
|
|
||||||
|
|
||||||
// TODO: proper error handling, delete cookies
|
|
||||||
await db.insert(table.users).values(user);
|
|
||||||
console.log('created user', user, 'with invite token', stateInviteToken);
|
|
||||||
|
|
||||||
const session = await createSession(user.id);
|
|
||||||
|
|
||||||
setSessionTokenCookie(event, session.id, session.expiresAt);
|
|
||||||
|
|
||||||
return new Response(null, {
|
|
||||||
status: 302,
|
|
||||||
headers: {
|
|
||||||
Location: '/',
|
|
||||||
},
|
|
||||||
});
|
|
||||||
};
|
|
@@ -13,7 +13,8 @@ export const GET: RequestHandler = async (event) => {
|
|||||||
const code = url.searchParams.get('code');
|
const code = url.searchParams.get('code');
|
||||||
const state = url.searchParams.get('state');
|
const state = url.searchParams.get('state');
|
||||||
const storedState = cookies.get('google_oauth_state') ?? null;
|
const storedState = cookies.get('google_oauth_state') ?? null;
|
||||||
const codeVerifier = cookies.get('google_code_verifier') ?? null;
|
const codeVerifier = cookies.get('google_code_verifier', ) ?? null;
|
||||||
|
const inviteToken = cookies.get('invite_token') ?? null;
|
||||||
|
|
||||||
if (code === null || state === null || storedState === null || codeVerifier === null) {
|
if (code === null || state === null || storedState === null || codeVerifier === null) {
|
||||||
return new Response(null, {
|
return new Response(null, {
|
||||||
@@ -21,14 +22,6 @@ export const GET: RequestHandler = async (event) => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const stateGeneratedToken = state.slice(0, storedState.length);
|
|
||||||
const stateInviteToken = state.slice(storedState.length);
|
|
||||||
if (stateGeneratedToken !== storedState) {
|
|
||||||
return new Response(null, {
|
|
||||||
status: 400,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
let tokens: OAuth2Tokens;
|
let tokens: OAuth2Tokens;
|
||||||
try {
|
try {
|
||||||
tokens = await google.validateAuthorizationCode(code, codeVerifier);
|
tokens = await google.validateAuthorizationCode(code, codeVerifier);
|
||||||
@@ -76,12 +69,10 @@ export const GET: RequestHandler = async (event) => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isValidInviteToken(stateInviteToken)) {
|
// TODO: proper error page
|
||||||
const message =
|
if (inviteToken === null || !isValidInviteToken(inviteToken)) {
|
||||||
stateInviteToken.length === 0 ? 'sign up with an invite link first' : 'invalid invite link';
|
return new Response(null, {
|
||||||
|
status: 400,
|
||||||
return new Response('Not Authorized: ' + message, {
|
|
||||||
status: 403,
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -94,7 +85,7 @@ export const GET: RequestHandler = async (event) => {
|
|||||||
|
|
||||||
// TODO: proper error handling, delete cookies
|
// TODO: proper error handling, delete cookies
|
||||||
await db.insert(table.users).values(user);
|
await db.insert(table.users).values(user);
|
||||||
console.log('created user', user, 'with invite token', stateInviteToken);
|
console.log('created user', user, 'with invite token', inviteToken);
|
||||||
|
|
||||||
const session = await createSession(user.id);
|
const session = await createSession(user.id);
|
||||||
|
|
||||||
|
@@ -1,47 +0,0 @@
|
|||||||
import type { PageServerLoad } from './$types';
|
|
||||||
import type { ConnectionDetails } from '$lib/connections';
|
|
||||||
import { findDevices } from '$lib/server/devices';
|
|
||||||
import wgProvider from '$lib/server/wg-provider';
|
|
||||||
import { error } from '@sveltejs/kit';
|
|
||||||
|
|
||||||
export const load: PageServerLoad = async ({ locals, setHeaders, depends }) => {
|
|
||||||
if (!locals.user) {
|
|
||||||
error(401, 'Unauthorized');
|
|
||||||
}
|
|
||||||
console.debug('/connections');
|
|
||||||
|
|
||||||
const peersResult = await wgProvider.findConnections(locals.user);
|
|
||||||
if (peersResult._tag === 'err') return error(500, peersResult.error.message);
|
|
||||||
|
|
||||||
const devices = await findDevices(locals.user.id);
|
|
||||||
console.debug('/connections: fetched db devices');
|
|
||||||
|
|
||||||
// TODO: this is all garbage performance
|
|
||||||
// filter devices with no recent handshakes
|
|
||||||
const peers = peersResult.value.filter((peer) => peer.latestHandshake);
|
|
||||||
|
|
||||||
// start from devices, to treat db as the source of truth
|
|
||||||
const connections: ConnectionDetails[] = [];
|
|
||||||
for (const device of devices) {
|
|
||||||
const peerData = peers.find((peer) => peer.publicKey === device.publicKey);
|
|
||||||
if (!peerData) continue;
|
|
||||||
connections.push({
|
|
||||||
deviceId: device.id,
|
|
||||||
deviceName: device.name,
|
|
||||||
devicePublicKey: device.publicKey,
|
|
||||||
deviceIps: peerData.allowedIps.split(','),
|
|
||||||
endpoint: peerData.endpoint,
|
|
||||||
// swap rx and tx, since the opnsense values are from the server perspective
|
|
||||||
transferRx: peerData.transferTx,
|
|
||||||
transferTx: peerData.transferRx,
|
|
||||||
latestHandshake: peerData.latestHandshake,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
setHeaders({
|
|
||||||
'Cache-Control': 'max-age=5',
|
|
||||||
});
|
|
||||||
|
|
||||||
depends('connections');
|
|
||||||
return { connections };
|
|
||||||
};
|
|
@@ -1,6 +1,6 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import type { PageData } from './$types';
|
import type { PageData } from './$types';
|
||||||
import { invalidate, invalidateAll } from '$app/navigation';
|
import { invalidate } from '$app/navigation';
|
||||||
import * as Table from '$lib/components/ui/table';
|
import * as Table from '$lib/components/ui/table';
|
||||||
import { Badge } from '$lib/components/ui/badge';
|
import { Badge } from '$lib/components/ui/badge';
|
||||||
|
|
||||||
@@ -10,7 +10,7 @@
|
|||||||
// refresh every 5 seconds
|
// refresh every 5 seconds
|
||||||
const interval = setInterval(() => {
|
const interval = setInterval(() => {
|
||||||
console.log('Refreshing connections');
|
console.log('Refreshing connections');
|
||||||
invalidate('/connections');
|
invalidate('/api/connections');
|
||||||
}, 5000);
|
}, 5000);
|
||||||
|
|
||||||
return () => clearInterval(interval);
|
return () => clearInterval(interval);
|
||||||
|
9
src/routes/connections/+page.ts
Normal file
9
src/routes/connections/+page.ts
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
import type { PageLoad } from './$types';
|
||||||
|
import type { ConnectionDetails } from '$lib/connections';
|
||||||
|
|
||||||
|
export const load: PageLoad = async ({ fetch }) => {
|
||||||
|
const res = await fetch('/api/connections');
|
||||||
|
const connections = await res.json() as ConnectionDetails[];
|
||||||
|
|
||||||
|
return { connections };
|
||||||
|
};
|
@@ -1,15 +1,7 @@
|
|||||||
import type { Actions, PageServerLoad } from './$types';
|
import type { Actions } from './$types';
|
||||||
import { createDevice, findDevices, mapDeviceToDetails } from '$lib/server/devices';
|
import { createDevice } from '$lib/server/devices';
|
||||||
import { error, fail, redirect } from '@sveltejs/kit';
|
import { error, fail, redirect } from '@sveltejs/kit';
|
||||||
|
import wgProvider from '$lib/server/wg-provider';
|
||||||
export const load: PageServerLoad = async (event) => {
|
|
||||||
if (!event.locals.user) {
|
|
||||||
error(401, 'Unauthorized');
|
|
||||||
}
|
|
||||||
|
|
||||||
const devices = await findDevices(event.locals.user.id);
|
|
||||||
return { devices };
|
|
||||||
};
|
|
||||||
|
|
||||||
export const actions = {
|
export const actions = {
|
||||||
create: async (event) => {
|
create: async (event) => {
|
||||||
|
@@ -4,7 +4,7 @@
|
|||||||
import { Badge } from '$lib/components/ui/badge';
|
import { Badge } from '$lib/components/ui/badge';
|
||||||
import { Button, buttonVariants } from '$lib/components/ui/button';
|
import { Button, buttonVariants } from '$lib/components/ui/button';
|
||||||
import { Input } from '$lib/components/ui/input';
|
import { Input } from '$lib/components/ui/input';
|
||||||
import { LucideLoaderCircle, LucidePlus } from '@lucide/svelte';
|
import { LucideLoaderCircle, LucidePlus } from 'lucide-svelte';
|
||||||
import type { PageData } from './$types';
|
import type { PageData } from './$types';
|
||||||
import { Label } from '$lib/components/ui/label';
|
import { Label } from '$lib/components/ui/label';
|
||||||
import { page } from '$app/state';
|
import { page } from '$app/state';
|
||||||
|
9
src/routes/devices/+page.ts
Normal file
9
src/routes/devices/+page.ts
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
import type { PageLoad } from './$types';
|
||||||
|
import type { DeviceDetails } from '$lib/devices';
|
||||||
|
|
||||||
|
export const load: PageLoad = async ({ fetch }) => {
|
||||||
|
const res = await fetch('/api/devices');
|
||||||
|
const { devices } = await res.json() as { devices: DeviceDetails[] };
|
||||||
|
|
||||||
|
return { devices };
|
||||||
|
};
|
@@ -1,7 +1,7 @@
|
|||||||
<script>
|
<script>
|
||||||
import { Button, buttonVariants } from '$lib/components/ui/button';
|
import { Button, buttonVariants } from '$lib/components/ui/button';
|
||||||
import * as Dialog from '$lib/components/ui/dialog';
|
import * as Dialog from '$lib/components/ui/dialog';
|
||||||
import { LucideLoaderCircle, LucideTrash } from '@lucide/svelte';
|
import { LucideLoaderCircle, LucideTrash } from 'lucide-svelte';
|
||||||
|
|
||||||
const { device } = $props();
|
const { device } = $props();
|
||||||
let submitted = $state(false);
|
let submitted = $state(false);
|
||||||
@@ -26,12 +26,10 @@
|
|||||||
<Button type="submit" variant="destructive" disabled={submitted}>
|
<Button type="submit" variant="destructive" disabled={submitted}>
|
||||||
Delete
|
Delete
|
||||||
</Button>
|
</Button>
|
||||||
<Dialog.Close>
|
<Dialog.Close asChild let:builder>
|
||||||
{#snippet child({ props })}
|
<button class={buttonVariants()} disabled={submitted} use:builder.action {...builder}>
|
||||||
<Button {...props} disabled={submitted}>
|
|
||||||
Cancel
|
Cancel
|
||||||
</Button>
|
</button>
|
||||||
{/snippet}
|
|
||||||
</Dialog.Close>
|
</Dialog.Close>
|
||||||
</Dialog.Footer>
|
</Dialog.Footer>
|
||||||
</form>
|
</form>
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { invalidate, invalidateAll } from '$app/navigation';
|
import { invalidate, invalidateAll } from '$app/navigation';
|
||||||
import { Button } from '$lib/components/ui/button';
|
import { Button } from '$lib/components/ui/button';
|
||||||
import { LucideLoaderCircle, LucideLogOut, LucideRefreshCw } from '@lucide/svelte';
|
import { LucideLoaderCircle, LucideLogOut, LucideRefreshCw } from 'lucide-svelte';
|
||||||
import { CodeSnippet } from '$lib/components/app/code-snippet/index.js';
|
import { CodeSnippet } from '$lib/components/app/code-snippet/index.js';
|
||||||
|
|
||||||
let { data } = $props();
|
let { data } = $props();
|
||||||
|
Reference in New Issue
Block a user