30 Commits

Author SHA1 Message Date
0cc24af26b 0.0.7
All checks were successful
Publish / lints (push) Successful in 29s
Publish / tests (push) Successful in 1m24s
Publish / Publishing (push) Successful in 1m41s
/ Misc Linters (push) Successful in 34s
/ Build App (push) Successful in 2m3s
2024-08-23 19:03:33 +02:00
c746f71622 Merge pull request 'chore(deps): update dependency svelte to v4.2.19' (#61) from renovate/svelte-4.x-lockfile into main
All checks were successful
/ Misc Linters (push) Successful in 37s
/ Build App (push) Successful in 1m35s
Reviewed-on: #61
2024-08-23 19:00:23 +02:00
b7e38dca12 chore(deps): update dependency svelte to v4.2.19
All checks were successful
/ Misc Linters (pull_request) Successful in 35s
/ Build App (pull_request) Successful in 1m43s
2024-08-23 16:56:16 +00:00
f098209b66 Merge pull request 'chore(deps): lock file maintenance' (#58) from renovate/lock-file-maintenance into main
All checks were successful
/ Misc Linters (push) Successful in 45s
/ Build App (push) Successful in 1m43s
Reviewed-on: #58
2024-08-23 18:53:20 +02:00
b5db49061e chore(deps): lock file maintenance
All checks were successful
/ Misc Linters (pull_request) Successful in 25s
/ Build App (pull_request) Successful in 1m48s
2024-08-23 18:44:14 +02:00
716799beb2 Merge pull request 'chore(deps): update dependency @sveltejs/vite-plugin-svelte to v3.1.2' (#59) from renovate/sveltejs-vite-plugin-svelte-3.x-lockfile into main
All checks were successful
/ Misc Linters (push) Successful in 30s
/ Build App (push) Successful in 1m49s
Reviewed-on: #59
2024-08-23 18:44:07 +02:00
a8c64f9d74 chore(deps): update dependency @sveltejs/vite-plugin-svelte to v3.1.2
All checks were successful
/ Misc Linters (pull_request) Successful in 30s
/ Build App (pull_request) Successful in 1m45s
2024-08-23 18:35:47 +02:00
fe7e1062d4 Merge pull request 'chore(deps): update dependency svelte-check to v3.8.6' (#60) from renovate/svelte-check-3.x-lockfile into main
All checks were successful
/ Misc Linters (push) Successful in 26s
/ Build App (push) Successful in 1m59s
Reviewed-on: #60
2024-08-23 18:35:38 +02:00
372d2b4253 chore(deps): update dependency svelte-check to v3.8.6
All checks were successful
/ Misc Linters (pull_request) Successful in 31s
/ Build App (pull_request) Successful in 1m37s
2024-08-22 19:02:02 +00:00
ad5705b00c Merge pull request 'chore(deps): update dependency vite to v5.4.2' (#57) from renovate/vite-5.x-lockfile into main
All checks were successful
/ Misc Linters (push) Successful in 24s
/ Build App (push) Successful in 1m39s
Reviewed-on: #57
2024-08-21 08:58:40 +02:00
868a518097 chore(deps): update dependency vite to v5.4.2
All checks were successful
/ Misc Linters (pull_request) Successful in 26s
/ Build App (pull_request) Successful in 1m34s
2024-08-20 16:02:01 +00:00
2836915aeb Merge pull request 'chore(deps): update dependency @sveltejs/kit to v2.5.24' (#55) from renovate/sveltejs-kit-2.x-lockfile into main
All checks were successful
/ Misc Linters (push) Successful in 24s
/ Build App (push) Successful in 1m45s
Reviewed-on: #55
2024-08-20 13:59:22 +02:00
353cc1bf1e Merge pull request 'chore(deps): update dependency typescript-eslint to v8.2.0' (#56) from renovate/typescript-eslint-monorepo into main
Some checks failed
/ Build App (push) Waiting to run
/ Misc Linters (push) Has been cancelled
Reviewed-on: #56
2024-08-20 13:59:12 +02:00
d60c1ddbbd Merge pull request 'chore(deps): update dependency @types/node to v20.16.1' (#54) from renovate/node-20.x-lockfile into main
Some checks failed
/ Build App (push) Has been cancelled
/ Misc Linters (push) Has been cancelled
Reviewed-on: #54
2024-08-20 13:58:58 +02:00
09a1e98afb chore(deps): update dependency typescript-eslint to v8.2.0
All checks were successful
/ Misc Linters (pull_request) Successful in 27s
/ Build App (pull_request) Successful in 1m27s
2024-08-19 19:01:56 +00:00
5c894e1462 chore(deps): update dependency @sveltejs/kit to v2.5.24
All checks were successful
/ Misc Linters (pull_request) Successful in 28s
/ Build App (pull_request) Successful in 1m36s
2024-08-19 13:01:38 +00:00
1eeec1215e chore(deps): update dependency @types/node to v20.16.1
All checks were successful
/ Misc Linters (pull_request) Successful in 18s
/ Build App (pull_request) Successful in 1m32s
2024-08-19 04:01:39 +00:00
d3016c765e Merge pull request 'chore(deps): update dependency @types/node to v20.16.0' (#52) from renovate/node-20.x-lockfile into main
All checks were successful
/ Misc Linters (push) Successful in 22s
/ Build App (push) Successful in 1m36s
Reviewed-on: #52
2024-08-18 16:03:16 +02:00
568da269a1 chore(deps): update dependency @types/node to v20.16.0
All checks were successful
/ Misc Linters (pull_request) Successful in 21s
/ Build App (pull_request) Successful in 1m20s
2024-08-18 15:51:05 +02:00
48b9a4c8fb Merge pull request 'Integration tests for API' (#53) from testing into main
All checks were successful
/ Misc Linters (push) Successful in 39s
/ Build App (push) Successful in 1m34s
Reviewed-on: #53
2024-08-18 15:48:51 +02:00
25ef50f358 Add test for page
All checks were successful
/ Misc Linters (pull_request) Successful in 27s
/ Build App (pull_request) Successful in 1m49s
2024-08-18 15:46:46 +02:00
20920586e0 Formatting
All checks were successful
/ Misc Linters (pull_request) Successful in 23s
/ Build App (pull_request) Successful in 1m21s
2024-08-18 15:38:03 +02:00
56f568634e Implement first integration tests
Some checks failed
/ Misc Linters (pull_request) Successful in 28s
/ Build App (pull_request) Failing after 27s
2024-08-18 15:34:36 +02:00
0fd1f178be Add playwright 2024-08-18 15:34:19 +02:00
da92ed6258 Move request-id header name to $lib 2024-08-18 15:33:56 +02:00
13b1d57e08 Merge pull request 'Healthcheck' (#51) from healthcheck into main
All checks were successful
/ Misc Linters (push) Successful in 31s
/ Build App (push) Successful in 1m8s
Reviewed-on: #51
2024-08-17 18:28:16 +02:00
1f69e739d0 Add healthcheck endpoint
All checks were successful
/ Misc Linters (pull_request) Successful in 18s
/ Build App (pull_request) Successful in 54s
2024-08-17 18:26:57 +02:00
58a65856aa Simplify
All checks were successful
/ Misc Linters (push) Successful in 23s
/ Build App (push) Successful in 52s
/ Misc Linters (pull_request) Successful in 27s
/ Build App (pull_request) Successful in 59s
2024-08-17 17:22:27 +02:00
e2a4335937 Define endpoint in TSy way
All checks were successful
/ Misc Linters (push) Successful in 25s
/ Build App (push) Successful in 52s
2024-08-17 17:12:05 +02:00
3160923f7e Fix typos and define handler in TSy way
All checks were successful
/ Misc Linters (push) Successful in 23s
/ Build App (push) Successful in 52s
2024-08-17 16:49:38 +02:00
11 changed files with 333 additions and 137 deletions

7
.gitignore vendored
View File

@ -20,5 +20,12 @@ Thumbs.db
vite.config.js.timestamp-* vite.config.js.timestamp-*
vite.config.ts.timestamp-* vite.config.ts.timestamp-*
# Playwright
/test-results/
/playwright-report/
/blob-report/
/playwright/.cache/
/playwright/.auth
uploads uploads
.direnv .direnv

304
package-lock.json generated
View File

@ -1,17 +1,18 @@
{ {
"name": "fotochallenge", "name": "fotochallenge",
"version": "0.0.6", "version": "0.0.7",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "fotochallenge", "name": "fotochallenge",
"version": "0.0.6", "version": "0.0.7",
"dependencies": { "dependencies": {
"bunyan": "^1.8.15", "bunyan": "^1.8.15",
"uuid": "^10.0.0" "uuid": "^10.0.0"
}, },
"devDependencies": { "devDependencies": {
"@playwright/test": "^1.46.1",
"@sveltejs/adapter-auto": "^3.0.0", "@sveltejs/adapter-auto": "^3.0.0",
"@sveltejs/adapter-node": "^5.2.0", "@sveltejs/adapter-node": "^5.2.0",
"@sveltejs/kit": "^2.0.0", "@sveltejs/kit": "^2.0.0",
@ -734,6 +735,22 @@
"node": ">=14" "node": ">=14"
} }
}, },
"node_modules/@playwright/test": {
"version": "1.46.1",
"resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.46.1.tgz",
"integrity": "sha512-Fq6SwLujA/DOIvNC2EL/SojJnkKf/rAwJ//APpJJHRyMi1PdKrY3Az+4XNQ51N4RTbItbIByQ0jgd1tayq1aeA==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
"playwright": "1.46.1"
},
"bin": {
"playwright": "cli.js"
},
"engines": {
"node": ">=18"
}
},
"node_modules/@polka/url": { "node_modules/@polka/url": {
"version": "1.0.0-next.25", "version": "1.0.0-next.25",
"resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.25.tgz", "resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.25.tgz",
@ -1091,9 +1108,9 @@
} }
}, },
"node_modules/@sveltejs/kit": { "node_modules/@sveltejs/kit": {
"version": "2.5.22", "version": "2.5.24",
"resolved": "https://registry.npmjs.org/@sveltejs/kit/-/kit-2.5.22.tgz", "resolved": "https://registry.npmjs.org/@sveltejs/kit/-/kit-2.5.24.tgz",
"integrity": "sha512-PQ98baF2WzvG5yiO4cZKJZJG60XjHTZD1jyho3u9Kmthx2ytdGYyVPPvKXgKXpKSq4wwctD9dl0d2blSbJMcOg==", "integrity": "sha512-Nr2oxsCsDfEkdS/zzQQQbsPYTbu692Qs3/iE3L7VHzCVjG2+WujF9oMUozWI7GuX98KxYSoPMlAsfmDLSg44hQ==",
"dev": true, "dev": true,
"hasInstallScript": true, "hasInstallScript": true,
"license": "MIT", "license": "MIT",
@ -1118,15 +1135,15 @@
"node": ">=18.13" "node": ">=18.13"
}, },
"peerDependencies": { "peerDependencies": {
"@sveltejs/vite-plugin-svelte": "^3.0.0", "@sveltejs/vite-plugin-svelte": "^3.0.0 || ^4.0.0-next.1",
"svelte": "^4.0.0 || ^5.0.0-next.0", "svelte": "^4.0.0 || ^5.0.0-next.0",
"vite": "^5.0.3" "vite": "^5.0.3"
} }
}, },
"node_modules/@sveltejs/vite-plugin-svelte": { "node_modules/@sveltejs/vite-plugin-svelte": {
"version": "3.1.1", "version": "3.1.2",
"resolved": "https://registry.npmjs.org/@sveltejs/vite-plugin-svelte/-/vite-plugin-svelte-3.1.1.tgz", "resolved": "https://registry.npmjs.org/@sveltejs/vite-plugin-svelte/-/vite-plugin-svelte-3.1.2.tgz",
"integrity": "sha512-rimpFEAboBBHIlzISibg94iP09k/KYdHgVhJlcsTfn7KMBhc70jFX/GRWkRdFCc2fdnk+4+Bdfej23cMDnJS6A==", "integrity": "sha512-Txsm1tJvtiYeLUVRNqxZGKR/mI+CzuIQuc2gn+YCs9rMTowpNZ2Nqt53JdL8KF9bLhAf2ruR/dr9eZCwdTriRA==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
@ -1207,13 +1224,13 @@
"license": "MIT" "license": "MIT"
}, },
"node_modules/@types/node": { "node_modules/@types/node": {
"version": "20.15.0", "version": "20.16.1",
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.15.0.tgz", "resolved": "https://registry.npmjs.org/@types/node/-/node-20.16.1.tgz",
"integrity": "sha512-eQf4OkH6gA9v1W0iEpht/neozCsZKMTK+C4cU6/fv7wtJCCL8LEQ4hie2Ln8ZP/0YYM2xGj7//f8xyqItkJ6QA==", "integrity": "sha512-zJDo7wEadFtSyNz5QITDfRcrhqDvQI1xQNQ0VoizPjM/dVAODqqIUWbJPkvsxmTI0MYRGRikcdjMPhOssnPejQ==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"undici-types": "~6.13.0" "undici-types": "~6.19.2"
} }
}, },
"node_modules/@types/pug": { "node_modules/@types/pug": {
@ -1238,17 +1255,17 @@
"license": "MIT" "license": "MIT"
}, },
"node_modules/@typescript-eslint/eslint-plugin": { "node_modules/@typescript-eslint/eslint-plugin": {
"version": "8.1.0", "version": "8.2.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.1.0.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.2.0.tgz",
"integrity": "sha512-LlNBaHFCEBPHyD4pZXb35mzjGkuGKXU5eeCA1SxvHfiRES0E82dOounfVpL4DCqYvJEKab0bZIA0gCRpdLKkCw==", "integrity": "sha512-02tJIs655em7fvt9gps/+4k4OsKULYGtLBPJfOsmOq1+3cdClYiF0+d6mHu6qDnTcg88wJBkcPLpQhq7FyDz0A==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@eslint-community/regexpp": "^4.10.0", "@eslint-community/regexpp": "^4.10.0",
"@typescript-eslint/scope-manager": "8.1.0", "@typescript-eslint/scope-manager": "8.2.0",
"@typescript-eslint/type-utils": "8.1.0", "@typescript-eslint/type-utils": "8.2.0",
"@typescript-eslint/utils": "8.1.0", "@typescript-eslint/utils": "8.2.0",
"@typescript-eslint/visitor-keys": "8.1.0", "@typescript-eslint/visitor-keys": "8.2.0",
"graphemer": "^1.4.0", "graphemer": "^1.4.0",
"ignore": "^5.3.1", "ignore": "^5.3.1",
"natural-compare": "^1.4.0", "natural-compare": "^1.4.0",
@ -1272,16 +1289,16 @@
} }
}, },
"node_modules/@typescript-eslint/parser": { "node_modules/@typescript-eslint/parser": {
"version": "8.1.0", "version": "8.2.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.1.0.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.2.0.tgz",
"integrity": "sha512-U7iTAtGgJk6DPX9wIWPPOlt1gO57097G06gIcl0N0EEnNw8RGD62c+2/DiP/zL7KrkqnnqF7gtFGR7YgzPllTA==", "integrity": "sha512-j3Di+o0lHgPrb7FxL3fdEy6LJ/j2NE8u+AP/5cQ9SKb+JLH6V6UHDqJ+e0hXBkHP1wn1YDFjYCS9LBQsZDlDEg==",
"dev": true, "dev": true,
"license": "BSD-2-Clause", "license": "BSD-2-Clause",
"dependencies": { "dependencies": {
"@typescript-eslint/scope-manager": "8.1.0", "@typescript-eslint/scope-manager": "8.2.0",
"@typescript-eslint/types": "8.1.0", "@typescript-eslint/types": "8.2.0",
"@typescript-eslint/typescript-estree": "8.1.0", "@typescript-eslint/typescript-estree": "8.2.0",
"@typescript-eslint/visitor-keys": "8.1.0", "@typescript-eslint/visitor-keys": "8.2.0",
"debug": "^4.3.4" "debug": "^4.3.4"
}, },
"engines": { "engines": {
@ -1301,14 +1318,14 @@
} }
}, },
"node_modules/@typescript-eslint/scope-manager": { "node_modules/@typescript-eslint/scope-manager": {
"version": "8.1.0", "version": "8.2.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.1.0.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.2.0.tgz",
"integrity": "sha512-DsuOZQji687sQUjm4N6c9xABJa7fjvfIdjqpSIIVOgaENf2jFXiM9hIBZOL3hb6DHK9Nvd2d7zZnoMLf9e0OtQ==", "integrity": "sha512-OFn80B38yD6WwpoHU2Tz/fTz7CgFqInllBoC3WP+/jLbTb4gGPTy9HBSTsbDWkMdN55XlVU0mMDYAtgvlUspGw==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@typescript-eslint/types": "8.1.0", "@typescript-eslint/types": "8.2.0",
"@typescript-eslint/visitor-keys": "8.1.0" "@typescript-eslint/visitor-keys": "8.2.0"
}, },
"engines": { "engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0" "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@ -1319,14 +1336,14 @@
} }
}, },
"node_modules/@typescript-eslint/type-utils": { "node_modules/@typescript-eslint/type-utils": {
"version": "8.1.0", "version": "8.2.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.1.0.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.2.0.tgz",
"integrity": "sha512-oLYvTxljVvsMnldfl6jIKxTaU7ok7km0KDrwOt1RHYu6nxlhN3TIx8k5Q52L6wR33nOwDgM7VwW1fT1qMNfFIA==", "integrity": "sha512-g1CfXGFMQdT5S+0PSO0fvGXUaiSkl73U1n9LTK5aRAFnPlJ8dLKkXr4AaLFvPedW8lVDoMgLLE3JN98ZZfsj0w==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@typescript-eslint/typescript-estree": "8.1.0", "@typescript-eslint/typescript-estree": "8.2.0",
"@typescript-eslint/utils": "8.1.0", "@typescript-eslint/utils": "8.2.0",
"debug": "^4.3.4", "debug": "^4.3.4",
"ts-api-utils": "^1.3.0" "ts-api-utils": "^1.3.0"
}, },
@ -1344,9 +1361,9 @@
} }
}, },
"node_modules/@typescript-eslint/types": { "node_modules/@typescript-eslint/types": {
"version": "8.1.0", "version": "8.2.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.1.0.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.2.0.tgz",
"integrity": "sha512-q2/Bxa0gMOu/2/AKALI0tCKbG2zppccnRIRCW6BaaTlRVaPKft4oVYPp7WOPpcnsgbr0qROAVCVKCvIQ0tbWog==", "integrity": "sha512-6a9QSK396YqmiBKPkJtxsgZZZVjYQ6wQ/TlI0C65z7vInaETuC6HAHD98AGLC8DyIPqHytvNuS8bBVvNLKyqvQ==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"engines": { "engines": {
@ -1358,14 +1375,14 @@
} }
}, },
"node_modules/@typescript-eslint/typescript-estree": { "node_modules/@typescript-eslint/typescript-estree": {
"version": "8.1.0", "version": "8.2.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.1.0.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.2.0.tgz",
"integrity": "sha512-NTHhmufocEkMiAord/g++gWKb0Fr34e9AExBRdqgWdVBaKoei2dIyYKD9Q0jBnvfbEA5zaf8plUFMUH6kQ0vGg==", "integrity": "sha512-kiG4EDUT4dImplOsbh47B1QnNmXSoUqOjWDvCJw/o8LgfD0yr7k2uy54D5Wm0j4t71Ge1NkynGhpWdS0dEIAUA==",
"dev": true, "dev": true,
"license": "BSD-2-Clause", "license": "BSD-2-Clause",
"dependencies": { "dependencies": {
"@typescript-eslint/types": "8.1.0", "@typescript-eslint/types": "8.2.0",
"@typescript-eslint/visitor-keys": "8.1.0", "@typescript-eslint/visitor-keys": "8.2.0",
"debug": "^4.3.4", "debug": "^4.3.4",
"globby": "^11.1.0", "globby": "^11.1.0",
"is-glob": "^4.0.3", "is-glob": "^4.0.3",
@ -1413,16 +1430,16 @@
} }
}, },
"node_modules/@typescript-eslint/utils": { "node_modules/@typescript-eslint/utils": {
"version": "8.1.0", "version": "8.2.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.1.0.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.2.0.tgz",
"integrity": "sha512-ypRueFNKTIFwqPeJBfeIpxZ895PQhNyH4YID6js0UoBImWYoSjBsahUn9KMiJXh94uOjVBgHD9AmkyPsPnFwJA==", "integrity": "sha512-O46eaYKDlV3TvAVDNcoDzd5N550ckSe8G4phko++OCSC1dYIb9LTc3HDGYdWqWIAT5qDUKphO6sd9RrpIJJPfg==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@eslint-community/eslint-utils": "^4.4.0", "@eslint-community/eslint-utils": "^4.4.0",
"@typescript-eslint/scope-manager": "8.1.0", "@typescript-eslint/scope-manager": "8.2.0",
"@typescript-eslint/types": "8.1.0", "@typescript-eslint/types": "8.2.0",
"@typescript-eslint/typescript-estree": "8.1.0" "@typescript-eslint/typescript-estree": "8.2.0"
}, },
"engines": { "engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0" "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@ -1436,13 +1453,13 @@
} }
}, },
"node_modules/@typescript-eslint/visitor-keys": { "node_modules/@typescript-eslint/visitor-keys": {
"version": "8.1.0", "version": "8.2.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.1.0.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.2.0.tgz",
"integrity": "sha512-ba0lNI19awqZ5ZNKh6wCModMwoZs457StTebQ0q1NP58zSi2F6MOZRXwfKZy+jB78JNJ/WH8GSh2IQNzXX8Nag==", "integrity": "sha512-sbgsPMW9yLvS7IhCi8IpuK1oBmtbWUNP+hBdwl/I9nzqVsszGnNGti5r9dUtF5RLivHUFFIdRvLiTsPhzSyJ3Q==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@typescript-eslint/types": "8.1.0", "@typescript-eslint/types": "8.2.0",
"eslint-visitor-keys": "^3.4.3" "eslint-visitor-keys": "^3.4.3"
}, },
"engines": { "engines": {
@ -3300,38 +3317,6 @@
"node": ">=0.8.0" "node": ">=0.8.0"
} }
}, },
"node_modules/mv/node_modules/glob": {
"version": "6.0.4",
"resolved": "https://registry.npmjs.org/glob/-/glob-6.0.4.tgz",
"integrity": "sha512-MKZeRNyYZAVVVG1oZeLaWie1uweH40m9AZwIwxyPbTSX4hHrVYSzLg0Ro5Z5R7XKkIX+Cc6oD1rqeDJnwsB8/A==",
"deprecated": "Glob versions prior to v9 are no longer supported",
"license": "ISC",
"optional": true,
"dependencies": {
"inflight": "^1.0.4",
"inherits": "2",
"minimatch": "2 || 3",
"once": "^1.3.0",
"path-is-absolute": "^1.0.0"
},
"engines": {
"node": "*"
}
},
"node_modules/mv/node_modules/rimraf": {
"version": "2.4.5",
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.4.5.tgz",
"integrity": "sha512-J5xnxTyqaiw06JjMftq7L9ouA448dw/E7dKghkP9WpKNuwmARNNg+Gk8/u5ryb9N/Yo2+z3MCwuqFK/+qPOPfQ==",
"deprecated": "Rimraf versions prior to v4 are no longer supported",
"license": "ISC",
"optional": true,
"dependencies": {
"glob": "^6.0.1"
},
"bin": {
"rimraf": "bin.js"
}
},
"node_modules/nan": { "node_modules/nan": {
"version": "2.20.0", "version": "2.20.0",
"resolved": "https://registry.npmjs.org/nan/-/nan-2.20.0.tgz", "resolved": "https://registry.npmjs.org/nan/-/nan-2.20.0.tgz",
@ -3643,6 +3628,53 @@
"url": "https://github.com/sponsors/jonschlinkert" "url": "https://github.com/sponsors/jonschlinkert"
} }
}, },
"node_modules/playwright": {
"version": "1.46.1",
"resolved": "https://registry.npmjs.org/playwright/-/playwright-1.46.1.tgz",
"integrity": "sha512-oPcr1yqoXLCkgKtD5eNUPLiN40rYEM39odNpIb6VE6S7/15gJmA1NzVv6zJYusV0e7tzvkU/utBFNa/Kpxmwng==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
"playwright-core": "1.46.1"
},
"bin": {
"playwright": "cli.js"
},
"engines": {
"node": ">=18"
},
"optionalDependencies": {
"fsevents": "2.3.2"
}
},
"node_modules/playwright-core": {
"version": "1.46.1",
"resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.46.1.tgz",
"integrity": "sha512-h9LqIQaAv+CYvWzsZ+h3RsrqCStkBHlgo6/TJlFst3cOTlLghBQlJwPOZKQJTKNaD3QIB7aAVQ+gfWbN3NXB7A==",
"dev": true,
"license": "Apache-2.0",
"bin": {
"playwright-core": "cli.js"
},
"engines": {
"node": ">=18"
}
},
"node_modules/playwright/node_modules/fsevents": {
"version": "2.3.2",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
"integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
"dev": true,
"hasInstallScript": true,
"license": "MIT",
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
}
},
"node_modules/postcss": { "node_modules/postcss": {
"version": "8.4.41", "version": "8.4.41",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.41.tgz", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.41.tgz",
@ -3881,39 +3913,35 @@
} }
}, },
"node_modules/rimraf": { "node_modules/rimraf": {
"version": "2.7.1", "version": "2.4.5",
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.4.5.tgz",
"integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", "integrity": "sha512-J5xnxTyqaiw06JjMftq7L9ouA448dw/E7dKghkP9WpKNuwmARNNg+Gk8/u5ryb9N/Yo2+z3MCwuqFK/+qPOPfQ==",
"deprecated": "Rimraf versions prior to v4 are no longer supported", "deprecated": "Rimraf versions prior to v4 are no longer supported",
"dev": true,
"license": "ISC", "license": "ISC",
"optional": true,
"dependencies": { "dependencies": {
"glob": "^7.1.3" "glob": "^6.0.1"
}, },
"bin": { "bin": {
"rimraf": "bin.js" "rimraf": "bin.js"
} }
}, },
"node_modules/rimraf/node_modules/glob": { "node_modules/rimraf/node_modules/glob": {
"version": "7.2.3", "version": "6.0.4",
"resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", "resolved": "https://registry.npmjs.org/glob/-/glob-6.0.4.tgz",
"integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", "integrity": "sha512-MKZeRNyYZAVVVG1oZeLaWie1uweH40m9AZwIwxyPbTSX4hHrVYSzLg0Ro5Z5R7XKkIX+Cc6oD1rqeDJnwsB8/A==",
"deprecated": "Glob versions prior to v9 are no longer supported", "deprecated": "Glob versions prior to v9 are no longer supported",
"dev": true,
"license": "ISC", "license": "ISC",
"optional": true,
"dependencies": { "dependencies": {
"fs.realpath": "^1.0.0",
"inflight": "^1.0.4", "inflight": "^1.0.4",
"inherits": "2", "inherits": "2",
"minimatch": "^3.1.1", "minimatch": "2 || 3",
"once": "^1.3.0", "once": "^1.3.0",
"path-is-absolute": "^1.0.0" "path-is-absolute": "^1.0.0"
}, },
"engines": { "engines": {
"node": "*" "node": "*"
},
"funding": {
"url": "https://github.com/sponsors/isaacs"
} }
}, },
"node_modules/rollup": { "node_modules/rollup": {
@ -4009,6 +4037,42 @@
"rimraf": "^2.5.2" "rimraf": "^2.5.2"
} }
}, },
"node_modules/sander/node_modules/glob": {
"version": "7.2.3",
"resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
"integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
"deprecated": "Glob versions prior to v9 are no longer supported",
"dev": true,
"license": "ISC",
"dependencies": {
"fs.realpath": "^1.0.0",
"inflight": "^1.0.4",
"inherits": "2",
"minimatch": "^3.1.1",
"once": "^1.3.0",
"path-is-absolute": "^1.0.0"
},
"engines": {
"node": "*"
},
"funding": {
"url": "https://github.com/sponsors/isaacs"
}
},
"node_modules/sander/node_modules/rimraf": {
"version": "2.7.1",
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz",
"integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==",
"deprecated": "Rimraf versions prior to v4 are no longer supported",
"dev": true,
"license": "ISC",
"dependencies": {
"glob": "^7.1.3"
},
"bin": {
"rimraf": "bin.js"
}
},
"node_modules/sass": { "node_modules/sass": {
"version": "1.77.8", "version": "1.77.8",
"resolved": "https://registry.npmjs.org/sass/-/sass-1.77.8.tgz", "resolved": "https://registry.npmjs.org/sass/-/sass-1.77.8.tgz",
@ -4324,9 +4388,9 @@
} }
}, },
"node_modules/svelte": { "node_modules/svelte": {
"version": "4.2.18", "version": "4.2.19",
"resolved": "https://registry.npmjs.org/svelte/-/svelte-4.2.18.tgz", "resolved": "https://registry.npmjs.org/svelte/-/svelte-4.2.19.tgz",
"integrity": "sha512-d0FdzYIiAePqRJEb90WlJDkjUEx42xhivxN8muUBmfZnP+tzUgz12DJ2hRJi8sIHCME7jeK1PTMgKPSfTd8JrA==", "integrity": "sha512-IY1rnGr6izd10B0A8LqsBfmlT5OILVuZ7XsI0vdGPEvuonFV7NYEUK4dAkm9Zg2q0Um92kYjTpS1CAP3Nh/KWw==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
@ -4350,9 +4414,9 @@
} }
}, },
"node_modules/svelte-check": { "node_modules/svelte-check": {
"version": "3.8.5", "version": "3.8.6",
"resolved": "https://registry.npmjs.org/svelte-check/-/svelte-check-3.8.5.tgz", "resolved": "https://registry.npmjs.org/svelte-check/-/svelte-check-3.8.6.tgz",
"integrity": "sha512-3OGGgr9+bJ/+1nbPgsvulkLC48xBsqsgtc8Wam281H4G9F5v3mYGa2bHRsPuwHC5brKl4AxJH95QF73kmfihGQ==", "integrity": "sha512-ij0u4Lw/sOTREP13BdWZjiXD/BlHE6/e2e34XzmVmsp5IN4kVa3PWP65NM32JAgwjZlwBg/+JtiNV1MM8khu0Q==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
@ -4668,15 +4732,15 @@
} }
}, },
"node_modules/typescript-eslint": { "node_modules/typescript-eslint": {
"version": "8.1.0", "version": "8.2.0",
"resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.1.0.tgz", "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.2.0.tgz",
"integrity": "sha512-prB2U3jXPJLpo1iVLN338Lvolh6OrcCZO+9Yv6AR+tvegPPptYCDBIHiEEUdqRi8gAv2bXNKfMUrgAd2ejn/ow==", "integrity": "sha512-DmnqaPcML0xYwUzgNbM1XaKXpEb7BShYf2P1tkUmmcl8hyeG7Pj08Er7R9bNy6AufabywzJcOybQAtnD/c9DGw==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@typescript-eslint/eslint-plugin": "8.1.0", "@typescript-eslint/eslint-plugin": "8.2.0",
"@typescript-eslint/parser": "8.1.0", "@typescript-eslint/parser": "8.2.0",
"@typescript-eslint/utils": "8.1.0" "@typescript-eslint/utils": "8.2.0"
}, },
"engines": { "engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0" "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@ -4692,9 +4756,9 @@
} }
}, },
"node_modules/undici-types": { "node_modules/undici-types": {
"version": "6.13.0", "version": "6.19.6",
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.13.0.tgz", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.6.tgz",
"integrity": "sha512-xtFJHudx8S2DSoujjMd1WeWvn7KKWFRESZTMeL1RptAYERu29D6jphMjjY+vn96jvN3kVPDNxU/E13VTaXj6jg==", "integrity": "sha512-e/vggGopEfTKSvj4ihnOLTsqhrKRN3LeO6qSN/GxohhuRv8qH9bNQ4B8W7e/vFL+0XTnmHPB4/kegunZGA4Org==",
"dev": true, "dev": true,
"license": "MIT" "license": "MIT"
}, },
@ -4729,15 +4793,15 @@
} }
}, },
"node_modules/vite": { "node_modules/vite": {
"version": "5.4.1", "version": "5.4.2",
"resolved": "https://registry.npmjs.org/vite/-/vite-5.4.1.tgz", "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.2.tgz",
"integrity": "sha512-1oE6yuNXssjrZdblI9AfBbHCC41nnyoVoEZxQnID6yvQZAFBzxxkqoFLtHUMkYunL8hwOLEjgTuxpkRxvba3kA==", "integrity": "sha512-dDrQTRHp5C1fTFzcSaMxjk6vdpKvT+2/mIdE07Gw2ykehT49O0z/VHS3zZ8iV/Gh8BJJKHWOe5RjaNrW5xf/GA==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"esbuild": "^0.21.3", "esbuild": "^0.21.3",
"postcss": "^8.4.41", "postcss": "^8.4.41",
"rollup": "^4.13.0" "rollup": "^4.20.0"
}, },
"bin": { "bin": {
"vite": "bin/vite.js" "vite": "bin/vite.js"

View File

@ -1,18 +1,21 @@
{ {
"name": "fotochallenge", "name": "fotochallenge",
"version": "0.0.6", "version": "0.0.7",
"private": true, "private": true,
"scripts": { "scripts": {
"dev": "vite dev", "dev": "vite dev",
"build": "vite build", "build": "vite build",
"preview": "vite preview", "preview": "vite preview",
"test": "vitest", "test": "npm run test:integration && npm run test:unit",
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json", "check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch", "check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
"lint": "prettier --check . && eslint .", "lint": "prettier --check . && eslint .",
"format": "prettier --write ." "format": "prettier --write .",
"test:integration": "playwright test",
"test:unit": "vitest"
}, },
"devDependencies": { "devDependencies": {
"@playwright/test": "^1.46.1",
"@sveltejs/adapter-auto": "^3.0.0", "@sveltejs/adapter-auto": "^3.0.0",
"@sveltejs/adapter-node": "^5.2.0", "@sveltejs/adapter-node": "^5.2.0",
"@sveltejs/kit": "^2.0.0", "@sveltejs/kit": "^2.0.0",

12
playwright.config.ts Normal file
View File

@ -0,0 +1,12 @@
import type { PlaywrightTestConfig } from '@playwright/test';
const config: PlaywrightTestConfig = {
webServer: {
command: 'npm run build && npm run preview',
port: 4173,
},
testDir: 'tests',
testMatch: /(.+\.)?(test|spec)\.[jt]s/,
};
export default config;

View File

@ -1,11 +1,9 @@
import { log, timedExecution } from '$lib'; import { log, timedExecution, requestIdHeader } from '$lib';
import { validate, v7 as uuidv7 } from 'uuid'; import { validate, v7 as uuidv7 } from 'uuid';
import type { Handle } from '@sveltejs/kit';
const requestIdHeader = 'x-request-id'; export const handle: Handle = async ({ event, resolve }) => {
// use incoming requestId, if it is a valid uuid, else generate one
/** @type {import('@sveltejs/kit').Handle} */
export async function handle({ event, resolve }) {
// use incomming requestId, if it is a valid uuid, else generate one
const reqIdFromRequest = event.request.headers.get(requestIdHeader); const reqIdFromRequest = event.request.headers.get(requestIdHeader);
const { requestId, fromRequest } = const { requestId, fromRequest } =
reqIdFromRequest && validate(reqIdFromRequest) reqIdFromRequest && validate(reqIdFromRequest)
@ -20,7 +18,7 @@ export async function handle({ event, resolve }) {
clientIP: event.getClientAddress(), clientIP: event.getClientAddress(),
}; };
if (fromRequest) { if (fromRequest) {
log.trace(context, 'using incomming request-id'); log.trace(context, 'using incoming request-id');
} }
// make requestId available to handlers // make requestId available to handlers
@ -42,4 +40,4 @@ export async function handle({ event, resolve }) {
); );
return response; return response;
} };

View File

@ -29,6 +29,8 @@ if (!('STORAGE_PATH' in process.env)) {
} }
export const storagePath: string = process.env.STORAGE_PATH ?? defaultPath; export const storagePath: string = process.env.STORAGE_PATH ?? defaultPath;
export const requestIdHeader = 'x-request-id';
export default safePath; export default safePath;
export async function timedExecution<T>( export async function timedExecution<T>(

View File

@ -0,0 +1,50 @@
import { storagePath } from '$lib';
import { json } from '@sveltejs/kit';
import { stat, access, constants } from 'fs/promises';
import type { RequestHandler } from './$types';
type Status = 'OK' | 'ERROR';
type Result = { status: Status; checks: Checks };
type Checks = { [key: string]: CheckResult };
type CheckResult = true | string;
const fileExists = async (path: string) => !!(await stat(path).catch(() => false));
const isDirectory = async (path: string) =>
!!(await stat(path)
.then((s) => s.isDirectory())
.catch(() => false));
const isWritable = async (path: string) =>
await access(path, constants.W_OK)
.then(() => true)
.catch(() => false);
const checkStoragePath = async (path: string): Promise<CheckResult> => {
if (!fileExists(path)) {
return '`STORAGE_PATH` does not exist';
} else if (!isDirectory(path)) {
return '`STORAGE_PATH` is not a directory';
} else if (!isWritable(path)) {
return '`STORAGE_PATH` is not writable';
}
return true;
};
export const GET: RequestHandler = async () => {
const storagePathResult = await checkStoragePath(storagePath);
const checks: Checks = {
storagePath: storagePathResult,
};
const healthy = Object.values(checks)
.map((r) => (r === true ? true : false))
.reduce((prev, next) => prev && next, true);
const status = healthy ? 'OK' : 'ERROR';
const result: Result = { status, checks };
const httpStatus = healthy ? 200 : 500;
return json(result, { status: httpStatus, headers: { healthy: healthy.toString() } });
};

View File

@ -1,12 +1,12 @@
import { storagePath } from '$lib'; import { storagePath } from '$lib';
import { json } from '@sveltejs/kit'; import { json } from '@sveltejs/kit';
import { readdirSync, statSync } from 'fs'; import { readdirSync, statSync } from 'fs';
import type { RequestHandler } from './$types';
export function GET() { export const GET: RequestHandler = () => {
const names = readdirSync(storagePath).filter((f) => const names = readdirSync(storagePath)
statSync(`${storagePath}/${f}`).isDirectory() .filter((f) => statSync(`${storagePath}/${f}`).isDirectory())
); .toSorted();
names.sort();
return json(names); return json(names);
} };

13
tests/health.test.ts Normal file
View File

@ -0,0 +1,13 @@
import { expect, test } from '@playwright/test';
test('healthy test', async ({ playwright }) => {
const context = await playwright.request.newContext();
const response = await context.get('health');
await expect(response.status()).toBe(200);
await expect(response.headers()).toHaveProperty('healthy');
await expect(response.headers()['healthy']).toBe('true');
const body = await response.json();
await expect(body).toHaveProperty('status');
await expect(body['status']).toBe('OK');
});

10
tests/page.test.ts Normal file
View File

@ -0,0 +1,10 @@
import { expect, test } from '@playwright/test';
test('contains header text', async ({ playwright }) => {
const context = await playwright.request.newContext();
const response = await context.get('');
await expect(response.status()).toBe(200);
const body = (await response.body()).toString();
await expect(body).toContain('Gabi und Hannes Fotochallenge');
});

37
tests/requestid.test.ts Normal file
View File

@ -0,0 +1,37 @@
import { expect, test } from '@playwright/test';
import { validate, NIL } from 'uuid';
import { requestIdHeader } from '$lib';
test('response contains request-id header', async ({ playwright }) => {
const context = await playwright.request.newContext();
const response = await context.get('health');
const headers = response.headers();
await expect(headers).toHaveProperty(requestIdHeader);
});
test('request-id is valid uuid', async ({ playwright }) => {
const context = await playwright.request.newContext();
const response = await context.get('health');
const headers = response.headers();
const requestId = headers[requestIdHeader];
await expect(validate(requestId)).toBe(true);
});
test('reuse valid incoming uuid', async ({ playwright }) => {
const context = await playwright.request.newContext();
const response = await context.get('health', { headers: { [requestIdHeader]: NIL } });
const headers = response.headers();
const requestId = headers[requestIdHeader];
await expect(requestId).toBe(NIL);
});
test('ignore invalid incoming uuid', async ({ playwright }) => {
const invalid = '00000000-0000-0000-0000-00000000000z';
const context = await playwright.request.newContext();
const response = await context.get('health', { headers: { [requestIdHeader]: invalid } });
const headers = response.headers();
const requestId = headers[requestIdHeader];
await expect(requestId).not.toBe(invalid);
});