Compare commits
16 Commits
Author | SHA1 | Date | |
---|---|---|---|
37d6cd848d
|
|||
1aff6554b5
|
|||
a6359d9516
|
|||
6ab52c1830
|
|||
cfb5564830
|
|||
48948a180a
|
|||
1557d2cca9
|
|||
019d1d2e62
|
|||
1b4bb1d2e2
|
|||
824852fe7a
|
|||
4e2b1b74a4
|
|||
0aa7a18f05
|
|||
10d6be5f82
|
|||
6615e2788a
|
|||
1f846bd5fe | |||
da7b056095 |
@ -1,7 +1,7 @@
|
||||
{
|
||||
"useTabs": true,
|
||||
"singleQuote": true,
|
||||
"trailingComma": "none",
|
||||
"trailingComma": "es5",
|
||||
"printWidth": 100,
|
||||
"plugins": ["prettier-plugin-svelte"],
|
||||
"overrides": [{ "files": "*.svelte", "options": { "parser": "svelte" } }]
|
||||
|
@ -1,4 +1,4 @@
|
||||
FROM node:21-alpine AS builder
|
||||
FROM node:22-alpine AS builder
|
||||
|
||||
WORKDIR /app
|
||||
COPY package.json package-lock.json ./
|
||||
@ -7,11 +7,12 @@ RUN npm ci
|
||||
COPY . .
|
||||
RUN npm run build
|
||||
|
||||
FROM node:21-alpine
|
||||
FROM node:22-alpine
|
||||
WORKDIR /app
|
||||
RUN chown -R node:node /app
|
||||
USER node:node
|
||||
COPY ./container/entrypoint.sh /entrypoint.sh
|
||||
COPY package.json .
|
||||
COPY --from=builder /app/node_modules ./node_modules
|
||||
COPY --from=builder /app/build ./build
|
||||
ENTRYPOINT ["sh", "/entrypoint.sh"]
|
||||
|
@ -15,19 +15,19 @@ export default [
|
||||
languageOptions: {
|
||||
globals: {
|
||||
...globals.browser,
|
||||
...globals.node
|
||||
}
|
||||
}
|
||||
...globals.node,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
files: ['**/*.svelte'],
|
||||
languageOptions: {
|
||||
parserOptions: {
|
||||
parser: ts.parser
|
||||
}
|
||||
}
|
||||
parser: ts.parser,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
ignores: ['build/', '.svelte-kit/', 'dist/']
|
||||
}
|
||||
ignores: ['build/', '.svelte-kit/', 'dist/'],
|
||||
},
|
||||
];
|
||||
|
189
package-lock.json
generated
189
package-lock.json
generated
@ -1,19 +1,25 @@
|
||||
{
|
||||
"name": "fotochallenge",
|
||||
"version": "0.0.5",
|
||||
"version": "0.0.6",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "fotochallenge",
|
||||
"version": "0.0.5",
|
||||
"version": "0.0.6",
|
||||
"dependencies": {
|
||||
"bunyan": "^1.8.15",
|
||||
"uuid": "^10.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@sveltejs/adapter-auto": "^3.0.0",
|
||||
"@sveltejs/adapter-node": "^5.2.0",
|
||||
"@sveltejs/kit": "^2.0.0",
|
||||
"@sveltejs/vite-plugin-svelte": "^3.0.0",
|
||||
"@types/bunyan": "^1.8.11",
|
||||
"@types/eslint": "^9.0.0",
|
||||
"@types/node": "^20.14.11",
|
||||
"@types/uuid": "^10.0.0",
|
||||
"bulma": "^1.0.1",
|
||||
"eslint": "^9.0.0",
|
||||
"eslint-config-prettier": "^9.1.0",
|
||||
@ -1158,6 +1164,16 @@
|
||||
"vite": "^5.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/bunyan": {
|
||||
"version": "1.8.11",
|
||||
"resolved": "https://registry.npmjs.org/@types/bunyan/-/bunyan-1.8.11.tgz",
|
||||
"integrity": "sha512-758fRH7umIMk5qt5ELmRMff4mLDlN+xyYzC+dkPTdKwbSkJFvz6xwyScrytPU0QIBbRRwbiE8/BIg8bpajerNQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/cookie": {
|
||||
"version": "0.6.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.6.0.tgz",
|
||||
@ -1191,13 +1207,13 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/node": {
|
||||
"version": "20.14.15",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.14.15.tgz",
|
||||
"integrity": "sha512-Fz1xDMCF/B00/tYSVMlmK7hVeLh7jE5f3B7X1/hmV0MJBwE27KlS7EvD/Yp+z1lm8mVhwV5w+n8jOZG8AfTlKw==",
|
||||
"version": "20.15.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.15.0.tgz",
|
||||
"integrity": "sha512-eQf4OkH6gA9v1W0iEpht/neozCsZKMTK+C4cU6/fv7wtJCCL8LEQ4hie2Ln8ZP/0YYM2xGj7//f8xyqItkJ6QA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"undici-types": "~5.26.4"
|
||||
"undici-types": "~6.13.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/pug": {
|
||||
@ -1214,6 +1230,13 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/uuid": {
|
||||
"version": "10.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-10.0.0.tgz",
|
||||
"integrity": "sha512-7gqG38EyHgyP1S+7+xomFtL+ZNHcKv6DwNaCZmJmo1vgMugyF3TCnXVg4t1uk89mLNwnLtnY3TpOpCOyp1/xHQ==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@typescript-eslint/eslint-plugin": {
|
||||
"version": "8.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.1.0.tgz",
|
||||
@ -1671,7 +1694,7 @@
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
|
||||
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
|
||||
"dev": true,
|
||||
"devOptional": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/binary-extensions": {
|
||||
@ -1691,7 +1714,7 @@
|
||||
"version": "1.1.11",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
|
||||
"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
|
||||
"dev": true,
|
||||
"devOptional": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"balanced-match": "^1.0.0",
|
||||
@ -1741,6 +1764,24 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/bunyan": {
|
||||
"version": "1.8.15",
|
||||
"resolved": "https://registry.npmjs.org/bunyan/-/bunyan-1.8.15.tgz",
|
||||
"integrity": "sha512-0tECWShh6wUysgucJcBAoYegf3JJoZWibxdqhTm7OHPeT42qdjkZ29QCMcKwbgU1kiH+auSIasNRXMLWXafXig==",
|
||||
"engines": [
|
||||
"node >=0.10.0"
|
||||
],
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
"bunyan": "bin/bunyan"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"dtrace-provider": "~0.8",
|
||||
"moment": "^2.19.3",
|
||||
"mv": "~2",
|
||||
"safe-json-stringify": "~1"
|
||||
}
|
||||
},
|
||||
"node_modules/cac": {
|
||||
"version": "6.7.14",
|
||||
"resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz",
|
||||
@ -1898,7 +1939,7 @@
|
||||
"version": "0.0.1",
|
||||
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
|
||||
"integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
|
||||
"dev": true,
|
||||
"devOptional": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/cookie": {
|
||||
@ -2038,6 +2079,20 @@
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/dtrace-provider": {
|
||||
"version": "0.8.8",
|
||||
"resolved": "https://registry.npmjs.org/dtrace-provider/-/dtrace-provider-0.8.8.tgz",
|
||||
"integrity": "sha512-b7Z7cNtHPhH9EJhNNbbeqTcXB8LGFFZhq1PGgEvpeHlzd36bhbdTWoE/Ba/YguqpBSlAPKnARWhVlhunCMwfxg==",
|
||||
"hasInstallScript": true,
|
||||
"license": "BSD-2-Clause",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"nan": "^2.14.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.10"
|
||||
}
|
||||
},
|
||||
"node_modules/eastasianwidth": {
|
||||
"version": "0.2.0",
|
||||
"resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz",
|
||||
@ -2779,7 +2834,7 @@
|
||||
"resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
|
||||
"integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==",
|
||||
"deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.",
|
||||
"dev": true,
|
||||
"devOptional": true,
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"once": "^1.3.0",
|
||||
@ -2790,7 +2845,7 @@
|
||||
"version": "2.0.4",
|
||||
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
|
||||
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
|
||||
"dev": true,
|
||||
"devOptional": true,
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/is-binary-path": {
|
||||
@ -3151,7 +3206,7 @@
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
|
||||
"integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
|
||||
"dev": true,
|
||||
"devOptional": true,
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"brace-expansion": "^1.1.7"
|
||||
@ -3164,7 +3219,7 @@
|
||||
"version": "1.2.8",
|
||||
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz",
|
||||
"integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==",
|
||||
"dev": true,
|
||||
"devOptional": true,
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
@ -3184,7 +3239,7 @@
|
||||
"version": "0.5.6",
|
||||
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz",
|
||||
"integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==",
|
||||
"dev": true,
|
||||
"devOptional": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"minimist": "^1.2.6"
|
||||
@ -3193,6 +3248,16 @@
|
||||
"mkdirp": "bin/cmd.js"
|
||||
}
|
||||
},
|
||||
"node_modules/moment": {
|
||||
"version": "2.30.1",
|
||||
"resolved": "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz",
|
||||
"integrity": "sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==",
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"engines": {
|
||||
"node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/mri": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/mri/-/mri-1.2.0.tgz",
|
||||
@ -3220,6 +3285,60 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/mv": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/mv/-/mv-2.1.1.tgz",
|
||||
"integrity": "sha512-at/ZndSy3xEGJ8i0ygALh8ru9qy7gWW1cmkaqBN29JmMlIvM//MEO9y1sk/avxuwnPcfhkejkLsuPxH81BrkSg==",
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"mkdirp": "~0.5.1",
|
||||
"ncp": "~2.0.0",
|
||||
"rimraf": "~2.4.0"
|
||||
},
|
||||
"engines": {
|
||||
"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": {
|
||||
"version": "2.20.0",
|
||||
"resolved": "https://registry.npmjs.org/nan/-/nan-2.20.0.tgz",
|
||||
"integrity": "sha512-bk3gXBZDGILuuo/6sKtr0DQmSThYHLtNCdSdXk9YkxD/jK6X2vmCyyXBBxyqZ4XcnzTyYEAThfX3DCEnLf6igw==",
|
||||
"license": "MIT",
|
||||
"optional": true
|
||||
},
|
||||
"node_modules/nanoid": {
|
||||
"version": "3.3.7",
|
||||
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz",
|
||||
@ -3246,6 +3365,16 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/ncp": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/ncp/-/ncp-2.0.0.tgz",
|
||||
"integrity": "sha512-zIdGUrPRFTUELUvr3Gmc7KZ2Sw/h1PiVM0Af/oHB6zgnV1ikqSfRk+TOufi79aHYCW3NiOXmr1BP5nWbzojLaA==",
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"bin": {
|
||||
"ncp": "bin/ncp"
|
||||
}
|
||||
},
|
||||
"node_modules/normalize-path": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
|
||||
@ -3289,7 +3418,7 @@
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
|
||||
"integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
|
||||
"dev": true,
|
||||
"devOptional": true,
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"wrappy": "1"
|
||||
@ -3395,7 +3524,7 @@
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
|
||||
"integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==",
|
||||
"dev": true,
|
||||
"devOptional": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
@ -3860,6 +3989,13 @@
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/safe-json-stringify": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/safe-json-stringify/-/safe-json-stringify-1.2.0.tgz",
|
||||
"integrity": "sha512-gH8eh2nZudPQO6TytOvbxnuhYBOvDBBLW52tz5q6X58lJcd/tkmqFR+5Z9adS8aJtURSXWThWy/xJtJwixErvg==",
|
||||
"license": "MIT",
|
||||
"optional": true
|
||||
},
|
||||
"node_modules/sander": {
|
||||
"version": "0.5.1",
|
||||
"resolved": "https://registry.npmjs.org/sander/-/sander-0.5.1.tgz",
|
||||
@ -4556,9 +4692,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/undici-types": {
|
||||
"version": "5.26.5",
|
||||
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz",
|
||||
"integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==",
|
||||
"version": "6.13.0",
|
||||
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.13.0.tgz",
|
||||
"integrity": "sha512-xtFJHudx8S2DSoujjMd1WeWvn7KKWFRESZTMeL1RptAYERu29D6jphMjjY+vn96jvN3kVPDNxU/E13VTaXj6jg==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
@ -4579,6 +4715,19 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/uuid": {
|
||||
"version": "10.0.0",
|
||||
"resolved": "https://registry.npmjs.org/uuid/-/uuid-10.0.0.tgz",
|
||||
"integrity": "sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==",
|
||||
"funding": [
|
||||
"https://github.com/sponsors/broofa",
|
||||
"https://github.com/sponsors/ctavan"
|
||||
],
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
"uuid": "dist/bin/uuid"
|
||||
}
|
||||
},
|
||||
"node_modules/vite": {
|
||||
"version": "5.4.1",
|
||||
"resolved": "https://registry.npmjs.org/vite/-/vite-5.4.1.tgz",
|
||||
@ -4890,7 +5039,7 @@
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
|
||||
"integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
|
||||
"dev": true,
|
||||
"devOptional": true,
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/yaml": {
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "fotochallenge",
|
||||
"version": "0.0.5",
|
||||
"version": "0.0.6",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "vite dev",
|
||||
@ -17,8 +17,10 @@
|
||||
"@sveltejs/adapter-node": "^5.2.0",
|
||||
"@sveltejs/kit": "^2.0.0",
|
||||
"@sveltejs/vite-plugin-svelte": "^3.0.0",
|
||||
"@types/bunyan": "^1.8.11",
|
||||
"@types/eslint": "^9.0.0",
|
||||
"@types/node": "^20.14.11",
|
||||
"@types/uuid": "^10.0.0",
|
||||
"bulma": "^1.0.1",
|
||||
"eslint": "^9.0.0",
|
||||
"eslint-config-prettier": "^9.1.0",
|
||||
@ -39,5 +41,9 @@
|
||||
"type": "module",
|
||||
"engines": {
|
||||
"node": ">20"
|
||||
},
|
||||
"dependencies": {
|
||||
"bunyan": "^1.8.15",
|
||||
"uuid": "^10.0.0"
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
{
|
||||
$schema: 'https://docs.renovatebot.com/renovate-schema.json',
|
||||
extends: ['local>renovate-bot/renovate-config']
|
||||
extends: ['local>renovate-bot/renovate-config'],
|
||||
}
|
||||
|
6
src/app.d.ts
vendored
6
src/app.d.ts
vendored
@ -9,3 +9,9 @@ declare global {
|
||||
// interface Platform {}
|
||||
}
|
||||
}
|
||||
|
||||
declare namespace App {
|
||||
interface Locals {
|
||||
requestId: string;
|
||||
}
|
||||
}
|
||||
|
45
src/hooks.server.ts
Normal file
45
src/hooks.server.ts
Normal file
@ -0,0 +1,45 @@
|
||||
import { log, timedExecution } from '$lib';
|
||||
import { validate, v7 as uuidv7 } from 'uuid';
|
||||
|
||||
const requestIdHeader = 'x-request-id';
|
||||
|
||||
/** @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 { requestId, fromRequest } =
|
||||
reqIdFromRequest && validate(reqIdFromRequest)
|
||||
? { requestId: reqIdFromRequest, fromRequest: true }
|
||||
: { requestId: uuidv7(), fromRequest: false };
|
||||
|
||||
const context = {
|
||||
requestId,
|
||||
route: event.route.id,
|
||||
method: event.request.method,
|
||||
userAgent: event.request.headers.get('user-agent'),
|
||||
clientIP: event.getClientAddress(),
|
||||
};
|
||||
if (fromRequest) {
|
||||
log.trace(context, 'using incomming request-id');
|
||||
}
|
||||
|
||||
// make requestId available to handlers
|
||||
event.locals.requestId = requestId;
|
||||
|
||||
const { executionTime, result: response } = await timedExecution(async () => {
|
||||
return await resolve(event);
|
||||
});
|
||||
response.headers.set(requestIdHeader, requestId);
|
||||
|
||||
log.info(
|
||||
{
|
||||
executionTime: `${executionTime}ms`,
|
||||
status: response.status,
|
||||
size: response.headers.get('content-length'),
|
||||
...context,
|
||||
},
|
||||
'finished request'
|
||||
);
|
||||
|
||||
return response;
|
||||
}
|
@ -17,4 +17,16 @@ describe('safe path', () => {
|
||||
it('accept happy path', () => {
|
||||
expect(safePath('./uplodas', 'foobar')).toBe(true);
|
||||
});
|
||||
|
||||
it('accept names starting with `..`', () => {
|
||||
expect(safePath('./uplodas', '..foobar')).toBe(true);
|
||||
});
|
||||
|
||||
it('accept names ending with `..`', () => {
|
||||
expect(safePath('./uplodas', 'foobar..')).toBe(true);
|
||||
});
|
||||
|
||||
it('accept names starting and ending with `..`', () => {
|
||||
expect(safePath('./uplodas', '..foobar..')).toBe(true);
|
||||
});
|
||||
});
|
||||
|
@ -1,5 +1,13 @@
|
||||
// place files you want to import through the `$lib` alias in this folder.
|
||||
import path from 'path';
|
||||
import bunyan from 'bunyan';
|
||||
import type { MaybePromise } from '@sveltejs/kit';
|
||||
|
||||
export const log = bunyan.createLogger({
|
||||
name: 'fotochallenge',
|
||||
level: process.env.NODE_ENV === 'production' ? 'info' : 'debug',
|
||||
src: true,
|
||||
});
|
||||
|
||||
function safePath(basePath: string, name: string): boolean {
|
||||
const fullPath = `${basePath}/${name}`;
|
||||
@ -7,9 +15,9 @@ function safePath(basePath: string, name: string): boolean {
|
||||
return (
|
||||
!!relative &&
|
||||
// does move out of `basePath`
|
||||
!relative.startsWith('..') &&
|
||||
!relative.startsWith(`..${path.sep}`) &&
|
||||
// exactly one layer deep, e.g. no `./uplodas/foo/bar`
|
||||
!relative.includes('/') &&
|
||||
!relative.includes(path.sep) &&
|
||||
// result is not an absolute path
|
||||
!path.isAbsolute(relative)
|
||||
);
|
||||
@ -17,8 +25,18 @@ function safePath(basePath: string, name: string): boolean {
|
||||
|
||||
const defaultPath: string = './uploads';
|
||||
if (!('STORAGE_PATH' in process.env)) {
|
||||
console.warn(`'STORAGE_PATH' environment variable is not set. Defaulting to ${defaultPath}`);
|
||||
log.warn(`'STORAGE_PATH' environment variable is not set. Defaulting to ${defaultPath}`);
|
||||
}
|
||||
export const storagePath: string = process.env.STORAGE_PATH ?? defaultPath;
|
||||
|
||||
export default safePath;
|
||||
|
||||
export async function timedExecution<T>(
|
||||
fn: () => MaybePromise<T>
|
||||
): Promise<{ executionTime: number; result: T }> {
|
||||
const start = process.hrtime();
|
||||
const result = await fn();
|
||||
const end = process.hrtime(start);
|
||||
const executionTime = (end[0] * 1e6 + end[1]) / 1e6;
|
||||
return { executionTime, result };
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { writeFileSync, mkdirSync, existsSync } from 'fs';
|
||||
import { fail } from '@sveltejs/kit';
|
||||
import type { RequestEvent } from './$types';
|
||||
import safePath, { storagePath } from '$lib';
|
||||
import safePath, { storagePath, log } from '$lib';
|
||||
import { hash } from 'crypto';
|
||||
import path from 'path';
|
||||
|
||||
@ -12,34 +12,44 @@ const mkdirIfNotExists = (path: string) => {
|
||||
};
|
||||
|
||||
export const actions = {
|
||||
default: async ({ request }: RequestEvent) => {
|
||||
default: async ({ request, locals }: RequestEvent) => {
|
||||
let context: { [key: string]: string | string[] } = { requestId: locals.requestId };
|
||||
const data = await request.formData();
|
||||
|
||||
const formFiles = data.getAll('files');
|
||||
if (!formFiles) {
|
||||
log.debug(context, 'missing files');
|
||||
return fail(400, { field: 'files', files: formFiles, missing: true });
|
||||
} else if (!(formFiles as File[])) {
|
||||
log.debug(context, 'invalid files');
|
||||
return fail(400, { field: 'files', files: formFiles, incorrect: true });
|
||||
}
|
||||
const files = formFiles as File[];
|
||||
console.log(files);
|
||||
const fileNames = files.map((file) => file.name);
|
||||
context = { fileNames, ...context };
|
||||
if (files.length === 0) {
|
||||
log.debug(context, 'empty files');
|
||||
return fail(400, { field: 'files', files: formFiles, empty: true });
|
||||
}
|
||||
|
||||
const formName = data.get('name');
|
||||
if (!formName) {
|
||||
log.debug(context, 'missing name');
|
||||
return fail(400, { field: 'name', name: formName, missing: true });
|
||||
} else if (!(formName as string)) {
|
||||
log.debug(context, 'invalid name');
|
||||
return fail(400, { field: 'name', name: formName, incorrect: true });
|
||||
}
|
||||
|
||||
const name = formName as string;
|
||||
context = { name, ...context };
|
||||
|
||||
if (!safePath(storagePath, name)) {
|
||||
log.warn(context, 'Supplied name would cause dir traversal. Rejecting...');
|
||||
return fail(400, { field: 'name', name: name, incorrect: true });
|
||||
}
|
||||
// const name = safePath(formName as string);
|
||||
|
||||
log.info(context, 'Uploading files');
|
||||
|
||||
files.forEach(async (file) => {
|
||||
const outPath = `${storagePath}/${name}`;
|
||||
@ -47,17 +57,19 @@ export const actions = {
|
||||
const ext = path.extname(file.name);
|
||||
|
||||
mkdirIfNotExists(outPath);
|
||||
const filename = hash('sha1', content);
|
||||
const fullPath = `${outPath}/${filename}${ext}`;
|
||||
const filename = `${hash('sha1', content)}${ext}`;
|
||||
const fullPath = `${outPath}/${filename}`;
|
||||
context = { file: fullPath, ...context };
|
||||
if (existsSync(fullPath)) {
|
||||
console.warn(`${fullPath} has already been uploaded. Skipping...`);
|
||||
log.debug(context, 'File has already been uploaded. Skipping...');
|
||||
} else {
|
||||
log.debug(context, 'saving file');
|
||||
writeFileSync(fullPath, Buffer.from(await file.arrayBuffer()), { flag: 'a+' });
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
success: true
|
||||
success: true,
|
||||
};
|
||||
}
|
||||
},
|
||||
};
|
||||
|
@ -13,7 +13,7 @@
|
||||
|
||||
const siPrefixes = new Map([
|
||||
[1_000_000, 'M'],
|
||||
[1_000, 'k']
|
||||
[1_000, 'k'],
|
||||
]);
|
||||
const fileSize = (files: FileList) => {
|
||||
const size = Array.from(files)
|
||||
|
@ -11,8 +11,8 @@ const config = {
|
||||
// adapter-auto only supports some environments, see https://kit.svelte.dev/docs/adapter-auto for a list.
|
||||
// If your environment is not supported, or you settled on a specific environment, switch out the adapter.
|
||||
// See https://kit.svelte.dev/docs/adapters for more information about adapters.
|
||||
adapter: adapter()
|
||||
}
|
||||
adapter: adapter(),
|
||||
},
|
||||
};
|
||||
|
||||
export default config;
|
||||
|
@ -5,14 +5,14 @@ export default defineConfig({
|
||||
plugins: [sveltekit()],
|
||||
|
||||
test: {
|
||||
include: ['src/**/*.{test,spec}.{js,ts}']
|
||||
include: ['src/**/*.{test,spec}.{js,ts}'],
|
||||
},
|
||||
|
||||
css: {
|
||||
preprocessorOptions: {
|
||||
scss: {
|
||||
additionalData: '@use "src/variables.scss" as *;'
|
||||
}
|
||||
}
|
||||
}
|
||||
additionalData: '@use "src/variables.scss" as *;',
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
Reference in New Issue
Block a user