diff --git a/package-lock.json b/package-lock.json index 7ad60d619..469d7d06c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1903,6 +1903,26 @@ "fastify-plugin": "^5.0.0" } }, + "node_modules/@fastify/cors": { + "version": "11.0.1", + "resolved": "https://registry.npmjs.org/@fastify/cors/-/cors-11.0.1.tgz", + "integrity": "sha512-dmZaE7M1f4SM8ZZuk5RhSsDJ+ezTgI7v3HHRj8Ow9CneczsPLZV6+2j2uwdaSLn8zhTv6QV0F4ZRcqdalGx1pQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "MIT", + "dependencies": { + "fastify-plugin": "^5.0.0", + "toad-cache": "^3.7.0" + } + }, "node_modules/@fastify/error": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/@fastify/error/-/error-4.1.0.tgz", @@ -3307,6 +3327,7 @@ "version": "4.1.7", "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.1.7.tgz", "integrity": "sha512-9rsOpdY9idRI2NH6CL4wORFY0+Q6fnx9XP9Ju+iq/0wJwGD5IByIgFmwVbyy4ymuyprj8Qh4ErxMKTUL4uNh3g==", + "dev": true, "license": "MIT", "dependencies": { "@ampproject/remapping": "^2.3.0", @@ -3322,6 +3343,7 @@ "version": "4.1.7", "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.1.7.tgz", "integrity": "sha512-5SF95Ctm9DFiUyjUPnDGkoKItPX/k+xifcQhcqX5RA85m50jw1pT/KzjdvlqxRja45Y52nR4MR9fD1JYd7f8NQ==", + "dev": true, "hasInstallScript": true, "license": "MIT", "dependencies": { @@ -3353,6 +3375,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -3369,6 +3392,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -3385,6 +3409,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -3401,6 +3426,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -3417,6 +3443,7 @@ "cpu": [ "arm" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -3433,6 +3460,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -3449,6 +3477,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -3465,6 +3494,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -3481,6 +3511,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -3505,6 +3536,7 @@ "cpu": [ "wasm32" ], + "dev": true, "license": "MIT", "optional": true, "dependencies": { @@ -3521,6 +3553,7 @@ }, "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/core": { "version": "1.4.3", + "dev": true, "inBundle": true, "license": "MIT", "optional": true, @@ -3531,6 +3564,7 @@ }, "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/runtime": { "version": "1.4.3", + "dev": true, "inBundle": true, "license": "MIT", "optional": true, @@ -3540,6 +3574,7 @@ }, "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/wasi-threads": { "version": "1.0.2", + "dev": true, "inBundle": true, "license": "MIT", "optional": true, @@ -3549,6 +3584,7 @@ }, "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@napi-rs/wasm-runtime": { "version": "0.2.9", + "dev": true, "inBundle": true, "license": "MIT", "optional": true, @@ -3560,6 +3596,7 @@ }, "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@tybys/wasm-util": { "version": "0.9.0", + "dev": true, "inBundle": true, "license": "MIT", "optional": true, @@ -3569,6 +3606,7 @@ }, "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/tslib": { "version": "2.8.0", + "dev": true, "inBundle": true, "license": "0BSD", "optional": true @@ -3580,6 +3618,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -3596,6 +3635,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -3620,19 +3660,327 @@ } }, "node_modules/@tailwindcss/vite": { - "version": "4.1.7", - "resolved": "https://registry.npmjs.org/@tailwindcss/vite/-/vite-4.1.7.tgz", - "integrity": "sha512-tYa2fO3zDe41I7WqijyVbRd8oWT0aEID1Eokz5hMT6wShLIHj3yvwj9XbfuloHP9glZ6H+aG2AN/+ZrxJ1Y5RQ==", + "version": "4.1.8", + "resolved": "https://registry.npmjs.org/@tailwindcss/vite/-/vite-4.1.8.tgz", + "integrity": "sha512-CQ+I8yxNV5/6uGaJjiuymgw0kEQiNKRinYbZXPdx1fk5WgiyReG0VaUx/Xq6aVNSUNJFzxm6o8FNKS5aMaim5A==", "license": "MIT", "dependencies": { - "@tailwindcss/node": "4.1.7", - "@tailwindcss/oxide": "4.1.7", - "tailwindcss": "4.1.7" + "@tailwindcss/node": "4.1.8", + "@tailwindcss/oxide": "4.1.8", + "tailwindcss": "4.1.8" }, "peerDependencies": { "vite": "^5.2.0 || ^6" } }, + "node_modules/@tailwindcss/vite/node_modules/@tailwindcss/node": { + "version": "4.1.8", + "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.1.8.tgz", + "integrity": "sha512-OWwBsbC9BFAJelmnNcrKuf+bka2ZxCE2A4Ft53Tkg4uoiE67r/PMEYwCsourC26E+kmxfwE0hVzMdxqeW+xu7Q==", + "license": "MIT", + "dependencies": { + "@ampproject/remapping": "^2.3.0", + "enhanced-resolve": "^5.18.1", + "jiti": "^2.4.2", + "lightningcss": "1.30.1", + "magic-string": "^0.30.17", + "source-map-js": "^1.2.1", + "tailwindcss": "4.1.8" + } + }, + "node_modules/@tailwindcss/vite/node_modules/@tailwindcss/oxide": { + "version": "4.1.8", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.1.8.tgz", + "integrity": "sha512-d7qvv9PsM5N3VNKhwVUhpK6r4h9wtLkJ6lz9ZY9aeZgrUWk1Z8VPyqyDT9MZlem7GTGseRQHkeB1j3tC7W1P+A==", + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "detect-libc": "^2.0.4", + "tar": "^7.4.3" + }, + "engines": { + "node": ">= 10" + }, + "optionalDependencies": { + "@tailwindcss/oxide-android-arm64": "4.1.8", + "@tailwindcss/oxide-darwin-arm64": "4.1.8", + "@tailwindcss/oxide-darwin-x64": "4.1.8", + "@tailwindcss/oxide-freebsd-x64": "4.1.8", + "@tailwindcss/oxide-linux-arm-gnueabihf": "4.1.8", + "@tailwindcss/oxide-linux-arm64-gnu": "4.1.8", + "@tailwindcss/oxide-linux-arm64-musl": "4.1.8", + "@tailwindcss/oxide-linux-x64-gnu": "4.1.8", + "@tailwindcss/oxide-linux-x64-musl": "4.1.8", + "@tailwindcss/oxide-wasm32-wasi": "4.1.8", + "@tailwindcss/oxide-win32-arm64-msvc": "4.1.8", + "@tailwindcss/oxide-win32-x64-msvc": "4.1.8" + } + }, + "node_modules/@tailwindcss/vite/node_modules/@tailwindcss/oxide-android-arm64": { + "version": "4.1.8", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.1.8.tgz", + "integrity": "sha512-Fbz7qni62uKYceWYvUjRqhGfZKwhZDQhlrJKGtnZfuNtHFqa8wmr+Wn74CTWERiW2hn3mN5gTpOoxWKk0jRxjg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/vite/node_modules/@tailwindcss/oxide-darwin-arm64": { + "version": "4.1.8", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.1.8.tgz", + "integrity": "sha512-RdRvedGsT0vwVVDztvyXhKpsU2ark/BjgG0huo4+2BluxdXo8NDgzl77qh0T1nUxmM11eXwR8jA39ibvSTbi7A==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/vite/node_modules/@tailwindcss/oxide-darwin-x64": { + "version": "4.1.8", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.1.8.tgz", + "integrity": "sha512-t6PgxjEMLp5Ovf7uMb2OFmb3kqzVTPPakWpBIFzppk4JE4ix0yEtbtSjPbU8+PZETpaYMtXvss2Sdkx8Vs4XRw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/vite/node_modules/@tailwindcss/oxide-freebsd-x64": { + "version": "4.1.8", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.1.8.tgz", + "integrity": "sha512-g8C8eGEyhHTqwPStSwZNSrOlyx0bhK/V/+zX0Y+n7DoRUzyS8eMbVshVOLJTDDC+Qn9IJnilYbIKzpB9n4aBsg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/vite/node_modules/@tailwindcss/oxide-linux-arm-gnueabihf": { + "version": "4.1.8", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.1.8.tgz", + "integrity": "sha512-Jmzr3FA4S2tHhaC6yCjac3rGf7hG9R6Gf2z9i9JFcuyy0u79HfQsh/thifbYTF2ic82KJovKKkIB6Z9TdNhCXQ==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/vite/node_modules/@tailwindcss/oxide-linux-arm64-gnu": { + "version": "4.1.8", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.1.8.tgz", + "integrity": "sha512-qq7jXtO1+UEtCmCeBBIRDrPFIVI4ilEQ97qgBGdwXAARrUqSn/L9fUrkb1XP/mvVtoVeR2bt/0L77xx53bPZ/Q==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/vite/node_modules/@tailwindcss/oxide-linux-arm64-musl": { + "version": "4.1.8", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.1.8.tgz", + "integrity": "sha512-O6b8QesPbJCRshsNApsOIpzKt3ztG35gfX9tEf4arD7mwNinsoCKxkj8TgEE0YRjmjtO3r9FlJnT/ENd9EVefQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/vite/node_modules/@tailwindcss/oxide-linux-x64-gnu": { + "version": "4.1.8", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.1.8.tgz", + "integrity": "sha512-32iEXX/pXwikshNOGnERAFwFSfiltmijMIAbUhnNyjFr3tmWmMJWQKU2vNcFX0DACSXJ3ZWcSkzNbaKTdngH6g==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/vite/node_modules/@tailwindcss/oxide-linux-x64-musl": { + "version": "4.1.8", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.1.8.tgz", + "integrity": "sha512-s+VSSD+TfZeMEsCaFaHTaY5YNj3Dri8rST09gMvYQKwPphacRG7wbuQ5ZJMIJXN/puxPcg/nU+ucvWguPpvBDg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/vite/node_modules/@tailwindcss/oxide-wasm32-wasi": { + "version": "4.1.8", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.1.8.tgz", + "integrity": "sha512-CXBPVFkpDjM67sS1psWohZ6g/2/cd+cq56vPxK4JeawelxwK4YECgl9Y9TjkE2qfF+9/s1tHHJqrC4SS6cVvSg==", + "bundleDependencies": [ + "@napi-rs/wasm-runtime", + "@emnapi/core", + "@emnapi/runtime", + "@tybys/wasm-util", + "@emnapi/wasi-threads", + "tslib" + ], + "cpu": [ + "wasm32" + ], + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "^1.4.3", + "@emnapi/runtime": "^1.4.3", + "@emnapi/wasi-threads": "^1.0.2", + "@napi-rs/wasm-runtime": "^0.2.10", + "@tybys/wasm-util": "^0.9.0", + "tslib": "^2.8.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@tailwindcss/vite/node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/core": { + "version": "1.4.3", + "inBundle": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/wasi-threads": "1.0.2", + "tslib": "^2.4.0" + } + }, + "node_modules/@tailwindcss/vite/node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/runtime": { + "version": "1.4.3", + "inBundle": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@tailwindcss/vite/node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/wasi-threads": { + "version": "1.0.2", + "inBundle": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@tailwindcss/vite/node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@napi-rs/wasm-runtime": { + "version": "0.2.10", + "inBundle": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "^1.4.3", + "@emnapi/runtime": "^1.4.3", + "@tybys/wasm-util": "^0.9.0" + } + }, + "node_modules/@tailwindcss/vite/node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@tybys/wasm-util": { + "version": "0.9.0", + "inBundle": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@tailwindcss/vite/node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/tslib": { + "version": "2.8.0", + "inBundle": true, + "license": "0BSD", + "optional": true + }, + "node_modules/@tailwindcss/vite/node_modules/@tailwindcss/oxide-win32-arm64-msvc": { + "version": "4.1.8", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.1.8.tgz", + "integrity": "sha512-7GmYk1n28teDHUjPlIx4Z6Z4hHEgvP5ZW2QS9ygnDAdI/myh3HTHjDqtSqgu1BpRoI4OiLx+fThAyA1JePoENA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/vite/node_modules/@tailwindcss/oxide-win32-x64-msvc": { + "version": "4.1.8", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.1.8.tgz", + "integrity": "sha512-fou+U20j+Jl0EHwK92spoWISON2OBnCazIc038Xj2TdweYV33ZRkS9nwqiUi2d/Wba5xg5UoHfvynnb/UB49cQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/vite/node_modules/tailwindcss": { + "version": "4.1.8", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.8.tgz", + "integrity": "sha512-kjeW8gjdxasbmFKpVGrGd5T4i40mV5J2Rasw48QARfYeQ8YS9x02ON9SFWax3Qf616rt4Cp3nVNIj6Hd1mP3og==", + "license": "MIT" + }, "node_modules/@tanstack/table-core": { "version": "8.21.3", "resolved": "https://registry.npmjs.org/@tanstack/table-core/-/table-core-8.21.3.tgz", @@ -4588,14 +4936,14 @@ } }, "node_modules/@vueuse/core": { - "version": "13.2.0", - "resolved": "https://registry.npmjs.org/@vueuse/core/-/core-13.2.0.tgz", - "integrity": "sha512-n5TZoIAxbWAQ3PqdVPDzLgIRQOujFfMlatdI+f7ditSmoEeNpPBvp7h2zamzikCmrhFIePAwdEQB6ENccHr7Rg==", + "version": "13.3.0", + "resolved": "https://registry.npmjs.org/@vueuse/core/-/core-13.3.0.tgz", + "integrity": "sha512-uYRz5oEfebHCoRhK4moXFM3NSCd5vu2XMLOq/Riz5FdqZMy2RvBtazdtL3gEcmDyqkztDe9ZP/zymObMIbiYSg==", "license": "MIT", "dependencies": { "@types/web-bluetooth": "^0.0.21", - "@vueuse/metadata": "13.2.0", - "@vueuse/shared": "13.2.0" + "@vueuse/metadata": "13.3.0", + "@vueuse/shared": "13.3.0" }, "funding": { "url": "https://github.com/sponsors/antfu" @@ -4605,9 +4953,9 @@ } }, "node_modules/@vueuse/core/node_modules/@vueuse/shared": { - "version": "13.2.0", - "resolved": "https://registry.npmjs.org/@vueuse/shared/-/shared-13.2.0.tgz", - "integrity": "sha512-vx9ZPDF5HcU9up3Jgt3G62dMUfZEdk6tLyBAHYAG4F4n73vpaA7J5hdncDI/lS9Vm7GA/FPlbOmh9TrDZROTpg==", + "version": "13.3.0", + "resolved": "https://registry.npmjs.org/@vueuse/shared/-/shared-13.3.0.tgz", + "integrity": "sha512-L1QKsF0Eg9tiZSFXTgodYnu0Rsa2P0En2LuLrIs/jgrkyiDuJSsPZK+tx+wU0mMsYHUYEjNsuE41uqqkuR8VhA==", "license": "MIT", "funding": { "url": "https://github.com/sponsors/antfu" @@ -4617,9 +4965,9 @@ } }, "node_modules/@vueuse/metadata": { - "version": "13.2.0", - "resolved": "https://registry.npmjs.org/@vueuse/metadata/-/metadata-13.2.0.tgz", - "integrity": "sha512-kPpzuQCU0+D8DZCzK0iPpIcXI+6ufWSgwnjJ6//GNpEn+SHViaCtR+XurzORChSgvpHO9YC8gGM97Y1kB+UabA==", + "version": "13.3.0", + "resolved": "https://registry.npmjs.org/@vueuse/metadata/-/metadata-13.3.0.tgz", + "integrity": "sha512-42IzJIOYCKIb0Yjv1JfaKpx8JlCiTmtCWrPxt7Ja6Wzoq0h79+YVXmBV03N966KEmDEESTbp5R/qO3AB5BDnGw==", "license": "MIT", "funding": { "url": "https://github.com/sponsors/antfu" @@ -11307,9 +11655,9 @@ } }, "node_modules/reka-ui": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/reka-ui/-/reka-ui-2.2.1.tgz", - "integrity": "sha512-oLHiyBn6gTIQGnTnv8G5LQuFp9j8HuUNl0qdnW3XPhFb/07hrxzFpjo2kt/jxOZive+n/XWDbOjSj2h9Hih3qA==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/reka-ui/-/reka-ui-2.3.0.tgz", + "integrity": "sha512-HKvJej9Sc0KYEvTAbsGHgOxpEWL4FWSR70Q6Ld+bVNuaCxK6LP3jyTtyTWS+A44hHA9/aYfOBZ1Q8WkgZsGZpA==", "license": "MIT", "dependencies": { "@floating-ui/dom": "^1.6.13", @@ -13315,6 +13663,7 @@ "version": "0.19.0", "dependencies": { "@fastify/cookie": "^11.0.2", + "@fastify/cors": "^11.0.1", "@lucia-auth/adapter-drizzle": "^1.1.0", "@node-rs/argon2": "^2.0.2", "arctic": "^3.7.0", @@ -13349,15 +13698,15 @@ "name": "@deploystack/frontend", "version": "0.12.0", "dependencies": { - "@tailwindcss/vite": "^4.1.7", + "@tailwindcss/vite": "^4.1.8", "@tanstack/vue-table": "^8.21.3", "@vee-validate/zod": "^4.15.0", - "@vueuse/core": "^13.2.0", + "@vueuse/core": "^13.3.0", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", "lucide-vue-next": "^0.511.0", "pinia": "^3.0.2", - "reka-ui": "^2.2.1", + "reka-ui": "^2.3.0", "tailwind-merge": "^3.3.0", "tailwindcss-animate": "^1.0.7", "vee-validate": "^4.15.0", diff --git a/services/backend/package.json b/services/backend/package.json index 505850b95..ff5d1f9c1 100644 --- a/services/backend/package.json +++ b/services/backend/package.json @@ -12,6 +12,7 @@ }, "dependencies": { "@fastify/cookie": "^11.0.2", + "@fastify/cors": "^11.0.1", "@lucia-auth/adapter-drizzle": "^1.1.0", "@node-rs/argon2": "^2.0.2", "arctic": "^3.7.0", diff --git a/services/backend/src/fastify/plugins/index.ts b/services/backend/src/fastify/plugins/index.ts index 7c0351e74..90e3bfac1 100644 --- a/services/backend/src/fastify/plugins/index.ts +++ b/services/backend/src/fastify/plugins/index.ts @@ -1,7 +1,19 @@ import { FastifyInstance } from 'fastify' import fastifyFavicon from 'fastify-favicon' +import fastifyCors from '@fastify/cors' export const registerFastifyPlugins = async (server: FastifyInstance): Promise => { + // Register CORS plugin + await server.register(fastifyCors, { + origin: [ + 'http://localhost:5173', // Vite dev server + 'http://localhost:3000', // Frontend production (if served from same port) + 'http://localhost:4173', // Vite preview + ], + credentials: true, + methods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'] + }) + // Register favicon plugin await server.register(fastifyFavicon, { path: '../shared/public/img', diff --git a/services/frontend/package.json b/services/frontend/package.json index 96833d9f6..f41181bd2 100644 --- a/services/frontend/package.json +++ b/services/frontend/package.json @@ -14,15 +14,15 @@ "release": "release-it --config=.release-it.js" }, "dependencies": { - "@tailwindcss/vite": "^4.1.7", + "@tailwindcss/vite": "^4.1.8", "@tanstack/vue-table": "^8.21.3", "@vee-validate/zod": "^4.15.0", - "@vueuse/core": "^13.2.0", + "@vueuse/core": "^13.3.0", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", "lucide-vue-next": "^0.511.0", "pinia": "^3.0.2", - "reka-ui": "^2.2.1", + "reka-ui": "^2.3.0", "tailwind-merge": "^3.3.0", "tailwindcss-animate": "^1.0.7", "vee-validate": "^4.15.0", diff --git a/services/frontend/src/App.vue b/services/frontend/src/App.vue index a3e63f2f4..ebde7b587 100644 --- a/services/frontend/src/App.vue +++ b/services/frontend/src/App.vue @@ -5,5 +5,18 @@ diff --git a/services/frontend/src/components/ui/alert/Alert.vue b/services/frontend/src/components/ui/alert/Alert.vue new file mode 100644 index 000000000..fbe122141 --- /dev/null +++ b/services/frontend/src/components/ui/alert/Alert.vue @@ -0,0 +1,20 @@ + + + diff --git a/services/frontend/src/components/ui/alert/AlertDescription.vue b/services/frontend/src/components/ui/alert/AlertDescription.vue new file mode 100644 index 000000000..f77e4662c --- /dev/null +++ b/services/frontend/src/components/ui/alert/AlertDescription.vue @@ -0,0 +1,17 @@ + + + diff --git a/services/frontend/src/components/ui/alert/AlertTitle.vue b/services/frontend/src/components/ui/alert/AlertTitle.vue new file mode 100644 index 000000000..91a92997c --- /dev/null +++ b/services/frontend/src/components/ui/alert/AlertTitle.vue @@ -0,0 +1,17 @@ + + + diff --git a/services/frontend/src/components/ui/alert/index.ts b/services/frontend/src/components/ui/alert/index.ts new file mode 100644 index 000000000..3ff70b4df --- /dev/null +++ b/services/frontend/src/components/ui/alert/index.ts @@ -0,0 +1,23 @@ +import { cva, type VariantProps } from 'class-variance-authority' + +export { default as Alert } from './Alert.vue' +export { default as AlertDescription } from './AlertDescription.vue' +export { default as AlertTitle } from './AlertTitle.vue' + +export const alertVariants = cva( + 'relative w-full rounded-lg border px-4 py-3 text-sm grid has-[>svg]:grid-cols-[calc(var(--spacing)*4)_1fr] grid-cols-[0_1fr] has-[>svg]:gap-x-3 gap-y-0.5 items-start [&>svg]:size-4 [&>svg]:translate-y-0.5 [&>svg]:text-current', + { + variants: { + variant: { + default: 'bg-card text-card-foreground', + destructive: + 'text-destructive bg-card [&>svg]:text-current *:data-[slot=alert-description]:text-destructive/90', + }, + }, + defaultVariants: { + variant: 'default', + }, + }, +) + +export type AlertVariants = VariantProps diff --git a/services/frontend/src/components/ui/select/Select.vue b/services/frontend/src/components/ui/select/Select.vue new file mode 100644 index 000000000..dc1f83dbc --- /dev/null +++ b/services/frontend/src/components/ui/select/Select.vue @@ -0,0 +1,18 @@ + + + diff --git a/services/frontend/src/components/ui/select/SelectContent.vue b/services/frontend/src/components/ui/select/SelectContent.vue new file mode 100644 index 000000000..71a6061a7 --- /dev/null +++ b/services/frontend/src/components/ui/select/SelectContent.vue @@ -0,0 +1,52 @@ + + + diff --git a/services/frontend/src/components/ui/select/SelectGroup.vue b/services/frontend/src/components/ui/select/SelectGroup.vue new file mode 100644 index 000000000..4f36d9256 --- /dev/null +++ b/services/frontend/src/components/ui/select/SelectGroup.vue @@ -0,0 +1,14 @@ + + + diff --git a/services/frontend/src/components/ui/select/SelectItem.vue b/services/frontend/src/components/ui/select/SelectItem.vue new file mode 100644 index 000000000..1cdfa634e --- /dev/null +++ b/services/frontend/src/components/ui/select/SelectItem.vue @@ -0,0 +1,42 @@ + + + diff --git a/services/frontend/src/components/ui/select/SelectItemText.vue b/services/frontend/src/components/ui/select/SelectItemText.vue new file mode 100644 index 000000000..cff32ac3c --- /dev/null +++ b/services/frontend/src/components/ui/select/SelectItemText.vue @@ -0,0 +1,14 @@ + + + diff --git a/services/frontend/src/components/ui/select/SelectLabel.vue b/services/frontend/src/components/ui/select/SelectLabel.vue new file mode 100644 index 000000000..37a84ddd8 --- /dev/null +++ b/services/frontend/src/components/ui/select/SelectLabel.vue @@ -0,0 +1,16 @@ + + + diff --git a/services/frontend/src/components/ui/select/SelectScrollDownButton.vue b/services/frontend/src/components/ui/select/SelectScrollDownButton.vue new file mode 100644 index 000000000..f03ff40e9 --- /dev/null +++ b/services/frontend/src/components/ui/select/SelectScrollDownButton.vue @@ -0,0 +1,25 @@ + + + diff --git a/services/frontend/src/components/ui/select/SelectScrollUpButton.vue b/services/frontend/src/components/ui/select/SelectScrollUpButton.vue new file mode 100644 index 000000000..078331ba1 --- /dev/null +++ b/services/frontend/src/components/ui/select/SelectScrollUpButton.vue @@ -0,0 +1,25 @@ + + + diff --git a/services/frontend/src/components/ui/select/SelectSeparator.vue b/services/frontend/src/components/ui/select/SelectSeparator.vue new file mode 100644 index 000000000..5ec715540 --- /dev/null +++ b/services/frontend/src/components/ui/select/SelectSeparator.vue @@ -0,0 +1,18 @@ + + + diff --git a/services/frontend/src/components/ui/select/SelectTrigger.vue b/services/frontend/src/components/ui/select/SelectTrigger.vue new file mode 100644 index 000000000..5ffd488cb --- /dev/null +++ b/services/frontend/src/components/ui/select/SelectTrigger.vue @@ -0,0 +1,32 @@ + + + diff --git a/services/frontend/src/components/ui/select/SelectValue.vue b/services/frontend/src/components/ui/select/SelectValue.vue new file mode 100644 index 000000000..f198b9650 --- /dev/null +++ b/services/frontend/src/components/ui/select/SelectValue.vue @@ -0,0 +1,14 @@ + + + diff --git a/services/frontend/src/components/ui/select/index.ts b/services/frontend/src/components/ui/select/index.ts new file mode 100644 index 000000000..31b929462 --- /dev/null +++ b/services/frontend/src/components/ui/select/index.ts @@ -0,0 +1,11 @@ +export { default as Select } from './Select.vue' +export { default as SelectContent } from './SelectContent.vue' +export { default as SelectGroup } from './SelectGroup.vue' +export { default as SelectItem } from './SelectItem.vue' +export { default as SelectItemText } from './SelectItemText.vue' +export { default as SelectLabel } from './SelectLabel.vue' +export { default as SelectScrollDownButton } from './SelectScrollDownButton.vue' +export { default as SelectScrollUpButton } from './SelectScrollUpButton.vue' +export { default as SelectSeparator } from './SelectSeparator.vue' +export { default as SelectTrigger } from './SelectTrigger.vue' +export { default as SelectValue } from './SelectValue.vue' diff --git a/services/frontend/src/i18n/locales/en/index.ts b/services/frontend/src/i18n/locales/en/index.ts index 239576ac3..5c74ddf91 100644 --- a/services/frontend/src/i18n/locales/en/index.ts +++ b/services/frontend/src/i18n/locales/en/index.ts @@ -1,9 +1,11 @@ import common from './common.ts' import login from './login.ts' import register from './register.ts' +import setup from './setup.ts' export default { ...common, login, register, + setup, } diff --git a/services/frontend/src/i18n/locales/en/setup.ts b/services/frontend/src/i18n/locales/en/setup.ts new file mode 100644 index 000000000..56ec196de --- /dev/null +++ b/services/frontend/src/i18n/locales/en/setup.ts @@ -0,0 +1,36 @@ +export default { + title: 'Database Setup', + description: 'Configure your database to get started with DeployStack', + alreadyConfigured: { + title: 'Setup Complete', + description: 'Database has already been configured and initialized.', + button: 'Continue to Login', + }, + form: { + databaseType: { + label: 'Database Type', + placeholder: 'Select database type', + description: 'Choose SQLite for quick setup or PostgreSQL for production use.', + options: { + sqlite: 'SQLite (Recommended for development)', + postgres: 'PostgreSQL (Production ready)', + }, + }, + connectionString: { + label: 'Connection String', + placeholder: 'postgresql://username:password@host:port/database', + description: 'Enter your PostgreSQL connection string.', + }, + }, + buttons: { + submit: 'Setup Database', + loading: 'Setting up...', + }, + errors: { + title: 'Setup Failed', + connectionFailed: 'Failed to connect to backend. Please ensure the server is running.', + setupFailed: 'Failed to setup database. Please try again.', + validationRequired: 'Please select a database type', + connectionStringRequired: 'Connection string is required for PostgreSQL', + }, +} diff --git a/services/frontend/src/router/index.ts b/services/frontend/src/router/index.ts index 2598113f8..db9f0af8e 100644 --- a/services/frontend/src/router/index.ts +++ b/services/frontend/src/router/index.ts @@ -1,24 +1,34 @@ import { createRouter, createWebHistory } from 'vue-router' +import { useDatabaseStore } from '@/stores/database' const routes = [ { path: '/', redirect: '/login', }, + { + path: '/setup', + name: 'Setup', + component: () => import('../views/Setup.vue'), + meta: { requiresSetup: false }, // This route is accessible without setup + }, { path: '/login', name: 'Login', component: () => import('../views/Login.vue'), + meta: { requiresSetup: true }, }, { path: '/register', name: 'Register', component: () => import('../views/Register.vue'), + meta: { requiresSetup: true }, }, { path: '/plugin-demo', name: 'PluginDemo', component: () => import('../views/PluginDemo.vue'), + meta: { requiresSetup: true }, }, ] @@ -27,4 +37,36 @@ const router = createRouter({ routes, }) +// Navigation guard to check database setup +router.beforeEach(async (to, from, next) => { + const databaseStore = useDatabaseStore() + + // Skip setup check for the setup route itself + if (to.name === 'Setup') { + next() + return + } + + // Check if route requires setup + if (to.meta.requiresSetup !== false) { + try { + // Check database status (use cache for performance) + const isSetup = await databaseStore.checkDatabaseStatus(true) + + if (!isSetup) { + // Database not setup, redirect to setup page + next('/setup') + return + } + } catch (error) { + console.error('Failed to check database status:', error) + // On error, redirect to setup page to be safe + next('/setup') + return + } + } + + next() +}) + export default router diff --git a/services/frontend/src/services/database.ts b/services/frontend/src/services/database.ts new file mode 100644 index 000000000..2c716d29e --- /dev/null +++ b/services/frontend/src/services/database.ts @@ -0,0 +1,103 @@ +import { getEnv } from '@/utils/env'; +import type { DbStatusResponse, DbSetupRequest, DbSetupResponse } from '@/types/database'; + +const STORAGE_KEY = 'deploystack_db_setup_status'; + +class DatabaseService { + private baseUrl: string; + + constructor() { + this.baseUrl = getEnv('VITE_API_URL') || 'http://localhost:3000'; + } + + /** + * Check database status from backend + */ + async checkStatus(): Promise { + try { + const response = await fetch(`${this.baseUrl}/api/db/status`); + + if (!response.ok) { + throw new Error(`HTTP error! status: ${response.status}`); + } + + const data = await response.json(); + + // Cache the result in localStorage + this.cacheSetupStatus(data.configured && data.initialized); + + return data; + } catch (error) { + console.error('Failed to check database status:', error); + throw new Error('setup.errors.connectionFailed'); + } + } + + /** + * Setup database with given configuration + */ + async setupDatabase(config: DbSetupRequest): Promise { + try { + const response = await fetch(`${this.baseUrl}/api/db/setup`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify(config), + }); + + const data = await response.json(); + + if (!response.ok) { + throw new Error(data.error || data.message || `HTTP error! status: ${response.status}`); + } + + // Cache successful setup + this.cacheSetupStatus(true); + + return data; + } catch (error) { + console.error('Failed to setup database:', error); + if (error instanceof Error) { + throw error; + } + throw new Error('setup.errors.setupFailed'); + } + } + + /** + * Get cached setup status from localStorage + */ + getCachedSetupStatus(): boolean | null { + try { + const cached = localStorage.getItem(STORAGE_KEY); + return cached ? JSON.parse(cached) : null; + } catch { + return null; + } + } + + /** + * Cache setup status in localStorage + */ + private cacheSetupStatus(isSetup: boolean): void { + try { + localStorage.setItem(STORAGE_KEY, JSON.stringify(isSetup)); + } catch (error) { + console.warn('Failed to cache setup status:', error); + } + } + + /** + * Clear cached setup status (useful for testing) + */ + clearCache(): void { + try { + localStorage.removeItem(STORAGE_KEY); + } catch (error) { + console.warn('Failed to clear setup status cache:', error); + } + } +} + +export const databaseService = new DatabaseService(); diff --git a/services/frontend/src/stores/database.ts b/services/frontend/src/stores/database.ts new file mode 100644 index 000000000..852eee90b --- /dev/null +++ b/services/frontend/src/stores/database.ts @@ -0,0 +1,112 @@ +import { defineStore } from 'pinia'; +import { ref, computed } from 'vue'; +import { databaseService } from '@/services/database'; +import type { DbSetupRequest, DatabaseType } from '@/types/database'; + +export const useDatabaseStore = defineStore('database', () => { + // State + const isConfigured = ref(false); + const isInitialized = ref(false); + const dialect = ref(null); + const isLoading = ref(false); + const error = ref(null); + const setupCompleted = ref(false); + + // Computed + const isSetupRequired = computed(() => !isConfigured.value || !isInitialized.value); + const canProceedToApp = computed(() => isConfigured.value && isInitialized.value); + + // Actions + async function checkDatabaseStatus(useCache = true): Promise { + // Check cache first if requested + if (useCache) { + const cached = databaseService.getCachedSetupStatus(); + if (cached !== null) { + setupCompleted.value = cached; + isConfigured.value = cached; + isInitialized.value = cached; + return cached; + } + } + + isLoading.value = true; + error.value = null; + + try { + const status = await databaseService.checkStatus(); + + isConfigured.value = status.configured; + isInitialized.value = status.initialized; + dialect.value = status.dialect; + setupCompleted.value = status.configured && status.initialized; + + return canProceedToApp.value; + } catch (err) { + error.value = err instanceof Error ? err.message : 'setup.errors.connectionFailed'; + return false; + } finally { + isLoading.value = false; + } + } + + async function setupDatabase(config: DbSetupRequest): Promise { + isLoading.value = true; + error.value = null; + + try { + await databaseService.setupDatabase(config); + + // Update state after successful setup + isConfigured.value = true; + isInitialized.value = true; + dialect.value = config.type; + setupCompleted.value = true; + + return true; + } catch (err) { + error.value = err instanceof Error ? err.message : 'setup.errors.setupFailed'; + return false; + } finally { + isLoading.value = false; + } + } + + function clearError(): void { + error.value = null; + } + + function resetState(): void { + isConfigured.value = false; + isInitialized.value = false; + dialect.value = null; + isLoading.value = false; + error.value = null; + setupCompleted.value = false; + } + + function clearCache(): void { + databaseService.clearCache(); + resetState(); + } + + return { + // State + isConfigured, + isInitialized, + dialect, + isLoading, + error, + setupCompleted, + + // Computed + isSetupRequired, + canProceedToApp, + + // Actions + checkDatabaseStatus, + setupDatabase, + clearError, + resetState, + clearCache, + }; +}); diff --git a/services/frontend/src/types/database.ts b/services/frontend/src/types/database.ts new file mode 100644 index 000000000..9ccb71e09 --- /dev/null +++ b/services/frontend/src/types/database.ts @@ -0,0 +1,28 @@ +export enum DatabaseType { + SQLite = 'sqlite', + Postgres = 'postgres', +} + +export interface DbStatusResponse { + configured: boolean; + initialized: boolean; + dialect: DatabaseType | null; +} + +export interface DbSetupRequest { + type: DatabaseType; + connectionString?: string; +} + +export interface DbSetupResponse { + message: string; +} + +export interface DatabaseState { + isConfigured: boolean; + isInitialized: boolean; + dialect: DatabaseType | null; + isLoading: boolean; + error: string | null; + setupCompleted: boolean; +} diff --git a/services/frontend/src/views/Login.vue b/services/frontend/src/views/Login.vue index 35ac7c6a2..d70f4b928 100644 --- a/services/frontend/src/views/Login.vue +++ b/services/frontend/src/views/Login.vue @@ -82,7 +82,7 @@ const navigateToRegister = () => { alt="Your Company" />

- {{ $t('login.title') }} here + {{ $t('login.title') }}

diff --git a/services/frontend/src/views/Setup.vue b/services/frontend/src/views/Setup.vue new file mode 100644 index 000000000..368d33523 --- /dev/null +++ b/services/frontend/src/views/Setup.vue @@ -0,0 +1,203 @@ + + +