diff --git a/.env.example b/.env.example index 5816fa9..8f5d482 100644 --- a/.env.example +++ b/.env.example @@ -1 +1 @@ -VITE_API_URL=https://api.projectsegfau.lt \ No newline at end of file +AUTH_SECRET=myauthsecret # generate with https://generate-secret.vercel.app/32 or openssl rand -hex 32 on unix-like \ No newline at end of file diff --git a/.gitignore b/.gitignore index dc61a8d..ccbc0b9 100644 --- a/.gitignore +++ b/.gitignore @@ -6,4 +6,5 @@ node_modules .env .env.* !.env.example +config/config.yml package-lock.json \ No newline at end of file diff --git a/compose.yml b/compose.yml index a4edb1a..1284c34 100644 --- a/compose.yml +++ b/compose.yml @@ -1,8 +1,28 @@ services: - website: - container_name: website - image: realprojectsegfault/website + segfaultapi: + container_name: segfaultapi + #image: realprojectsegfault/segfaultapi restart: always - #build: . + build: + context: . + dockerfile: Dockerfile ports: - - "80:80" \ No newline at end of file + - 6893:6893 + volumes: + - ./config:/app/config + segfaultapi-db: + image: mongo + container_name: segfaultapi-db + restart: always + ports: + - 27017:27017 + volumes: + - segfaultapi-db-data:/data/db + environment: + MONGO_INITDB_ROOT_USERNAME: $MONGO_USER + MONGO_INITDB_ROOT_PASSWORD: $MONGO_PASSWORD + MONGO_INITDB_DATABASE: segfaultapi + command: [--auth] + +volumes: + segfaultapi-db-data: \ No newline at end of file diff --git a/config/config.example.yml b/config/config.example.yml new file mode 100644 index 0000000..e861780 --- /dev/null +++ b/config/config.example.yml @@ -0,0 +1,12 @@ +db: + url: "postgres://user:password@host:5432/database" + +app: + auth: + clientId: "authentik-client-id" + clientSecret: "authentik-client-secret" + issuer: "https://yourdomain.com/application/o/app-name/" + hcaptcha: + secret: "your-hcaptcha-secret" + sitekey: "your-hcaptcha-sitekey" + webhook: "your-discord-webhook-url" \ No newline at end of file diff --git a/package.json b/package.json index deb0224..fd39fce 100644 --- a/package.json +++ b/package.json @@ -14,7 +14,10 @@ "@iconify-json/simple-icons": "^1.1.39", "@sveltejs/adapter-node": "1.0.0", "@sveltejs/kit": "1.0.0", + "axios": "^1.2.2", + "consola": "^2.15.3", "dayjs": "^1.11.7", + "discord-webhook-node": "^1.1.8", "mdsvex": "^0.10.6", "prettier": "^2.8.1", "prettier-plugin-svelte": "^2.9.0", @@ -28,7 +31,16 @@ "tslib": "^2.4.1", "typescript": "^4.9.4", "unocss": "^0.47.6", - "vite": "4.0.1" + "vite": "4.0.1", + "yaml": "^2.2.0" }, - "type": "module" + "type": "module", + "dependencies": { + "@auth/core": "^0.2.3", + "@auth/sveltekit": "^0.1.10", + "joi": "^17.7.0", + "pg": "^8.8.0", + "pg-hstore": "^2.3.4", + "sequelize": "^6.28.0" + } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 9dfa890..6c8face 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1,14 +1,23 @@ lockfileVersion: 5.4 specifiers: + '@auth/core': ^0.2.3 + '@auth/sveltekit': ^0.1.10 '@iconify-json/fa6-solid': ^1.1.9 '@iconify-json/simple-icons': ^1.1.39 '@sveltejs/adapter-node': 1.0.0 '@sveltejs/kit': 1.0.0 + axios: ^1.2.2 + consola: ^2.15.3 dayjs: ^1.11.7 + discord-webhook-node: ^1.1.8 + joi: ^17.7.0 mdsvex: ^0.10.6 + pg: ^8.8.0 + pg-hstore: ^2.3.4 prettier: ^2.8.1 prettier-plugin-svelte: ^2.9.0 + sequelize: ^6.28.0 svelte: ^3.55.0 svelte-check: ^2.10.2 svelte-dark-mode: ^2.1.0 @@ -20,13 +29,25 @@ specifiers: typescript: ^4.9.4 unocss: ^0.47.6 vite: 4.0.1 + yaml: ^2.2.0 + +dependencies: + '@auth/core': 0.2.3 + '@auth/sveltekit': 0.1.10_e24fmtavzua53hjiuev7juit3i + joi: 17.7.0 + pg: 8.8.0 + pg-hstore: 2.3.4 + sequelize: 6.28.0_pg-hstore@2.3.4+pg@8.8.0 devDependencies: '@iconify-json/fa6-solid': 1.1.9 '@iconify-json/simple-icons': 1.1.39 '@sveltejs/adapter-node': 1.0.0_@sveltejs+kit@1.0.0 '@sveltejs/kit': 1.0.0_svelte@3.55.0+vite@4.0.1 + axios: 1.2.2 + consola: 2.15.3 dayjs: 1.11.7 + discord-webhook-node: 1.1.8 mdsvex: 0.10.6_svelte@3.55.0 prettier: 2.8.1 prettier-plugin-svelte: 2.9.0_ajxj753sv7dbwexjherrch25ta @@ -41,6 +62,7 @@ devDependencies: typescript: 4.9.4 unocss: 0.47.6_vite@4.0.1 vite: 4.0.1 + yaml: 2.2.0 packages: @@ -63,13 +85,57 @@ packages: resolution: {integrity: sha512-CQkeV+oJxUazwjlHD0/3ZD08QWKuGQkhnrKo3e6ly5pd48VUpXbb77q0xMU4+vc2CkJnDS02Eq/M9ugyX20XZA==} dev: true + /@auth/core/0.2.2: + resolution: {integrity: sha512-hEs3no7OjbFTZjb5XjbE6hlkmYw+NVfG+MwQ3IhHTwVx8hAQ4BV704UMWiPmBVXeJrkoVrNFYlkGpEAc3pGNgg==} + peerDependencies: + nodemailer: 6.8.0 + peerDependenciesMeta: + nodemailer: + optional: true + dependencies: + '@panva/hkdf': 1.0.2 + cookie: 0.5.0 + jose: 4.11.1 + oauth4webapi: 2.0.5 + preact: 10.11.3 + preact-render-to-string: 5.2.3_preact@10.11.3 + dev: false + + /@auth/core/0.2.3: + resolution: {integrity: sha512-GJJkCh1jmI1kJlGmVOGMHRimQbHQCCwgmZLIx0Fpod1e0g4gtk6hGL5LmKjb7rN3j3HQbxQOEg5j6jJLZ7dDJw==} + peerDependencies: + nodemailer: 6.8.0 + peerDependenciesMeta: + nodemailer: + optional: true + dependencies: + '@panva/hkdf': 1.0.2 + cookie: 0.5.0 + jose: 4.11.1 + oauth4webapi: 2.0.5 + preact: 10.11.3 + preact-render-to-string: 5.2.3_preact@10.11.3 + dev: false + + /@auth/sveltekit/0.1.10_e24fmtavzua53hjiuev7juit3i: + resolution: {integrity: sha512-sVuk1AE0HEpttBpInB1zVs+v8l05TlonGa+UqiWk6ZHnvc1+nlsoAm7UFqZTuq+lgVNcXm1heNiaVwBiAqlpTQ==} + peerDependencies: + '@sveltejs/kit': ^1.0.0 + svelte: ^3.54.0 + dependencies: + '@auth/core': 0.2.2 + '@sveltejs/kit': 1.0.0_svelte@3.55.0+vite@4.0.1 + svelte: 3.55.0 + transitivePeerDependencies: + - nodemailer + dev: false + /@esbuild/android-arm/0.16.6: resolution: {integrity: sha512-wc1AyHlFS8eejfAdePn2wr8/5zEa+FvF3ipBeTo4Qm9Xl0A0miTUfphwzXa3xdxU2pHimRCzIAUhjlbSSts8JQ==} engines: {node: '>=12'} cpu: [arm] os: [android] requiresBuild: true - dev: true optional: true /@esbuild/android-arm64/0.16.6: @@ -78,7 +144,6 @@ packages: cpu: [arm64] os: [android] requiresBuild: true - dev: true optional: true /@esbuild/android-x64/0.16.6: @@ -87,7 +152,6 @@ packages: cpu: [x64] os: [android] requiresBuild: true - dev: true optional: true /@esbuild/darwin-arm64/0.16.6: @@ -96,7 +160,6 @@ packages: cpu: [arm64] os: [darwin] requiresBuild: true - dev: true optional: true /@esbuild/darwin-x64/0.16.6: @@ -105,7 +168,6 @@ packages: cpu: [x64] os: [darwin] requiresBuild: true - dev: true optional: true /@esbuild/freebsd-arm64/0.16.6: @@ -114,7 +176,6 @@ packages: cpu: [arm64] os: [freebsd] requiresBuild: true - dev: true optional: true /@esbuild/freebsd-x64/0.16.6: @@ -123,7 +184,6 @@ packages: cpu: [x64] os: [freebsd] requiresBuild: true - dev: true optional: true /@esbuild/linux-arm/0.16.6: @@ -132,7 +192,6 @@ packages: cpu: [arm] os: [linux] requiresBuild: true - dev: true optional: true /@esbuild/linux-arm64/0.16.6: @@ -141,7 +200,6 @@ packages: cpu: [arm64] os: [linux] requiresBuild: true - dev: true optional: true /@esbuild/linux-ia32/0.16.6: @@ -150,7 +208,6 @@ packages: cpu: [ia32] os: [linux] requiresBuild: true - dev: true optional: true /@esbuild/linux-loong64/0.16.6: @@ -159,7 +216,6 @@ packages: cpu: [loong64] os: [linux] requiresBuild: true - dev: true optional: true /@esbuild/linux-mips64el/0.16.6: @@ -168,7 +224,6 @@ packages: cpu: [mips64el] os: [linux] requiresBuild: true - dev: true optional: true /@esbuild/linux-ppc64/0.16.6: @@ -177,7 +232,6 @@ packages: cpu: [ppc64] os: [linux] requiresBuild: true - dev: true optional: true /@esbuild/linux-riscv64/0.16.6: @@ -186,7 +240,6 @@ packages: cpu: [riscv64] os: [linux] requiresBuild: true - dev: true optional: true /@esbuild/linux-s390x/0.16.6: @@ -195,7 +248,6 @@ packages: cpu: [s390x] os: [linux] requiresBuild: true - dev: true optional: true /@esbuild/linux-x64/0.16.6: @@ -204,7 +256,6 @@ packages: cpu: [x64] os: [linux] requiresBuild: true - dev: true optional: true /@esbuild/netbsd-x64/0.16.6: @@ -213,7 +264,6 @@ packages: cpu: [x64] os: [netbsd] requiresBuild: true - dev: true optional: true /@esbuild/openbsd-x64/0.16.6: @@ -222,7 +272,6 @@ packages: cpu: [x64] os: [openbsd] requiresBuild: true - dev: true optional: true /@esbuild/sunos-x64/0.16.6: @@ -231,7 +280,6 @@ packages: cpu: [x64] os: [sunos] requiresBuild: true - dev: true optional: true /@esbuild/win32-arm64/0.16.6: @@ -240,7 +288,6 @@ packages: cpu: [arm64] os: [win32] requiresBuild: true - dev: true optional: true /@esbuild/win32-ia32/0.16.6: @@ -249,7 +296,6 @@ packages: cpu: [ia32] os: [win32] requiresBuild: true - dev: true optional: true /@esbuild/win32-x64/0.16.6: @@ -258,9 +304,18 @@ packages: cpu: [x64] os: [win32] requiresBuild: true - dev: true optional: true + /@hapi/hoek/9.3.0: + resolution: {integrity: sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ==} + dev: false + + /@hapi/topo/5.1.0: + resolution: {integrity: sha512-foQZKJig7Ob0BMAYBfcJk8d77QtOe7Wo4ox7ff1lQYoNNAb6jwcY1ncdoy2e9wQZzvNy7ODZCYJkK8kzmcAnAg==} + dependencies: + '@hapi/hoek': 9.3.0 + dev: false + /@iconify-json/fa6-solid/1.1.9: resolution: {integrity: sha512-jBX86UGcK9qCRqI0ZT8BfuNiEC7yYPRH9M1bW5MXmuaQ2BW3Sttr+j/245VZO45SokI7ng1Xfu2OMJxLbQuqhA==} dependencies: @@ -310,7 +365,6 @@ packages: /@jridgewell/sourcemap-codec/1.4.14: resolution: {integrity: sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==} - dev: true /@jridgewell/trace-mapping/0.3.17: resolution: {integrity: sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g==} @@ -340,9 +394,12 @@ packages: fastq: 1.14.0 dev: true + /@panva/hkdf/1.0.2: + resolution: {integrity: sha512-MSAs9t3Go7GUkMhpKC44T58DJ5KGk2vBo+h1cqQeqlMfdGkxaVB78ZWpv9gYi/g2fa4sopag9gJsNvS8XGgWJA==} + dev: false + /@polka/url/1.0.0-next.21: resolution: {integrity: sha512-a5Sab1C4/icpTZVzZc5Ghpz88yQtGOyNqYXcZgOssB2uuAr+wF/MvN6bgtW32q7HHrvBki+BsZ0OuNv6EV3K9g==} - dev: true /@rollup/plugin-commonjs/23.0.4_rollup@3.7.4: resolution: {integrity: sha512-bOPJeTZg56D2MCm+TT4psP8e8Jmf1Jsi7pFUMl8BN5kOADNzofNHe47+84WVCt7D095xPghC235/YKuNDEhczg==} @@ -422,6 +479,20 @@ packages: rollup: 3.7.4 dev: true + /@sideway/address/4.1.4: + resolution: {integrity: sha512-7vwq+rOHVWjyXxVlR76Agnvhy8I9rpzjosTESvmhNeXOXdZZB15Fl+TI9x1SiHZH5Jv2wTGduSxFDIaq0m3DUw==} + dependencies: + '@hapi/hoek': 9.3.0 + dev: false + + /@sideway/formula/3.0.1: + resolution: {integrity: sha512-/poHZJJVjx3L+zVD6g9KgHfYnb443oi7wLu/XKojDviHy6HOEOA6z1Trk5aR1dGcmPenJEgb2sK2I80LeS3MIg==} + dev: false + + /@sideway/pinpoint/2.0.0: + resolution: {integrity: sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ==} + dev: false + /@sveltejs/adapter-node/1.0.0_@sveltejs+kit@1.0.0: resolution: {integrity: sha512-Q8an8CXEt5XlFbyT1NBM4xELNZD8xPVZfKCcgorCfPkeBP5ftDgPaK12JIokXA5koYJ54AJcNY4ams9TZ7yGxA==} peerDependencies: @@ -460,7 +531,6 @@ packages: vite: 4.0.1 transitivePeerDependencies: - supports-color - dev: true /@sveltejs/vite-plugin-svelte/2.0.2_svelte@3.55.0+vite@4.0.1: resolution: {integrity: sha512-xCEan0/NNpQuL0l5aS42FjwQ6wwskdxC3pW1OeFtEKNZwRg7Evro9lac9HesGP6TdFsTv2xMes5ASQVKbCacxg==} @@ -479,19 +549,26 @@ packages: vitefu: 0.2.3_vite@4.0.1 transitivePeerDependencies: - supports-color - dev: true /@types/cookie/0.5.1: resolution: {integrity: sha512-COUnqfB2+ckwXXSFInsFdOAWQzCCx+a5hq2ruyj+Vjund94RJQd4LG2u9hnvJrTgunKAaax7ancBYlDrNYxA0g==} - dev: true + + /@types/debug/4.1.7: + resolution: {integrity: sha512-9AonUzyTjXXhEOa0DnqpzZi6VHlqKMswga9EXjpXnnqxwLtdvPPtlO8evrI5D9S6asFRCQ6v+wpiUKbw+vKqyg==} + dependencies: + '@types/ms': 0.7.31 + dev: false /@types/estree/1.0.0: resolution: {integrity: sha512-WulqXMDUTYAXCjZnk6JtIHPigp55cVtDgDrO2gHRwhyJto21+1zbVCtOYB2L1F9w4qCQ0rOGWBnBe0FNTiEJIQ==} dev: true + /@types/ms/0.7.31: + resolution: {integrity: sha512-iiUgKzV9AuaEkZqkOLDIvlQiL6ltuZd9tGcW3gwpnX8JbuiuhFlEGmmFXEXkN50Cvq7Os88IY2v0dkDqXYWVgA==} + dev: false + /@types/node/18.11.15: resolution: {integrity: sha512-VkhBbVo2+2oozlkdHXLrb3zjsRkpdnaU2bXmX8Wgle3PUi569eLRaHGlgETQHR7lLL1w7GiG3h9SnePhxNDecw==} - dev: true /@types/pug/2.0.6: resolution: {integrity: sha512-SnHmG9wN1UVmagJOnyo/qkk0Z7gejYxOYYmaAwr5u2yFYfsupN3sg10kyzN8Hep/2zbHxCnsumxOoRIRMBwKCg==} @@ -511,6 +588,10 @@ packages: resolution: {integrity: sha512-PBjIUxZHOuj0R15/xuwJYjFi+KZdNFrehocChv4g5hu6aFroHue8m0lBP0POdK2nKzbw0cgV1mws8+V/JAcEkQ==} dev: true + /@types/validator/13.7.10: + resolution: {integrity: sha512-t1yxFAR2n0+VO6hd/FJ9F2uezAZVWHLmpmlJzm1eX03+H7+HsuTAp7L8QJs+2pQCfWkP1+EXsGK9Z9v7o/qPVQ==} + dev: false + /@unocss/astro/0.47.6_vite@4.0.1: resolution: {integrity: sha512-8lR4KwuCeVxOTKk6g6hx6VUHhW1u+hki8oRsJaKEB0s5iUPmY6rCNtb/iaBJdceY11bZMMy5LZHJFTkod/T/zg==} dependencies: @@ -678,6 +759,20 @@ packages: picomatch: 2.3.1 dev: true + /asynckit/0.4.0: + resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} + dev: true + + /axios/1.2.2: + resolution: {integrity: sha512-bz/J4gS2S3I7mpN/YZfGFTqhXTYzRho8Ay38w2otuuDR322KzFIWm/4W2K6gIwvWaws5n+mnb7D1lN9uD+QH6Q==} + dependencies: + follow-redirects: 1.15.2 + form-data: 4.0.0 + proxy-from-env: 1.1.0 + transitivePeerDependencies: + - debug + dev: true + /balanced-match/1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} dev: true @@ -711,6 +806,11 @@ packages: resolution: {integrity: sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==} dev: true + /buffer-writer/2.0.0: + resolution: {integrity: sha512-a7ZpuTZU1TRtnwyCNW3I5dc0wWNC3VR9S++Ewyk2HHZdrO3CQJqSpd+95Us590V6AL7JqUAH2IwZ/398PmNFgw==} + engines: {node: '>=4'} + dev: false + /builtin-modules/3.3.0: resolution: {integrity: sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==} engines: {node: '>=6'} @@ -721,7 +821,6 @@ packages: engines: {node: '>=10.16.0'} dependencies: streamsearch: 1.1.0 - dev: true /cac/6.7.14: resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==} @@ -752,6 +851,13 @@ packages: resolution: {integrity: sha512-3tlv/dIP7FWvj3BsbHrGLJ6l/oKh1O3TcgBqMn+yyCagOxc23fyzDS6HypQbgxWbkpDnf52p1LuR4eWDQ/K9WQ==} dev: true + /combined-stream/1.0.8: + resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} + engines: {node: '>= 0.8'} + dependencies: + delayed-stream: 1.0.0 + dev: true + /commondir/1.0.1: resolution: {integrity: sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==} dev: true @@ -767,7 +873,6 @@ packages: /cookie/0.5.0: resolution: {integrity: sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==} engines: {node: '>= 0.6'} - dev: true /cross-spawn/7.0.3: resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==} @@ -800,17 +905,20 @@ packages: optional: true dependencies: ms: 2.1.2 - dev: true /deepmerge/4.2.2: resolution: {integrity: sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==} engines: {node: '>=0.10.0'} - dev: true /defu/6.1.1: resolution: {integrity: sha512-aA964RUCsBt0FGoNIlA3uFgo2hO+WWC0fiC6DBps/0SFzkKcYoM/3CzVLIa5xSsrFjdioMdYgAIbwo80qp2MoA==} dev: true + /delayed-stream/1.0.0: + resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} + engines: {node: '>=0.4.0'} + dev: true + /destr/1.2.2: resolution: {integrity: sha512-lrbCJwD9saUQrqUfXvl6qoM+QN3W7tLV5pAOs+OqOmopCCz/JkE05MHedJR1xfk4IAnZuJXPVuN5+7jNA2ZCiA==} dev: true @@ -822,8 +930,20 @@ packages: /devalue/4.2.0: resolution: {integrity: sha512-mbjoAaCL2qogBKgeFxFPOXAUsZchircF+B/79LD4sHH0+NHfYm8gZpQrskKDn5gENGt35+5OI1GUF7hLVnkPDw==} + + /discord-webhook-node/1.1.8: + resolution: {integrity: sha512-3u0rrwywwYGc6HrgYirN/9gkBYqmdpvReyQjapoXARAHi0P0fIyf3W5tS5i3U3cc7e44E+e7dIHYUeec7yWaug==} + dependencies: + form-data: 3.0.1 + node-fetch: 2.6.7 + transitivePeerDependencies: + - encoding dev: true + /dottie/2.0.2: + resolution: {integrity: sha512-fmrwR04lsniq/uSr8yikThDTrM7epXHBAAjH9TbeH3rEA8tdCO7mRzB9hdmdGyJCxF8KERo9CITcm3kGuoyMhg==} + dev: false + /duplexer/0.1.2: resolution: {integrity: sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==} dev: true @@ -860,11 +980,9 @@ packages: '@esbuild/win32-arm64': 0.16.6 '@esbuild/win32-ia32': 0.16.6 '@esbuild/win32-x64': 0.16.6 - dev: true /esm-env/1.0.0: resolution: {integrity: sha512-Cf6VksWPsTuW01vU9Mk/3vRue91Zevka5SjyNf3nEpokFRuqt/KjUQoGAwq9qMmhpLTHmXzSIrFRw8zxWzmFBA==} - dev: true /estree-walker/2.0.2: resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==} @@ -917,6 +1035,34 @@ packages: path-exists: 4.0.0 dev: true + /follow-redirects/1.15.2: + resolution: {integrity: sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==} + engines: {node: '>=4.0'} + peerDependencies: + debug: '*' + peerDependenciesMeta: + debug: + optional: true + dev: true + + /form-data/3.0.1: + resolution: {integrity: sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==} + engines: {node: '>= 6'} + dependencies: + asynckit: 0.4.0 + combined-stream: 1.0.8 + mime-types: 2.1.35 + dev: true + + /form-data/4.0.0: + resolution: {integrity: sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==} + engines: {node: '>= 6'} + dependencies: + asynckit: 0.4.0 + combined-stream: 1.0.8 + mime-types: 2.1.35 + dev: true + /fs.realpath/1.0.0: resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} dev: true @@ -926,12 +1072,10 @@ packages: engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} os: [darwin] requiresBuild: true - dev: true optional: true /function-bind/1.1.1: resolution: {integrity: sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==} - dev: true /get-stream/6.0.1: resolution: {integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==} @@ -969,11 +1113,9 @@ packages: /globalyzer/0.1.0: resolution: {integrity: sha512-40oNTM9UfG6aBmuKxk/giHn5nQ8RVz/SS4Ir6zgzOv9/qC3kKZ9v4etGTcJbEl/NyVQH7FGU7d+X1egr57Md2Q==} - dev: true /globrex/0.1.2: resolution: {integrity: sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg==} - dev: true /graceful-fs/4.2.10: resolution: {integrity: sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==} @@ -991,7 +1133,6 @@ packages: engines: {node: '>= 0.4.0'} dependencies: function-bind: 1.1.1 - dev: true /human-signals/2.1.0: resolution: {integrity: sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==} @@ -1006,6 +1147,11 @@ packages: resolve-from: 4.0.0 dev: true + /inflection/1.13.4: + resolution: {integrity: sha512-6I/HUDeYFfuNCVS3td055BaXBwKYuzw7K3ExVMStBowKo9oOAMJIXIHvdyR3iboTCp1b+1i5DSkIZTcwIktuDw==} + engines: {'0': node >= 0.4.0} + dev: false + /inflight/1.0.6: resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} dependencies: @@ -1035,7 +1181,6 @@ packages: resolution: {integrity: sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==} dependencies: has: 1.0.3 - dev: true /is-extglob/2.1.1: resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} @@ -1078,10 +1223,23 @@ packages: hasBin: true dev: true + /joi/17.7.0: + resolution: {integrity: sha512-1/ugc8djfn93rTE3WRKdCzGGt/EtiYKxITMO4Wiv6q5JL1gl9ePt4kBsl1S499nbosspfctIQTpYIhSmHA3WAg==} + dependencies: + '@hapi/hoek': 9.3.0 + '@hapi/topo': 5.1.0 + '@sideway/address': 4.1.4 + '@sideway/formula': 3.0.1 + '@sideway/pinpoint': 2.0.0 + dev: false + + /jose/4.11.1: + resolution: {integrity: sha512-YRv4Tk/Wlug8qicwqFNFVEZSdbROCHRAC6qu/i0dyNKr5JQdoa2pIGoS04lLO/jXQX7Z9omoNewYIVIxqZBd9Q==} + dev: false + /kleur/4.1.5: resolution: {integrity: sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==} engines: {node: '>=6'} - dev: true /kolorist/1.6.0: resolution: {integrity: sha512-dLkz37Ab97HWMx9KTes3Tbi3D1ln9fCAy2zr2YVExJasDRPGRaKcoE4fycWNtnCAJfjFqe0cnY+f8KT2JePEXQ==} @@ -1099,6 +1257,17 @@ packages: p-locate: 5.0.0 dev: true + /lodash/4.17.21: + resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} + dev: false + + /lru-cache/6.0.0: + resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==} + engines: {node: '>=10'} + dependencies: + yallist: 4.0.0 + dev: false + /magic-string/0.25.9: resolution: {integrity: sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ==} dependencies: @@ -1117,7 +1286,6 @@ packages: engines: {node: '>=12'} dependencies: '@jridgewell/sourcemap-codec': 1.4.14 - dev: true /mdn-data/2.0.30: resolution: {integrity: sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==} @@ -1152,11 +1320,22 @@ packages: picomatch: 2.3.1 dev: true + /mime-db/1.52.0: + resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} + engines: {node: '>= 0.6'} + dev: true + + /mime-types/2.1.35: + resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} + engines: {node: '>= 0.6'} + dependencies: + mime-db: 1.52.0 + dev: true + /mime/3.0.0: resolution: {integrity: sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A==} engines: {node: '>=10.0.0'} hasBin: true - dev: true /mimic-fn/2.1.0: resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==} @@ -1192,30 +1371,48 @@ packages: minimist: 1.2.7 dev: true + /moment-timezone/0.5.40: + resolution: {integrity: sha512-tWfmNkRYmBkPJz5mr9GVDn9vRlVZOTe6yqY92rFxiOdWXbjaR0+9LwQnZGGuNR63X456NqmEkbskte8tWL5ePg==} + dependencies: + moment: 2.29.4 + dev: false + + /moment/2.29.4: + resolution: {integrity: sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w==} + dev: false + /mri/1.2.0: resolution: {integrity: sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==} engines: {node: '>=4'} - dev: true /mrmime/1.0.1: resolution: {integrity: sha512-hzzEagAgDyoU1Q6yg5uI+AorQgdvMCur3FcKf7NhMKWsaYg+RnbTyHRa/9IlLF9rf455MOCtcqqrQQ83pPP7Uw==} engines: {node: '>=10'} - dev: true /ms/2.1.2: resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==} - dev: true /nanoid/3.3.4: resolution: {integrity: sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==} engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} hasBin: true - dev: true /node-fetch-native/0.1.8: resolution: {integrity: sha512-ZNaury9r0NxaT2oL65GvdGDy+5PlSaHTovT6JV5tOW07k1TQmgC0olZETa4C9KZg0+6zBr99ctTYa3Utqj9P/Q==} dev: true + /node-fetch/2.6.7: + resolution: {integrity: sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==} + engines: {node: 4.x || >=6.0.0} + peerDependencies: + encoding: ^0.1.0 + peerDependenciesMeta: + encoding: + optional: true + dependencies: + whatwg-url: 5.0.0 + dev: true + /normalize-path/3.0.0: resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} engines: {node: '>=0.10.0'} @@ -1228,6 +1425,10 @@ packages: path-key: 3.1.1 dev: true + /oauth4webapi/2.0.5: + resolution: {integrity: sha512-KmoR3KxCwmr9KvL/c/6UVzQnc4CUjo+j8NSgD3bWYlZXpUmyOVw97nDVb0BKZhCcUtGsbll16v8vsnR5JbTZ9A==} + dev: false + /ohmyfetch/0.4.21: resolution: {integrity: sha512-VG7f/JRvqvBOYvL0tHyEIEG7XHWm7OqIfAs6/HqwWwDfjiJ1g0huIpe5sFEmyb+7hpFa1EGNH2aERWR72tlClw==} dependencies: @@ -1264,6 +1465,10 @@ packages: p-limit: 3.1.0 dev: true + /packet-reader/1.0.0: + resolution: {integrity: sha512-HAKu/fG3HpHFO0AA8WE8q2g+gBJaZ9MG7fcKk+IJPLTGAD6Psw4443l+9DGRbOIh3/aXr7Phy0TjilYivJo5XQ==} + dev: false + /parent-module/1.0.1: resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} engines: {node: '>=6'} @@ -1288,7 +1493,6 @@ packages: /path-parse/1.0.7: resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} - dev: true /pathe/1.0.0: resolution: {integrity: sha512-nPdMG0Pd09HuSsr7QOKUXO2Jr9eqaDiZvDwdyIhNG5SHYujkQHYKDfGQkulBxvbDHz8oHLsTgKN86LSwYzSHAg==} @@ -1298,9 +1502,71 @@ packages: resolution: {integrity: sha512-NOT9AcKiDGpnV/HBhI22Str++XWcErO/bALvHCuhv33owZW/CjH8KAFLZDCmu3727sihe0wTxpDhyGc6M8qacQ==} dev: true + /pg-connection-string/2.5.0: + resolution: {integrity: sha512-r5o/V/ORTA6TmUnyWZR9nCj1klXCO2CEKNRlVuJptZe85QuhFayC7WeMic7ndayT5IRIR0S0xFxFi2ousartlQ==} + dev: false + + /pg-hstore/2.3.4: + resolution: {integrity: sha512-N3SGs/Rf+xA1M2/n0JBiXFDVMzdekwLZLAO0g7mpDY9ouX+fDI7jS6kTq3JujmYbtNSJ53TJ0q4G98KVZSM4EA==} + engines: {node: '>= 0.8.x'} + dependencies: + underscore: 1.13.6 + dev: false + + /pg-int8/1.0.1: + resolution: {integrity: sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==} + engines: {node: '>=4.0.0'} + dev: false + + /pg-pool/3.5.2_pg@8.8.0: + resolution: {integrity: sha512-His3Fh17Z4eg7oANLob6ZvH8xIVen3phEZh2QuyrIl4dQSDVEabNducv6ysROKpDNPSD+12tONZVWfSgMvDD9w==} + peerDependencies: + pg: '>=8.0' + dependencies: + pg: 8.8.0 + dev: false + + /pg-protocol/1.5.0: + resolution: {integrity: sha512-muRttij7H8TqRNu/DxrAJQITO4Ac7RmX3Klyr/9mJEOBeIpgnF8f9jAfRz5d3XwQZl5qBjF9gLsUtMPJE0vezQ==} + dev: false + + /pg-types/2.2.0: + resolution: {integrity: sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==} + engines: {node: '>=4'} + dependencies: + pg-int8: 1.0.1 + postgres-array: 2.0.0 + postgres-bytea: 1.0.0 + postgres-date: 1.0.7 + postgres-interval: 1.2.0 + dev: false + + /pg/8.8.0: + resolution: {integrity: sha512-UXYN0ziKj+AeNNP7VDMwrehpACThH7LUl/p8TDFpEUuSejCUIwGSfxpHsPvtM6/WXFy6SU4E5RG4IJV/TZAGjw==} + engines: {node: '>= 8.0.0'} + peerDependencies: + pg-native: '>=3.0.1' + peerDependenciesMeta: + pg-native: + optional: true + dependencies: + buffer-writer: 2.0.0 + packet-reader: 1.0.0 + pg-connection-string: 2.5.0 + pg-pool: 3.5.2_pg@8.8.0 + pg-protocol: 1.5.0 + pg-types: 2.2.0 + pgpass: 1.0.5 + dev: false + + /pgpass/1.0.5: + resolution: {integrity: sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==} + dependencies: + split2: 4.1.0 + dev: false + /picocolors/1.0.0: resolution: {integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==} - dev: true /picomatch/2.3.1: resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} @@ -1314,7 +1580,41 @@ packages: nanoid: 3.3.4 picocolors: 1.0.0 source-map-js: 1.0.2 - dev: true + + /postgres-array/2.0.0: + resolution: {integrity: sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==} + engines: {node: '>=4'} + dev: false + + /postgres-bytea/1.0.0: + resolution: {integrity: sha512-xy3pmLuQqRBZBXDULy7KbaitYqLcmxigw14Q5sj8QBVLqEwXfeybIKVWiqAXTlcvdvb0+xkOtDbfQMOf4lST1w==} + engines: {node: '>=0.10.0'} + dev: false + + /postgres-date/1.0.7: + resolution: {integrity: sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==} + engines: {node: '>=0.10.0'} + dev: false + + /postgres-interval/1.2.0: + resolution: {integrity: sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==} + engines: {node: '>=0.10.0'} + dependencies: + xtend: 4.0.2 + dev: false + + /preact-render-to-string/5.2.3_preact@10.11.3: + resolution: {integrity: sha512-aPDxUn5o3GhWdtJtW0svRC2SS/l8D9MAgo2+AWml+BhDImb27ALf04Q2d+AHqUUOc6RdSXFIBVa2gxzgMKgtZA==} + peerDependencies: + preact: '>=10' + dependencies: + preact: 10.11.3 + pretty-format: 3.8.0 + dev: false + + /preact/10.11.3: + resolution: {integrity: sha512-eY93IVpod/zG3uMF22Unl8h9KkrcKIRs2EGar8hwLZZDU1lkjph303V9HZBwufh2s736U6VXuhD109LYqPoffg==} + dev: false /prettier-plugin-svelte/2.9.0_ajxj753sv7dbwexjherrch25ta: resolution: {integrity: sha512-3doBi5NO4IVgaNPtwewvrgPpqAcvNv0NwJNflr76PIGgi9nf1oguQV1Hpdm9TI2ALIQVn/9iIwLpBO5UcD2Jiw==} @@ -1332,6 +1632,10 @@ packages: hasBin: true dev: true + /pretty-format/3.8.0: + resolution: {integrity: sha512-WuxUnVtlWL1OfZFQFuqvnvs6MiAGk9UNsBostyBOB0Is9wb5uRESevA6rnl/rkksXaGX3GzZhPup5d6Vp1nFew==} + dev: false + /prism-svelte/0.4.7: resolution: {integrity: sha512-yABh19CYbM24V7aS7TuPYRNMqthxwbvx6FF/Rw920YbyBWO3tnyPIqRMgHuSVsLmuHkkBS1Akyof463FVdkeDQ==} dev: true @@ -1341,6 +1645,10 @@ packages: engines: {node: '>=6'} dev: true + /proxy-from-env/1.1.0: + resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==} + dev: true + /queue-microtask/1.2.3: resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} dev: true @@ -1364,7 +1672,10 @@ packages: is-core-module: 2.11.0 path-parse: 1.0.7 supports-preserve-symlinks-flag: 1.0.0 - dev: true + + /retry-as-promised/7.0.3: + resolution: {integrity: sha512-SEvMa4khHvpU/o6zgh7sK24qm6rxVgKnrSyzb5POeDvZx5N9Bf0s5sQsQ4Fl+HjRp0X+w2UzACGfUnXtx6cJ9Q==} + dev: false /reusify/1.0.4: resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==} @@ -1384,7 +1695,6 @@ packages: hasBin: true optionalDependencies: fsevents: 2.3.2 - dev: true /run-parallel/1.2.0: resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} @@ -1397,7 +1707,6 @@ packages: engines: {node: '>=6'} dependencies: mri: 1.2.0 - dev: true /sander/0.5.1: resolution: {integrity: sha512-3lVqBir7WuKDHGrKRDn/1Ye3kwpXaDOMsiRP1wd6wpZW56gJhsbp5RqQpA6JG/P+pkXizygnr1dKR8vzWaVsfA==} @@ -1416,9 +1725,76 @@ packages: typescript: 4.9.4 dev: true + /semver/7.3.8: + resolution: {integrity: sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==} + engines: {node: '>=10'} + hasBin: true + dependencies: + lru-cache: 6.0.0 + dev: false + + /sequelize-pool/7.1.0: + resolution: {integrity: sha512-G9c0qlIWQSK29pR/5U2JF5dDQeqqHRragoyahj/Nx4KOOQ3CPPfzxnfqFPCSB7x5UgjOgnZ61nSxz+fjDpRlJg==} + engines: {node: '>= 10.0.0'} + dev: false + + /sequelize/6.28.0_pg-hstore@2.3.4+pg@8.8.0: + resolution: {integrity: sha512-+WHqvUQgTp19GLkt+gyQ+F6qg+FIEO2O5F9C0TOYV/PjZ2a/XwWvVkL1NCkS4VSIjVVvAUutiW6Wv9ofveGaVw==} + engines: {node: '>=10.0.0'} + peerDependencies: + ibm_db: '*' + mariadb: '*' + mysql2: '*' + oracledb: '*' + pg: '*' + pg-hstore: '*' + snowflake-sdk: '*' + sqlite3: '*' + tedious: '*' + peerDependenciesMeta: + ibm_db: + optional: true + mariadb: + optional: true + mysql2: + optional: true + oracledb: + optional: true + pg: + optional: true + pg-hstore: + optional: true + snowflake-sdk: + optional: true + sqlite3: + optional: true + tedious: + optional: true + dependencies: + '@types/debug': 4.1.7 + '@types/validator': 13.7.10 + debug: 4.3.4 + dottie: 2.0.2 + inflection: 1.13.4 + lodash: 4.17.21 + moment: 2.29.4 + moment-timezone: 0.5.40 + pg: 8.8.0 + pg-connection-string: 2.5.0 + pg-hstore: 2.3.4 + retry-as-promised: 7.0.3 + semver: 7.3.8 + sequelize-pool: 7.1.0 + toposort-class: 1.0.1 + uuid: 8.3.2 + validator: 13.7.0 + wkx: 0.5.0 + transitivePeerDependencies: + - supports-color + dev: false + /set-cookie-parser/2.5.1: resolution: {integrity: sha512-1jeBGaKNGdEq4FgIrORu/N570dwoPYio8lSoYLWmX7sQ//0JY08Xh9o5pBcgmHQ/MbsYp/aZnOe1s1lIsbLprQ==} - dev: true /shebang-command/2.0.0: resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} @@ -1443,7 +1819,6 @@ packages: '@polka/url': 1.0.0-next.21 mrmime: 1.0.1 totalist: 3.0.0 - dev: true /sorcery/0.10.0: resolution: {integrity: sha512-R5ocFmKZQFfSTstfOtHjJuAwbpGyf9qjQa1egyhvXSbM7emjrtLXtGdZsDJDABC85YBfVvrOiGWKSYXPKdvP1g==} @@ -1458,17 +1833,20 @@ packages: /source-map-js/1.0.2: resolution: {integrity: sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==} engines: {node: '>=0.10.0'} - dev: true /sourcemap-codec/1.4.8: resolution: {integrity: sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==} deprecated: Please use @jridgewell/sourcemap-codec instead dev: true + /split2/4.1.0: + resolution: {integrity: sha512-VBiJxFkxiXRlUIeyMQi8s4hgvKCSjtknJv/LVYbrgALPwf5zSKmEwV9Lst25AkvMDnvxODugjdl6KZgwKM1WYQ==} + engines: {node: '>= 10.x'} + dev: false + /streamsearch/1.1.0: resolution: {integrity: sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==} engines: {node: '>=10.0.0'} - dev: true /strip-final-newline/2.0.0: resolution: {integrity: sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==} @@ -1485,7 +1863,6 @@ packages: /supports-preserve-symlinks-flag/1.0.0: resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} engines: {node: '>= 0.4'} - dev: true /svelte-check/2.10.2_svelte@3.55.0: resolution: {integrity: sha512-h1Tuiir0m8J5yqN+Vx6qgKKk1L871e6a9o7rMwVWfu8Qs6Wg7x2R+wcxS3SO3VpW5JCxCat90rxPsZMYgz+HaQ==} @@ -1530,7 +1907,6 @@ packages: svelte: '>=3.19.0' dependencies: svelte: 3.55.0 - dev: true /svelte-preprocess/4.10.7_niwyv7xychq2ag6arq5eqxbomm: resolution: {integrity: sha512-sNPBnqYD6FnmdBrUmBCaqS00RyCsCpj2BG58A1JBswNF7b0OKviwxqVrOL/CKyJrLSClrSeqQv5BXNg2RUbPOw==} @@ -1646,14 +2022,12 @@ packages: /svelte/3.55.0: resolution: {integrity: sha512-uGu2FVMlOuey4JoKHKrpZFkoYyj0VLjJdz47zX5+gVK5odxHM40RVhar9/iK2YFRVxvfg9FkhfVlR0sjeIrOiA==} engines: {node: '>= 8'} - dev: true /tiny-glob/0.2.9: resolution: {integrity: sha512-g/55ssRPUjShh+xkfx9UPDXqhckHEsHr4Vd9zX55oSdGZc/MD0m3sferOkwWtp98bv+kcVfEHtRJgBVJzelrzg==} dependencies: globalyzer: 0.1.0 globrex: 0.1.2 - dev: true /to-regex-range/5.0.1: resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} @@ -1662,9 +2036,16 @@ packages: is-number: 7.0.0 dev: true + /toposort-class/1.0.1: + resolution: {integrity: sha512-OsLcGGbYF3rMjPUf8oKktyvCiUxSbqMMS39m33MAjLTC1DVIH6x3WSt63/M77ihI09+Sdfk1AXvfhCEeUmC7mg==} + dev: false + /totalist/3.0.0: resolution: {integrity: sha512-eM+pCBxXO/njtF7vdFsHuqb+ElbxqtI4r5EAvk6grfAFyJ6IvWlSkfZ5T9ozC6xWw3Fj1fGoSmrl0gUs46JVIw==} engines: {node: '>=6'} + + /tr46/0.0.3: + resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==} dev: true /tslib/2.4.1: @@ -1689,12 +2070,15 @@ packages: jiti: 1.16.0 dev: true + /underscore/1.13.6: + resolution: {integrity: sha512-+A5Sja4HP1M08MaXya7p5LvjuM7K6q/2EaC0+iovj/wOcMsTzMvDFbasi/oSapiwOlt252IqsKqPjCl7huKS0A==} + dev: false + /undici/5.14.0: resolution: {integrity: sha512-yJlHYw6yXPPsuOH0x2Ib1Km61vu4hLiRRQoafs+WUgX1vO64vgnxiCEN9dpIrhZyHFsai3F0AEj4P9zy19enEQ==} engines: {node: '>=12.18'} dependencies: busboy: 1.6.0 - dev: true /unist-util-stringify-position/2.0.3: resolution: {integrity: sha512-3faScn5I+hy9VleOq/qNbAd6pAx7iH5jYBMS9I1HgQVijz/4mv5Bvw5iw1sC/90CODiKo81G/ps8AJrISn687g==} @@ -1734,6 +2118,16 @@ packages: - vite dev: true + /uuid/8.3.2: + resolution: {integrity: sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==} + hasBin: true + dev: false + + /validator/13.7.0: + resolution: {integrity: sha512-nYXQLCBkpJ8X6ltALua9dRrZDHVYxjJ1wgskNt1lH9fzGjs3tgojGSCBjmEPwkWS1y29+DrizMTW19Pr9uB2nw==} + engines: {node: '>= 0.10'} + dev: false + /vfile-message/2.0.4: resolution: {integrity: sha512-DjssxRGkMvifUOJre00juHoP9DPWuzjxKuMDrhNbk2TdaYYBNMStsNhEOt3idrtI12VQYM/1+iM0KOzXi4pxwQ==} dependencies: @@ -1772,7 +2166,6 @@ packages: rollup: 3.7.4 optionalDependencies: fsevents: 2.3.2 - dev: true /vitefu/0.2.3_vite@4.0.1: resolution: {integrity: sha512-75l7TTuU8isAhz1QFtNKjDkqjxvndfMC1AfIMjJ0ZQ59ZD0Ow9QOIsJJX16Wv9PS8f+zMzp6fHy5cCbKG/yVUQ==} @@ -1783,6 +2176,16 @@ packages: optional: true dependencies: vite: 4.0.1 + + /webidl-conversions/3.0.1: + resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} + dev: true + + /whatwg-url/5.0.0: + resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==} + dependencies: + tr46: 0.0.3 + webidl-conversions: 3.0.1 dev: true /which/2.0.2: @@ -1793,10 +2196,30 @@ packages: isexe: 2.0.0 dev: true + /wkx/0.5.0: + resolution: {integrity: sha512-Xng/d4Ichh8uN4l0FToV/258EjMGU9MGcA0HV2d9B/ZpZB3lqQm7nkOdZdm5GhKtLLhAE7PiVQwN4eN+2YJJUg==} + dependencies: + '@types/node': 18.11.15 + dev: false + /wrappy/1.0.2: resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} dev: true + /xtend/4.0.2: + resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==} + engines: {node: '>=0.4'} + dev: false + + /yallist/4.0.0: + resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} + dev: false + + /yaml/2.2.0: + resolution: {integrity: sha512-auf7Gi6QwO7HW//GA9seGvTXVGWl1CM/ADWh1+RxtXr6XOxnT65ovDl9fTi4e0monEyJxCHqDpF6QnFDXmJE4g==} + engines: {node: '>= 14'} + dev: true + /yocto-queue/0.1.0: resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} engines: {node: '>=10'} diff --git a/src/_blog/+layout.server.ts b/src/_blog/+layout.server.ts deleted file mode 100644 index 50a49ff..0000000 --- a/src/_blog/+layout.server.ts +++ /dev/null @@ -1,10 +0,0 @@ -import type { PageServerLoad } from "./$types"; -import { env } from "$env/dynamic/private"; - -export const load: PageServerLoad = async () => { - return { - state: await fetch(env.VITE_API_URL + "/api/v1/state/blog").then( - (res) => res.json() - ).catch(() => ({})) - }; -}; diff --git a/src/_blog/+layout.svelte b/src/_blog/+layout.svelte deleted file mode 100644 index d50f252..0000000 --- a/src/_blog/+layout.svelte +++ /dev/null @@ -1,14 +0,0 @@ - - -{#if data.state.enabled} - -{:else} -
-
- The blog is currently disabled. -
-{/if} \ No newline at end of file diff --git a/src/_blog/+page.server.ts b/src/_blog/+page.server.ts deleted file mode 100644 index 53ee701..0000000 --- a/src/_blog/+page.server.ts +++ /dev/null @@ -1,10 +0,0 @@ -import type { PageServerLoad } from "./$types"; -import { env } from "$env/dynamic/private"; - -export const load: PageServerLoad = async () => { - return { - posts: await fetch(env.VITE_API_URL + "/api/v1/blog").then( - (res) => res.json() - ).catch(() => ({})) - }; -}; diff --git a/src/_blog/[title]/+page.server.ts b/src/_blog/[title]/+page.server.ts deleted file mode 100644 index 79aed18..0000000 --- a/src/_blog/[title]/+page.server.ts +++ /dev/null @@ -1,16 +0,0 @@ -import type { PageServerLoad } from "./$types"; -import { env } from "$env/dynamic/private"; -import { compile } from "mdsvex"; - -export const load: PageServerLoad = async ({ params }) => { - return { - post: await fetch(env.VITE_API_URL + "/api/v1/blog/" + params.title).then( - (res) => res.json() - ).catch(() => ({})), - content: await fetch(env.VITE_API_URL + "/api/v1/blog/" + params.title) - .then((res) => res.json()) - .then((res) => compile(res.content)) - .then((res) => res?.code) - .catch(() => ({})), - }; -}; diff --git a/src/_blog/authors/+page.server.ts b/src/_blog/authors/+page.server.ts deleted file mode 100644 index b939735..0000000 --- a/src/_blog/authors/+page.server.ts +++ /dev/null @@ -1,10 +0,0 @@ -import type { PageServerLoad } from "./$types"; -import { env } from "$env/dynamic/private"; - -export const load: PageServerLoad = async ({ params }) => { - return { - authors: await fetch(env.VITE_API_URL + "/api/v1/blog/authors").then( - (res) => res.json() - ).catch(() => ({})), - }; -}; diff --git a/src/_blog/authors/[author]/+page.server.ts b/src/_blog/authors/[author]/+page.server.ts deleted file mode 100644 index 1d105f6..0000000 --- a/src/_blog/authors/[author]/+page.server.ts +++ /dev/null @@ -1,11 +0,0 @@ -import type { PageServerLoad } from "./$types"; -import { env } from "$env/dynamic/private"; - -export const load: PageServerLoad = async ({ params }) => { - return { - posts: await fetch(env.VITE_API_URL + "/api/v1/blog/authors/" + params.author).then( - (res) => res.json() - ).catch(() => ({})), - authorName: params.author - }; -}; diff --git a/src/_blog/tags/+page.server.ts b/src/_blog/tags/+page.server.ts deleted file mode 100644 index 717d3a6..0000000 --- a/src/_blog/tags/+page.server.ts +++ /dev/null @@ -1,10 +0,0 @@ -import type { PageServerLoad } from "./$types"; -import { env } from "$env/dynamic/private"; - -export const load: PageServerLoad = async ({ params }) => { - return { - tags: await fetch(env.VITE_API_URL + "/api/v1/blog/tags").then( - (res) => res.json() - ).catch(() => ({})), - }; -}; diff --git a/src/_blog/tags/[tag]/+page.server.ts b/src/_blog/tags/[tag]/+page.server.ts deleted file mode 100644 index 3d183da..0000000 --- a/src/_blog/tags/[tag]/+page.server.ts +++ /dev/null @@ -1,11 +0,0 @@ -import type { PageServerLoad } from "./$types"; -import { env } from "$env/dynamic/private"; - -export const load: PageServerLoad = async ({ params }) => { - return { - posts: await fetch(env.VITE_API_URL + "/api/v1/blog/tags/" + params.tag).then( - (res) => res.json() - ).catch(() => ({})), - tagName: params.tag - }; -}; \ No newline at end of file diff --git a/src/hooks.server.ts b/src/hooks.server.ts new file mode 100644 index 0000000..c854589 --- /dev/null +++ b/src/hooks.server.ts @@ -0,0 +1,14 @@ +import { SvelteKitAuth } from "@auth/sveltekit" +import Authentik from '@auth/core/providers/authentik'; +import config from "$lib/config"; + +export const handle = SvelteKitAuth({ + providers: [ + //@ts-ignore + Authentik({ + clientId: config.app.auth.clientId, + clientSecret: config.app.auth.clientSecret, + issuer: config.app.auth.issuer + }) + ] +}) \ No newline at end of file diff --git a/src/lib/Form/Captcha.svelte b/src/lib/Form/Captcha.svelte index a17432b..be10897 100644 --- a/src/lib/Form/Captcha.svelte +++ b/src/lib/Form/Captcha.svelte @@ -1,6 +1,7 @@ -{#if data.state.enabled} - {#if !announcements.error} -
-
+{#if announcements} +
+
+
-
- {#if announcements.severity === "info"} -
-
- Info -
- {:else if announcements.severity === "low"} -
-
- Resolved -
- {:else if announcements.severity === "medium"} -
-
- Attention -
- {:else if announcements.severity === "high"} -
-
- Attention -
- {/if} - -
- {announcements.author} - - -
- {dayjs - .unix(announcements.created) - .format("DD/MM/YYYY HH:mm")} - -
- -
-
- {@html data.content} + {#if announcements.severity === "info"} +
+
+ Info
-
- - {#if announcements.link} -
- Read more... + {:else if announcements.severity === "low"} +
+
+ Resolved +
+ {:else if announcements.severity === "medium"} +
+
+ Attention +
+ {:else if announcements.severity === "high"} +
+
+ Attention
{/if} + +
+ {announcements.author} + + +
+ {dayjs + .unix(announcements.created) + .format("DD/MM/YYYY HH:mm")} +
+ +
+
+ {@html data.content} +
+
+ + {#if announcements.link} + + {/if}
- - {#if announcements.severity === "info"} - - {:else if announcements.severity === "low"} - - {:else if announcements.severity === "medium"} - - {:else if announcements.severity === "high"} - - {/if} - {/if} -{:else} -
-
- Announcements are currently disabled.
-{/if} + + {#if announcements.severity === "info"} + + {:else if announcements.severity === "low"} + + {:else if announcements.severity === "medium"} + + {:else if announcements.severity === "high"} + + {/if} +{/if} \ No newline at end of file diff --git a/src/routes/admin/+layout.server.ts b/src/routes/admin/+layout.server.ts new file mode 100644 index 0000000..37388d9 --- /dev/null +++ b/src/routes/admin/+layout.server.ts @@ -0,0 +1,8 @@ +import type { LayoutServerLoad } from "./$types" +import { redirect } from "@sveltejs/kit"; + +export const load: LayoutServerLoad = async ({ locals }) => { + if (!await locals.getSession()) { + throw redirect(302, "/login"); + } +} diff --git a/src/routes/admin/+layout.svelte b/src/routes/admin/+layout.svelte new file mode 100644 index 0000000..c67fd30 --- /dev/null +++ b/src/routes/admin/+layout.svelte @@ -0,0 +1,10 @@ + + + \ No newline at end of file diff --git a/src/routes/admin/+page.svelte b/src/routes/admin/+page.svelte new file mode 100644 index 0000000..ae31b65 --- /dev/null +++ b/src/routes/admin/+page.svelte @@ -0,0 +1,6 @@ +

Admin dashboard

+ + \ No newline at end of file diff --git a/src/routes/admin/announcements/+page.server.ts b/src/routes/admin/announcements/+page.server.ts new file mode 100644 index 0000000..e04f543 --- /dev/null +++ b/src/routes/admin/announcements/+page.server.ts @@ -0,0 +1,46 @@ +import type { Actions } from "./$types"; +import Joi from "joi"; +import { fail } from "@sveltejs/kit"; +import db from "$lib/db"; + +export const actions: Actions = { + add: async ({ request }) => { + const Announcements = db.model("Announcements"); + + const formData = await request.formData(); + + const BodyTypeSchema = Joi.object({ + title: Joi.string().required(), + severity: Joi.string().required(), + author: Joi.string().required(), + link: Joi.string().optional().allow("") + }); + + if (BodyTypeSchema.validate(Object.fromEntries(formData.entries())).error) { + return fail(400, { addError: true, addMessage: String(BodyTypeSchema.validate(Object.fromEntries(formData.entries())).error) }); + } else { + const now = Math.floor(Date.now() / 1000); + const data = { + ...Object.fromEntries(formData.entries()), + created: now + }; + + await Announcements.sync(); + + await Announcements.destroy({ where: {} }); + + await Announcements.create(data); + + return { addSuccess: true, addMessage: "Your announcement has been posted." }; + } + }, + delete: async () => { + const Announcements = db.model("Announcements"); + + await Announcements.sync(); + + await Announcements.destroy({ where: {} }); + + return { deleteSuccess: true, deleteMessage: "Your announcement has been deleted." }; + } +} \ No newline at end of file diff --git a/src/routes/admin/announcements/+page.svelte b/src/routes/admin/announcements/+page.svelte new file mode 100644 index 0000000..1d5da89 --- /dev/null +++ b/src/routes/admin/announcements/+page.svelte @@ -0,0 +1,55 @@ + + +
+

Post Announcement

+
+ + + + + {#if form?.addSuccess} + {form.addMessage} + {/if} + + {#if form?.addError} + {form.addMessage} + {/if} + +
+

Delete Announcement

+
+ {#if form?.deleteSuccess} + {form.deleteMessage} + {/if} + +
+
\ No newline at end of file diff --git a/src/routes/admin/blog/+page.server.ts b/src/routes/admin/blog/+page.server.ts new file mode 100644 index 0000000..70cb8de --- /dev/null +++ b/src/routes/admin/blog/+page.server.ts @@ -0,0 +1,143 @@ +import type { Actions, PageServerLoad } from "./$types"; +import db from "$lib/db"; +import Joi from "joi"; +import { fail } from "@sveltejs/kit"; +  +export const load = ( async () => { + const Posts = db.model("Posts"); + + return { + postTitles: await Posts.findAll({ attributes: ["title"] }).then((docs) => { + const titles = docs.map((doc) => doc.get("title")); + return titles; + }) + } +}) satisfies PageServerLoad; + +export const actions: Actions = { + add: async ({ request }) => { + const formData = await request.formData(); + + const AddPostTypeSchema = Joi.object({ + title: Joi.string().required(), + content: Joi.string().required(), + tags: Joi.string().optional().allow(""), + author: Joi.string().required() + }); + + if (AddPostTypeSchema.validate(Object.fromEntries(formData.entries())).error) { + return fail(400, { addError: true, addMessage: String(AddPostTypeSchema.validate(Object.fromEntries(formData.entries())).error) }); + } else { + const Posts = db.model("Posts"); + + //@ts-ignore + const words = formData.get("content")!.trim().split(/\s+/).length; + + //@ts-ignore + const tags = formData.get("tags") ? formData.get("tags").split(" ") : []; + + const now = Math.floor(Date.now() / 1000); + + const data = { + title: formData.get("title"), + content: formData.get("content"), + tags: tags, + author: formData.get("author"), + created: now, + words: words, + readingTime: Math.ceil(words / 225) + }; + + if (await Posts.findOne({ where: { title: data.title } })) { + return fail(409, { addError: true, addMessage: "A post with that title already exists." }); + } else { + await Posts.create(data); + + return { addSuccess: true, addMessage: "Your post has been posted." }; + } + } + }, + delete: async ({ request }) => { + const Posts = db.model("Posts"); + + const formData = await request.formData(); + + const deleteFromDb = await Posts.destroy({ where: { title: formData.get("title") } }); + + if (!deleteFromDb) { + return fail(404, { deleteError: true, deleteMessage: "A post with that title does not exist." }); + } else { + return { deleteSuccess: true, deleteMessage: "Your post has been deleted." }; + } + }, + edit: async ({ request }) => { + const EditPostTypeSchema = Joi.object({ + title: Joi.string().required(), + newTitle: Joi.string().optional().allow(""), + content: Joi.string().optional().allow(""), + tags: Joi.string().optional().allow(""), + area: Joi.string().required().allow("title", "content", "tags") + }); + + const formData = await request.formData(); + + if (EditPostTypeSchema.validate(Object.fromEntries(formData.entries())).error) { + return fail(400, { editError: true, editMessage: String(EditPostTypeSchema.validate(Object.fromEntries(formData.entries())).error) }); + } else { + if (formData.get("area") === "title") { + const Posts = db.model("Posts"); + + const updateOnDb = await Posts.update( + { title: formData.get("newTitle") }, + { where: { title: formData.get("title") } } + ); + + if (updateOnDb[0] === 0) { + return fail(404, { editError: true, editMessage: "A post with that title does not exist." }); + } else { + return { editSuccess: true, editMessage: "Your post has been edited." }; + } + } else if (formData.get("area") === "content") { + const Posts = db.model("Posts"); + + //@ts-ignore + const words = formData.get("content")!.trim().split(/\s+/).length; + + const now = Math.floor(Date.now() / 1000); + + const updateonDb = await Posts.update( + { + content: formData.get("content"), + words: words, + readingTime: Math.ceil(words / 225), + updated: now + }, + { where: { title: formData.get("title") } } + ); + + if (updateonDb[0] === 0) { + return fail(404, { editError: true, editMessage: "A post with that title does not exist." }); + } else { + return { editSuccess: true, editMessage: "Your post has been edited." }; + } + } else if (formData.get("area") === "tags") { + const Posts = db.model("Posts"); + + //@ts-ignore + const tags = formData.get("tags") ? formData.get("tags").split(" ") : []; + + const updateOnDb = await Posts.update( + { tags: tags + }, + { where: { title: formData.get("title") } } + ); + + if (updateOnDb[0] === 0) { + return fail(404, { editError: true, editMessage: "A post with that title does not exist." }); + } else { + return { editSuccess: true, editMessage: "Your post has been edited." }; + } + } + } + } +} \ No newline at end of file diff --git a/src/routes/admin/blog/+page.svelte b/src/routes/admin/blog/+page.svelte new file mode 100644 index 0000000..1cd2e7d --- /dev/null +++ b/src/routes/admin/blog/+page.svelte @@ -0,0 +1,81 @@ + + +

Add post

+ +
+ + + + + {#if form?.addSuccess} + {form.addMessage} + {/if} + + {#if form?.addError} + {form.addMessage} + {/if} + +
+ +

Delete post

+ +
+ + {#if form?.deleteSuccess} + {form.deleteMessage} + {/if} + + {#if form?.deleteError} + {form.deleteMessage} + {/if} + +
+ +

Edit post

+ +
+ + + + + + {#if form?.editSuccess} + {form.editMessage} + {/if} + + {#if form?.editError} + {form.editMessage} + {/if} + +
\ No newline at end of file diff --git a/src/routes/api/status/+server.ts b/src/routes/api/status/+server.ts new file mode 100644 index 0000000..4aea238 --- /dev/null +++ b/src/routes/api/status/+server.ts @@ -0,0 +1,25 @@ +import type { RequestHandler } from './$types'; +import statusData from "./statusData"; + +const map = new Map(); + +const updateMap = () => { + map.set("data", { + status: statusData, + updated: Math.floor(Date.now() / 1000) + }); +}; + +updateMap(); + +setInterval(updateMap, 30000); + +export const GET = (() => { + const data = map.get("data"); + + return new Response(JSON.stringify(data), { + headers: { + "content-type": "application/json; charset=utf-8" + } + }); +}) satisfies RequestHandler; \ No newline at end of file diff --git a/src/routes/api/status/statusData.ts b/src/routes/api/status/statusData.ts new file mode 100644 index 0000000..090066a --- /dev/null +++ b/src/routes/api/status/statusData.ts @@ -0,0 +1,119 @@ +import axios from "axios"; + +const fetchStatus = (domain: string) => { + const req = axios("https://" + domain, { timeout: 10000 }) + .then((res) => res.status) + .catch((err) => err.response.status); + + return req; +}; + +const statusData = [ + { + name: "Privacy front-ends", + data: [ + { + name: "Invidious", + description: "A frontend for YouTube.", + link: "https://invidious.projectsegfau.lt/", + us: "https://inv.us.projectsegfau.lt", + bp: "https://inv.bp.projectsegfau.lt", + icon: "https://github.com/iv-org/invidious/raw/master/assets/invidious-colored-vector.svg", + status: await fetchStatus("invidious.projectsegfau.lt"), + statusUs: await fetchStatus("inv.us.projectsegfau.lt"), + statusBp: await fetchStatus("inv.bp.projectsegfau.lt") + }, + { + name: "Librarian", + description: "A frontend for Odysee.", + link: "https://lbry.projectsegfau.lt/", + icon: "https://codeberg.org/avatars/dd785d92b4d4df06d448db075cd29274", + status: await fetchStatus("lbry.projectsegfau.lt") + }, + { + name: "Libreddit", + description: "A frontend for Reddit.", + link: "https://libreddit.projectsegfau.lt/", + us: "https://libreddit.us.projectsegfau.lt", + icon: "https://github.com/spikecodes/libreddit/raw/master/static/logo.png", + status: await fetchStatus("libreddit.projectsegfau.lt"), + statusUs: await fetchStatus("libreddit.us.projectsegfau.lt") + }, + { + name: "Nitter", + description: "A frontend for Twitter.", + link: "https://nitter.projectsegfau.lt/", + us: "https://nitter.us.projectsegfau.lt", + icon: "https://github.com/zedeus/nitter/raw/master/public/logo.png", + status: await fetchStatus("nitter.projectsegfau.lt"), + statusUs: await fetchStatus("nitter.us.projectsegfau.lt") + }, + { + name: "Piped", + description: "Another frontend for YouTube.", + link: "https://piped.projectsegfau.lt/", + us: "https://piped.us.projectsegfau.lt", + icon: "https://github.com/TeamPiped/Piped/raw/master/public/img/icons/logo.svg", + status: await fetchStatus("piped.projectsegfau.lt"), + statusUs: await fetchStatus("piped.us.projectsegfau.lt") + } + ] + }, + { + name: "Useful tools and services", + data: [ + { + name: "Element", + description: + "An open source and decentralized chat application.", + link: "https://chat.projectsegfau.lt/", + icon: "https://element.io/images/logo-mark-primary.svg", + status: await fetchStatus("chat.projectsegfau.lt") + }, + { + name: "SearXNG", + description: "A private meta-search engine.", + link: "https://search.projectsegfau.lt/search", + us: "https://search.us.projectsegfau.lt", + icon: "https://docs.searxng.org/_static/searxng-wordmark.svg", + status: await fetchStatus("search.projectsegfau.lt"), + statusUs: await fetchStatus("search.us.projectsegfau.lt") + }, + { + name: "Gitea", + description: "A web interface for Git, alternative to GitHub.", + link: "https://git.projectsegfau.lt/", + icon: "https://gitea.io/images/gitea.png", + status: await fetchStatus("git.projectsegfau.lt") + } + ] + }, + { + name: "Internal services", + data: [ + { + name: "Portainer", + description: "Portainer instance for our servers.", + link: "https://portainer.projectsegfau.lt/", + icon: "https://avatars.githubusercontent.com/u/22225832", + status: await fetchStatus("portainer.projectsegfau.lt") + }, + { + name: "mailcow", + description: "Our mail server and webmail.", + link: "https://mail.projectsegfau.lt/", + icon: "https://mailcow.email/images/cow_mailcow.svg", + status: await fetchStatus("mail.projectsegfau.lt") + }, + { + name: "Plausible analytics", + description: "Analytics for our website.", + link: "https://analytics.projectsegfau.lt/projectsegfau.lt", + icon: "https://avatars.githubusercontent.com/u/54802774", + status: await fetchStatus("analytics.projectsegfau.lt") + } + ] + } +]; + +export default statusData; \ No newline at end of file diff --git a/src/routes/blog/+page.server.ts b/src/routes/blog/+page.server.ts new file mode 100644 index 0000000..405e68f --- /dev/null +++ b/src/routes/blog/+page.server.ts @@ -0,0 +1,20 @@ +import type { PageServerLoad } from "./$types"; +import db from "$lib/db"; + +export const load: PageServerLoad = async () => { + const Posts = db.model("Posts"); + + const posts = await Posts.findAll().then((docs) => { + return docs.map((doc) => doc.get()); + }); + + if (posts.length === 0 || posts[0] === undefined) { + return { + posts: [] + } + } else { + return { + posts: posts.sort((a, b) => b["created"] - a["created"]) + } + } +}; diff --git a/src/_blog/+page.svelte b/src/routes/blog/+page.svelte similarity index 95% rename from src/_blog/+page.svelte rename to src/routes/blog/+page.svelte index 85efa1c..71e986a 100644 --- a/src/_blog/+page.svelte +++ b/src/routes/blog/+page.svelte @@ -7,10 +7,10 @@ - Timeline | Project Segfault + Blog | Project Segfault diff --git a/src/routes/blog/[title]/+page.server.ts b/src/routes/blog/[title]/+page.server.ts new file mode 100644 index 0000000..4b5f7ee --- /dev/null +++ b/src/routes/blog/[title]/+page.server.ts @@ -0,0 +1,27 @@ +import type { PageServerLoad } from "./$types"; +import db from "$lib/db"; +import { compile } from "mdsvex"; + +export const load: PageServerLoad = async ({ params }) => { + const Posts = db.model("Posts"); + + const data = await Posts.findAll({ + where: { + title: params.title + } + }).then((docs) => { + return docs.map((doc) => doc.get()); + }); + + if (data.length === 0 || data[0] === undefined) { + return { + post: {}, + content: {} + } + } else { + return { + post: data[0], + content: compile(data[0].content).then((res) => res?.code) + } + } +}; diff --git a/src/_blog/[title]/+page.svelte b/src/routes/blog/[title]/+page.svelte similarity index 100% rename from src/_blog/[title]/+page.svelte rename to src/routes/blog/[title]/+page.svelte diff --git a/src/routes/blog/authors/+page.server.ts b/src/routes/blog/authors/+page.server.ts new file mode 100644 index 0000000..21f31ba --- /dev/null +++ b/src/routes/blog/authors/+page.server.ts @@ -0,0 +1,23 @@ +import type { PageServerLoad } from "./$types"; +import db from "$lib/db"; + +export const load: PageServerLoad = async ({ params }) => { + const Posts = db.model("Posts"); + + const data = await Posts.findAll({ + attributes: ["author"] + }) + + if (data.length === 0 || data[0] === undefined) { + return { + authors: [] + } + } else { + const authors = data.map((post) => post["author"]); + const uniqueAuthors = [...new Set(authors)]; + + return { + authors: uniqueAuthors + } + } +}; diff --git a/src/_blog/authors/+page.svelte b/src/routes/blog/authors/+page.svelte similarity index 100% rename from src/_blog/authors/+page.svelte rename to src/routes/blog/authors/+page.svelte diff --git a/src/routes/blog/authors/[author]/+page.server.ts b/src/routes/blog/authors/[author]/+page.server.ts new file mode 100644 index 0000000..6937ffb --- /dev/null +++ b/src/routes/blog/authors/[author]/+page.server.ts @@ -0,0 +1,26 @@ +import type { PageServerLoad } from "./$types"; +import db from "$lib/db"; + +export const load: PageServerLoad = async ({ params }) => { + const Posts = db.model("Posts"); + + const data = await Posts.findAll({ + where: { + author: params.author + } + }).then((docs) => { + return docs.map((doc) => doc.get()); + }); + + if (data.length === 0 || data[0] === undefined) { + return { + posts: [], + authorName: params.author + } + } else { + return { + posts: data, + authorName: params.author + } + } +}; \ No newline at end of file diff --git a/src/_blog/authors/[author]/+page.svelte b/src/routes/blog/authors/[author]/+page.svelte similarity index 100% rename from src/_blog/authors/[author]/+page.svelte rename to src/routes/blog/authors/[author]/+page.svelte diff --git a/src/routes/blog/tags/+page.server.ts b/src/routes/blog/tags/+page.server.ts new file mode 100644 index 0000000..4542281 --- /dev/null +++ b/src/routes/blog/tags/+page.server.ts @@ -0,0 +1,20 @@ +import type { PageServerLoad } from "./$types"; +import db from "$lib/db"; + +export const load: PageServerLoad = async () => { + const Posts = db.model("Posts"); + + const data = await Posts.findAll({ attributes: ["tags"] }) + + if (data.length === 0 || data[0] === undefined) { + return { + tags: [] + } + } else { + const tags = data.map((post) => post["tags"]).flat(); + const uniqueTags = [...new Set(tags)]; + return { + tags: uniqueTags + } + } +}; diff --git a/src/_blog/tags/+page.svelte b/src/routes/blog/tags/+page.svelte similarity index 100% rename from src/_blog/tags/+page.svelte rename to src/routes/blog/tags/+page.svelte diff --git a/src/routes/blog/tags/[tag]/+page.server.ts b/src/routes/blog/tags/[tag]/+page.server.ts new file mode 100644 index 0000000..e006ad8 --- /dev/null +++ b/src/routes/blog/tags/[tag]/+page.server.ts @@ -0,0 +1,29 @@ +import type { PageServerLoad } from "./$types"; +import db from "$lib/db"; +import { Op } from "sequelize"; + +export const load: PageServerLoad = async ({ params }) => { + const Posts = db.model("Posts"); + + const data = await Posts.findAll({ + where: { + tags: { + [Op.contains]: [params.tag] + } + } + }).then((docs) => { + return docs.map((doc) => doc.get()); + }); + + if (data.length === 0 || data[0] === undefined) { + return { + posts: [], + tagName: params.tag + } + } else { + return { + posts: data, + tagName: params.tag + } + } +}; \ No newline at end of file diff --git a/src/_blog/tags/[tag]/+page.svelte b/src/routes/blog/tags/[tag]/+page.svelte similarity index 100% rename from src/_blog/tags/[tag]/+page.svelte rename to src/routes/blog/tags/[tag]/+page.svelte diff --git a/src/routes/contact/+page.server.ts b/src/routes/contact/+page.server.ts index d20ee47..0ffb8b0 100644 --- a/src/routes/contact/+page.server.ts +++ b/src/routes/contact/+page.server.ts @@ -1,11 +1,64 @@ -import type { PageServerLoad } from "../$types"; -import { env } from "$env/dynamic/private"; +import type { Actions } from "./$types"; +import { Webhook, MessageBuilder } from "discord-webhook-node"; +import Joi from "joi"; +import { fail } from "@sveltejs/kit"; +import config from "$lib/config"; -export const load: PageServerLoad = async () => { - return { - state: await fetch(env.VITE_API_URL + "/api/v1/state/form").then( - (res) => res.json() - ).catch(() => ({})), - apiUrl: env.VITE_API_URL - }; -}; +export const actions: Actions = { + form: async ({ request, getClientAddress, fetch }) => { + const formData = await request.formData(); + + const BodyTypeSchema = Joi.object({ + email: Joi.string().email().required(), + commentType: Joi.string().required(), + message: Joi.string().required(), + "h-captcha-response": Joi.string().required(), + "g-recaptcha-response": Joi.string().optional().allow("") + }); + + if (BodyTypeSchema.validate(Object.fromEntries(formData.entries())).error) { + return fail(400, { error: true, message: String(BodyTypeSchema.validate(Object.fromEntries(formData.entries())).error) }); + } else { + const ip = getClientAddress(); + + const verify = await fetch("https://hcaptcha.com/siteverify", { + method: "POST", + headers: { + "Content-Type": "application/x-www-form-urlencoded" + }, + body: new URLSearchParams({ + secret: config.app.hcaptcha.secret, + response: String(formData.get("h-captcha-response")), + remoteip: ip + }) + }).then((res) => res.json()) + + + const hook = new Webhook(config.app.webhook); + + const data = await verify; + + if (data.success) { + const embed = new MessageBuilder() + .setAuthor( + `${ip}, ${formData.get("email")}, https://abuseipdb.com/check/${ip}` + ) + // @ts-ignore + .addField("Comment type", formData.get("commentType"), true) + // @ts-ignore + .addField("Message", formData.get("message")) + .setTimestamp(); + + hook.send(embed); + + return { success: true, message: "Thanks for your message, we will get back to you as soon as possible." }; + } else { + hook.send( + `IP: ${ip}, https://abuseipdb.com/check/${ip}\nfailed to complete the captcha with error: ${data["error-codes"]}.` + ); + + return fail(400, { error: true, message: "Captcha failed or expired, please try again. If this keeps happening, assume the captcha is broken and contact us on Matrix." + " Error: " + data["error-codes"] }); + } + } + } +} \ No newline at end of file diff --git a/src/routes/contact/+page.svelte b/src/routes/contact/+page.svelte index eb9f785..bcf6753 100644 --- a/src/routes/contact/+page.svelte +++ b/src/routes/contact/+page.svelte @@ -1,58 +1,58 @@ - {title} + Contact us | Project Segfault

Contact us

Contact form

- {#if data.state.enabled === true} -
+ + - - Select a type of comment - - - - - - -