- prepare for external testing
This commit is contained in:
parent
1547af6ae0
commit
7e9f7b5481
14 changed files with 389 additions and 47 deletions
22
Dockerfile
Normal file
22
Dockerfile
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
FROM docker.io/node:23 as builder
|
||||||
|
|
||||||
|
WORKDIR /build
|
||||||
|
|
||||||
|
COPY ./src ./src
|
||||||
|
COPY ./static ./static
|
||||||
|
COPY .npmrc package.json package-lock.json postcss.config.js svelte.config.js tailwind.config.ts tsconfig.json vite.config.ts ./
|
||||||
|
RUN npm ci
|
||||||
|
RUN npm run build
|
||||||
|
|
||||||
|
FROM docker.io/node:23
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
COPY --from=builder /build/build ./
|
||||||
|
COPY --from=builder /build/package.json /build/package-lock.json ./
|
||||||
|
COPY ./entrypoint.sh ./drizzle-init.js ./
|
||||||
|
COPY ./drizzle ./
|
||||||
|
RUN npm ci --production
|
||||||
|
|
||||||
|
EXPOSE 3000
|
||||||
|
# ENTRYPOINT ["./entrypoint.sh"]
|
||||||
|
ENTRYPOINT ["node", "index.js"]
|
16
docker-compose.yml
Normal file
16
docker-compose.yml
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
services:
|
||||||
|
three60:
|
||||||
|
image: three60:latest
|
||||||
|
ports:
|
||||||
|
- 3000:3000
|
||||||
|
environment:
|
||||||
|
- DATABASE_URL=mysql://root:my-pwd@db:3306/three60
|
||||||
|
- ENABLE_REGISTER=true
|
||||||
|
- ALLOWABLE_DOMAINS=example.com
|
||||||
|
- INIT_DB=true
|
||||||
|
|
||||||
|
db:
|
||||||
|
image: docker.io/mariadb:latest
|
||||||
|
environment:
|
||||||
|
- MARIADB_ROOT_PASSWORD=my-pwd
|
||||||
|
- MARIADB_DATABASE=three60
|
275
package-lock.json
generated
275
package-lock.json
generated
|
@ -17,11 +17,13 @@
|
||||||
"d3-array": "^3.2.4",
|
"d3-array": "^3.2.4",
|
||||||
"d3-scale": "^4.0.2",
|
"d3-scale": "^4.0.2",
|
||||||
"d3-shape": "^3.2.0",
|
"d3-shape": "^3.2.0",
|
||||||
"drizzle-orm": "^0.36.3",
|
"drizzle-orm": "^0.36.4",
|
||||||
"layerchart": "^0.59.1"
|
"layerchart": "^0.59.1",
|
||||||
|
"mysql2": "^3.11.4"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@sveltejs/adapter-auto": "^3.0.0",
|
"@sveltejs/adapter-auto": "^3.0.0",
|
||||||
|
"@sveltejs/adapter-node": "^5.2.9",
|
||||||
"@sveltejs/kit": "^2.0.0",
|
"@sveltejs/kit": "^2.0.0",
|
||||||
"@sveltejs/vite-plugin-svelte": "^4.0.0",
|
"@sveltejs/vite-plugin-svelte": "^4.0.0",
|
||||||
"@types/d3-array": "^3.2.1",
|
"@types/d3-array": "^3.2.1",
|
||||||
|
@ -1817,6 +1819,112 @@
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/@rollup/plugin-commonjs": {
|
||||||
|
"version": "28.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@rollup/plugin-commonjs/-/plugin-commonjs-28.0.1.tgz",
|
||||||
|
"integrity": "sha512-+tNWdlWKbpB3WgBN7ijjYkq9X5uhjmcvyjEght4NmH5fAU++zfQzAJ6wumLS+dNcvwEZhKx2Z+skY8m7v0wGSA==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@rollup/pluginutils": "^5.0.1",
|
||||||
|
"commondir": "^1.0.1",
|
||||||
|
"estree-walker": "^2.0.2",
|
||||||
|
"fdir": "^6.2.0",
|
||||||
|
"is-reference": "1.2.1",
|
||||||
|
"magic-string": "^0.30.3",
|
||||||
|
"picomatch": "^4.0.2"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=16.0.0 || 14 >= 14.17"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"rollup": "^2.68.0||^3.0.0||^4.0.0"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"rollup": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@rollup/plugin-commonjs/node_modules/is-reference": {
|
||||||
|
"version": "1.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/is-reference/-/is-reference-1.2.1.tgz",
|
||||||
|
"integrity": "sha512-U82MsXXiFIrjCK4otLT+o2NA2Cd2g5MLoOVXUZjIOhLurrRxpEXzI8O0KZHr3IjLvlAH1kTPYSuqer5T9ZVBKQ==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@types/estree": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@rollup/plugin-json": {
|
||||||
|
"version": "6.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@rollup/plugin-json/-/plugin-json-6.1.0.tgz",
|
||||||
|
"integrity": "sha512-EGI2te5ENk1coGeADSIwZ7G2Q8CJS2sF120T7jLw4xFw9n7wIOXHo+kIYRAoVpJAN+kmqZSoO3Fp4JtoNF4ReA==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@rollup/pluginutils": "^5.1.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=14.0.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"rollup": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@rollup/plugin-node-resolve": {
|
||||||
|
"version": "15.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-15.3.0.tgz",
|
||||||
|
"integrity": "sha512-9eO5McEICxMzJpDW9OnMYSv4Sta3hmt7VtBFz5zR9273suNOydOyq/FrGeGy+KsTRFm8w0SLVhzig2ILFT63Ag==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@rollup/pluginutils": "^5.0.1",
|
||||||
|
"@types/resolve": "1.20.2",
|
||||||
|
"deepmerge": "^4.2.2",
|
||||||
|
"is-module": "^1.0.0",
|
||||||
|
"resolve": "^1.22.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=14.0.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"rollup": "^2.78.0||^3.0.0||^4.0.0"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"rollup": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@rollup/pluginutils": {
|
||||||
|
"version": "5.1.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.1.3.tgz",
|
||||||
|
"integrity": "sha512-Pnsb6f32CD2W3uCaLZIzDmeFyQ2b8UWMFI7xtwUezpcGBDVDW6y9XgAWIlARiGAo6eNF5FK5aQTr0LFyNyqq5A==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@types/estree": "^1.0.0",
|
||||||
|
"estree-walker": "^2.0.2",
|
||||||
|
"picomatch": "^4.0.2"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=14.0.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"rollup": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@rollup/rollup-android-arm-eabi": {
|
"node_modules/@rollup/rollup-android-arm-eabi": {
|
||||||
"version": "4.27.2",
|
"version": "4.27.2",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.27.2.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.27.2.tgz",
|
||||||
|
@ -2082,6 +2190,22 @@
|
||||||
"@sveltejs/kit": "^2.0.0"
|
"@sveltejs/kit": "^2.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@sveltejs/adapter-node": {
|
||||||
|
"version": "5.2.9",
|
||||||
|
"resolved": "https://registry.npmjs.org/@sveltejs/adapter-node/-/adapter-node-5.2.9.tgz",
|
||||||
|
"integrity": "sha512-51euNrx0AcaTu8//wDfVh7xmqQSVgU52rfinE/MwvGkJa4nHPJMHmzv6+OIpmxg7gZaF6+5NVlxnieCzxLD59g==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@rollup/plugin-commonjs": "^28.0.1",
|
||||||
|
"@rollup/plugin-json": "^6.1.0",
|
||||||
|
"@rollup/plugin-node-resolve": "^15.3.0",
|
||||||
|
"rollup": "^4.9.5"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@sveltejs/kit": "^2.4.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@sveltejs/kit": {
|
"node_modules/@sveltejs/kit": {
|
||||||
"version": "2.8.1",
|
"version": "2.8.1",
|
||||||
"resolved": "https://registry.npmjs.org/@sveltejs/kit/-/kit-2.8.1.tgz",
|
"resolved": "https://registry.npmjs.org/@sveltejs/kit/-/kit-2.8.1.tgz",
|
||||||
|
@ -2273,6 +2397,13 @@
|
||||||
"undici-types": "~6.19.8"
|
"undici-types": "~6.19.8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@types/resolve": {
|
||||||
|
"version": "1.20.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.20.2.tgz",
|
||||||
|
"integrity": "sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/@types/ws": {
|
"node_modules/@types/ws": {
|
||||||
"version": "8.5.13",
|
"version": "8.5.13",
|
||||||
"resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.13.tgz",
|
"resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.13.tgz",
|
||||||
|
@ -2683,6 +2814,15 @@
|
||||||
"postcss": "^8.1.0"
|
"postcss": "^8.1.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/aws-ssl-profiles": {
|
||||||
|
"version": "1.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/aws-ssl-profiles/-/aws-ssl-profiles-1.1.2.tgz",
|
||||||
|
"integrity": "sha512-NZKeq9AfyQvEeNlN0zSYAaWrmBffJh3IELMZfRpJVWgrpEbtEpnjvzqBPf+mxoI287JohRDoa+/nsfqqiZmF6g==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 6.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/axobject-query": {
|
"node_modules/axobject-query": {
|
||||||
"version": "4.1.0",
|
"version": "4.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-4.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-4.1.0.tgz",
|
||||||
|
@ -2882,6 +3022,13 @@
|
||||||
"node": ">= 6"
|
"node": ">= 6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/commondir": {
|
||||||
|
"version": "1.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz",
|
||||||
|
"integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/concat-map": {
|
"node_modules/concat-map": {
|
||||||
"version": "0.0.1",
|
"version": "0.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
|
||||||
|
@ -3321,6 +3468,15 @@
|
||||||
"robust-predicates": "^3.0.2"
|
"robust-predicates": "^3.0.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/denque": {
|
||||||
|
"version": "2.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/denque/-/denque-2.1.0.tgz",
|
||||||
|
"integrity": "sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==",
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.10"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/detect-libc": {
|
"node_modules/detect-libc": {
|
||||||
"version": "2.0.2",
|
"version": "2.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.2.tgz",
|
||||||
|
@ -3796,9 +3952,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/drizzle-orm": {
|
"node_modules/drizzle-orm": {
|
||||||
"version": "0.36.3",
|
"version": "0.36.4",
|
||||||
"resolved": "https://registry.npmjs.org/drizzle-orm/-/drizzle-orm-0.36.3.tgz",
|
"resolved": "https://registry.npmjs.org/drizzle-orm/-/drizzle-orm-0.36.4.tgz",
|
||||||
"integrity": "sha512-ffQB7CcyCTvQBK6xtRLMl/Jsd5xFTBs+UTHrgs1hbk68i5TPkbsoCPbKEwiEsQZfq2I7VH632XJpV1g7LS2H9Q==",
|
"integrity": "sha512-1OZY3PXD7BR00Gl61UUOFihslDldfH4NFRH2MbP54Yxi0G/PKn4HfO65JYZ7c16DeP3SpM3Aw+VXVG9j6CRSXA==",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@aws-sdk/client-rds-data": ">=3",
|
"@aws-sdk/client-rds-data": ">=3",
|
||||||
|
@ -3806,7 +3962,7 @@
|
||||||
"@electric-sql/pglite": ">=0.2.0",
|
"@electric-sql/pglite": ">=0.2.0",
|
||||||
"@libsql/client": ">=0.10.0",
|
"@libsql/client": ">=0.10.0",
|
||||||
"@libsql/client-wasm": ">=0.10.0",
|
"@libsql/client-wasm": ">=0.10.0",
|
||||||
"@neondatabase/serverless": ">=0.1",
|
"@neondatabase/serverless": ">=0.10.0",
|
||||||
"@op-engineering/op-sqlite": ">=2",
|
"@op-engineering/op-sqlite": ">=2",
|
||||||
"@opentelemetry/api": "^1.4.1",
|
"@opentelemetry/api": "^1.4.1",
|
||||||
"@planetscale/database": ">=1",
|
"@planetscale/database": ">=1",
|
||||||
|
@ -4252,6 +4408,13 @@
|
||||||
"node": ">=4.0"
|
"node": ">=4.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/estree-walker": {
|
||||||
|
"version": "2.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz",
|
||||||
|
"integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/esutils": {
|
"node_modules/esutils": {
|
||||||
"version": "2.0.3",
|
"version": "2.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz",
|
||||||
|
@ -4501,6 +4664,15 @@
|
||||||
"url": "https://github.com/sponsors/ljharb"
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/generate-function": {
|
||||||
|
"version": "2.3.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/generate-function/-/generate-function-2.3.1.tgz",
|
||||||
|
"integrity": "sha512-eeB5GfMNeevm/GRYq20ShmsaGcmI81kIX2K9XQx5miC8KdHaC6Jm0qQ8ZNeGOi7wYB8OsdxKs+Y2oVuTFuVwKQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"is-property": "^1.0.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/get-tsconfig": {
|
"node_modules/get-tsconfig": {
|
||||||
"version": "4.8.1",
|
"version": "4.8.1",
|
||||||
"resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.8.1.tgz",
|
"resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.8.1.tgz",
|
||||||
|
@ -4762,6 +4934,13 @@
|
||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/is-module": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/is-module/-/is-module-1.0.0.tgz",
|
||||||
|
"integrity": "sha512-51ypPSPCoTEIN9dy5Oy+h4pShgJmPCygKfyRCISBI+JoWT/2oJvK8QPxmwv7b/p239jXrm9M1mlQbyKJ5A152g==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/is-number": {
|
"node_modules/is-number": {
|
||||||
"version": "7.0.0",
|
"version": "7.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
|
||||||
|
@ -4771,6 +4950,12 @@
|
||||||
"node": ">=0.12.0"
|
"node": ">=0.12.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/is-property": {
|
||||||
|
"version": "1.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/is-property/-/is-property-1.0.2.tgz",
|
||||||
|
"integrity": "sha512-Ks/IoX00TtClbGQr4TWXemAnktAQvYB7HzcCxDGqEZU6oCmb2INHuOoKxbtR+HFkmYWBKv/dOZtGRiAjDhj92g==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/is-reference": {
|
"node_modules/is-reference": {
|
||||||
"version": "3.0.3",
|
"version": "3.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/is-reference/-/is-reference-3.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/is-reference/-/is-reference-3.0.3.tgz",
|
||||||
|
@ -5037,12 +5222,33 @@
|
||||||
"integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==",
|
"integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/long": {
|
||||||
|
"version": "5.2.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/long/-/long-5.2.3.tgz",
|
||||||
|
"integrity": "sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q==",
|
||||||
|
"license": "Apache-2.0"
|
||||||
|
},
|
||||||
"node_modules/lru-cache": {
|
"node_modules/lru-cache": {
|
||||||
"version": "10.4.3",
|
"version": "10.4.3",
|
||||||
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz",
|
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz",
|
||||||
"integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==",
|
"integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==",
|
||||||
"license": "ISC"
|
"license": "ISC"
|
||||||
},
|
},
|
||||||
|
"node_modules/lru.min": {
|
||||||
|
"version": "1.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/lru.min/-/lru.min-1.1.1.tgz",
|
||||||
|
"integrity": "sha512-FbAj6lXil6t8z4z3j0E5mfRlPzxkySotzUHwRXjlpRh10vc6AI6WN62ehZj82VG7M20rqogJ0GLwar2Xa05a8Q==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"bun": ">=1.0.0",
|
||||||
|
"deno": ">=1.30.0",
|
||||||
|
"node": ">=8.0.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/wellwelwel"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/magic-string": {
|
"node_modules/magic-string": {
|
||||||
"version": "0.30.12",
|
"version": "0.30.12",
|
||||||
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.12.tgz",
|
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.12.tgz",
|
||||||
|
@ -5144,6 +5350,26 @@
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/mysql2": {
|
||||||
|
"version": "3.11.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/mysql2/-/mysql2-3.11.4.tgz",
|
||||||
|
"integrity": "sha512-Z2o3tY4Z8EvSRDwknaC40MdZ3+m0sKbpnXrShQLdxPrAvcNli7jLrD2Zd2IzsRMw4eK9Yle500FDmlkIqp+krg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"aws-ssl-profiles": "^1.1.1",
|
||||||
|
"denque": "^2.1.0",
|
||||||
|
"generate-function": "^2.3.1",
|
||||||
|
"iconv-lite": "^0.6.3",
|
||||||
|
"long": "^5.2.1",
|
||||||
|
"lru.min": "^1.0.0",
|
||||||
|
"named-placeholders": "^1.1.3",
|
||||||
|
"seq-queue": "^0.0.5",
|
||||||
|
"sqlstring": "^2.3.2"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 8.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/mz": {
|
"node_modules/mz": {
|
||||||
"version": "2.7.0",
|
"version": "2.7.0",
|
||||||
"resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz",
|
"resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz",
|
||||||
|
@ -5155,6 +5381,27 @@
|
||||||
"thenify-all": "^1.0.0"
|
"thenify-all": "^1.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/named-placeholders": {
|
||||||
|
"version": "1.1.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/named-placeholders/-/named-placeholders-1.1.3.tgz",
|
||||||
|
"integrity": "sha512-eLoBxg6wE/rZkJPhU/xRX1WTpkFEwDJEN96oxFrTsqBdbT5ec295Q+CoHrL9IT0DipqKhmGcaZmwOt8OON5x1w==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"lru-cache": "^7.14.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/named-placeholders/node_modules/lru-cache": {
|
||||||
|
"version": "7.18.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz",
|
||||||
|
"integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==",
|
||||||
|
"license": "ISC",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/nanoid": {
|
"node_modules/nanoid": {
|
||||||
"version": "3.3.7",
|
"version": "3.3.7",
|
||||||
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz",
|
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz",
|
||||||
|
@ -5393,8 +5640,6 @@
|
||||||
"integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==",
|
"integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
|
||||||
"peer": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=12"
|
"node": ">=12"
|
||||||
},
|
},
|
||||||
|
@ -5964,6 +6209,11 @@
|
||||||
"node": ">=10"
|
"node": ">=10"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/seq-queue": {
|
||||||
|
"version": "0.0.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/seq-queue/-/seq-queue-0.0.5.tgz",
|
||||||
|
"integrity": "sha512-hr3Wtp/GZIc/6DAGPDcV4/9WoZhjrkXsi5B/07QgX8tsdc6ilr7BFM6PM6rbdAX1kFSDYeZGLipIZZKyQP0O5Q=="
|
||||||
|
},
|
||||||
"node_modules/set-cookie-parser": {
|
"node_modules/set-cookie-parser": {
|
||||||
"version": "2.7.1",
|
"version": "2.7.1",
|
||||||
"resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.1.tgz",
|
"resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.1.tgz",
|
||||||
|
@ -6079,6 +6329,15 @@
|
||||||
"source-map": "^0.6.0"
|
"source-map": "^0.6.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/sqlstring": {
|
||||||
|
"version": "2.3.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/sqlstring/-/sqlstring-2.3.3.tgz",
|
||||||
|
"integrity": "sha512-qC9iz2FlN7DQl3+wjwn3802RTyjCx7sDvfQEXchwa6CWOx07/WVfh91gBmQ9fahw8snwGEWU3xGzOt4tFyHLxg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.6"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/stream-source": {
|
"node_modules/stream-source": {
|
||||||
"version": "0.3.5",
|
"version": "0.3.5",
|
||||||
"resolved": "https://registry.npmjs.org/stream-source/-/stream-source-0.3.5.tgz",
|
"resolved": "https://registry.npmjs.org/stream-source/-/stream-source-0.3.5.tgz",
|
||||||
|
|
|
@ -13,6 +13,7 @@
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@sveltejs/adapter-auto": "^3.0.0",
|
"@sveltejs/adapter-auto": "^3.0.0",
|
||||||
|
"@sveltejs/adapter-node": "^5.2.9",
|
||||||
"@sveltejs/kit": "^2.0.0",
|
"@sveltejs/kit": "^2.0.0",
|
||||||
"@sveltejs/vite-plugin-svelte": "^4.0.0",
|
"@sveltejs/vite-plugin-svelte": "^4.0.0",
|
||||||
"@types/d3-array": "^3.2.1",
|
"@types/d3-array": "^3.2.1",
|
||||||
|
@ -45,7 +46,8 @@
|
||||||
"d3-array": "^3.2.4",
|
"d3-array": "^3.2.4",
|
||||||
"d3-scale": "^4.0.2",
|
"d3-scale": "^4.0.2",
|
||||||
"d3-shape": "^3.2.0",
|
"d3-shape": "^3.2.0",
|
||||||
"drizzle-orm": "^0.36.3",
|
"drizzle-orm": "^0.36.4",
|
||||||
"layerchart": "^0.59.1"
|
"layerchart": "^0.59.1",
|
||||||
|
"mysql2": "^3.11.4"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,10 @@
|
||||||
import { drizzle } from 'drizzle-orm/libsql';
|
import { drizzle } from 'drizzle-orm/mysql2';
|
||||||
import * as schema from './schema';
|
import * as schema from './schema';
|
||||||
|
import { env } from '$env/dynamic/private';
|
||||||
|
|
||||||
export const db = drizzle({
|
export const db = drizzle(
|
||||||
connection: 'file:db.sqlite',
|
env.DATABASE_URL ?? '',
|
||||||
schema
|
{
|
||||||
|
schema,
|
||||||
|
mode: 'default'
|
||||||
});
|
});
|
|
@ -1,41 +1,41 @@
|
||||||
import { index, int, primaryKey, sqliteTable, text } from "drizzle-orm/sqlite-core";
|
import { index, int, primaryKey, mysqlTable, text, varchar, bigint } from "drizzle-orm/mysql-core";
|
||||||
|
|
||||||
export const usersTable = sqliteTable("users_table", {
|
export const usersTable = mysqlTable("users_table", {
|
||||||
id: int().primaryKey({ autoIncrement: true }),
|
id: int().autoincrement().primaryKey(),
|
||||||
email: text().notNull().unique(),
|
email: text().notNull().unique(),
|
||||||
password_hash: text().notNull(),
|
password_hash: text().notNull(),
|
||||||
});
|
});
|
||||||
|
|
||||||
export const sessions = sqliteTable("sessions", {
|
export const sessions = mysqlTable("sessions", {
|
||||||
token: text().notNull().primaryKey(),
|
token: varchar({ length: 255 }).notNull().primaryKey(),
|
||||||
userId: int().notNull().references(() => usersTable.id, { onDelete: "cascade" }),
|
userId: int().notNull().references(() => usersTable.id, { onDelete: "cascade" }),
|
||||||
expires: int().notNull(),
|
expires: bigint({ mode: "number" }).notNull(),
|
||||||
});
|
});
|
||||||
|
|
||||||
export const surveysTable = sqliteTable("surveys_table", {
|
export const surveysTable = mysqlTable("surveys_table", {
|
||||||
id: int().primaryKey({ autoIncrement: true }),
|
id: int().autoincrement().primaryKey(),
|
||||||
title: text().notNull(),
|
title: text().notNull(),
|
||||||
description: text(),
|
description: text(),
|
||||||
owner: int().notNull().references(() => usersTable.id, { onDelete: "cascade" }),
|
owner: int().notNull().references(() => usersTable.id, { onDelete: "cascade" }),
|
||||||
});
|
});
|
||||||
|
|
||||||
export const surveySkillsTable = sqliteTable("survey_skills_table", {
|
export const surveySkillsTable = mysqlTable("survey_skills_table", {
|
||||||
id: int().primaryKey({ autoIncrement: true }),
|
id: int().autoincrement().primaryKey(),
|
||||||
surveyId: int().notNull().references(() => surveysTable.id, { onDelete: "cascade" }),
|
surveyId: int().notNull().references(() => surveysTable.id, { onDelete: "cascade" }),
|
||||||
title: text().notNull(),
|
title: text().notNull(),
|
||||||
description: text(),
|
description: text(),
|
||||||
})
|
})
|
||||||
|
|
||||||
export const surveyAccessTable = sqliteTable("survey_access_table", {
|
export const surveyAccessTable = mysqlTable("survey_access_table", {
|
||||||
id: int().primaryKey({ autoIncrement: true }),
|
id: int().autoincrement().primaryKey(),
|
||||||
surveyId: int().notNull().references(() => surveysTable.id, { onDelete: "cascade" }),
|
surveyId: int().notNull().references(() => surveysTable.id, { onDelete: "cascade" }),
|
||||||
recepientEmail: text().notNull(),
|
recepientEmail: text().notNull(),
|
||||||
accessToken: text().notNull(),
|
accessToken: varchar({ length: 255 }).notNull(),
|
||||||
}, (table) => ({
|
}, (table) => ({
|
||||||
tokenIndex: index("token_index").on(table.accessToken),
|
tokenIndex: index("token_index").on(table.accessToken),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
export const surveyAnswersTable = sqliteTable("survey_answers_table", {
|
export const surveyAnswersTable = mysqlTable("survey_answers_table", {
|
||||||
participantId: int().notNull().references(() => surveyAccessTable.id, { onDelete: "cascade" }),
|
participantId: int().notNull().references(() => surveyAccessTable.id, { onDelete: "cascade" }),
|
||||||
skillId: int().notNull().references(() => surveySkillsTable.id, { onDelete: "cascade" }),
|
skillId: int().notNull().references(() => surveySkillsTable.id, { onDelete: "cascade" }),
|
||||||
rating: int().notNull(),
|
rating: int().notNull(),
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
title: string;
|
title: string;
|
||||||
description: string | null;
|
description: string | null;
|
||||||
value: number;
|
value: number;
|
||||||
|
noAnswer: boolean;
|
||||||
};
|
};
|
||||||
type Props = { skill: SkillProps };
|
type Props = { skill: SkillProps };
|
||||||
|
|
||||||
|
@ -29,8 +30,19 @@
|
||||||
bind:value={skill.value}
|
bind:value={skill.value}
|
||||||
class="w-80 justify-self-start"
|
class="w-80 justify-self-start"
|
||||||
list="values"
|
list="values"
|
||||||
|
disabled={skill.noAnswer}
|
||||||
/>
|
/>
|
||||||
|
<label class="ml-2 text-xs">
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
name="disable-{skill.id}"
|
||||||
|
bind:checked={skill.noAnswer}
|
||||||
|
class="text-xs"
|
||||||
|
/> No answer</label
|
||||||
|
>
|
||||||
|
{#if !skill.noAnswer}
|
||||||
<span class="ml-2">{skill.value}</span>
|
<span class="ml-2">{skill.value}</span>
|
||||||
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
<datalist id="values">
|
<datalist id="values">
|
||||||
<option value="0" label="0"></option>
|
<option value="0" label="0"></option>
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
import { encodeBase32LowerCaseNoPadding } from '@oslojs/encoding';
|
|
||||||
import { db } from '../../db';
|
import { db } from '../../db';
|
||||||
import { sessions } from '../../db/schema';
|
import { sessions } from '../../db/schema';
|
||||||
import { eq, lt } from 'drizzle-orm';
|
import { eq, lt } from 'drizzle-orm';
|
||||||
|
|
|
@ -45,7 +45,7 @@ export const actions = {
|
||||||
error(400, 'At least one skill is required');
|
error(400, 'At least one skill is required');
|
||||||
}
|
}
|
||||||
|
|
||||||
const ids = await db.insert(surveysTable).values({ title, description, owner }).returning({ id: surveysTable.id });
|
const ids = await db.insert(surveysTable).values({ title, description, owner }).$returningId();
|
||||||
|
|
||||||
const surveyId = ids[0].id;
|
const surveyId = ids[0].id;
|
||||||
for (const participant of participants) {
|
for (const participant of participants) {
|
||||||
|
|
|
@ -56,7 +56,7 @@ export const actions = {
|
||||||
|
|
||||||
const formData = await request.formData();
|
const formData = await request.formData();
|
||||||
// validate that the form doesn't contain invalid skill IDs
|
// validate that the form doesn't contain invalid skill IDs
|
||||||
const skillEntries = [...formData.entries()];
|
const skillEntries = [...formData.entries()].filter(([key, _]) => key.startsWith('disable-'));
|
||||||
const allIdsValid = skillEntries.every(([key, _]) => skills.some(skill => skill.id.toString() === key));
|
const allIdsValid = skillEntries.every(([key, _]) => skills.some(skill => skill.id.toString() === key));
|
||||||
if (!allIdsValid || skillEntries.length !== skills.length) {
|
if (!allIdsValid || skillEntries.length !== skills.length) {
|
||||||
error(400, 'Invalid skill ID');
|
error(400, 'Invalid skill ID');
|
||||||
|
|
|
@ -10,7 +10,8 @@
|
||||||
id: skill.id.toString(),
|
id: skill.id.toString(),
|
||||||
title: skill.title,
|
title: skill.title,
|
||||||
description: skill.description,
|
description: skill.description,
|
||||||
value: 0
|
value: 0,
|
||||||
|
noAnswer: false
|
||||||
}))
|
}))
|
||||||
);
|
);
|
||||||
</script>
|
</script>
|
||||||
|
@ -27,13 +28,15 @@
|
||||||
<button type="submit" class="mt-5 w-40 bg-slate-200">Submit</button>
|
<button type="submit" class="mt-5 w-40 bg-slate-200">Submit</button>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
<dl class="ml-8 mt-5 grid grid-cols-[2rem_1fr] text-sm">
|
<dl class="ml-8 mt-5 grid grid-cols-[7rem_1fr] text-sm">
|
||||||
<dt class="font-semibold">0</dt>
|
<dt class="text-center font-semibold">(No answer)</dt>
|
||||||
<dd>The person does not have this skill or I can't answer.</dd>
|
<dd>I don't know and can't give useful answer.</dd>
|
||||||
<dt class="font-semibold">1</dt>
|
<dt class="text-center font-semibold">0</dt>
|
||||||
|
<dd>The person does not have this skill.</dd>
|
||||||
|
<dt class="text-center font-semibold">1</dt>
|
||||||
<dd>The person can use this skill with guidance by more experienced people.</dd>
|
<dd>The person can use this skill with guidance by more experienced people.</dd>
|
||||||
<dt class="font-semibold">2</dt>
|
<dt class="text-center font-semibold">2</dt>
|
||||||
<dd>The person is able to use this skill by themselves.</dd>
|
<dd>The person is able to use this skill by themselves.</dd>
|
||||||
<dt class="font-semibold">3</dt>
|
<dt class="text-center font-semibold">3</dt>
|
||||||
<dd>The person is able to guide others to use this skill.</dd>
|
<dd>The person is able to guide others to use this skill.</dd>
|
||||||
</dl>
|
</dl>
|
||||||
|
|
|
@ -1,14 +1,31 @@
|
||||||
import { hash } from '@node-rs/argon2';
|
import { hash } from '@node-rs/argon2';
|
||||||
import type { Actions } from './$types';
|
import type { Actions, PageServerLoad } from './$types';
|
||||||
import { usersTable } from '../../db/schema';
|
import { usersTable } from '../../db/schema';
|
||||||
import { db } from '../../db';
|
import { db } from '../../db';
|
||||||
import { redirect } from '@sveltejs/kit';
|
import { error, redirect } from '@sveltejs/kit';
|
||||||
|
import { env } from '$env/dynamic/private';
|
||||||
|
|
||||||
|
export const load: PageServerLoad = () => {
|
||||||
|
return {
|
||||||
|
allowableDomains: env.ALLOWABLE_DOMAINS
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export const actions = {
|
export const actions = {
|
||||||
default: async (event) => {
|
default: async (event) => {
|
||||||
|
const allowedDomains = env.ALLOWABLE_DOMAINS?.split(',');
|
||||||
const formData = await event.request.formData();
|
const formData = await event.request.formData();
|
||||||
const email = formData.get('email');
|
const email = formData.get('email')?.toString();
|
||||||
const password = formData.get('password');
|
const password = formData.get('password')?.toString();
|
||||||
|
|
||||||
|
if (!email || !password) {
|
||||||
|
error(400, 'Email and password are required');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (allowedDomains && !allowedDomains?.some(domain => email.endsWith(domain))) {
|
||||||
|
error(400, 'Invalid email domain');
|
||||||
|
}
|
||||||
|
|
||||||
const hashedPassword = await hash(password);
|
const hashedPassword = await hash(password);
|
||||||
|
|
||||||
await db.insert(usersTable).values({
|
await db.insert(usersTable).values({
|
||||||
|
|
|
@ -1,12 +1,21 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
let password = $state('');
|
let password = $state('');
|
||||||
let password_repeat = $state('');
|
let password_repeat = $state('');
|
||||||
|
|
||||||
|
const { data } = $props();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<h1 class="text-3xl">Register a new Account</h1>
|
<h1 class="text-3xl">Register a new Account</h1>
|
||||||
<div>
|
<div>
|
||||||
<form class="grid grid-cols-2 gap-1" method="POST">
|
<form class="grid grid-cols-2 gap-1" method="POST">
|
||||||
<label for="name" class="justify-self-end">Email address</label>
|
<label for="name" class="justify-self-end"
|
||||||
|
><div>Email address</div>
|
||||||
|
{#if data.allowableDomains}
|
||||||
|
<div class="text-xs text-slate-500">
|
||||||
|
Allowed domains: {data.allowableDomains}
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
</label>
|
||||||
<input type="email" name="email" id="email" class="justify-self-start" required />
|
<input type="email" name="email" id="email" class="justify-self-start" required />
|
||||||
<label for="password" class="justify-self-end">Password</label>
|
<label for="password" class="justify-self-end">Password</label>
|
||||||
<input
|
<input
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import adapter from '@sveltejs/adapter-auto';
|
import adapter from '@sveltejs/adapter-node';
|
||||||
import { vitePreprocess } from '@sveltejs/vite-plugin-svelte';
|
import { vitePreprocess } from '@sveltejs/vite-plugin-svelte';
|
||||||
|
|
||||||
/** @type {import('@sveltejs/kit').Config} */
|
/** @type {import('@sveltejs/kit').Config} */
|
||||||
|
|
Loading…
Add table
Reference in a new issue