diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..f9b350b --- /dev/null +++ b/Dockerfile @@ -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"] diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..b361ce4 --- /dev/null +++ b/docker-compose.yml @@ -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 \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index e0332ee..499c5ab 100644 --- a/package-lock.json +++ b/package-lock.json @@ -17,11 +17,13 @@ "d3-array": "^3.2.4", "d3-scale": "^4.0.2", "d3-shape": "^3.2.0", - "drizzle-orm": "^0.36.3", - "layerchart": "^0.59.1" + "drizzle-orm": "^0.36.4", + "layerchart": "^0.59.1", + "mysql2": "^3.11.4" }, "devDependencies": { "@sveltejs/adapter-auto": "^3.0.0", + "@sveltejs/adapter-node": "^5.2.9", "@sveltejs/kit": "^2.0.0", "@sveltejs/vite-plugin-svelte": "^4.0.0", "@types/d3-array": "^3.2.1", @@ -1817,6 +1819,112 @@ "dev": true, "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": { "version": "4.27.2", "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" } }, + "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": { "version": "2.8.1", "resolved": "https://registry.npmjs.org/@sveltejs/kit/-/kit-2.8.1.tgz", @@ -2273,6 +2397,13 @@ "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": { "version": "8.5.13", "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.13.tgz", @@ -2683,6 +2814,15 @@ "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": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-4.1.0.tgz", @@ -2882,6 +3022,13 @@ "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": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -3321,6 +3468,15 @@ "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": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.2.tgz", @@ -3796,9 +3952,9 @@ } }, "node_modules/drizzle-orm": { - "version": "0.36.3", - "resolved": "https://registry.npmjs.org/drizzle-orm/-/drizzle-orm-0.36.3.tgz", - "integrity": "sha512-ffQB7CcyCTvQBK6xtRLMl/Jsd5xFTBs+UTHrgs1hbk68i5TPkbsoCPbKEwiEsQZfq2I7VH632XJpV1g7LS2H9Q==", + "version": "0.36.4", + "resolved": "https://registry.npmjs.org/drizzle-orm/-/drizzle-orm-0.36.4.tgz", + "integrity": "sha512-1OZY3PXD7BR00Gl61UUOFihslDldfH4NFRH2MbP54Yxi0G/PKn4HfO65JYZ7c16DeP3SpM3Aw+VXVG9j6CRSXA==", "license": "Apache-2.0", "peerDependencies": { "@aws-sdk/client-rds-data": ">=3", @@ -3806,7 +3962,7 @@ "@electric-sql/pglite": ">=0.2.0", "@libsql/client": ">=0.10.0", "@libsql/client-wasm": ">=0.10.0", - "@neondatabase/serverless": ">=0.1", + "@neondatabase/serverless": ">=0.10.0", "@op-engineering/op-sqlite": ">=2", "@opentelemetry/api": "^1.4.1", "@planetscale/database": ">=1", @@ -4252,6 +4408,13 @@ "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": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", @@ -4501,6 +4664,15 @@ "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": { "version": "4.8.1", "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.8.1.tgz", @@ -4762,6 +4934,13 @@ "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": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", @@ -4771,6 +4950,12 @@ "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": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/is-reference/-/is-reference-3.0.3.tgz", @@ -5037,12 +5222,33 @@ "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", "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": { "version": "10.4.3", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", "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": { "version": "0.30.12", "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.12.tgz", @@ -5144,6 +5350,26 @@ "dev": true, "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": { "version": "2.7.0", "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", @@ -5155,6 +5381,27 @@ "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": { "version": "3.3.7", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", @@ -5393,8 +5640,6 @@ "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", "dev": true, "license": "MIT", - "optional": true, - "peer": true, "engines": { "node": ">=12" }, @@ -5964,6 +6209,11 @@ "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": { "version": "2.7.1", "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.1.tgz", @@ -6079,6 +6329,15 @@ "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": { "version": "0.3.5", "resolved": "https://registry.npmjs.org/stream-source/-/stream-source-0.3.5.tgz", diff --git a/package.json b/package.json index 5a94bfd..b66096d 100644 --- a/package.json +++ b/package.json @@ -13,6 +13,7 @@ }, "devDependencies": { "@sveltejs/adapter-auto": "^3.0.0", + "@sveltejs/adapter-node": "^5.2.9", "@sveltejs/kit": "^2.0.0", "@sveltejs/vite-plugin-svelte": "^4.0.0", "@types/d3-array": "^3.2.1", @@ -45,7 +46,8 @@ "d3-array": "^3.2.4", "d3-scale": "^4.0.2", "d3-shape": "^3.2.0", - "drizzle-orm": "^0.36.3", - "layerchart": "^0.59.1" + "drizzle-orm": "^0.36.4", + "layerchart": "^0.59.1", + "mysql2": "^3.11.4" } } diff --git a/src/db/index.ts b/src/db/index.ts index c4d4953..84361cb 100644 --- a/src/db/index.ts +++ b/src/db/index.ts @@ -1,7 +1,10 @@ -import { drizzle } from 'drizzle-orm/libsql'; +import { drizzle } from 'drizzle-orm/mysql2'; import * as schema from './schema'; +import { env } from '$env/dynamic/private'; -export const db = drizzle({ - connection: 'file:db.sqlite', - schema -}); \ No newline at end of file +export const db = drizzle( + env.DATABASE_URL ?? '', + { + schema, + mode: 'default' + }); \ No newline at end of file diff --git a/src/db/schema.ts b/src/db/schema.ts index 860cfca..38dc3ab 100644 --- a/src/db/schema.ts +++ b/src/db/schema.ts @@ -1,44 +1,44 @@ -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", { - id: int().primaryKey({ autoIncrement: true }), +export const usersTable = mysqlTable("users_table", { + id: int().autoincrement().primaryKey(), email: text().notNull().unique(), password_hash: text().notNull(), }); -export const sessions = sqliteTable("sessions", { - token: text().notNull().primaryKey(), +export const sessions = mysqlTable("sessions", { + token: varchar({ length: 255 }).notNull().primaryKey(), userId: int().notNull().references(() => usersTable.id, { onDelete: "cascade" }), - expires: int().notNull(), + expires: bigint({ mode: "number" }).notNull(), }); -export const surveysTable = sqliteTable("surveys_table", { - id: int().primaryKey({ autoIncrement: true }), +export const surveysTable = mysqlTable("surveys_table", { + id: int().autoincrement().primaryKey(), title: text().notNull(), description: text(), owner: int().notNull().references(() => usersTable.id, { onDelete: "cascade" }), }); -export const surveySkillsTable = sqliteTable("survey_skills_table", { - id: int().primaryKey({ autoIncrement: true }), +export const surveySkillsTable = mysqlTable("survey_skills_table", { + id: int().autoincrement().primaryKey(), surveyId: int().notNull().references(() => surveysTable.id, { onDelete: "cascade" }), title: text().notNull(), description: text(), }) -export const surveyAccessTable = sqliteTable("survey_access_table", { - id: int().primaryKey({ autoIncrement: true }), +export const surveyAccessTable = mysqlTable("survey_access_table", { + id: int().autoincrement().primaryKey(), surveyId: int().notNull().references(() => surveysTable.id, { onDelete: "cascade" }), recepientEmail: text().notNull(), - accessToken: text().notNull(), + accessToken: varchar({ length: 255 }).notNull(), }, (table) => ({ 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" }), skillId: int().notNull().references(() => surveySkillsTable.id, { onDelete: "cascade" }), rating: int().notNull(), }, (table) => ({ pk: primaryKey({ columns: [table.participantId, table.skillId] }), -})); \ No newline at end of file +})); diff --git a/src/lib/SkillInput.svelte b/src/lib/SkillInput.svelte index 6ff8d3b..cb3adb4 100644 --- a/src/lib/SkillInput.svelte +++ b/src/lib/SkillInput.svelte @@ -6,6 +6,7 @@ title: string; description: string | null; value: number; + noAnswer: boolean; }; type Props = { skill: SkillProps }; @@ -29,8 +30,19 @@ bind:value={skill.value} class="w-80 justify-self-start" list="values" + disabled={skill.noAnswer} /> - {skill.value} + + {#if !skill.noAnswer} + {skill.value} + {/if} diff --git a/src/lib/session/session.ts b/src/lib/session/session.ts index 2147179..5067889 100644 --- a/src/lib/session/session.ts +++ b/src/lib/session/session.ts @@ -1,4 +1,3 @@ -import { encodeBase32LowerCaseNoPadding } from '@oslojs/encoding'; import { db } from '../../db'; import { sessions } from '../../db/schema'; import { eq, lt } from 'drizzle-orm'; diff --git a/src/routes/(app)/survey/new/+page.server.ts b/src/routes/(app)/survey/new/+page.server.ts index 37f6bc5..848bd95 100644 --- a/src/routes/(app)/survey/new/+page.server.ts +++ b/src/routes/(app)/survey/new/+page.server.ts @@ -45,7 +45,7 @@ export const actions = { 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; for (const participant of participants) { diff --git a/src/routes/[accessToken]/+page.server.ts b/src/routes/[accessToken]/+page.server.ts index 354287d..eaf10ef 100644 --- a/src/routes/[accessToken]/+page.server.ts +++ b/src/routes/[accessToken]/+page.server.ts @@ -56,7 +56,7 @@ export const actions = { const formData = await request.formData(); // 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)); if (!allIdsValid || skillEntries.length !== skills.length) { error(400, 'Invalid skill ID'); diff --git a/src/routes/[accessToken]/+page.svelte b/src/routes/[accessToken]/+page.svelte index eab1431..73b1a46 100644 --- a/src/routes/[accessToken]/+page.svelte +++ b/src/routes/[accessToken]/+page.svelte @@ -10,7 +10,8 @@ id: skill.id.toString(), title: skill.title, description: skill.description, - value: 0 + value: 0, + noAnswer: false })) ); @@ -27,13 +28,15 @@ -
-
0
-
The person does not have this skill or I can't answer.
-
1
+
+
(No answer)
+
I don't know and can't give useful answer.
+
0
+
The person does not have this skill.
+
1
The person can use this skill with guidance by more experienced people.
-
2
+
2
The person is able to use this skill by themselves.
-
3
+
3
The person is able to guide others to use this skill.
diff --git a/src/routes/register/+page.server.ts b/src/routes/register/+page.server.ts index c63e86f..8c7488a 100644 --- a/src/routes/register/+page.server.ts +++ b/src/routes/register/+page.server.ts @@ -1,14 +1,31 @@ import { hash } from '@node-rs/argon2'; -import type { Actions } from './$types'; +import type { Actions, PageServerLoad } from './$types'; import { usersTable } from '../../db/schema'; 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 = { default: async (event) => { + const allowedDomains = env.ALLOWABLE_DOMAINS?.split(','); const formData = await event.request.formData(); - const email = formData.get('email'); - const password = formData.get('password'); + const email = formData.get('email')?.toString(); + 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); await db.insert(usersTable).values({ diff --git a/src/routes/register/+page.svelte b/src/routes/register/+page.svelte index cb55a72..d44d688 100644 --- a/src/routes/register/+page.svelte +++ b/src/routes/register/+page.svelte @@ -1,12 +1,21 @@

Register a new Account

- +