From 49ff503898fbf82598a63aa9926bc3bbb701c953 Mon Sep 17 00:00:00 2001 From: Mawoka Date: Fri, 15 Apr 2022 16:10:56 +0200 Subject: [PATCH] :sparkles: Added session-management with a map! --- classquiz/routers/users.py | 6 + classquiz/routers/utils.py | 36 ++- frontend/Dockerfile | 2 + frontend/package.json | 7 + frontend/pnpm-lock.yaml | 318 ++++++++++++++++++++ frontend/src/lib/Map.svelte | 17 ++ frontend/src/lib/Spinner.svelte | 10 + frontend/src/routes/account/settings.svelte | 180 ++++++++++- frontend/src/routes/docs/develop.svelte | 6 + frontend/src/routes/docs/self-host.svelte | 34 ++- frontend/svelte.config.js | 4 +- 11 files changed, 606 insertions(+), 14 deletions(-) create mode 100644 frontend/src/lib/Map.svelte create mode 100644 frontend/src/lib/Spinner.svelte diff --git a/classquiz/routers/users.py b/classquiz/routers/users.py index 971c597..78def79 100644 --- a/classquiz/routers/users.py +++ b/classquiz/routers/users.py @@ -252,3 +252,9 @@ async def delete_session(session_id: str, user: User = Depends(get_current_user) raise HTTPException(status_code=400, detail="Invalid session id") await UserSession.objects.filter(user=user, id=session_id).delete() return {"message": "Session deleted"} + + +@router.get("/session", response_model=UserSession, response_model_exclude={"user", "session_key"}) +async def get_session(user: User = Depends(get_current_user)): + session = await UserSession.objects.filter(user=user).first() + return session diff --git a/classquiz/routers/utils.py b/classquiz/routers/utils.py index e4fdbf9..bf01348 100644 --- a/classquiz/routers/utils.py +++ b/classquiz/routers/utils.py @@ -2,10 +2,16 @@ import io import qrcode import qrcode.image.svg -from fastapi import APIRouter +from fastapi import APIRouter, Depends from fastapi.responses import Response +from pydantic import BaseModel, Field, ValidationError +from fastapi.responses import JSONResponse +from classquiz.auth import get_current_user from classquiz.config import settings +from aiohttp import ClientSession + +from classquiz.db.models import User settings = settings() @@ -23,3 +29,31 @@ async def get_qr(quiz_pin: str): qr.make(fit=True) qr.make_image(fill_color="black", back_color="white").save(buf, "SVG") return Response(content=buf.getvalue(), media_type="image/svg+xml") + + +class IpResponse(BaseModel): + status: str + country: str + countryCode: str + region: str + regionName: str + city: str + zip: str + lat: float + lon: float + timezone: str + isp: str + org: str + as_attr: str = Field(alias="as") + query: str + + +@router.get("/ip-lookup/{ip}", response_model=IpResponse) +async def get_ip_data(ip: str, _: User = Depends(get_current_user)): + async with ClientSession() as session: + async with session.get(f"http://ip-api.com/json/{ip}") as response: + data = await response.json() + try: + return IpResponse(**data) + except ValidationError: + return JSONResponse(status_code=response.status, content=data) diff --git a/frontend/Dockerfile b/frontend/Dockerfile index 0ebef4a..806d216 100644 --- a/frontend/Dockerfile +++ b/frontend/Dockerfile @@ -4,6 +4,8 @@ FROM node:17.8-bullseye as builder # ENV API_URL=http://api:80 ENV API_URL=https://mawoka.eu ENV REDIS_URL=CHANGE_ME +ENV VITE_MAPBOX_ACCESS_TOKEN=pk.eyJ1IjoibWF3b2thIiwiYSI6ImNsMjBob3d4ZjBhcGszYnE0bWp4aXB1ZW4ifQ.IByxV1qeIuEWpHCWsuB88A +# This Mpbox-token is restricted to the following urls: classquiz.de, classquiz.mawoka.eu, test.com ENV VITE_HCAPTCHA=ee81b2a1-acf3-4d20-b2a4-a7ea94c7eba5 ENV VITE_SENTRY=https://4981de1f72f24fd7b5e21b8913b93a02@o661934.ingest.sentry.io/6254641 # change working directory diff --git a/frontend/package.json b/frontend/package.json index d9856f7..9880a32 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -14,6 +14,7 @@ "translations-scan": "i18next-scanner --config i18next-scanner.config.engine.cjs src/**/*.svelte" }, "devDependencies": { + "@beyonk/svelte-mapbox": "^8.1.4", "@felte/validator-yup": "^1.0.3", "@fontsource/marck-script": "^4.5.5", "@sentry/browser": "^6.19.6", @@ -25,6 +26,7 @@ "@types/cookie": "^0.4.1", "@types/js-cookie": "^3.0.1", "@types/luxon": "^2.3.1", + "@types/ua-parser-js": "^0.7.36", "@types/yup": "^0.29.13", "@typescript-eslint/eslint-plugin": "^5.19.0", "@typescript-eslint/parser": "^5.19.0", @@ -39,7 +41,9 @@ "i18next-browser-languagedetector": "^6.1.4", "ioredis": "^5.0.4", "js-cookie": "^3.0.1", + "leaflet": "^1.7.1", "luxon": "^2.3.1", + "mapbox-gl": "^2.8.1", "node-fetch": "^3.2.3", "postcss": "^8.4.12", "postcss-load-config": "^3.1.4", @@ -49,6 +53,7 @@ "socket.io-client": "^4.4.1", "svelte": "^3.47.0", "svelte-check": "^2.6.0", + "svelte-leafletjs": "^0.8.2", "svelte-preprocess": "^4.10.5", "svelte-tippy": "^1.3.2", "swiper": "^8.1.0", @@ -56,6 +61,8 @@ "tippy.js": "^6.3.7", "tslib": "^2.3.1", "typescript": "~4.6", + "ua-parser-js": "^1.0.2", + "vite-plugin-iso-import": "^0.1.3", "yup": "^0.32.11" }, "type": "module", diff --git a/frontend/pnpm-lock.yaml b/frontend/pnpm-lock.yaml index d65661f..58efe3a 100644 --- a/frontend/pnpm-lock.yaml +++ b/frontend/pnpm-lock.yaml @@ -1,6 +1,7 @@ lockfileVersion: 5.3 specifiers: + '@beyonk/svelte-mapbox': ^8.1.4 '@felte/validator-yup': ^1.0.3 '@fontsource/marck-script': ^4.5.5 '@sentry/browser': ^6.19.6 @@ -12,6 +13,7 @@ specifiers: '@types/cookie': ^0.4.1 '@types/js-cookie': ^3.0.1 '@types/luxon': ^2.3.1 + '@types/ua-parser-js': ^0.7.36 '@types/yup': ^0.29.13 '@typescript-eslint/eslint-plugin': ^5.19.0 '@typescript-eslint/parser': ^5.19.0 @@ -27,7 +29,9 @@ specifiers: i18next-browser-languagedetector: ^6.1.4 ioredis: ^5.0.4 js-cookie: ^3.0.1 + leaflet: ^1.7.1 luxon: ^2.3.1 + mapbox-gl: ^2.8.1 node-fetch: ^3.2.3 postcss: ^8.4.12 postcss-load-config: ^3.1.4 @@ -37,6 +41,7 @@ specifiers: socket.io-client: ^4.4.1 svelte: ^3.47.0 svelte-check: ^2.6.0 + svelte-leafletjs: ^0.8.2 svelte-preprocess: ^4.10.5 svelte-tippy: ^1.3.2 swiper: ^8.1.0 @@ -44,12 +49,15 @@ specifiers: tippy.js: ^6.3.7 tslib: ^2.3.1 typescript: ~4.6 + ua-parser-js: ^1.0.2 + vite-plugin-iso-import: ^0.1.3 yup: ^0.32.11 dependencies: i18next: 21.6.16 devDependencies: + '@beyonk/svelte-mapbox': 8.1.4 '@felte/validator-yup': 1.0.3_yup@0.32.11 '@fontsource/marck-script': 4.5.5 '@sentry/browser': 6.19.6 @@ -61,6 +69,7 @@ devDependencies: '@types/cookie': 0.4.1 '@types/js-cookie': 3.0.1 '@types/luxon': 2.3.1 + '@types/ua-parser-js': 0.7.36 '@types/yup': 0.29.13 '@typescript-eslint/eslint-plugin': 5.19.0_f34adc8488d2e4f014fe61432d70cbf2 '@typescript-eslint/parser': 5.19.0_eslint@8.13.0+typescript@4.6.3 @@ -75,7 +84,9 @@ devDependencies: i18next-browser-languagedetector: 6.1.4 ioredis: 5.0.4 js-cookie: 3.0.1 + leaflet: 1.7.1 luxon: 2.3.1 + mapbox-gl: 2.8.1 node-fetch: 3.2.3 postcss: 8.4.12 postcss-load-config: 3.1.4_postcss@8.4.12 @@ -85,6 +96,7 @@ devDependencies: socket.io-client: 4.4.1 svelte: 3.47.0 svelte-check: 2.6.0_1b6d41eb44819e98e49be8cacdc96c52 + svelte-leafletjs: 0.8.2 svelte-preprocess: 4.10.5_f6bc70e70cf1a2fde5bc22fd7000ea23 svelte-tippy: 1.3.2 swiper: 8.1.0 @@ -92,6 +104,8 @@ devDependencies: tippy.js: 6.3.7 tslib: 2.3.1 typescript: 4.6.3 + ua-parser-js: 1.0.2 + vite-plugin-iso-import: 0.1.3 yup: 0.32.11 packages: @@ -104,6 +118,13 @@ packages: dependencies: regenerator-runtime: 0.13.9 + /@beyonk/svelte-mapbox/8.1.4: + resolution: + { + integrity: sha512-H7c5i+iWReb3try5Vk2rIeIXJ6BqmO3eb8OeBntrdKAiKEA0ye7n5t4C13Ne4vvMrfqqzyHuV6kGsM0y/g+lJg== + } + dev: true + /@eslint/eslintrc/1.2.1: resolution: { @@ -197,6 +218,68 @@ packages: } dev: true + /@mapbox/geojson-rewind/0.5.1: + resolution: + { + integrity: sha512-eL7fMmfTBKjrb+VFHXCGv9Ot0zc3C0U+CwXo1IrP+EPwDczLoXv34Tgq3y+2mPSFNVUXgU42ILWJTC7145KPTA== + } + hasBin: true + dependencies: + get-stream: 6.0.1 + minimist: 1.2.6 + dev: true + + /@mapbox/geojson-types/1.0.2: + resolution: + { + integrity: sha512-e9EBqHHv3EORHrSfbR9DqecPNn+AmuAoQxV6aL8Xu30bJMJR1o8PZLZzpk1Wq7/NfCbuhmakHTPYRhoqLsXRnw== + } + dev: true + + /@mapbox/jsonlint-lines-primitives/2.0.2: + resolution: { integrity: sha1-zlblOfg1UrWNENZy6k1vya3HsjQ= } + engines: { node: '>= 0.6' } + dev: true + + /@mapbox/mapbox-gl-supported/2.0.1: + resolution: + { + integrity: sha512-HP6XvfNIzfoMVfyGjBckjiAOQK9WfX0ywdLubuPMPv+Vqf5fj0uCbgBQYpiqcWZT6cbyyRnTSXDheT1ugvF6UQ== + } + dev: true + + /@mapbox/point-geometry/0.1.0: + resolution: { integrity: sha1-ioP5M1x4YO/6Lu7KJUMyqgru2PI= } + dev: true + + /@mapbox/tiny-sdf/2.0.5: + resolution: + { + integrity: sha512-OhXt2lS//WpLdkqrzo/KwB7SRD8AiNTFFzuo9n14IBupzIMa67yGItcK7I2W9D8Ghpa4T04Sw9FWsKCJG50Bxw== + } + dev: true + + /@mapbox/unitbezier/0.0.0: + resolution: { integrity: sha1-FWUb1VOme4WB+zmIEMmK2Go0Uk4= } + dev: true + + /@mapbox/vector-tile/1.3.1: + resolution: + { + integrity: sha512-MCEddb8u44/xfQ3oD+Srl/tNcQoqTw3goGk2oLsrFxOTc3dUp+kAnby3PvAeeBYSMSjSPD1nd1AJA6W49WnoUw== + } + dependencies: + '@mapbox/point-geometry': 0.1.0 + dev: true + + /@mapbox/whoots-js/3.1.0: + resolution: + { + integrity: sha512-Es6WcD0nO5l+2BOQS4uLfNPYQaNDfbot3X1XUoloz+x0mPDS3eeORZJl06HXjwBG1fOGwCRnzK88LMdxKRrd6Q== + } + engines: { node: '>=6.0.0' } + dev: true + /@nodelib/fs.scandir/2.1.5: resolution: { @@ -521,6 +604,13 @@ packages: '@types/node': 17.0.23 dev: true + /@types/ua-parser-js/0.7.36: + resolution: + { + integrity: sha512-N1rW+njavs70y2cApeIw1vLMYXRwfBy+7trgavGuuTfOd7j1Yh7QTRc/yqsPl6ncokt72ZXuxEU0PiCp9bSwNQ== + } + dev: true + /@types/yup/0.29.13: resolution: { @@ -808,6 +898,17 @@ packages: postcss-value-parser: 4.2.0 dev: true + /axios/0.25.0: + resolution: + { + integrity: sha512-cD8FOb0tRH3uuEe6+evtAbgJtfxr7ly3fQjYcMcuPlgkwVS9xboaVIpcDV+cYQe+yGykgwZCs1pzjntcGa6l5g== + } + dependencies: + follow-redirects: 1.14.9 + transitivePeerDependencies: + - debug + dev: true + /backo2/1.0.2: resolution: { integrity: sha1-MasayLEpNjRj41s+u2n038+6eUc= } dev: true @@ -1042,6 +1143,10 @@ packages: engines: { node: '>= 6' } dev: true + /csscolorparser/1.0.3: + resolution: { integrity: sha1-s085HupNqPPpgjHizNjfnAQfFxs= } + dev: true + /cssesc/3.0.0: resolution: { @@ -1274,6 +1379,13 @@ packages: domhandler: 4.3.1 dev: true + /earcut/2.2.3: + resolution: + { + integrity: sha512-iRDI1QeCQIhMCZk48DRDMVgQSSBDmbzzNhnxIo+pwx3swkfjMh6vh0nWLq1NdvGHLKH6wIrAM3vQWeTj6qeoug== + } + dev: true + /electron-to-chromium/1.4.107: resolution: { @@ -1319,6 +1431,13 @@ packages: } dev: true + /es-module-lexer/0.9.3: + resolution: + { + integrity: sha512-1HQ2M2sPtxwnvOvT1ZClHyQDiggdNjURWpY2we6aMKCQiUVxTmVs2UYPLIrD84sS+kMdUwfBSylbJPwNnBrnHQ== + } + dev: true + /es6-promise/3.3.1: resolution: { integrity: sha1-oIzd6EzNvzTQJ6FFG8kdS80ophM= } dev: true @@ -1900,6 +2019,19 @@ packages: } dev: true + /follow-redirects/1.14.9: + resolution: + { + integrity: sha512-MQDfihBQYMcyy5dhRDJUHcw7lb2Pv/TuE6xP1vyraLukNDHKbDxDNaOE3NbCAdKQApno+GPRyo1YAp89yCjK4w== + } + engines: { node: '>=4.0' } + peerDependencies: + debug: '*' + peerDependenciesMeta: + debug: + optional: true + dev: true + /formdata-polyfill/4.0.10: resolution: { @@ -1943,6 +2075,28 @@ packages: resolution: { integrity: sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc= } dev: true + /geojson-vt/3.2.1: + resolution: + { + integrity: sha512-EvGQQi/zPrDA6zr6BnJD/YhwAkBP8nnJ9emh3EnHQKVMfg/MRVtPbMYdgVy/IaEmn4UfagD2a6fafPDL5hbtwg== + } + dev: true + + /get-stream/6.0.1: + resolution: + { + integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg== + } + engines: { node: '>=10' } + dev: true + + /gl-matrix/3.4.3: + resolution: + { + integrity: sha512-wcCp8vu8FT22BnvKVPjXa/ICBWRq/zjFfdofZy1WSpQZpphblv12/bOQLBC1rMM7SGOFS9ltVmKOHil5+Ml7gA== + } + dev: true + /glob-parent/5.1.2: resolution: { @@ -2023,6 +2177,13 @@ packages: } dev: true + /grid-index/1.1.0: + resolution: + { + integrity: sha512-HZRwumpOGUrHyxO5bqKZL0B0GlUpwtCAzZ42sgxUPniu33R1LSFH5yrIcBCHjkctCAh3mtWKcKd9J4vDDdeVHA== + } + dev: true + /has-cors/1.1.0: resolution: { integrity: sha1-XkdHk/fqmEPRu5nCPu9J/xJv/zk= } dev: true @@ -2071,6 +2232,13 @@ packages: '@babel/runtime': 7.17.9 dev: false + /ieee754/1.2.1: + resolution: + { + integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== + } + dev: true + /ignore/5.2.0: resolution: { @@ -2211,6 +2379,13 @@ packages: resolution: { integrity: sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE= } dev: true + /kdbush/3.0.0: + resolution: + { + integrity: sha512-hRkd6/XW4HTsA9vjVpY9tuXJYLSlelnkTmVFu4M9/7MIYQtFcHpbugAU7UbOfjOiVSVYl2fqgBuJ32JUmRo5Ew== + } + dev: true + /kleur/4.1.4: resolution: { @@ -2219,6 +2394,13 @@ packages: engines: { node: '>=6' } dev: true + /leaflet/1.7.1: + resolution: + { + integrity: sha512-/xwPEBidtg69Q3HlqPdU3DnrXQOvQU/CCHA1tcDQVzOwm91YMYaILjNp7L4Eaw5Z4sOYdbBz6koWyibppd8Zqw== + } + dev: true + /levn/0.4.1: resolution: { @@ -2320,6 +2502,37 @@ packages: sourcemap-codec: 1.4.8 dev: true + /mapbox-gl/2.8.1: + resolution: + { + integrity: sha512-AmttsfvuWonyuOu7HcwM6Ok738A0yVYx5h3tjCAFrAsivY8SiCvF/stK2CXhSw2/6/BbZ3qe/S03J7GvisNskA== + } + dependencies: + '@mapbox/geojson-rewind': 0.5.1 + '@mapbox/geojson-types': 1.0.2 + '@mapbox/jsonlint-lines-primitives': 2.0.2 + '@mapbox/mapbox-gl-supported': 2.0.1 + '@mapbox/point-geometry': 0.1.0 + '@mapbox/tiny-sdf': 2.0.5 + '@mapbox/unitbezier': 0.0.0 + '@mapbox/vector-tile': 1.3.1 + '@mapbox/whoots-js': 3.1.0 + csscolorparser: 1.0.3 + earcut: 2.2.3 + geojson-vt: 3.2.1 + gl-matrix: 3.4.3 + grid-index: 1.1.0 + minimist: 1.2.6 + murmurhash-js: 1.0.0 + pbf: 3.2.1 + potpack: 1.0.2 + quickselect: 2.0.0 + rw: 1.3.3 + supercluster: 7.1.5 + tinyqueue: 2.0.3 + vt-pbf: 3.1.3 + dev: true + /mdn-data/2.0.14: resolution: { @@ -2403,6 +2616,10 @@ packages: } dev: true + /murmurhash-js/1.0.0: + resolution: { integrity: sha1-sGJ44h/Gw3+lMTcysEEry2rhX1E= } + dev: true + /nanoclone/0.2.1: resolution: { @@ -2561,6 +2778,17 @@ packages: engines: { node: '>=8' } dev: true + /pbf/3.2.1: + resolution: + { + integrity: sha512-ClrV7pNOn7rtmoQVF4TS1vyU0WhYRnP92fzbfF75jAIwpnzdJXf8iTd4CMEqO4yUenH6NDqLiwjqlh6QgZzgLQ== + } + hasBin: true + dependencies: + ieee754: 1.2.1 + resolve-protobuf-schema: 2.1.0 + dev: true + /picocolors/1.0.0: resolution: { @@ -3014,6 +3242,13 @@ packages: source-map-js: 1.0.2 dev: true + /potpack/1.0.2: + resolution: + { + integrity: sha512-choctRBIV9EMT9WGAZHn3V7t0Z2pMQyl0EZE6pFc/6ml3ssw7Dlf/oAOvFwjm1HVsqfQN8GfeFyJ+d8tRzqueQ== + } + dev: true + /prelude-ls/1.2.1: resolution: { @@ -3051,6 +3286,13 @@ packages: } dev: true + /protocol-buffers-schema/3.6.0: + resolution: + { + integrity: sha512-TdDRD+/QNdrCGCE7v8340QyuXd4kIWIgapsE2+n/SaGiSSbomYl4TjHlvIoCWRpE7wFt02EpB35VVA2ImcBVqw== + } + dev: true + /punycode/2.1.1: resolution: { @@ -3074,6 +3316,13 @@ packages: engines: { node: '>=10' } dev: true + /quickselect/2.0.0: + resolution: + { + integrity: sha512-RKJ22hX8mHe3Y6wH/N3wCM6BWtjaxIyyUIkpHOvfFnxdI4yD4tBXEBKSbriGujF6jnSVkJrffuo6vxACiSSxIw== + } + dev: true + /readdirp/3.6.0: resolution: { @@ -3126,6 +3375,15 @@ packages: engines: { node: '>=4' } dev: true + /resolve-protobuf-schema/2.1.0: + resolution: + { + integrity: sha512-kI5ffTiZWmJaS/huM8wZfEMer1eRd7oJQhDuxeCLe3t7N7mX3z94CN0xPxBQxFYQTSNz9T0i+v6inKqSdK8xrQ== + } + dependencies: + protocol-buffers-schema: 3.6.0 + dev: true + /resolve/1.22.0: resolution: { @@ -3186,6 +3444,10 @@ packages: queue-microtask: 1.2.3 dev: true + /rw/1.3.3: + resolution: { integrity: sha1-P4Yt+pGrdmsUiF700BEkv9oHT7Q= } + dev: true + /sade/1.8.1: resolution: { @@ -3391,6 +3653,15 @@ packages: postcss-selector-parser: 6.0.10 dev: true + /supercluster/7.1.5: + resolution: + { + integrity: sha512-EulshI3pGUM66o6ZdH3ReiFcvHpM3vAigyK+vcxdjpJyEbIIrtbmBdY23mGgnI24uXiGFvrGq9Gkum/8U7vJWg== + } + dependencies: + kdbush: 3.0.0 + dev: true + /supports-color/7.2.0: resolution: { @@ -3452,6 +3723,18 @@ packages: svelte: 3.47.0 dev: true + /svelte-leafletjs/0.8.2: + resolution: + { + integrity: sha512-TCywmwL2Dis28xKJacT6vC7OT0+tt40fbHLaavS8r9uItJOWrOM89qyeXrIMP/QbcJI/8OLfNrm6NuE3C0s8Ow== + } + dependencies: + axios: 0.25.0 + leaflet: 1.7.1 + transitivePeerDependencies: + - debug + dev: true + /svelte-preprocess/4.10.5_f6bc70e70cf1a2fde5bc22fd7000ea23: resolution: { @@ -3602,6 +3885,13 @@ packages: globrex: 0.1.2 dev: true + /tinyqueue/2.0.3: + resolution: + { + integrity: sha512-ppJZNDuKGgxzkHihX8v9v9G5f+18gzaTfrukGrq6ueg0lmH4nqVnA2IPG0AEH3jKEk2GRJCUhDoqpoiw3PHLBA== + } + dev: true + /tippy.js/6.3.7: resolution: { @@ -3679,6 +3969,13 @@ packages: hasBin: true dev: true + /ua-parser-js/1.0.2: + resolution: + { + integrity: sha512-00y/AXhx0/SsnI51fTc0rLRmafiGOM4/O+ny10Ps7f+j/b8p/ZY11ytMgznXkOVo4GQ+KwQG5UQLkLGirsACRg== + } + dev: true + /uri-js/4.4.1: resolution: { @@ -3699,6 +3996,16 @@ packages: } dev: true + /vite-plugin-iso-import/0.1.3: + resolution: + { + integrity: sha512-gMReA6eqfdSKMhNml0B8lT8VGHOY2FxLmq8rIJWStfuoLQItRzHKb2ntB+zNn8VWWeMe5/jtthfSxrzpgjhCMQ== + } + dependencies: + es-module-lexer: 0.9.3 + magic-string: 0.25.9 + dev: true + /vite/2.9.2_sass@1.50.0: resolution: { @@ -3727,6 +4034,17 @@ packages: fsevents: 2.3.2 dev: true + /vt-pbf/3.1.3: + resolution: + { + integrity: sha512-2LzDFzt0mZKZ9IpVF2r69G9bXaP2Q2sArJCmcCgvfTdCCZzSyz4aCLoQyUilu37Ll56tCblIZrXFIjNUpGIlmA== + } + dependencies: + '@mapbox/point-geometry': 0.1.0 + '@mapbox/vector-tile': 1.3.1 + pbf: 3.2.1 + dev: true + /web-streams-polyfill/3.2.1: resolution: { diff --git a/frontend/src/lib/Map.svelte b/frontend/src/lib/Map.svelte new file mode 100644 index 0000000..009623e --- /dev/null +++ b/frontend/src/lib/Map.svelte @@ -0,0 +1,17 @@ + + +{#if browser} + + + +{/if} diff --git a/frontend/src/lib/Spinner.svelte b/frontend/src/lib/Spinner.svelte new file mode 100644 index 0000000..298bab4 --- /dev/null +++ b/frontend/src/lib/Spinner.svelte @@ -0,0 +1,10 @@ + + + + diff --git a/frontend/src/routes/account/settings.svelte b/frontend/src/routes/account/settings.svelte index 2d3d59e..5f5a796 100644 --- a/frontend/src/routes/account/settings.svelte +++ b/frontend/src/routes/account/settings.svelte @@ -3,7 +3,7 @@ if (!session.authenticated) { return { status: 302, - redirect: '/account/login' + redirect: '/account/login?returnTo=/account/settings' }; } return { @@ -16,9 +16,14 @@ {#await getUser()} - - - - + {:then user}
@@ -146,3 +187,120 @@
{/await} +{#await getSessions()} + +{:then sessions} + + + + + + + + + + + + + {#each sessions as session} + + + + + + + + + {/each} + +
+ {$t('overview_page.created_at')} + + Last seen + + Browser + + Check location + + Delete this session + + This session? +
+ {formatDate(session.created_at)} + + {formatDate(session.last_seen)} + + {getFormattedUserAgent(session.user_agent)} + + + + + + {#if session.id === this_session.id} + ✅ + {:else} + ❌ + {/if} +
+{/await} + +
+ {#if showMap} + {#await import('$lib/Map.svelte')} + + {:then c} + +
+ +
+ {/await} + {/if} +
diff --git a/frontend/src/routes/docs/develop.svelte b/frontend/src/routes/docs/develop.svelte index 0745048..eaa0681 100644 --- a/frontend/src/routes/docs/develop.svelte +++ b/frontend/src/routes/docs/develop.svelte @@ -23,6 +23,7 @@
  • Redis
  • Meilisearch
  • (Optional) Caddy
  • +
  • Linux
  • Frontend

    @@ -62,6 +63,11 @@ +
  • + Add the following line to your /etc/hosts-file, so you can visit ClassQuiz + via test.com (Required for the Captcha and Mapbox) +
    127.0.0.1 test.com
    +
  • Set your config up in your .env-file. What you have to set up can you see in the classquiz/config.py-file. The things you have to set are the following: diff --git a/frontend/src/routes/docs/self-host.svelte b/frontend/src/routes/docs/self-host.svelte index 9501ac5..62e68b3 100644 --- a/frontend/src/routes/docs/self-host.svelte +++ b/frontend/src/routes/docs/self-host.svelte @@ -30,6 +30,7 @@

    Requirements

    +

    Software

    +

    3rd-Parties

    +

    Required

    + +

    Optional

    +

    Installation

    At first, clone the repo:

    @@ -52,6 +63,15 @@

    You must set a valid hCaptcha-Sitekey in the frontend/Dockerfile.

    +

    + You'll also have to provide a valid VITE_MAPBOX_ACCESS_TOKEN in + frontend/Dockerfile. The provided token only works on the following urls: +

    +
      +
    • classquiz.mawoka.eu
    • +
    • classquiz.de
    • +
    • test.com
    • +

    Configuration

    Storage Provider

    @@ -113,6 +133,7 @@ services: MAIL_PORT: "587" # SMTP-Port REDIS: "redis://redis:6379/0?decode_responses=True" # decode_response is important! SECRET_KEY: "ghfvfgjgvjgvbh" # openssl rand -hex 32 + MEILISEARCH_URL: "http://meilisearch:7700" ACCESS_TOKEN_EXPIRE_MINUTES: 30 HCAPTCHA_KEY: "" # Private hCaptcha key for verification STORAGE_BACKEND: "deta" # MUST BE EITHER "deta" OR "local" @@ -151,9 +172,20 @@ services: ports: - "8000:8080" # Adjust the 8000 to your needs + meilisearch: + image: getmeili/meilisearch:latest + restart: always + environment: + MEILI_NO_ANALYTICS: true + volumes: + - meilisearch-data:/data.ms volumes: data: + meilisearch-data:

    Now build and deploy:

    -
    docker compose build && docker compose up -d
    +
    docker compose build && docker compose up -d
    +

    You'll have to create an index in Meilisearch with the following command:

    +
    docker compose exec api python3 import_to_meili.py
    +

    Enjoy! ❤️

    diff --git a/frontend/svelte.config.js b/frontend/svelte.config.js index fcb4d98..d150aff 100644 --- a/frontend/svelte.config.js +++ b/frontend/svelte.config.js @@ -1,5 +1,6 @@ import adapter from '@sveltejs/adapter-node'; import preprocess from 'svelte-preprocess'; +import { isoImport } from 'vite-plugin-iso-import'; /** @type {import('@sveltejs/kit').Config} */ const config = { @@ -22,7 +23,8 @@ const config = { }, build: { sourcemap: true - } + }, + plugins: [isoImport()] } } };