From 026274f7c79d2293e0e1d5a1659d6a583df829a4 Mon Sep 17 00:00:00 2001 From: Daniel Maeckelmann Date: Thu, 20 Jul 2023 13:33:08 +0200 Subject: [PATCH] Added Track upload form & generic interface improvements --- Website/.env.development | 1 + Website/package-lock.json | 162 ++++++++++++++------- Website/package.json | 7 +- Website/src/app/add_track/page.tsx | 47 ++++++ Website/src/app/api/auth/route.ts | 14 +- Website/src/app/api/tracks/new/route.ts | 36 +++++ Website/src/app/api/update/route.ts | 24 ++- Website/src/app/components/footer.tsx | 8 + Website/src/app/components/header.tsx | 11 ++ Website/src/app/data_protection/page.tsx | 13 ++ Website/src/app/login/page.tsx | 11 +- Website/src/app/page.tsx | 129 +++------------- Website/src/app/signup/page.tsx | 15 ++ Website/src/components/footer.tsx | 8 - Website/src/components/layout.tsx | 10 +- Website/src/components/login.tsx | 7 +- Website/src/components/track_selection.tsx | 2 +- Website/src/lib/api.website.ts | 38 ++++- Website/src/lib/data.ts | 80 ++++++---- Website/src/lib/types.ts | 16 ++ Website/src/middleware.ts | 7 +- Website/tailwind.config.js | 4 +- 22 files changed, 418 insertions(+), 232 deletions(-) create mode 100644 Website/.env.development create mode 100644 Website/src/app/add_track/page.tsx create mode 100644 Website/src/app/api/tracks/new/route.ts create mode 100644 Website/src/app/components/footer.tsx create mode 100644 Website/src/app/components/header.tsx create mode 100644 Website/src/app/data_protection/page.tsx create mode 100644 Website/src/app/signup/page.tsx delete mode 100644 Website/src/components/footer.tsx diff --git a/Website/.env.development b/Website/.env.development new file mode 100644 index 00000000..ea3bc065 --- /dev/null +++ b/Website/.env.development @@ -0,0 +1 @@ +BACKEND_URI="http://127.0.0.1:8080" \ No newline at end of file diff --git a/Website/package-lock.json b/Website/package-lock.json index 6394f521..28f4c869 100644 --- a/Website/package-lock.json +++ b/Website/package-lock.json @@ -19,12 +19,17 @@ "eslint-config-next": "13.4.3", "leaflet": "^1.9.4", "leaflet-rotatedmarker": "^0.2.0", - "next": "^13.4.7", + "next": "^13.4.10", "postcss": "8.4.23", "react": "18.2.0", "react-dom": "18.2.0", + "server-only": "^0.0.1", + "swr": "^2.2.0", "tailwindcss": "3.3.2", "typescript": "5.0.4" + }, + "devDependencies": { + "@tailwindcss/forms": "^0.5.4" } }, "node_modules/@alloc/quick-lru": { @@ -180,9 +185,9 @@ "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==" }, "node_modules/@next/env": { - "version": "13.4.7", - "resolved": "https://registry.npmjs.org/@next/env/-/env-13.4.7.tgz", - "integrity": "sha512-ZlbiFulnwiFsW9UV1ku1OvX/oyIPLtMk9p/nnvDSwI0s7vSoZdRtxXNsaO+ZXrLv/pMbXVGq4lL8TbY9iuGmVw==" + "version": "13.4.10", + "resolved": "https://registry.npmjs.org/@next/env/-/env-13.4.10.tgz", + "integrity": "sha512-3G1yD/XKTSLdihyDSa8JEsaWOELY+OWe08o0LUYzfuHp1zHDA8SObQlzKt+v+wrkkPcnPweoLH1ImZeUa0A1NQ==" }, "node_modules/@next/eslint-plugin-next": { "version": "13.4.3", @@ -193,9 +198,9 @@ } }, "node_modules/@next/swc-darwin-arm64": { - "version": "13.4.7", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-13.4.7.tgz", - "integrity": "sha512-VZTxPv1b59KGiv/pZHTO5Gbsdeoxcj2rU2cqJu03btMhHpn3vwzEK0gUSVC/XW96aeGO67X+cMahhwHzef24/w==", + "version": "13.4.10", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-13.4.10.tgz", + "integrity": "sha512-4bsdfKmmg7mgFGph0UorD1xWfZ5jZEw4kKRHYEeTK9bT1QnMbPVPlVXQRIiFPrhoDQnZUoa6duuPUJIEGLV1Jg==", "cpu": [ "arm64" ], @@ -208,9 +213,9 @@ } }, "node_modules/@next/swc-darwin-x64": { - "version": "13.4.7", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-13.4.7.tgz", - "integrity": "sha512-gO2bw+2Ymmga+QYujjvDz9955xvYGrWofmxTq7m70b9pDPvl7aDFABJOZ2a8SRCuSNB5mXU8eTOmVVwyp/nAew==", + "version": "13.4.10", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-13.4.10.tgz", + "integrity": "sha512-ngXhUBbcZIWZWqNbQSNxQrB9T1V+wgfCzAor2olYuo/YpaL6mUYNUEgeBMhr8qwV0ARSgKaOp35lRvB7EmCRBg==", "cpu": [ "x64" ], @@ -223,9 +228,9 @@ } }, "node_modules/@next/swc-linux-arm64-gnu": { - "version": "13.4.7", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-13.4.7.tgz", - "integrity": "sha512-6cqp3vf1eHxjIDhEOc7Mh/s8z1cwc/l5B6ZNkOofmZVyu1zsbEM5Hmx64s12Rd9AYgGoiCz4OJ4M/oRnkE16/Q==", + "version": "13.4.10", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-13.4.10.tgz", + "integrity": "sha512-SjCZZCOmHD4uyM75MVArSAmF5Y+IJSGroPRj2v9/jnBT36SYFTORN8Ag/lhw81W9EeexKY/CUg2e9mdebZOwsg==", "cpu": [ "arm64" ], @@ -238,9 +243,9 @@ } }, "node_modules/@next/swc-linux-arm64-musl": { - "version": "13.4.7", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-13.4.7.tgz", - "integrity": "sha512-T1kD2FWOEy5WPidOn1si0rYmWORNch4a/NR52Ghyp4q7KyxOCuiOfZzyhVC5tsLIBDH3+cNdB5DkD9afpNDaOw==", + "version": "13.4.10", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-13.4.10.tgz", + "integrity": "sha512-F+VlcWijX5qteoYIOxNiBbNE8ruaWuRlcYyIRK10CugqI/BIeCDzEDyrHIHY8AWwbkTwe6GRHabMdE688Rqq4Q==", "cpu": [ "arm64" ], @@ -253,9 +258,9 @@ } }, "node_modules/@next/swc-linux-x64-gnu": { - "version": "13.4.7", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-13.4.7.tgz", - "integrity": "sha512-zaEC+iEiAHNdhl6fuwl0H0shnTzQoAoJiDYBUze8QTntE/GNPfTYpYboxF5LRYIjBwETUatvE0T64W6SKDipvg==", + "version": "13.4.10", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-13.4.10.tgz", + "integrity": "sha512-WDv1YtAV07nhfy3i1visr5p/tjiH6CeXp4wX78lzP1jI07t4PnHHG1WEDFOduXh3WT4hG6yN82EQBQHDi7hBrQ==", "cpu": [ "x64" ], @@ -268,9 +273,9 @@ } }, "node_modules/@next/swc-linux-x64-musl": { - "version": "13.4.7", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-13.4.7.tgz", - "integrity": "sha512-X6r12F8d8SKAtYJqLZBBMIwEqcTRvUdVm+xIq+l6pJqlgT2tNsLLf2i5Cl88xSsIytBICGsCNNHd+siD2fbWBA==", + "version": "13.4.10", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-13.4.10.tgz", + "integrity": "sha512-zFkzqc737xr6qoBgDa3AwC7jPQzGLjDlkNmt/ljvQJ/Veri5ECdHjZCUuiTUfVjshNIIpki6FuP0RaQYK9iCRg==", "cpu": [ "x64" ], @@ -283,9 +288,9 @@ } }, "node_modules/@next/swc-win32-arm64-msvc": { - "version": "13.4.7", - "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-13.4.7.tgz", - "integrity": "sha512-NPnmnV+vEIxnu6SUvjnuaWRglZzw4ox5n/MQTxeUhb5iwVWFedolPFebMNwgrWu4AELwvTdGtWjqof53AiWHcw==", + "version": "13.4.10", + "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-13.4.10.tgz", + "integrity": "sha512-IboRS8IWz5mWfnjAdCekkl8s0B7ijpWeDwK2O8CdgZkoCDY0ZQHBSGiJ2KViAG6+BJVfLvcP+a2fh6cdyBr9QQ==", "cpu": [ "arm64" ], @@ -298,9 +303,9 @@ } }, "node_modules/@next/swc-win32-ia32-msvc": { - "version": "13.4.7", - "resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-13.4.7.tgz", - "integrity": "sha512-6Hxijm6/a8XqLQpOOf/XuwWRhcuc/g4rBB2oxjgCMuV9Xlr2bLs5+lXyh8w9YbAUMYR3iC9mgOlXbHa79elmXw==", + "version": "13.4.10", + "resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-13.4.10.tgz", + "integrity": "sha512-bSA+4j8jY4EEiwD/M2bol4uVEu1lBlgsGdvM+mmBm/BbqofNBfaZ2qwSbwE2OwbAmzNdVJRFRXQZ0dkjopTRaQ==", "cpu": [ "ia32" ], @@ -313,9 +318,9 @@ } }, "node_modules/@next/swc-win32-x64-msvc": { - "version": "13.4.7", - "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-13.4.7.tgz", - "integrity": "sha512-sW9Yt36Db1nXJL+mTr2Wo0y+VkPWeYhygvcHj1FF0srVtV+VoDjxleKtny21QHaG05zdeZnw2fCtf2+dEqgwqA==", + "version": "13.4.10", + "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-13.4.10.tgz", + "integrity": "sha512-g2+tU63yTWmcVQKDGY0MV1PjjqgZtwM4rB1oVVi/v0brdZAcrcTV+04agKzWtvWroyFz6IqtT0MoZJA7PNyLVw==", "cpu": [ "x64" ], @@ -391,6 +396,18 @@ "tslib": "^2.4.0" } }, + "node_modules/@tailwindcss/forms": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/@tailwindcss/forms/-/forms-0.5.4.tgz", + "integrity": "sha512-YAm12D3R7/9Mh4jFbYSMnsd6jG++8KxogWgqs7hbdo/86aWjjlIEvL7+QYdVELmAI0InXTpZqFIg5e7aDVWI2Q==", + "dev": true, + "dependencies": { + "mini-svg-data-uri": "^1.2.3" + }, + "peerDependencies": { + "tailwindcss": ">=3.0.0 || >= 3.0.0-alpha.1" + } + }, "node_modules/@types/cookie": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.4.1.tgz", @@ -1556,9 +1573,9 @@ } }, "node_modules/eslint-plugin-import/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "bin": { "semver": "bin/semver.js" } @@ -1593,9 +1610,9 @@ } }, "node_modules/eslint-plugin-jsx-a11y/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "bin": { "semver": "bin/semver.js" } @@ -1667,9 +1684,9 @@ } }, "node_modules/eslint-plugin-react/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "bin": { "semver": "bin/semver.js" } @@ -2724,6 +2741,15 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/mini-svg-data-uri": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/mini-svg-data-uri/-/mini-svg-data-uri-1.4.4.tgz", + "integrity": "sha512-r9deDe9p5FJUPZAk3A59wGH7Ii9YrjjWw0jmw/liSbHl2CHiyXj6FcDXDu2K3TjVAXqiJdaw3xxwlZZr9E6nHg==", + "dev": true, + "bin": { + "mini-svg-data-uri": "cli.js" + } + }, "node_modules/minimatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", @@ -2781,11 +2807,11 @@ "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==" }, "node_modules/next": { - "version": "13.4.7", - "resolved": "https://registry.npmjs.org/next/-/next-13.4.7.tgz", - "integrity": "sha512-M8z3k9VmG51SRT6v5uDKdJXcAqLzP3C+vaKfLIAM0Mhx1um1G7MDnO63+m52qPdZfrTFzMZNzfsgvm3ghuVHIQ==", + "version": "13.4.10", + "resolved": "https://registry.npmjs.org/next/-/next-13.4.10.tgz", + "integrity": "sha512-4ep6aKxVTQ7rkUW2fBLhpBr/5oceCuf4KmlUpvG/aXuDTIf9mexNSpabUD6RWPspu6wiJJvozZREhXhueYO36A==", "dependencies": { - "@next/env": "13.4.7", + "@next/env": "13.4.10", "@swc/helpers": "0.5.1", "busboy": "1.6.0", "caniuse-lite": "^1.0.30001406", @@ -2801,15 +2827,15 @@ "node": ">=16.8.0" }, "optionalDependencies": { - "@next/swc-darwin-arm64": "13.4.7", - "@next/swc-darwin-x64": "13.4.7", - "@next/swc-linux-arm64-gnu": "13.4.7", - "@next/swc-linux-arm64-musl": "13.4.7", - "@next/swc-linux-x64-gnu": "13.4.7", - "@next/swc-linux-x64-musl": "13.4.7", - "@next/swc-win32-arm64-msvc": "13.4.7", - "@next/swc-win32-ia32-msvc": "13.4.7", - "@next/swc-win32-x64-msvc": "13.4.7" + "@next/swc-darwin-arm64": "13.4.10", + "@next/swc-darwin-x64": "13.4.10", + "@next/swc-linux-arm64-gnu": "13.4.10", + "@next/swc-linux-arm64-musl": "13.4.10", + "@next/swc-linux-x64-gnu": "13.4.10", + "@next/swc-linux-x64-musl": "13.4.10", + "@next/swc-win32-arm64-msvc": "13.4.10", + "@next/swc-win32-ia32-msvc": "13.4.10", + "@next/swc-win32-x64-msvc": "13.4.10" }, "peerDependencies": { "@opentelemetry/api": "^1.1.0", @@ -3613,6 +3639,11 @@ "node": ">=10" } }, + "node_modules/server-only": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/server-only/-/server-only-0.0.1.tgz", + "integrity": "sha512-qepMx2JxAa5jjfzxG79yPPq+8BuFToHd1hm7kI+Z4zAq1ftQiP7HcxMhDDItrbtwVeLg/cY2JnKnrcFkmiswNA==" + }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -3859,6 +3890,17 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/swr": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/swr/-/swr-2.2.0.tgz", + "integrity": "sha512-AjqHOv2lAhkuUdIiBu9xbuettzAzWXmCEcLONNKJRba87WAefz8Ca9d6ds/SzrPc235n1IxWYdhJ2zF3MNUaoQ==", + "dependencies": { + "use-sync-external-store": "^1.2.0" + }, + "peerDependencies": { + "react": "^16.11.0 || ^17.0.0 || ^18.0.0" + } + }, "node_modules/synckit": { "version": "0.8.5", "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.8.5.tgz", @@ -4111,6 +4153,14 @@ "punycode": "^2.1.0" } }, + "node_modules/use-sync-external-store": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz", + "integrity": "sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", @@ -4177,9 +4227,9 @@ } }, "node_modules/word-wrap": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", - "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.4.tgz", + "integrity": "sha512-2V81OA4ugVo5pRo46hAoD2ivUJx8jXmWXfUkY4KFNw0hEptvN0QfH3K4nHiwzGeKl5rFKedV48QVoqYavy4YpA==", "engines": { "node": ">=0.10.0" } diff --git a/Website/package.json b/Website/package.json index 660781c4..ba42a374 100644 --- a/Website/package.json +++ b/Website/package.json @@ -20,11 +20,16 @@ "eslint-config-next": "13.4.3", "leaflet": "^1.9.4", "leaflet-rotatedmarker": "^0.2.0", - "next": "^13.4.7", + "next": "^13.4.10", "postcss": "8.4.23", "react": "18.2.0", "react-dom": "18.2.0", + "server-only": "^0.0.1", + "swr": "^2.2.0", "tailwindcss": "3.3.2", "typescript": "5.0.4" + }, + "devDependencies": { + "@tailwindcss/forms": "^0.5.4" } } diff --git a/Website/src/app/add_track/page.tsx b/Website/src/app/add_track/page.tsx new file mode 100644 index 00000000..ef277e0e --- /dev/null +++ b/Website/src/app/add_track/page.tsx @@ -0,0 +1,47 @@ +'use client' + +import {FormEvent, useRef} from "react"; +import {TrackPath} from "@/lib/api.website"; + +export default function Home(x: any) { + const formRef = useRef(null as (null | HTMLFormElement)) + + async function submit(e: FormEvent) { + e.preventDefault() + const form = formRef.current; + if (!form) throw new Error('Form missing'); + const formData = new FormData(form); + const trackFile = formData.get('track') as File + const path = JSON.parse(await trackFile.text()) + + const uploadRequest: TrackPath = { + path, + start: formData.get('trackStart') as string, + end: formData.get('trackEnd') as string, + } + + const result = await fetch('/api/tracks/new', { + body: JSON.stringify(uploadRequest), + headers: {"Content-Type": "application/json"}, + method: 'PUT' + }, ) + + } + + return ( +
+
+
+
+ + + + +
+
+
+
+ ) +} \ No newline at end of file diff --git a/Website/src/app/api/auth/route.ts b/Website/src/app/api/auth/route.ts index 278207c1..ed821243 100644 --- a/Website/src/app/api/auth/route.ts +++ b/Website/src/app/api/auth/route.ts @@ -2,6 +2,7 @@ import {AuthenticationRequest, AuthenticationResponse} from "@/lib/api.website"; import {cookies} from "next/headers"; import {NextRequest, NextResponse} from "next/server"; import {authenticate} from "@/lib/data"; +import {NextURL} from "next/dist/server/web/next-url"; // export async function GET(request: NextRequest) { // return new NextResponse(null, { status: 405 }) @@ -9,20 +10,21 @@ import {authenticate} from "@/lib/data"; export async function POST(request: NextRequest) { const url = request.nextUrl.clone(); - console.log('baz', request.destination); + const base_host = request.headers.get('host') ?? request.headers.get('x-forwarded-host') + // console.log('baz', request.destination); const data = await request.formData(); - console.log('foo', data); + // console.log('foo', data); url.pathname = data.get("dst_url")?.toString() || '/'; - console.log("new url", url) + // console.log("new url", url) const username = data.get("username")?.toString() const password = data.get("password")?.toString() if (username && password) { - const token = await authenticate(username, password); + const token = await authenticate(username, password, data.get('signup')?.toString()); if (token) { cookies().set({ name: 'token', value: token, - sameSite: true, + sameSite: 'lax', httpOnly: true }); url.searchParams.set('success', 'true') @@ -32,5 +34,5 @@ export async function POST(request: NextRequest) { } } - return NextResponse.redirect(url) + return NextResponse.redirect(new NextURL(url, {base: base_host ?? undefined})) } \ No newline at end of file diff --git a/Website/src/app/api/tracks/new/route.ts b/Website/src/app/api/tracks/new/route.ts new file mode 100644 index 00000000..f7e92210 --- /dev/null +++ b/Website/src/app/api/tracks/new/route.ts @@ -0,0 +1,36 @@ +import {NextRequest, NextResponse} from "next/server"; +import {sendTrack} from "@/lib/data"; +import {cookies} from "next/headers"; +import {UnauthorizedError} from "@/lib/types"; +import {deleteCookie} from "cookies-next"; + + +export async function PUT(request: NextRequest, x: any, y: any, z: any) { + const payload = await request.json() + const token = cookies().get('token')?.value + if (token) { + try { + console.log('Adding new track with token', token); + const newID = await sendTrack(token, payload) + if (newID) { + return new NextResponse(newID, {status: 200}) + } else { + return new NextResponse("Backend error", {status: 502}) + } + } catch (e) { + if (e instanceof UnauthorizedError) { + // token may have expired. Delete token. + cookies().set({ + name: 'token', + value: '', + sameSite: 'lax', + httpOnly: true, + expires: new Date(0) + }) + return new NextResponse('Unauthorized', {status: 401}) + } + } + } else { + return new NextResponse("Unauthorized", {status: 401}) + } +} \ No newline at end of file diff --git a/Website/src/app/api/update/route.ts b/Website/src/app/api/update/route.ts index df1cd355..3e10a304 100644 --- a/Website/src/app/api/update/route.ts +++ b/Website/src/app/api/update/route.ts @@ -9,6 +9,7 @@ import { STATUS_CODES } from "http"; import { redirect } from "next/dist/server/api-utils"; import { cookies } from "next/headers"; import { NextRequest, NextResponse } from "next/server"; +import {UnauthorizedError} from "@/lib/types"; export async function POST(request: NextRequest) { @@ -18,9 +19,26 @@ export async function POST(request: NextRequest) { // console.log("requested track_id", track_id); if (token) { - const vehicles = await getVehicleData(token, track_id); - // console.log("vehicles", vehicles) - return new NextResponse(JSON.stringify(vehicles), {headers: {"Content-Type": "application/json"}}) + try { + const vehicles = await getVehicleData(token, track_id); + // console.log("vehicles", vehicles) + return NextResponse.json(vehicles); + } + catch (e: any) { + if (e instanceof UnauthorizedError) { + // token may have expired. Delete token. + cookies().set({ + name: 'token', + value: '', + sameSite: 'lax', + httpOnly: true, + expires: new Date(0) + }) + console.log('UnauthorizedError') + return new NextResponse('Unauthorized', {status: 401}) + } else + return new NextResponse("Error" + e.toString(), {status: 500}) + } } else { return new NextResponse("Unauthorized", {status: 401}) diff --git a/Website/src/app/components/footer.tsx b/Website/src/app/components/footer.tsx new file mode 100644 index 00000000..48d905ce --- /dev/null +++ b/Website/src/app/components/footer.tsx @@ -0,0 +1,8 @@ +import Link from "next/link"; + +export default function Footer() { + + return +} \ No newline at end of file diff --git a/Website/src/app/components/header.tsx b/Website/src/app/components/header.tsx new file mode 100644 index 00000000..f89cb00c --- /dev/null +++ b/Website/src/app/components/header.tsx @@ -0,0 +1,11 @@ +import Link from "next/link"; +import {actionAsyncStorage} from "next/dist/server/app-render/entry-base"; + +export default function Header() { + + return (
+

RailTrail Admin interface

+
+

Login | Logout

+
) +} \ No newline at end of file diff --git a/Website/src/app/data_protection/page.tsx b/Website/src/app/data_protection/page.tsx new file mode 100644 index 00000000..023b2c56 --- /dev/null +++ b/Website/src/app/data_protection/page.tsx @@ -0,0 +1,13 @@ +const Page = () => ( +
+

Der Schutz Ihrer Daten ist uns sehr wichtig. Wir erheben über Sie möglicherweise folgende Daten über Sie:

+ +

TODO: Add some more legal text here

+ +
) + +export default Page \ No newline at end of file diff --git a/Website/src/app/login/page.tsx b/Website/src/app/login/page.tsx index fd1fedca..ffa20625 100644 --- a/Website/src/app/login/page.tsx +++ b/Website/src/app/login/page.tsx @@ -2,18 +2,13 @@ import Login from "@/components/login"; export default function Home(x: any) { - - const searchParams = x.searchParams; - - console.log('x', searchParams) - console.log('x', x) return ( //
-
- +
+
-
+
// ) diff --git a/Website/src/app/page.tsx b/Website/src/app/page.tsx index 7e802967..9702a827 100644 --- a/Website/src/app/page.tsx +++ b/Website/src/app/page.tsx @@ -1,113 +1,24 @@ import Image from 'next/image' +import Footer from "@/app/components/footer"; +import Link from "next/link"; +import Header from "@/app/components/header"; export default function Home() { - return ( -
-
-

- Get started by editing  - src/app/page.tsx -

-
- - By{' '} - Vercel Logo - -
-
- -
- Next.js Logo -
- -
- -

- Docs{' '} - - -> - -

-

- Find in-depth information about Next.js features and API. -

-
- - -

- Learn{' '} - - -> - -

-

- Learn about Next.js in an interactive course with quizzes! -

-
- - -

- Templates{' '} - - -> - -

-

- Explore the Next.js 13 playground. -

-
- - -

- Deploy{' '} - - -> - -

-

- Instantly deploy your Next.js site to a shareable URL with Vercel. -

-
-
-
- ) + return ( +
+

This website offers some administrative thingies:

+ +
+ ) } diff --git a/Website/src/app/signup/page.tsx b/Website/src/app/signup/page.tsx new file mode 100644 index 00000000..c93a3159 --- /dev/null +++ b/Website/src/app/signup/page.tsx @@ -0,0 +1,15 @@ +import Login from "@/components/login"; + + +export default function Home(x: any) { + + return ( + //
+
+
+ +
+
+ //
+ ) + } \ No newline at end of file diff --git a/Website/src/components/footer.tsx b/Website/src/components/footer.tsx deleted file mode 100644 index ccf34b60..00000000 --- a/Website/src/components/footer.tsx +++ /dev/null @@ -1,8 +0,0 @@ -import Link from "next/link"; - -export default function Footer() { - - return -} \ No newline at end of file diff --git a/Website/src/components/layout.tsx b/Website/src/components/layout.tsx index 1c003aef..5d1ebd4f 100644 --- a/Website/src/components/layout.tsx +++ b/Website/src/components/layout.tsx @@ -1,5 +1,7 @@ import './globals.css' import { Inter } from 'next/font/google' +import Header from "@/app/components/header"; +import Footer from "@/app/components/footer"; const inter = Inter({ subsets: ['latin'] }) @@ -10,7 +12,13 @@ export default function RootLayout({ }) { return ( - {children} + +
+
+ {children} +
+
+ ) } \ No newline at end of file diff --git a/Website/src/components/login.tsx b/Website/src/components/login.tsx index 91ad2869..b354a69f 100644 --- a/Website/src/components/login.tsx +++ b/Website/src/components/login.tsx @@ -4,18 +4,19 @@ import { usePathname } from "next/navigation"; import {useEffect, useRef} from "react"; import { UrlObject, format } from 'url'; -import Footer from "@/components/footer"; +import Footer from "@/app/components/footer"; type Url = string | UrlObject; -export default function Login({dst_url}: {dst_url?: Url}) { +export default function Login({dst_url, signup}: {dst_url?: Url, signup?: boolean}) { const pathname = usePathname() || '/'; return ( -
+ + {signup && }
) diff --git a/Website/src/components/track_selection.tsx b/Website/src/components/track_selection.tsx index e9321bde..65226b47 100644 --- a/Website/src/components/track_selection.tsx +++ b/Website/src/components/track_selection.tsx @@ -4,7 +4,7 @@ import { usePathname } from "next/navigation"; import {useEffect, useRef} from "react"; import { UrlObject, format } from 'url'; -import Footer from "@/components/footer"; +import Footer from "@/app/components/footer"; type Url = string | UrlObject; export default function Selection({dst_url}: {dst_url?: Url}) { diff --git a/Website/src/lib/api.website.ts b/Website/src/lib/api.website.ts index aece1847..70cafaef 100644 --- a/Website/src/lib/api.website.ts +++ b/Website/src/lib/api.website.ts @@ -1,3 +1,5 @@ +import { FeatureCollection, GeoJsonProperties, Point } from "geojson"; + export interface AuthenticationRequest { username: string; // The username that was entered into the login-form password: string; // The password that was entered into the login-form @@ -7,6 +9,7 @@ export interface AuthenticationResponse { token: string; // A jwt session token } + export interface TrackListEntry { id: number; name: string; // human readable name @@ -68,15 +71,36 @@ export interface User { username: string; } -export interface TrackMetaData { - trackName: string; // E.g. Malente-Lütjenburg +export interface TrackPath { + start: string, + end: string, + path: FeatureCollection; // The track as a geojson } -export interface TrackMetaDataResponse { - uploadId: number; // A unique id for uploading a geojson +export interface VehicleListItem { + uid: number, // Uid of the vehicle + name: string, // The name, that is attached to the vehicle, e.g. "1" for "Draisine 1" + typeId: number, // The id of the type + trackerIds?: string[] // A unique id to identify the tracker belonging to that vehicle } -export interface TrackPath { - uploadId: number; - path: GeoJSON.GeoJSON; // The track as a geojson +export interface VehicleCrU { + uid?: number, // Null, if creating vehicle, some other value otherwise + name: string, // The name, that is attached to the vehicle, e.g. "1" for "Draisine 1" + typeId: number, // The id of the type + trackerIds?: string[]// A unique id to identify the tracker belonging to that vehicle } + +export interface VehicleTypeListItem { + uid: number, // A unique id of a vehicle type + name: string, // A descriptive name of the vehicle type, e.g. "Draisine", "High-Speed Train",.. + description?: string // Perhaps a description of the type of vehicle, that is falls into this category +} + +export interface VehicleTypeCrU { + uid?: number, // Null, if creating vehicle type, some other value otherwise + name: string, // A descriptive name of the vehicle type, e.g. "Draisine", "High-Speed Train",.. + description?: string // Perhaps a description of the type of vehicle, that is falls into this category +} + +export type TrackList = TrackListEntry[] diff --git a/Website/src/lib/data.ts b/Website/src/lib/data.ts index 90cb088e..2aa3da8a 100644 --- a/Website/src/lib/data.ts +++ b/Website/src/lib/data.ts @@ -1,37 +1,42 @@ -import {AuthenticationRequest, AuthenticationResponse, Vehicle} from "./api.website"; +import { + AuthenticationRequest, + AuthenticationResponse, + InitResponse, TrackList, + TrackListEntry, + TrackPath, + Vehicle +} from "./api.website"; +import {UnauthorizedError} from "@/lib/types"; +import 'server-only' const BACKEND_BASE_PATH = process.env['BACKEND_URI'] export const getVehicleData = async (token: string, track_id: number) => { - + const auth_header_line = `Bearer ${token}` - console.log('Trying Backend at') - try { - const x = await fetch(`${BACKEND_BASE_PATH}/api/vehicles/website/${track_id}`, { - cache: 'no-store', headers: - { - "Authorization": auth_header_line - } - }) - if (x.ok) { - const data: Vehicle[] = await x.json(); - // console.log("data", data); - return data - } else { - console.log("Could not fetch vehicle positions (server)", x.status, x.statusText) - return [] - } - } catch (e) { - console.error("An error fetching", e); - return []; + const x = await fetch(`${BACKEND_BASE_PATH}/api/vehicles/website/${track_id}`, { + cache: 'no-store', headers: + { + "Authorization": auth_header_line + } + }) + if (x.ok) { + const data: Vehicle[] = await x.json(); + // console.log("data", data); + return data + } else if (x.status == 401) { + throw new UnauthorizedError('Token expired'); + } else { + console.log("Could not fetch vehicle positions (server)", x.status, x.statusText) + return [] } - } +} -export async function authenticate(username: string, password: string): Promise { +export async function authenticate(username: string, password: string, signup?: string): Promise { console.log("Trying to authenticate with", username, password) - const auth_msg: AuthenticationRequest = { username: username, password: password }; - const auth_resp_json = await fetch(`${BACKEND_BASE_PATH}/api/login/website`, { + const auth_msg: AuthenticationRequest = {username: username, password: password}; + const auth_resp_json = await fetch(signup ? `${BACKEND_BASE_PATH}/api/login/signup` : `${BACKEND_BASE_PATH}/api/login/website`, { method: "POST", body: JSON.stringify(auth_msg), headers: { "Content-Type": "application/json", // 'Content-Type': 'application/x-www-form-urlencoded', @@ -43,4 +48,29 @@ export async function authenticate(username: string, password: string): Promise< } // return 'aaaaabbbbaaabbabaaa'; return; +} + +export async function sendTrack(token: string, trackPayload: TrackPath) { + const auth_header_line = `Bearer ${token}` + try { + const x = await fetch(`${BACKEND_BASE_PATH}/api/trackupload/website`, { + method: "POST", body: JSON.stringify(trackPayload), headers: { + "Content-Type": "application/json", + "Authorization": auth_header_line + }, + }) + if (x.ok) { + const trackid: string = await x.text(); + // console.log("data", data); + return trackid; + } else if (x.status == 401) { + throw new UnauthorizedError('Token expired'); + } else { + console.log("Could not upload track", x.status, x.statusText) + return undefined; + } + } catch (e) { + console.error("An error uploading track", e); + return undefined; + } } \ No newline at end of file diff --git a/Website/src/lib/types.ts b/Website/src/lib/types.ts index fc837de6..300655fd 100644 --- a/Website/src/lib/types.ts +++ b/Website/src/lib/types.ts @@ -9,4 +9,20 @@ export interface IMapConfig { export interface IMapRefreshConfig extends IMapConfig { track_id: number +} + +export class UnauthorizedError extends Error {} + +export class RevalidateError extends Error { + private _statusCode: number; + + constructor(message: string, statusCode: number, options?: ErrorOptions) { + super(message, options); + this._statusCode = statusCode; + } + + get statusCode(): number { + return this._statusCode; + } + } \ No newline at end of file diff --git a/Website/src/middleware.ts b/Website/src/middleware.ts index 59a5da1c..2b3a3613 100644 --- a/Website/src/middleware.ts +++ b/Website/src/middleware.ts @@ -3,11 +3,12 @@ import {NextRequest} from 'next/server'; // This function can be marked `async` if using `await` inside export async function middleware(request: NextRequest) { - console.log("Hi!") + console.log(request.method, request.nextUrl.toString(), 'from', request.ip ?? request.headers.get('x-forwarded-for')) + // console.log(request.headers) if (request.headers.get('Content-Type') == 'application/x-www-form-urlencoded') { - console.log("Foo!"); + // console.log("Foo!"); const body = await request.formData(); - console.log(body); + // console.log(body); const headers = request.headers; headers.set('Content-Type', 'application/json') const req = new NextRequest(request.nextUrl, diff --git a/Website/tailwind.config.js b/Website/tailwind.config.js index d53b2eaa..2eebed2e 100644 --- a/Website/tailwind.config.js +++ b/Website/tailwind.config.js @@ -14,5 +14,7 @@ module.exports = { }, }, }, - plugins: [], + plugins: [ + require('@tailwindcss/forms') + ], }