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}
{/await}
+{#await getSessions()}
+
+{:then sessions}
+
+
+
+
+ {$t('overview_page.created_at')}
+
+
+ Last seen
+
+
+ Browser
+
+
+ Check location
+
+
+ Delete this session
+
+
+ This session?
+
+
+
+
+ {#each sessions as session}
+
+
+ {formatDate(session.created_at)}
+
+
+ {formatDate(session.last_seen)}
+
+
+ {getFormattedUserAgent(session.user_agent)}
+
+
+ {
+ checkLocation(session.ip_address);
+ }}>View
+
+
+ {
+ deleteSession(session.id);
+ }}>Delete
+
+
+ {#if session.id === this_session.id}
+ ✅
+ {:else}
+ ❌
+ {/if}
+
+
+ {/each}
+
+
+{/await}
+
+
+ {#if showMap}
+ {#await import('$lib/Map.svelte')}
+
+ {:then c}
+
{
+ showMap = false;
+ }}
+ class="bg-black text-white rounded-t-lg px-1">Close
+
+
+
+ {/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()]
}
}
};