forked from PolarisPyra/daphnis
first commit
This commit is contained in:
commit
8926934d2d
26
.gitignore
vendored
Normal file
26
.gitignore
vendored
Normal file
@ -0,0 +1,26 @@
|
||||
# Node.js
|
||||
node_modules/
|
||||
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
|
||||
# Dependency directories
|
||||
pids
|
||||
logs
|
||||
*.pid
|
||||
*.seed
|
||||
*.pid.lock
|
||||
|
||||
# Optional npm cache directory
|
||||
.npm
|
||||
|
||||
# Optional eslint cache
|
||||
.eslintcache
|
||||
|
||||
# Optional REPL history
|
||||
.node_repl_history
|
||||
|
||||
# Environment variables
|
||||
.env*
|
15
.next/app-build-manifest.json
Normal file
15
.next/app-build-manifest.json
Normal file
@ -0,0 +1,15 @@
|
||||
{
|
||||
"pages": {
|
||||
"/page": [
|
||||
"static/chunks/webpack.js",
|
||||
"static/chunks/main-app.js",
|
||||
"static/chunks/app/page.js"
|
||||
],
|
||||
"/layout": [
|
||||
"static/chunks/webpack.js",
|
||||
"static/chunks/main-app.js",
|
||||
"static/css/app/layout.css",
|
||||
"static/chunks/app/layout.js"
|
||||
]
|
||||
}
|
||||
}
|
19
.next/build-manifest.json
Normal file
19
.next/build-manifest.json
Normal file
@ -0,0 +1,19 @@
|
||||
{
|
||||
"polyfillFiles": [
|
||||
"static/chunks/polyfills.js"
|
||||
],
|
||||
"devFiles": [],
|
||||
"ampDevFiles": [],
|
||||
"lowPriorityFiles": [
|
||||
"static/development/_buildManifest.js",
|
||||
"static/development/_ssgManifest.js"
|
||||
],
|
||||
"rootMainFiles": [
|
||||
"static/chunks/webpack.js",
|
||||
"static/chunks/main-app.js"
|
||||
],
|
||||
"pages": {
|
||||
"/_app": []
|
||||
},
|
||||
"ampFirstPages": []
|
||||
}
|
BIN
.next/cache/webpack/client-development/0.pack.gz
vendored
Normal file
BIN
.next/cache/webpack/client-development/0.pack.gz
vendored
Normal file
Binary file not shown.
BIN
.next/cache/webpack/client-development/1.pack.gz
vendored
Normal file
BIN
.next/cache/webpack/client-development/1.pack.gz
vendored
Normal file
Binary file not shown.
BIN
.next/cache/webpack/client-development/index.pack.gz
vendored
Normal file
BIN
.next/cache/webpack/client-development/index.pack.gz
vendored
Normal file
Binary file not shown.
BIN
.next/cache/webpack/client-development/index.pack.gz.old
vendored
Normal file
BIN
.next/cache/webpack/client-development/index.pack.gz.old
vendored
Normal file
Binary file not shown.
BIN
.next/cache/webpack/server-development/0.pack.gz
vendored
Normal file
BIN
.next/cache/webpack/server-development/0.pack.gz
vendored
Normal file
Binary file not shown.
BIN
.next/cache/webpack/server-development/index.pack.gz
vendored
Normal file
BIN
.next/cache/webpack/server-development/index.pack.gz
vendored
Normal file
Binary file not shown.
1
.next/package.json
Normal file
1
.next/package.json
Normal file
@ -0,0 +1 @@
|
||||
{"type": "commonjs"}
|
1
.next/react-loadable-manifest.json
Normal file
1
.next/react-loadable-manifest.json
Normal file
@ -0,0 +1 @@
|
||||
{}
|
4
.next/server/app-paths-manifest.json
Normal file
4
.next/server/app-paths-manifest.json
Normal file
@ -0,0 +1,4 @@
|
||||
{
|
||||
"/page": "app/page.js",
|
||||
"/favicon.ico/route": "app/favicon.ico/route.js"
|
||||
}
|
66
.next/server/app/favicon.ico/route.js
Normal file
66
.next/server/app/favicon.ico/route.js
Normal file
File diff suppressed because one or more lines are too long
643
.next/server/app/page.js
Normal file
643
.next/server/app/page.js
Normal file
File diff suppressed because one or more lines are too long
1
.next/server/app/page_client-reference-manifest.js
Normal file
1
.next/server/app/page_client-reference-manifest.js
Normal file
File diff suppressed because one or more lines are too long
1
.next/server/interception-route-rewrite-manifest.js
Normal file
1
.next/server/interception-route-rewrite-manifest.js
Normal file
@ -0,0 +1 @@
|
||||
self.__INTERCEPTION_ROUTE_REWRITE_MANIFEST="[]"
|
1
.next/server/middleware-build-manifest.js
Normal file
1
.next/server/middleware-build-manifest.js
Normal file
@ -0,0 +1 @@
|
||||
self.__BUILD_MANIFEST={"polyfillFiles":["static/chunks/polyfills.js"],"devFiles":[],"ampDevFiles":[],"lowPriorityFiles":["static/development/_buildManifest.js","static/development/_ssgManifest.js"],"rootMainFiles":["static/chunks/webpack.js","static/chunks/main-app.js"],"pages":{"/_app":[]},"ampFirstPages":[]}
|
6
.next/server/middleware-manifest.json
Normal file
6
.next/server/middleware-manifest.json
Normal file
@ -0,0 +1,6 @@
|
||||
{
|
||||
"version": 3,
|
||||
"middleware": {},
|
||||
"functions": {},
|
||||
"sortedMiddleware": []
|
||||
}
|
1
.next/server/middleware-react-loadable-manifest.js
Normal file
1
.next/server/middleware-react-loadable-manifest.js
Normal file
@ -0,0 +1 @@
|
||||
self.__REACT_LOADABLE_MANIFEST="{}"
|
1
.next/server/next-font-manifest.js
Normal file
1
.next/server/next-font-manifest.js
Normal file
@ -0,0 +1 @@
|
||||
self.__NEXT_FONT_MANIFEST="{\"pages\":{},\"app\":{\"/home/polaris/Documents/daphnis/app/layout\":[\"static/media/e11418ac562b8ac1-s.p.woff2\"]},\"appUsingSizeAdjust\":true,\"pagesUsingSizeAdjust\":false}"
|
1
.next/server/next-font-manifest.json
Normal file
1
.next/server/next-font-manifest.json
Normal file
@ -0,0 +1 @@
|
||||
{"pages":{},"app":{"/home/polaris/Documents/daphnis/app/layout":["static/media/e11418ac562b8ac1-s.p.woff2"]},"appUsingSizeAdjust":true,"pagesUsingSizeAdjust":false}
|
1
.next/server/pages-manifest.json
Normal file
1
.next/server/pages-manifest.json
Normal file
@ -0,0 +1 @@
|
||||
{}
|
1
.next/server/server-reference-manifest.js
Normal file
1
.next/server/server-reference-manifest.js
Normal file
@ -0,0 +1 @@
|
||||
self.__RSC_SERVER_MANIFEST="{\n \"node\": {\n \"53cc5bb38de2c5f5010807f77d18551505069f4c\": {\n \"workers\": {\n \"app/page\": \"(action-browser)/./node_modules/next/dist/build/webpack/loaders/next-flight-action-entry-loader.js?actions=%5B%5B%22%2Fhome%2Fpolaris%2FDocuments%2Fdaphnis%2Fauth%2Fcomponents%2Fsignin%2Faction.ts%22%2C%5B%22signIn%22%5D%5D%5D&__client_imported__=true!\"\n },\n \"layer\": {\n \"app/page\": \"action-browser\"\n }\n }\n },\n \"edge\": {},\n \"encryptionKey\": \"n80wJVX22coaSQA6as0aLUKetJyDnlKpxtJSHfa5eYY=\"\n}"
|
14
.next/server/server-reference-manifest.json
Normal file
14
.next/server/server-reference-manifest.json
Normal file
@ -0,0 +1,14 @@
|
||||
{
|
||||
"node": {
|
||||
"53cc5bb38de2c5f5010807f77d18551505069f4c": {
|
||||
"workers": {
|
||||
"app/page": "(action-browser)/./node_modules/next/dist/build/webpack/loaders/next-flight-action-entry-loader.js?actions=%5B%5B%22%2Fhome%2Fpolaris%2FDocuments%2Fdaphnis%2Fauth%2Fcomponents%2Fsignin%2Faction.ts%22%2C%5B%22signIn%22%5D%5D%5D&__client_imported__=true!"
|
||||
},
|
||||
"layer": {
|
||||
"app/page": "action-browser"
|
||||
}
|
||||
}
|
||||
},
|
||||
"edge": {},
|
||||
"encryptionKey": "n80wJVX22coaSQA6as0aLUKetJyDnlKpxtJSHfa5eYY="
|
||||
}
|
25
.next/server/vendor-chunks/@lucia-auth.js
Normal file
25
.next/server/vendor-chunks/@lucia-auth.js
Normal file
File diff suppressed because one or more lines are too long
175
.next/server/vendor-chunks/@radix-ui.js
Normal file
175
.next/server/vendor-chunks/@radix-ui.js
Normal file
File diff suppressed because one or more lines are too long
75
.next/server/vendor-chunks/@swc.js
Normal file
75
.next/server/vendor-chunks/@swc.js
Normal file
@ -0,0 +1,75 @@
|
||||
"use strict";
|
||||
/*
|
||||
* ATTENTION: An "eval-source-map" devtool has been used.
|
||||
* This devtool is neither made for production nor for readable output files.
|
||||
* It uses "eval()" calls to create a separate source file with attached SourceMaps in the browser devtools.
|
||||
* If you are trying to read the output file, select a different devtool (https://webpack.js.org/configuration/devtool/)
|
||||
* or disable the default devtool with "devtool: false".
|
||||
* If you are looking for production-ready output files, see mode: "production" (https://webpack.js.org/configuration/mode/).
|
||||
*/
|
||||
exports.id = "vendor-chunks/@swc";
|
||||
exports.ids = ["vendor-chunks/@swc"];
|
||||
exports.modules = {
|
||||
|
||||
/***/ "(ssr)/./node_modules/@swc/helpers/esm/_class_private_field_loose_base.js":
|
||||
/*!**************************************************************************!*\
|
||||
!*** ./node_modules/@swc/helpers/esm/_class_private_field_loose_base.js ***!
|
||||
\**************************************************************************/
|
||||
/***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => {
|
||||
|
||||
eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ _: () => (/* binding */ _class_private_field_loose_base),\n/* harmony export */ _class_private_field_loose_base: () => (/* binding */ _class_private_field_loose_base)\n/* harmony export */ });\nfunction _class_private_field_loose_base(receiver, privateKey) {\n if (!Object.prototype.hasOwnProperty.call(receiver, privateKey)) {\n throw new TypeError(\"attempted to use private field on non-instance\");\n }\n\n return receiver;\n}\n\n//# sourceURL=[module]\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiKHNzcikvLi9ub2RlX21vZHVsZXMvQHN3Yy9oZWxwZXJzL2VzbS9fY2xhc3NfcHJpdmF0ZV9maWVsZF9sb29zZV9iYXNlLmpzIiwibWFwcGluZ3MiOiI7Ozs7O0FBQU87QUFDUDtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNnRCIsInNvdXJjZXMiOlsid2VicGFjazovL2RhcGhuaXMvLi9ub2RlX21vZHVsZXMvQHN3Yy9oZWxwZXJzL2VzbS9fY2xhc3NfcHJpdmF0ZV9maWVsZF9sb29zZV9iYXNlLmpzPzQwNGQiXSwic291cmNlc0NvbnRlbnQiOlsiZXhwb3J0IGZ1bmN0aW9uIF9jbGFzc19wcml2YXRlX2ZpZWxkX2xvb3NlX2Jhc2UocmVjZWl2ZXIsIHByaXZhdGVLZXkpIHtcbiAgICBpZiAoIU9iamVjdC5wcm90b3R5cGUuaGFzT3duUHJvcGVydHkuY2FsbChyZWNlaXZlciwgcHJpdmF0ZUtleSkpIHtcbiAgICAgICAgdGhyb3cgbmV3IFR5cGVFcnJvcihcImF0dGVtcHRlZCB0byB1c2UgcHJpdmF0ZSBmaWVsZCBvbiBub24taW5zdGFuY2VcIik7XG4gICAgfVxuXG4gICAgcmV0dXJuIHJlY2VpdmVyO1xufVxuZXhwb3J0IHsgX2NsYXNzX3ByaXZhdGVfZmllbGRfbG9vc2VfYmFzZSBhcyBfIH07XG4iXSwibmFtZXMiOltdLCJzb3VyY2VSb290IjoiIn0=\n//# sourceURL=webpack-internal:///(ssr)/./node_modules/@swc/helpers/esm/_class_private_field_loose_base.js\n");
|
||||
|
||||
/***/ }),
|
||||
|
||||
/***/ "(ssr)/./node_modules/@swc/helpers/esm/_class_private_field_loose_key.js":
|
||||
/*!*************************************************************************!*\
|
||||
!*** ./node_modules/@swc/helpers/esm/_class_private_field_loose_key.js ***!
|
||||
\*************************************************************************/
|
||||
/***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => {
|
||||
|
||||
eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ _: () => (/* binding */ _class_private_field_loose_key),\n/* harmony export */ _class_private_field_loose_key: () => (/* binding */ _class_private_field_loose_key)\n/* harmony export */ });\nvar id = 0;\n\nfunction _class_private_field_loose_key(name) {\n return \"__private_\" + id++ + \"_\" + name;\n}\n\n//# sourceURL=[module]\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiKHNzcikvLi9ub2RlX21vZHVsZXMvQHN3Yy9oZWxwZXJzL2VzbS9fY2xhc3NfcHJpdmF0ZV9maWVsZF9sb29zZV9rZXkuanMiLCJtYXBwaW5ncyI6Ijs7Ozs7QUFBQTs7QUFFTztBQUNQO0FBQ0E7QUFDK0MiLCJzb3VyY2VzIjpbIndlYnBhY2s6Ly9kYXBobmlzLy4vbm9kZV9tb2R1bGVzL0Bzd2MvaGVscGVycy9lc20vX2NsYXNzX3ByaXZhdGVfZmllbGRfbG9vc2Vfa2V5LmpzPzQ5MjgiXSwic291cmNlc0NvbnRlbnQiOlsidmFyIGlkID0gMDtcblxuZXhwb3J0IGZ1bmN0aW9uIF9jbGFzc19wcml2YXRlX2ZpZWxkX2xvb3NlX2tleShuYW1lKSB7XG4gICAgcmV0dXJuIFwiX19wcml2YXRlX1wiICsgaWQrKyArIFwiX1wiICsgbmFtZTtcbn1cbmV4cG9ydCB7IF9jbGFzc19wcml2YXRlX2ZpZWxkX2xvb3NlX2tleSBhcyBfIH07XG4iXSwibmFtZXMiOltdLCJzb3VyY2VSb290IjoiIn0=\n//# sourceURL=webpack-internal:///(ssr)/./node_modules/@swc/helpers/esm/_class_private_field_loose_key.js\n");
|
||||
|
||||
/***/ }),
|
||||
|
||||
/***/ "(ssr)/./node_modules/@swc/helpers/esm/_interop_require_default.js":
|
||||
/*!*******************************************************************!*\
|
||||
!*** ./node_modules/@swc/helpers/esm/_interop_require_default.js ***!
|
||||
\*******************************************************************/
|
||||
/***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => {
|
||||
|
||||
eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ _: () => (/* binding */ _interop_require_default),\n/* harmony export */ _interop_require_default: () => (/* binding */ _interop_require_default)\n/* harmony export */ });\nfunction _interop_require_default(obj) {\n return obj && obj.__esModule ? obj : { default: obj };\n}\n\n//# sourceURL=[module]\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiKHNzcikvLi9ub2RlX21vZHVsZXMvQHN3Yy9oZWxwZXJzL2VzbS9faW50ZXJvcF9yZXF1aXJlX2RlZmF1bHQuanMiLCJtYXBwaW5ncyI6Ijs7Ozs7QUFBTztBQUNQLDJDQUEyQztBQUMzQztBQUN5QyIsInNvdXJjZXMiOlsid2VicGFjazovL2RhcGhuaXMvLi9ub2RlX21vZHVsZXMvQHN3Yy9oZWxwZXJzL2VzbS9faW50ZXJvcF9yZXF1aXJlX2RlZmF1bHQuanM/YjMxOSJdLCJzb3VyY2VzQ29udGVudCI6WyJleHBvcnQgZnVuY3Rpb24gX2ludGVyb3BfcmVxdWlyZV9kZWZhdWx0KG9iaikge1xuICAgIHJldHVybiBvYmogJiYgb2JqLl9fZXNNb2R1bGUgPyBvYmogOiB7IGRlZmF1bHQ6IG9iaiB9O1xufVxuZXhwb3J0IHsgX2ludGVyb3BfcmVxdWlyZV9kZWZhdWx0IGFzIF8gfTtcbiJdLCJuYW1lcyI6W10sInNvdXJjZVJvb3QiOiIifQ==\n//# sourceURL=webpack-internal:///(ssr)/./node_modules/@swc/helpers/esm/_interop_require_default.js\n");
|
||||
|
||||
/***/ }),
|
||||
|
||||
/***/ "(ssr)/./node_modules/@swc/helpers/esm/_interop_require_wildcard.js":
|
||||
/*!********************************************************************!*\
|
||||
!*** ./node_modules/@swc/helpers/esm/_interop_require_wildcard.js ***!
|
||||
\********************************************************************/
|
||||
/***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => {
|
||||
|
||||
eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ _: () => (/* binding */ _interop_require_wildcard),\n/* harmony export */ _interop_require_wildcard: () => (/* binding */ _interop_require_wildcard)\n/* harmony export */ });\nfunction _getRequireWildcardCache(nodeInterop) {\n if (typeof WeakMap !== \"function\") return null;\n\n var cacheBabelInterop = new WeakMap();\n var cacheNodeInterop = new WeakMap();\n\n return (_getRequireWildcardCache = function(nodeInterop) {\n return nodeInterop ? cacheNodeInterop : cacheBabelInterop;\n })(nodeInterop);\n}\nfunction _interop_require_wildcard(obj, nodeInterop) {\n if (!nodeInterop && obj && obj.__esModule) return obj;\n if (obj === null || typeof obj !== \"object\" && typeof obj !== \"function\") return { default: obj };\n\n var cache = _getRequireWildcardCache(nodeInterop);\n\n if (cache && cache.has(obj)) return cache.get(obj);\n\n var newObj = { __proto__: null };\n var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor;\n\n for (var key in obj) {\n if (key !== \"default\" && Object.prototype.hasOwnProperty.call(obj, key)) {\n var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null;\n if (desc && (desc.get || desc.set)) Object.defineProperty(newObj, key, desc);\n else newObj[key] = obj[key];\n }\n }\n\n newObj.default = obj;\n\n if (cache) cache.set(obj, newObj);\n\n return newObj;\n}\n\n//# sourceURL=[module]\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiKHNzcikvLi9ub2RlX21vZHVsZXMvQHN3Yy9oZWxwZXJzL2VzbS9faW50ZXJvcF9yZXF1aXJlX3dpbGRjYXJkLmpzIiwibWFwcGluZ3MiOiI7Ozs7O0FBQUE7QUFDQTs7QUFFQTtBQUNBOztBQUVBO0FBQ0E7QUFDQSxLQUFLO0FBQ0w7QUFDTztBQUNQO0FBQ0EsdUZBQXVGOztBQUV2Rjs7QUFFQTs7QUFFQSxtQkFBbUI7QUFDbkI7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7O0FBRUE7O0FBRUE7QUFDQTtBQUMwQyIsInNvdXJjZXMiOlsid2VicGFjazovL2RhcGhuaXMvLi9ub2RlX21vZHVsZXMvQHN3Yy9oZWxwZXJzL2VzbS9faW50ZXJvcF9yZXF1aXJlX3dpbGRjYXJkLmpzPzRhOWQiXSwic291cmNlc0NvbnRlbnQiOlsiZnVuY3Rpb24gX2dldFJlcXVpcmVXaWxkY2FyZENhY2hlKG5vZGVJbnRlcm9wKSB7XG4gICAgaWYgKHR5cGVvZiBXZWFrTWFwICE9PSBcImZ1bmN0aW9uXCIpIHJldHVybiBudWxsO1xuXG4gICAgdmFyIGNhY2hlQmFiZWxJbnRlcm9wID0gbmV3IFdlYWtNYXAoKTtcbiAgICB2YXIgY2FjaGVOb2RlSW50ZXJvcCA9IG5ldyBXZWFrTWFwKCk7XG5cbiAgICByZXR1cm4gKF9nZXRSZXF1aXJlV2lsZGNhcmRDYWNoZSA9IGZ1bmN0aW9uKG5vZGVJbnRlcm9wKSB7XG4gICAgICAgIHJldHVybiBub2RlSW50ZXJvcCA/IGNhY2hlTm9kZUludGVyb3AgOiBjYWNoZUJhYmVsSW50ZXJvcDtcbiAgICB9KShub2RlSW50ZXJvcCk7XG59XG5leHBvcnQgZnVuY3Rpb24gX2ludGVyb3BfcmVxdWlyZV93aWxkY2FyZChvYmosIG5vZGVJbnRlcm9wKSB7XG4gICAgaWYgKCFub2RlSW50ZXJvcCAmJiBvYmogJiYgb2JqLl9fZXNNb2R1bGUpIHJldHVybiBvYmo7XG4gICAgaWYgKG9iaiA9PT0gbnVsbCB8fCB0eXBlb2Ygb2JqICE9PSBcIm9iamVjdFwiICYmIHR5cGVvZiBvYmogIT09IFwiZnVuY3Rpb25cIikgcmV0dXJuIHsgZGVmYXVsdDogb2JqIH07XG5cbiAgICB2YXIgY2FjaGUgPSBfZ2V0UmVxdWlyZVdpbGRjYXJkQ2FjaGUobm9kZUludGVyb3ApO1xuXG4gICAgaWYgKGNhY2hlICYmIGNhY2hlLmhhcyhvYmopKSByZXR1cm4gY2FjaGUuZ2V0KG9iaik7XG5cbiAgICB2YXIgbmV3T2JqID0geyBfX3Byb3RvX186IG51bGwgfTtcbiAgICB2YXIgaGFzUHJvcGVydHlEZXNjcmlwdG9yID0gT2JqZWN0LmRlZmluZVByb3BlcnR5ICYmIE9iamVjdC5nZXRPd25Qcm9wZXJ0eURlc2NyaXB0b3I7XG5cbiAgICBmb3IgKHZhciBrZXkgaW4gb2JqKSB7XG4gICAgICAgIGlmIChrZXkgIT09IFwiZGVmYXVsdFwiICYmIE9iamVjdC5wcm90b3R5cGUuaGFzT3duUHJvcGVydHkuY2FsbChvYmosIGtleSkpIHtcbiAgICAgICAgICAgIHZhciBkZXNjID0gaGFzUHJvcGVydHlEZXNjcmlwdG9yID8gT2JqZWN0LmdldE93blByb3BlcnR5RGVzY3JpcHRvcihvYmosIGtleSkgOiBudWxsO1xuICAgICAgICAgICAgaWYgKGRlc2MgJiYgKGRlc2MuZ2V0IHx8IGRlc2Muc2V0KSkgT2JqZWN0LmRlZmluZVByb3BlcnR5KG5ld09iaiwga2V5LCBkZXNjKTtcbiAgICAgICAgICAgIGVsc2UgbmV3T2JqW2tleV0gPSBvYmpba2V5XTtcbiAgICAgICAgfVxuICAgIH1cblxuICAgIG5ld09iai5kZWZhdWx0ID0gb2JqO1xuXG4gICAgaWYgKGNhY2hlKSBjYWNoZS5zZXQob2JqLCBuZXdPYmopO1xuXG4gICAgcmV0dXJuIG5ld09iajtcbn1cbmV4cG9ydCB7IF9pbnRlcm9wX3JlcXVpcmVfd2lsZGNhcmQgYXMgXyB9O1xuIl0sIm5hbWVzIjpbXSwic291cmNlUm9vdCI6IiJ9\n//# sourceURL=webpack-internal:///(ssr)/./node_modules/@swc/helpers/esm/_interop_require_wildcard.js\n");
|
||||
|
||||
/***/ }),
|
||||
|
||||
/***/ "(ssr)/./node_modules/@swc/helpers/esm/_tagged_template_literal_loose.js":
|
||||
/*!*************************************************************************!*\
|
||||
!*** ./node_modules/@swc/helpers/esm/_tagged_template_literal_loose.js ***!
|
||||
\*************************************************************************/
|
||||
/***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => {
|
||||
|
||||
eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ _: () => (/* binding */ _tagged_template_literal_loose),\n/* harmony export */ _tagged_template_literal_loose: () => (/* binding */ _tagged_template_literal_loose)\n/* harmony export */ });\nfunction _tagged_template_literal_loose(strings, raw) {\n if (!raw) raw = strings.slice(0);\n\n strings.raw = raw;\n\n return strings;\n}\n\n//# sourceURL=[module]\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiKHNzcikvLi9ub2RlX21vZHVsZXMvQHN3Yy9oZWxwZXJzL2VzbS9fdGFnZ2VkX3RlbXBsYXRlX2xpdGVyYWxfbG9vc2UuanMiLCJtYXBwaW5ncyI6Ijs7Ozs7QUFBTztBQUNQOztBQUVBOztBQUVBO0FBQ0E7QUFDK0MiLCJzb3VyY2VzIjpbIndlYnBhY2s6Ly9kYXBobmlzLy4vbm9kZV9tb2R1bGVzL0Bzd2MvaGVscGVycy9lc20vX3RhZ2dlZF90ZW1wbGF0ZV9saXRlcmFsX2xvb3NlLmpzPzI5M2UiXSwic291cmNlc0NvbnRlbnQiOlsiZXhwb3J0IGZ1bmN0aW9uIF90YWdnZWRfdGVtcGxhdGVfbGl0ZXJhbF9sb29zZShzdHJpbmdzLCByYXcpIHtcbiAgICBpZiAoIXJhdykgcmF3ID0gc3RyaW5ncy5zbGljZSgwKTtcblxuICAgIHN0cmluZ3MucmF3ID0gcmF3O1xuXG4gICAgcmV0dXJuIHN0cmluZ3M7XG59XG5leHBvcnQgeyBfdGFnZ2VkX3RlbXBsYXRlX2xpdGVyYWxfbG9vc2UgYXMgXyB9O1xuIl0sIm5hbWVzIjpbXSwic291cmNlUm9vdCI6IiJ9\n//# sourceURL=webpack-internal:///(ssr)/./node_modules/@swc/helpers/esm/_tagged_template_literal_loose.js\n");
|
||||
|
||||
/***/ }),
|
||||
|
||||
/***/ "(rsc)/./node_modules/@swc/helpers/esm/_interop_require_default.js":
|
||||
/*!*******************************************************************!*\
|
||||
!*** ./node_modules/@swc/helpers/esm/_interop_require_default.js ***!
|
||||
\*******************************************************************/
|
||||
/***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => {
|
||||
|
||||
eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ _: () => (/* binding */ _interop_require_default),\n/* harmony export */ _interop_require_default: () => (/* binding */ _interop_require_default)\n/* harmony export */ });\nfunction _interop_require_default(obj) {\n return obj && obj.__esModule ? obj : { default: obj };\n}\n\n//# sourceURL=[module]\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiKHJzYykvLi9ub2RlX21vZHVsZXMvQHN3Yy9oZWxwZXJzL2VzbS9faW50ZXJvcF9yZXF1aXJlX2RlZmF1bHQuanMiLCJtYXBwaW5ncyI6Ijs7Ozs7QUFBTztBQUNQLDJDQUEyQztBQUMzQztBQUN5QyIsInNvdXJjZXMiOlsid2VicGFjazovL2RhcGhuaXMvLi9ub2RlX21vZHVsZXMvQHN3Yy9oZWxwZXJzL2VzbS9faW50ZXJvcF9yZXF1aXJlX2RlZmF1bHQuanM/MzQ5ZiJdLCJzb3VyY2VzQ29udGVudCI6WyJleHBvcnQgZnVuY3Rpb24gX2ludGVyb3BfcmVxdWlyZV9kZWZhdWx0KG9iaikge1xuICAgIHJldHVybiBvYmogJiYgb2JqLl9fZXNNb2R1bGUgPyBvYmogOiB7IGRlZmF1bHQ6IG9iaiB9O1xufVxuZXhwb3J0IHsgX2ludGVyb3BfcmVxdWlyZV9kZWZhdWx0IGFzIF8gfTtcbiJdLCJuYW1lcyI6W10sInNvdXJjZVJvb3QiOiIifQ==\n//# sourceURL=webpack-internal:///(rsc)/./node_modules/@swc/helpers/esm/_interop_require_default.js\n");
|
||||
|
||||
/***/ })
|
||||
|
||||
};
|
||||
;
|
155
.next/server/vendor-chunks/@tanstack.js
Normal file
155
.next/server/vendor-chunks/@tanstack.js
Normal file
File diff suppressed because one or more lines are too long
35
.next/server/vendor-chunks/class-variance-authority.js
Normal file
35
.next/server/vendor-chunks/class-variance-authority.js
Normal file
File diff suppressed because one or more lines are too long
25
.next/server/vendor-chunks/clsx.js
Normal file
25
.next/server/vendor-chunks/clsx.js
Normal file
@ -0,0 +1,25 @@
|
||||
"use strict";
|
||||
/*
|
||||
* ATTENTION: An "eval-source-map" devtool has been used.
|
||||
* This devtool is neither made for production nor for readable output files.
|
||||
* It uses "eval()" calls to create a separate source file with attached SourceMaps in the browser devtools.
|
||||
* If you are trying to read the output file, select a different devtool (https://webpack.js.org/configuration/devtool/)
|
||||
* or disable the default devtool with "devtool: false".
|
||||
* If you are looking for production-ready output files, see mode: "production" (https://webpack.js.org/configuration/mode/).
|
||||
*/
|
||||
exports.id = "vendor-chunks/clsx";
|
||||
exports.ids = ["vendor-chunks/clsx"];
|
||||
exports.modules = {
|
||||
|
||||
/***/ "(ssr)/./node_modules/clsx/dist/clsx.mjs":
|
||||
/*!*****************************************!*\
|
||||
!*** ./node_modules/clsx/dist/clsx.mjs ***!
|
||||
\*****************************************/
|
||||
/***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => {
|
||||
|
||||
eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ clsx: () => (/* binding */ clsx),\n/* harmony export */ \"default\": () => (__WEBPACK_DEFAULT_EXPORT__)\n/* harmony export */ });\nfunction r(e){var t,f,n=\"\";if(\"string\"==typeof e||\"number\"==typeof e)n+=e;else if(\"object\"==typeof e)if(Array.isArray(e)){var o=e.length;for(t=0;t<o;t++)e[t]&&(f=r(e[t]))&&(n&&(n+=\" \"),n+=f)}else for(f in e)e[f]&&(n&&(n+=\" \"),n+=f);return n}function clsx(){for(var e,t,f=0,n=\"\",o=arguments.length;f<o;f++)(e=arguments[f])&&(t=r(e))&&(n&&(n+=\" \"),n+=t);return n}/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (clsx);//# sourceURL=[module]\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiKHNzcikvLi9ub2RlX21vZHVsZXMvY2xzeC9kaXN0L2Nsc3gubWpzIiwibWFwcGluZ3MiOiI7Ozs7O0FBQUEsY0FBYyxhQUFhLCtDQUErQyxnREFBZ0QsZUFBZSxRQUFRLElBQUksMENBQTBDLHlDQUF5QyxTQUFnQixnQkFBZ0Isd0NBQXdDLElBQUksbURBQW1ELFNBQVMsaUVBQWUsSUFBSSIsInNvdXJjZXMiOlsid2VicGFjazovL2RhcGhuaXMvLi9ub2RlX21vZHVsZXMvY2xzeC9kaXN0L2Nsc3gubWpzPzZkOTYiXSwic291cmNlc0NvbnRlbnQiOlsiZnVuY3Rpb24gcihlKXt2YXIgdCxmLG49XCJcIjtpZihcInN0cmluZ1wiPT10eXBlb2YgZXx8XCJudW1iZXJcIj09dHlwZW9mIGUpbis9ZTtlbHNlIGlmKFwib2JqZWN0XCI9PXR5cGVvZiBlKWlmKEFycmF5LmlzQXJyYXkoZSkpe3ZhciBvPWUubGVuZ3RoO2Zvcih0PTA7dDxvO3QrKyllW3RdJiYoZj1yKGVbdF0pKSYmKG4mJihuKz1cIiBcIiksbis9Zil9ZWxzZSBmb3IoZiBpbiBlKWVbZl0mJihuJiYobis9XCIgXCIpLG4rPWYpO3JldHVybiBufWV4cG9ydCBmdW5jdGlvbiBjbHN4KCl7Zm9yKHZhciBlLHQsZj0wLG49XCJcIixvPWFyZ3VtZW50cy5sZW5ndGg7ZjxvO2YrKykoZT1hcmd1bWVudHNbZl0pJiYodD1yKGUpKSYmKG4mJihuKz1cIiBcIiksbis9dCk7cmV0dXJuIG59ZXhwb3J0IGRlZmF1bHQgY2xzeDsiXSwibmFtZXMiOltdLCJzb3VyY2VSb290IjoiIn0=\n//# sourceURL=webpack-internal:///(ssr)/./node_modules/clsx/dist/clsx.mjs\n");
|
||||
|
||||
/***/ })
|
||||
|
||||
};
|
||||
;
|
25
.next/server/vendor-chunks/geist.js
Normal file
25
.next/server/vendor-chunks/geist.js
Normal file
@ -0,0 +1,25 @@
|
||||
"use strict";
|
||||
/*
|
||||
* ATTENTION: An "eval-source-map" devtool has been used.
|
||||
* This devtool is neither made for production nor for readable output files.
|
||||
* It uses "eval()" calls to create a separate source file with attached SourceMaps in the browser devtools.
|
||||
* If you are trying to read the output file, select a different devtool (https://webpack.js.org/configuration/devtool/)
|
||||
* or disable the default devtool with "devtool: false".
|
||||
* If you are looking for production-ready output files, see mode: "production" (https://webpack.js.org/configuration/mode/).
|
||||
*/
|
||||
exports.id = "vendor-chunks/geist";
|
||||
exports.ids = ["vendor-chunks/geist"];
|
||||
exports.modules = {
|
||||
|
||||
/***/ "(rsc)/./node_modules/geist/dist/sans.js":
|
||||
/*!*****************************************!*\
|
||||
!*** ./node_modules/geist/dist/sans.js ***!
|
||||
\*****************************************/
|
||||
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
|
||||
|
||||
eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ GeistSans: () => (/* reexport default from dynamic */ next_font_local_target_css_path_node_modules_geist_dist_sans_js_import_arguments_src_fonts_geist_sans_Geist_Variable_woff2_variable_font_geist_sans_weight_100_900_variableName_GeistSans___WEBPACK_IMPORTED_MODULE_0___default.a)\n/* harmony export */ });\n/* harmony import */ var next_font_local_target_css_path_node_modules_geist_dist_sans_js_import_arguments_src_fonts_geist_sans_Geist_Variable_woff2_variable_font_geist_sans_weight_100_900_variableName_GeistSans___WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! next/font/local/target.css?{\"path\":\"node_modules/geist/dist/sans.js\",\"import\":\"\",\"arguments\":[{\"src\":\"./fonts/geist-sans/Geist-Variable.woff2\",\"variable\":\"--font-geist-sans\",\"weight\":\"100 900\"}],\"variableName\":\"GeistSans\"} */ \"(rsc)/./node_modules/next/font/local/target.css?{\\\"path\\\":\\\"node_modules/geist/dist/sans.js\\\",\\\"import\\\":\\\"\\\",\\\"arguments\\\":[{\\\"src\\\":\\\"./fonts/geist-sans/Geist-Variable.woff2\\\",\\\"variable\\\":\\\"--font-geist-sans\\\",\\\"weight\\\":\\\"100 900\\\"}],\\\"variableName\\\":\\\"GeistSans\\\"}\");\n/* harmony import */ var next_font_local_target_css_path_node_modules_geist_dist_sans_js_import_arguments_src_fonts_geist_sans_Geist_Variable_woff2_variable_font_geist_sans_weight_100_900_variableName_GeistSans___WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(next_font_local_target_css_path_node_modules_geist_dist_sans_js_import_arguments_src_fonts_geist_sans_Geist_Variable_woff2_variable_font_geist_sans_weight_100_900_variableName_GeistSans___WEBPACK_IMPORTED_MODULE_0__);\n\n\n//# sourceURL=[module]\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiKHJzYykvLi9ub2RlX21vZHVsZXMvZ2Vpc3QvZGlzdC9zYW5zLmpzIiwibWFwcGluZ3MiOiI7Ozs7OztBQUVhQTtBQUFBQSIsInNvdXJjZXMiOlsid2VicGFjazovL2RhcGhuaXMvLi9ub2RlX21vZHVsZXMvZ2Vpc3QvZGlzdC9zYW5zLmpzP2U4NWIiXSwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IGxvY2FsRm9udCBmcm9tIFwibmV4dC9mb250L2xvY2FsXCI7XG5cbmV4cG9ydCBjb25zdCBHZWlzdFNhbnMgPSBsb2NhbEZvbnQoe1xuICBzcmM6IFwiLi9mb250cy9nZWlzdC1zYW5zL0dlaXN0LVZhcmlhYmxlLndvZmYyXCIsXG4gIHZhcmlhYmxlOiBcIi0tZm9udC1nZWlzdC1zYW5zXCIsXG4gIHdlaWdodDogXCIxMDAgOTAwXCIsXG59KTtcbiJdLCJuYW1lcyI6WyJHZWlzdFNhbnMiXSwic291cmNlUm9vdCI6IiJ9\n//# sourceURL=webpack-internal:///(rsc)/./node_modules/geist/dist/sans.js\n");
|
||||
|
||||
/***/ })
|
||||
|
||||
};
|
||||
;
|
55
.next/server/vendor-chunks/lucia.js
Normal file
55
.next/server/vendor-chunks/lucia.js
Normal file
File diff suppressed because one or more lines are too long
65
.next/server/vendor-chunks/lucide-react.js
Normal file
65
.next/server/vendor-chunks/lucide-react.js
Normal file
File diff suppressed because one or more lines are too long
3306
.next/server/vendor-chunks/next.js
Normal file
3306
.next/server/vendor-chunks/next.js
Normal file
File diff suppressed because one or more lines are too long
205
.next/server/vendor-chunks/oslo.js
Normal file
205
.next/server/vendor-chunks/oslo.js
Normal file
File diff suppressed because one or more lines are too long
25
.next/server/vendor-chunks/tailwind-merge.js
Normal file
25
.next/server/vendor-chunks/tailwind-merge.js
Normal file
File diff suppressed because one or more lines are too long
220
.next/server/webpack-runtime.js
Normal file
220
.next/server/webpack-runtime.js
Normal file
@ -0,0 +1,220 @@
|
||||
/*
|
||||
* ATTENTION: An "eval-source-map" devtool has been used.
|
||||
* This devtool is neither made for production nor for readable output files.
|
||||
* It uses "eval()" calls to create a separate source file with attached SourceMaps in the browser devtools.
|
||||
* If you are trying to read the output file, select a different devtool (https://webpack.js.org/configuration/devtool/)
|
||||
* or disable the default devtool with "devtool: false".
|
||||
* If you are looking for production-ready output files, see mode: "production" (https://webpack.js.org/configuration/mode/).
|
||||
*/
|
||||
/******/ (() => { // webpackBootstrap
|
||||
/******/ "use strict";
|
||||
/******/ var __webpack_modules__ = ({});
|
||||
/************************************************************************/
|
||||
/******/ // The module cache
|
||||
/******/ var __webpack_module_cache__ = {};
|
||||
/******/
|
||||
/******/ // The require function
|
||||
/******/ function __webpack_require__(moduleId) {
|
||||
/******/ // Check if module is in cache
|
||||
/******/ var cachedModule = __webpack_module_cache__[moduleId];
|
||||
/******/ if (cachedModule !== undefined) {
|
||||
/******/ return cachedModule.exports;
|
||||
/******/ }
|
||||
/******/ // Create a new module (and put it into the cache)
|
||||
/******/ var module = __webpack_module_cache__[moduleId] = {
|
||||
/******/ id: moduleId,
|
||||
/******/ loaded: false,
|
||||
/******/ exports: {}
|
||||
/******/ };
|
||||
/******/
|
||||
/******/ // Execute the module function
|
||||
/******/ var threw = true;
|
||||
/******/ try {
|
||||
/******/ __webpack_modules__[moduleId](module, module.exports, __webpack_require__);
|
||||
/******/ threw = false;
|
||||
/******/ } finally {
|
||||
/******/ if(threw) delete __webpack_module_cache__[moduleId];
|
||||
/******/ }
|
||||
/******/
|
||||
/******/ // Flag the module as loaded
|
||||
/******/ module.loaded = true;
|
||||
/******/
|
||||
/******/ // Return the exports of the module
|
||||
/******/ return module.exports;
|
||||
/******/ }
|
||||
/******/
|
||||
/******/ // expose the modules object (__webpack_modules__)
|
||||
/******/ __webpack_require__.m = __webpack_modules__;
|
||||
/******/
|
||||
/************************************************************************/
|
||||
/******/ /* webpack/runtime/amd options */
|
||||
/******/ (() => {
|
||||
/******/ __webpack_require__.amdO = {};
|
||||
/******/ })();
|
||||
/******/
|
||||
/******/ /* webpack/runtime/compat get default export */
|
||||
/******/ (() => {
|
||||
/******/ // getDefaultExport function for compatibility with non-harmony modules
|
||||
/******/ __webpack_require__.n = (module) => {
|
||||
/******/ var getter = module && module.__esModule ?
|
||||
/******/ () => (module['default']) :
|
||||
/******/ () => (module);
|
||||
/******/ __webpack_require__.d(getter, { a: getter });
|
||||
/******/ return getter;
|
||||
/******/ };
|
||||
/******/ })();
|
||||
/******/
|
||||
/******/ /* webpack/runtime/create fake namespace object */
|
||||
/******/ (() => {
|
||||
/******/ var getProto = Object.getPrototypeOf ? (obj) => (Object.getPrototypeOf(obj)) : (obj) => (obj.__proto__);
|
||||
/******/ var leafPrototypes;
|
||||
/******/ // create a fake namespace object
|
||||
/******/ // mode & 1: value is a module id, require it
|
||||
/******/ // mode & 2: merge all properties of value into the ns
|
||||
/******/ // mode & 4: return value when already ns object
|
||||
/******/ // mode & 16: return value when it's Promise-like
|
||||
/******/ // mode & 8|1: behave like require
|
||||
/******/ __webpack_require__.t = function(value, mode) {
|
||||
/******/ if(mode & 1) value = this(value);
|
||||
/******/ if(mode & 8) return value;
|
||||
/******/ if(typeof value === 'object' && value) {
|
||||
/******/ if((mode & 4) && value.__esModule) return value;
|
||||
/******/ if((mode & 16) && typeof value.then === 'function') return value;
|
||||
/******/ }
|
||||
/******/ var ns = Object.create(null);
|
||||
/******/ __webpack_require__.r(ns);
|
||||
/******/ var def = {};
|
||||
/******/ leafPrototypes = leafPrototypes || [null, getProto({}), getProto([]), getProto(getProto)];
|
||||
/******/ for(var current = mode & 2 && value; typeof current == 'object' && !~leafPrototypes.indexOf(current); current = getProto(current)) {
|
||||
/******/ Object.getOwnPropertyNames(current).forEach((key) => (def[key] = () => (value[key])));
|
||||
/******/ }
|
||||
/******/ def['default'] = () => (value);
|
||||
/******/ __webpack_require__.d(ns, def);
|
||||
/******/ return ns;
|
||||
/******/ };
|
||||
/******/ })();
|
||||
/******/
|
||||
/******/ /* webpack/runtime/define property getters */
|
||||
/******/ (() => {
|
||||
/******/ // define getter functions for harmony exports
|
||||
/******/ __webpack_require__.d = (exports, definition) => {
|
||||
/******/ for(var key in definition) {
|
||||
/******/ if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {
|
||||
/******/ Object.defineProperty(exports, key, { enumerable: true, get: definition[key] });
|
||||
/******/ }
|
||||
/******/ }
|
||||
/******/ };
|
||||
/******/ })();
|
||||
/******/
|
||||
/******/ /* webpack/runtime/ensure chunk */
|
||||
/******/ (() => {
|
||||
/******/ __webpack_require__.f = {};
|
||||
/******/ // This file contains only the entry chunk.
|
||||
/******/ // The chunk loading function for additional chunks
|
||||
/******/ __webpack_require__.e = (chunkId) => {
|
||||
/******/ return Promise.all(Object.keys(__webpack_require__.f).reduce((promises, key) => {
|
||||
/******/ __webpack_require__.f[key](chunkId, promises);
|
||||
/******/ return promises;
|
||||
/******/ }, []));
|
||||
/******/ };
|
||||
/******/ })();
|
||||
/******/
|
||||
/******/ /* webpack/runtime/get javascript chunk filename */
|
||||
/******/ (() => {
|
||||
/******/ // This function allow to reference async chunks and sibling chunks for the entrypoint
|
||||
/******/ __webpack_require__.u = (chunkId) => {
|
||||
/******/ // return url for filenames based on template
|
||||
/******/ return "" + chunkId + ".js";
|
||||
/******/ };
|
||||
/******/ })();
|
||||
/******/
|
||||
/******/ /* webpack/runtime/getFullHash */
|
||||
/******/ (() => {
|
||||
/******/ __webpack_require__.h = () => ("3b31194daa7b21f8")
|
||||
/******/ })();
|
||||
/******/
|
||||
/******/ /* webpack/runtime/hasOwnProperty shorthand */
|
||||
/******/ (() => {
|
||||
/******/ __webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))
|
||||
/******/ })();
|
||||
/******/
|
||||
/******/ /* webpack/runtime/make namespace object */
|
||||
/******/ (() => {
|
||||
/******/ // define __esModule on exports
|
||||
/******/ __webpack_require__.r = (exports) => {
|
||||
/******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
|
||||
/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
||||
/******/ }
|
||||
/******/ Object.defineProperty(exports, '__esModule', { value: true });
|
||||
/******/ };
|
||||
/******/ })();
|
||||
/******/
|
||||
/******/ /* webpack/runtime/node module decorator */
|
||||
/******/ (() => {
|
||||
/******/ __webpack_require__.nmd = (module) => {
|
||||
/******/ module.paths = [];
|
||||
/******/ if (!module.children) module.children = [];
|
||||
/******/ return module;
|
||||
/******/ };
|
||||
/******/ })();
|
||||
/******/
|
||||
/******/ /* webpack/runtime/startup entrypoint */
|
||||
/******/ (() => {
|
||||
/******/ __webpack_require__.X = (result, chunkIds, fn) => {
|
||||
/******/ // arguments: chunkIds, moduleId are deprecated
|
||||
/******/ var moduleId = chunkIds;
|
||||
/******/ if(!fn) chunkIds = result, fn = () => (__webpack_require__(__webpack_require__.s = moduleId));
|
||||
/******/ chunkIds.map(__webpack_require__.e, __webpack_require__)
|
||||
/******/ var r = fn();
|
||||
/******/ return r === undefined ? result : r;
|
||||
/******/ }
|
||||
/******/ })();
|
||||
/******/
|
||||
/******/ /* webpack/runtime/require chunk loading */
|
||||
/******/ (() => {
|
||||
/******/ // no baseURI
|
||||
/******/
|
||||
/******/ // object to store loaded chunks
|
||||
/******/ // "1" means "loaded", otherwise not loaded yet
|
||||
/******/ var installedChunks = {
|
||||
/******/ "webpack-runtime": 1
|
||||
/******/ };
|
||||
/******/
|
||||
/******/ // no on chunks loaded
|
||||
/******/
|
||||
/******/ var installChunk = (chunk) => {
|
||||
/******/ var moreModules = chunk.modules, chunkIds = chunk.ids, runtime = chunk.runtime;
|
||||
/******/ for(var moduleId in moreModules) {
|
||||
/******/ if(__webpack_require__.o(moreModules, moduleId)) {
|
||||
/******/ __webpack_require__.m[moduleId] = moreModules[moduleId];
|
||||
/******/ }
|
||||
/******/ }
|
||||
/******/ if(runtime) runtime(__webpack_require__);
|
||||
/******/ for(var i = 0; i < chunkIds.length; i++)
|
||||
/******/ installedChunks[chunkIds[i]] = 1;
|
||||
/******/
|
||||
/******/ };
|
||||
/******/
|
||||
/******/ // require() chunk loading for javascript
|
||||
/******/ __webpack_require__.f.require = (chunkId, promises) => {
|
||||
/******/ // "1" is the signal for "already loaded"
|
||||
/******/ if(!installedChunks[chunkId]) {
|
||||
/******/ if("webpack-runtime" != chunkId) {
|
||||
/******/ installChunk(require("./" + __webpack_require__.u(chunkId)));
|
||||
/******/ } else installedChunks[chunkId] = 1;
|
||||
/******/ }
|
||||
/******/ };
|
||||
/******/
|
||||
/******/ module.exports = __webpack_require__;
|
||||
/******/ __webpack_require__.C = installChunk;
|
||||
/******/
|
||||
/******/ // no HMR
|
||||
/******/
|
||||
/******/ // no HMR manifest
|
||||
/******/ })();
|
||||
/******/
|
||||
/************************************************************************/
|
||||
/******/
|
||||
/******/
|
||||
/******/ })()
|
||||
;
|
138
.next/static/chunks/app-pages-internals.js
Normal file
138
.next/static/chunks/app-pages-internals.js
Normal file
File diff suppressed because one or more lines are too long
554
.next/static/chunks/app/layout.js
Normal file
554
.next/static/chunks/app/layout.js
Normal file
File diff suppressed because one or more lines are too long
538
.next/static/chunks/app/page.js
Normal file
538
.next/static/chunks/app/page.js
Normal file
File diff suppressed because one or more lines are too long
1989
.next/static/chunks/main-app.js
Normal file
1989
.next/static/chunks/main-app.js
Normal file
File diff suppressed because one or more lines are too long
1
.next/static/chunks/polyfills.js
Normal file
1
.next/static/chunks/polyfills.js
Normal file
File diff suppressed because one or more lines are too long
1410
.next/static/chunks/webpack.js
Normal file
1410
.next/static/chunks/webpack.js
Normal file
File diff suppressed because it is too large
Load Diff
2653
.next/static/css/app/layout.css
Normal file
2653
.next/static/css/app/layout.css
Normal file
File diff suppressed because it is too large
Load Diff
1
.next/static/development/_buildManifest.js
Normal file
1
.next/static/development/_buildManifest.js
Normal file
@ -0,0 +1 @@
|
||||
self.__BUILD_MANIFEST = {__rewrites:{afterFiles:[],beforeFiles:[],fallback:[]},sortedPages:["\u002F_app"]};self.__BUILD_MANIFEST_CB && self.__BUILD_MANIFEST_CB()
|
1
.next/static/development/_ssgManifest.js
Normal file
1
.next/static/development/_ssgManifest.js
Normal file
@ -0,0 +1 @@
|
||||
self.__SSG_MANIFEST=new Set;self.__SSG_MANIFEST_CB&&self.__SSG_MANIFEST_CB()
|
BIN
.next/static/media/e11418ac562b8ac1-s.p.woff2
Normal file
BIN
.next/static/media/e11418ac562b8ac1-s.p.woff2
Normal file
Binary file not shown.
1
.next/static/webpack/633457081244afec._.hot-update.json
Normal file
1
.next/static/webpack/633457081244afec._.hot-update.json
Normal file
@ -0,0 +1 @@
|
||||
{"c":[],"r":[],"m":[]}
|
@ -0,0 +1,22 @@
|
||||
"use strict";
|
||||
/*
|
||||
* ATTENTION: An "eval-source-map" devtool has been used.
|
||||
* This devtool is neither made for production nor for readable output files.
|
||||
* It uses "eval()" calls to create a separate source file with attached SourceMaps in the browser devtools.
|
||||
* If you are trying to read the output file, select a different devtool (https://webpack.js.org/configuration/devtool/)
|
||||
* or disable the default devtool with "devtool: false".
|
||||
* If you are looking for production-ready output files, see mode: "production" (https://webpack.js.org/configuration/mode/).
|
||||
*/
|
||||
self["webpackHotUpdate_N_E"]("app/layout",{
|
||||
|
||||
/***/ "(app-pages-browser)/./app/globals.css":
|
||||
/*!*************************!*\
|
||||
!*** ./app/globals.css ***!
|
||||
\*************************/
|
||||
/***/ (function(module, __webpack_exports__, __webpack_require__) {
|
||||
|
||||
eval(__webpack_require__.ts("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (\"7fee949e42a3\");\nif (true) { module.hot.accept() }\n//# sourceURL=[module]\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiKGFwcC1wYWdlcy1icm93c2VyKS8uL2FwcC9nbG9iYWxzLmNzcyIsIm1hcHBpbmdzIjoiO0FBQUEsK0RBQWUsY0FBYztBQUM3QixJQUFJLElBQVUsSUFBSSxpQkFBaUIiLCJzb3VyY2VzIjpbIndlYnBhY2s6Ly9fTl9FLy4vYXBwL2dsb2JhbHMuY3NzPzlkMzMiXSwic291cmNlc0NvbnRlbnQiOlsiZXhwb3J0IGRlZmF1bHQgXCI3ZmVlOTQ5ZTQyYTNcIlxuaWYgKG1vZHVsZS5ob3QpIHsgbW9kdWxlLmhvdC5hY2NlcHQoKSB9XG4iXSwibmFtZXMiOltdLCJzb3VyY2VSb290IjoiIn0=\n//# sourceURL=webpack-internal:///(app-pages-browser)/./app/globals.css\n"));
|
||||
|
||||
/***/ })
|
||||
|
||||
});
|
@ -0,0 +1 @@
|
||||
{"c":["app/layout","webpack"],"r":[],"m":[]}
|
18
.next/static/webpack/webpack.f8d98021d173fafc.hot-update.js
Normal file
18
.next/static/webpack/webpack.f8d98021d173fafc.hot-update.js
Normal file
@ -0,0 +1,18 @@
|
||||
"use strict";
|
||||
/*
|
||||
* ATTENTION: An "eval-source-map" devtool has been used.
|
||||
* This devtool is neither made for production nor for readable output files.
|
||||
* It uses "eval()" calls to create a separate source file with attached SourceMaps in the browser devtools.
|
||||
* If you are trying to read the output file, select a different devtool (https://webpack.js.org/configuration/devtool/)
|
||||
* or disable the default devtool with "devtool: false".
|
||||
* If you are looking for production-ready output files, see mode: "production" (https://webpack.js.org/configuration/mode/).
|
||||
*/
|
||||
self["webpackHotUpdate_N_E"]("webpack",{},
|
||||
/******/ function(__webpack_require__) { // webpackRuntimeModules
|
||||
/******/ /* webpack/runtime/getFullHash */
|
||||
/******/ !function() {
|
||||
/******/ __webpack_require__.h = function() { return "dfbed2d3019c9946"; }
|
||||
/******/ }();
|
||||
/******/
|
||||
/******/ }
|
||||
);
|
27
.next/trace
Normal file
27
.next/trace
Normal file
File diff suppressed because one or more lines are too long
79
.next/types/app/layout.ts
Normal file
79
.next/types/app/layout.ts
Normal file
@ -0,0 +1,79 @@
|
||||
// File: /home/polaris/Documents/daphnis/app/layout.tsx
|
||||
import * as entry from '../../../app/layout.js'
|
||||
import type { ResolvingMetadata, ResolvingViewport } from 'next/dist/lib/metadata/types/metadata-interface.js'
|
||||
|
||||
type TEntry = typeof import('../../../app/layout.js')
|
||||
|
||||
// Check that the entry is a valid entry
|
||||
checkFields<Diff<{
|
||||
default: Function
|
||||
config?: {}
|
||||
generateStaticParams?: Function
|
||||
revalidate?: RevalidateRange<TEntry> | false
|
||||
dynamic?: 'auto' | 'force-dynamic' | 'error' | 'force-static'
|
||||
dynamicParams?: boolean
|
||||
fetchCache?: 'auto' | 'force-no-store' | 'only-no-store' | 'default-no-store' | 'default-cache' | 'only-cache' | 'force-cache'
|
||||
preferredRegion?: 'auto' | 'global' | 'home' | string | string[]
|
||||
runtime?: 'nodejs' | 'experimental-edge' | 'edge'
|
||||
maxDuration?: number
|
||||
|
||||
metadata?: any
|
||||
generateMetadata?: Function
|
||||
viewport?: any
|
||||
generateViewport?: Function
|
||||
|
||||
}, TEntry, ''>>()
|
||||
|
||||
// Check the prop type of the entry function
|
||||
checkFields<Diff<LayoutProps, FirstArg<TEntry['default']>, 'default'>>()
|
||||
|
||||
// Check the arguments and return type of the generateMetadata function
|
||||
if ('generateMetadata' in entry) {
|
||||
checkFields<Diff<LayoutProps, FirstArg<MaybeField<TEntry, 'generateMetadata'>>, 'generateMetadata'>>()
|
||||
checkFields<Diff<ResolvingMetadata, SecondArg<MaybeField<TEntry, 'generateMetadata'>>, 'generateMetadata'>>()
|
||||
}
|
||||
|
||||
// Check the arguments and return type of the generateViewport function
|
||||
if ('generateViewport' in entry) {
|
||||
checkFields<Diff<LayoutProps, FirstArg<MaybeField<TEntry, 'generateViewport'>>, 'generateViewport'>>()
|
||||
checkFields<Diff<ResolvingViewport, SecondArg<MaybeField<TEntry, 'generateViewport'>>, 'generateViewport'>>()
|
||||
}
|
||||
|
||||
// Check the arguments and return type of the generateStaticParams function
|
||||
if ('generateStaticParams' in entry) {
|
||||
checkFields<Diff<{ params: PageParams }, FirstArg<MaybeField<TEntry, 'generateStaticParams'>>, 'generateStaticParams'>>()
|
||||
checkFields<Diff<{ __tag__: 'generateStaticParams', __return_type__: any[] | Promise<any[]> }, { __tag__: 'generateStaticParams', __return_type__: ReturnType<MaybeField<TEntry, 'generateStaticParams'>> }>>()
|
||||
}
|
||||
|
||||
type PageParams = any
|
||||
export interface PageProps {
|
||||
params?: any
|
||||
searchParams?: any
|
||||
}
|
||||
export interface LayoutProps {
|
||||
children?: React.ReactNode
|
||||
|
||||
params?: any
|
||||
}
|
||||
|
||||
// =============
|
||||
// Utility types
|
||||
type RevalidateRange<T> = T extends { revalidate: any } ? NonNegative<T['revalidate']> : never
|
||||
|
||||
// If T is unknown or any, it will be an empty {} type. Otherwise, it will be the same as Omit<T, keyof Base>.
|
||||
type OmitWithTag<T, K extends keyof any, _M> = Omit<T, K>
|
||||
type Diff<Base, T extends Base, Message extends string = ''> = 0 extends (1 & T) ? {} : OmitWithTag<T, keyof Base, Message>
|
||||
|
||||
type FirstArg<T extends Function> = T extends (...args: [infer T, any]) => any ? unknown extends T ? any : T : never
|
||||
type SecondArg<T extends Function> = T extends (...args: [any, infer T]) => any ? unknown extends T ? any : T : never
|
||||
type MaybeField<T, K extends string> = T extends { [k in K]: infer G } ? G extends Function ? G : never : never
|
||||
|
||||
|
||||
|
||||
function checkFields<_ extends { [k in keyof any]: never }>() {}
|
||||
|
||||
// https://github.com/sindresorhus/type-fest
|
||||
type Numeric = number | bigint
|
||||
type Zero = 0 | 0n
|
||||
type Negative<T extends Numeric> = T extends Zero ? never : `${T}` extends `-${string}` ? T : never
|
||||
type NonNegative<T extends Numeric> = T extends Zero ? T : Negative<T> extends never ? T : '__invalid_negative_number__'
|
79
.next/types/app/page.ts
Normal file
79
.next/types/app/page.ts
Normal file
@ -0,0 +1,79 @@
|
||||
// File: /home/polaris/Documents/daphnis/app/page.tsx
|
||||
import * as entry from '../../../app/page.js'
|
||||
import type { ResolvingMetadata, ResolvingViewport } from 'next/dist/lib/metadata/types/metadata-interface.js'
|
||||
|
||||
type TEntry = typeof import('../../../app/page.js')
|
||||
|
||||
// Check that the entry is a valid entry
|
||||
checkFields<Diff<{
|
||||
default: Function
|
||||
config?: {}
|
||||
generateStaticParams?: Function
|
||||
revalidate?: RevalidateRange<TEntry> | false
|
||||
dynamic?: 'auto' | 'force-dynamic' | 'error' | 'force-static'
|
||||
dynamicParams?: boolean
|
||||
fetchCache?: 'auto' | 'force-no-store' | 'only-no-store' | 'default-no-store' | 'default-cache' | 'only-cache' | 'force-cache'
|
||||
preferredRegion?: 'auto' | 'global' | 'home' | string | string[]
|
||||
runtime?: 'nodejs' | 'experimental-edge' | 'edge'
|
||||
maxDuration?: number
|
||||
|
||||
metadata?: any
|
||||
generateMetadata?: Function
|
||||
viewport?: any
|
||||
generateViewport?: Function
|
||||
|
||||
}, TEntry, ''>>()
|
||||
|
||||
// Check the prop type of the entry function
|
||||
checkFields<Diff<PageProps, FirstArg<TEntry['default']>, 'default'>>()
|
||||
|
||||
// Check the arguments and return type of the generateMetadata function
|
||||
if ('generateMetadata' in entry) {
|
||||
checkFields<Diff<PageProps, FirstArg<MaybeField<TEntry, 'generateMetadata'>>, 'generateMetadata'>>()
|
||||
checkFields<Diff<ResolvingMetadata, SecondArg<MaybeField<TEntry, 'generateMetadata'>>, 'generateMetadata'>>()
|
||||
}
|
||||
|
||||
// Check the arguments and return type of the generateViewport function
|
||||
if ('generateViewport' in entry) {
|
||||
checkFields<Diff<PageProps, FirstArg<MaybeField<TEntry, 'generateViewport'>>, 'generateViewport'>>()
|
||||
checkFields<Diff<ResolvingViewport, SecondArg<MaybeField<TEntry, 'generateViewport'>>, 'generateViewport'>>()
|
||||
}
|
||||
|
||||
// Check the arguments and return type of the generateStaticParams function
|
||||
if ('generateStaticParams' in entry) {
|
||||
checkFields<Diff<{ params: PageParams }, FirstArg<MaybeField<TEntry, 'generateStaticParams'>>, 'generateStaticParams'>>()
|
||||
checkFields<Diff<{ __tag__: 'generateStaticParams', __return_type__: any[] | Promise<any[]> }, { __tag__: 'generateStaticParams', __return_type__: ReturnType<MaybeField<TEntry, 'generateStaticParams'>> }>>()
|
||||
}
|
||||
|
||||
type PageParams = any
|
||||
export interface PageProps {
|
||||
params?: any
|
||||
searchParams?: any
|
||||
}
|
||||
export interface LayoutProps {
|
||||
children?: React.ReactNode
|
||||
|
||||
params?: any
|
||||
}
|
||||
|
||||
// =============
|
||||
// Utility types
|
||||
type RevalidateRange<T> = T extends { revalidate: any } ? NonNegative<T['revalidate']> : never
|
||||
|
||||
// If T is unknown or any, it will be an empty {} type. Otherwise, it will be the same as Omit<T, keyof Base>.
|
||||
type OmitWithTag<T, K extends keyof any, _M> = Omit<T, K>
|
||||
type Diff<Base, T extends Base, Message extends string = ''> = 0 extends (1 & T) ? {} : OmitWithTag<T, keyof Base, Message>
|
||||
|
||||
type FirstArg<T extends Function> = T extends (...args: [infer T, any]) => any ? unknown extends T ? any : T : never
|
||||
type SecondArg<T extends Function> = T extends (...args: [any, infer T]) => any ? unknown extends T ? any : T : never
|
||||
type MaybeField<T, K extends string> = T extends { [k in K]: infer G } ? G extends Function ? G : never : never
|
||||
|
||||
|
||||
|
||||
function checkFields<_ extends { [k in keyof any]: never }>() {}
|
||||
|
||||
// https://github.com/sindresorhus/type-fest
|
||||
type Numeric = number | bigint
|
||||
type Zero = 0 | 0n
|
||||
type Negative<T extends Numeric> = T extends Zero ? never : `${T}` extends `-${string}` ? T : never
|
||||
type NonNegative<T extends Numeric> = T extends Zero ? T : Negative<T> extends never ? T : '__invalid_negative_number__'
|
1
.next/types/package.json
Normal file
1
.next/types/package.json
Normal file
@ -0,0 +1 @@
|
||||
{"type": "module"}
|
65
README.md
Normal file
65
README.md
Normal file
@ -0,0 +1,65 @@
|
||||
|
||||
|
||||
Needs BunJS and a Mysql DB
|
||||
|
||||
|
||||
1 - create a mysql database called lachesis
|
||||
|
||||
make a .env in the root directory containing the following (be sure to use your own data)
|
||||
|
||||
DATABASE_URL="mysql://root:password@localhost:3306/lachesis"
|
||||
DATABASE_AIME_URL = "mysql://root:password@localhost:3306/aime"
|
||||
|
||||
---
|
||||
2 - delete the migrations folder(s) inside schemas/artemis and schemas/lachesis
|
||||
|
||||
----
|
||||
3 - run the below
|
||||
|
||||
$ `bun db:init`
|
||||
|
||||
What it does:
|
||||
|
||||
It will create a empty data base for lachesis and pull your existing artemis one into its own schema via introspection.
|
||||
|
||||
`"db:init": "npx prisma migrate dev --name init --schema prisma/schemas lachesis/schema.prisma; npx prisma db pull --schema prisma/schemas/artemis/schema.prisma"`
|
||||
|
||||
---
|
||||
|
||||
4 - run the below
|
||||
|
||||
$ `bun lachesis:generate`
|
||||
|
||||
What it does:
|
||||
|
||||
generates the schema output
|
||||
|
||||
`"lachesis:generate": "prisma generate --schema=./prisma/schemas/lachesis/schema.prisma"`
|
||||
|
||||
---
|
||||
|
||||
5 - run the below
|
||||
|
||||
generates the schema output
|
||||
|
||||
$ `bun aretmis:generate`
|
||||
|
||||
What it does:
|
||||
|
||||
`"artemis:generate": "prisma generate --schema=./prisma/schemas/artemis/schema.prisma"`
|
||||
|
||||
---
|
||||
|
||||
6 - start lachesis
|
||||
|
||||
$ `bun run dev`
|
||||
|
||||
What it does:
|
||||
|
||||
`"dev": "next dev",`
|
||||
|
||||
You can look at the package.json to figure out how to migrate lachesis if you have the desire to make changes.
|
||||
|
||||
If you download the files as is you should just need to update the env and do a migrate to your db for lachesis, check your database after to make sure all the schemas are there.
|
||||
|
||||
`"lachesis:migrate": "prisma migrate dev --schema=./prisma/schemas/lachesis/schema.prisma",`
|
@ -0,0 +1,30 @@
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Label } from "@radix-ui/react-dropdown-menu";
|
||||
import React from "react";
|
||||
|
||||
const ExtractData = () => {
|
||||
return (
|
||||
<Card x-chunk="aimecard">
|
||||
<CardHeader>
|
||||
<CardTitle className="text-2xl">Extract Jacket Art</CardTitle>
|
||||
</CardHeader>
|
||||
|
||||
<CardContent>
|
||||
<form className="grid gap-4">
|
||||
<div className="grid gap-2">
|
||||
<Label>Game Data Path: </Label>
|
||||
<Input
|
||||
name="accessCode"
|
||||
type="text"
|
||||
placeholder="*******************"
|
||||
/>
|
||||
</div>
|
||||
</form>
|
||||
</CardContent>
|
||||
</Card>
|
||||
);
|
||||
};
|
||||
|
||||
export default ExtractData;
|
@ -0,0 +1,10 @@
|
||||
import ExtractData from "./extraction";
|
||||
|
||||
const FileExtractionPage = async () => {
|
||||
return (
|
||||
<div className="flex min-h-screen w-full flex-col">
|
||||
<ExtractData />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
export default FileExtractionPage;
|
@ -0,0 +1,34 @@
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Label } from "@radix-ui/react-dropdown-menu";
|
||||
import React from "react";
|
||||
|
||||
const AdminHome = () => {
|
||||
return (
|
||||
<Card x-chunk="aimecard">
|
||||
<CardHeader>
|
||||
<CardTitle className="text-2xl">Link New Aime Card</CardTitle>
|
||||
</CardHeader>
|
||||
|
||||
<CardContent>
|
||||
<div className="text-lg mb-4">Current Access Code:</div>
|
||||
<form className="grid gap-4">
|
||||
<div className="grid gap-2">
|
||||
<Label>Access Code</Label>
|
||||
<Input
|
||||
name="accessCode"
|
||||
type="text"
|
||||
placeholder="*******************"
|
||||
/>
|
||||
</div>
|
||||
<Button type="submit" className="w-full">
|
||||
Link
|
||||
</Button>
|
||||
</form>
|
||||
</CardContent>
|
||||
</Card>
|
||||
);
|
||||
};
|
||||
|
||||
export default AdminHome;
|
@ -0,0 +1,14 @@
|
||||
import { getAuth } from "@/auth/queries/getauth";
|
||||
import AdminHome from "./home";
|
||||
|
||||
const ProtectedDashboardPage = async () => {
|
||||
const { user } = await getAuth();
|
||||
|
||||
return (
|
||||
<div>
|
||||
<AdminHome />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ProtectedDashboardPage;
|
@ -0,0 +1,10 @@
|
||||
import UnlockUser from "./unlock";
|
||||
|
||||
const FileExtractionPage = async () => {
|
||||
return (
|
||||
<div className="flex min-h-screen w-full flex-col">
|
||||
<UnlockUser />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
export default FileExtractionPage;
|
@ -0,0 +1,34 @@
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Label } from "@radix-ui/react-dropdown-menu";
|
||||
import React from "react";
|
||||
|
||||
const UnlockUser = () => {
|
||||
return (
|
||||
<Card x-chunk="aimecard">
|
||||
<CardHeader>
|
||||
<CardTitle className="text-2xl">Link New Aime Card</CardTitle>
|
||||
</CardHeader>
|
||||
|
||||
<CardContent>
|
||||
<div className="text-lg mb-4">Current Access Code:</div>
|
||||
<form className="grid gap-4">
|
||||
<div className="grid gap-2">
|
||||
<Label>Access Code</Label>
|
||||
<Input
|
||||
name="accessCode"
|
||||
type="text"
|
||||
placeholder="*******************"
|
||||
/>
|
||||
</div>
|
||||
<Button type="submit" className="w-full">
|
||||
Link
|
||||
</Button>
|
||||
</form>
|
||||
</CardContent>
|
||||
</Card>
|
||||
);
|
||||
};
|
||||
|
||||
export default UnlockUser;
|
35
app/(authenticated)/(admin)/layout.tsx
Normal file
35
app/(authenticated)/(admin)/layout.tsx
Normal file
@ -0,0 +1,35 @@
|
||||
import { getAuth } from "@/auth/queries/getauth";
|
||||
import { redirect } from "next/navigation";
|
||||
import AdminSubNavigation from "../../../components/navigationbar/adminnavigation";
|
||||
|
||||
export default async function AuthenticatedLayout({
|
||||
children,
|
||||
}: Readonly<{
|
||||
children: React.ReactNode;
|
||||
}>) {
|
||||
const { user } = await getAuth();
|
||||
|
||||
if (!user) {
|
||||
redirect("/");
|
||||
}
|
||||
|
||||
if (user.role === "ADMIN") {
|
||||
return (
|
||||
<>
|
||||
<main className="flex min-h-[calc(100vh-_theme(spacing.16))] flex-1 flex-col gap-4 bg-muted/40 p-4 md:gap-8 md:p-10">
|
||||
<div className="mx-auto grid w-full max-w-6xl gap-2">
|
||||
<h1 className="text-2xl font-semibold">Admin</h1>
|
||||
</div>
|
||||
<div className="mx-auto grid w-full max-w-6xl items-start gap-6 md:grid-cols-[180px_1fr] lg:grid-cols-[250px_1fr]">
|
||||
<AdminSubNavigation />
|
||||
|
||||
{children}
|
||||
</div>
|
||||
</main>
|
||||
</>
|
||||
);
|
||||
} else {
|
||||
console.log("not an admin");
|
||||
redirect("/home");
|
||||
}
|
||||
}
|
36
app/(authenticated)/(settings)/layout.tsx
Normal file
36
app/(authenticated)/(settings)/layout.tsx
Normal file
@ -0,0 +1,36 @@
|
||||
import { getAuth } from "@/auth/queries/getauth";
|
||||
import { redirect } from "next/navigation";
|
||||
import AdminSubNavigation from "../../../components/navigationbar/adminnavigation";
|
||||
import SettingsSubMenuNavigation from "../../../components/navigationbar/settingsnavigation";
|
||||
|
||||
export default async function AuthenticatedLayout({
|
||||
children,
|
||||
}: Readonly<{
|
||||
children: React.ReactNode;
|
||||
}>) {
|
||||
const { user } = await getAuth();
|
||||
|
||||
if (!user) {
|
||||
redirect("/");
|
||||
}
|
||||
|
||||
if (user.role === "ADMIN") {
|
||||
return (
|
||||
<>
|
||||
<main className="flex min-h-[calc(100vh-_theme(spacing.16))] flex-1 flex-col gap-4 bg-muted/40 p-4 md:gap-8 md:p-10">
|
||||
<div className="mx-auto grid w-full max-w-6xl gap-2">
|
||||
<h1 className="text-2xl font-semibold">Settings</h1>
|
||||
</div>
|
||||
<div className="mx-auto grid w-full max-w-6xl items-start gap-6 md:grid-cols-[180px_1fr] lg:grid-cols-[250px_1fr]">
|
||||
<SettingsSubMenuNavigation />
|
||||
|
||||
{children}
|
||||
</div>
|
||||
</main>
|
||||
</>
|
||||
);
|
||||
} else {
|
||||
console.log("not an admin");
|
||||
redirect("/home");
|
||||
}
|
||||
}
|
@ -0,0 +1,137 @@
|
||||
// Client-side React component
|
||||
"use client";
|
||||
import { useEffect, useState } from "react";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import {
|
||||
Card,
|
||||
CardContent,
|
||||
CardDescription,
|
||||
CardHeader,
|
||||
CardTitle,
|
||||
} from "@/components/ui/card";
|
||||
import { Label } from "@/components/ui/label";
|
||||
import { useToast } from "@/components/ui/use-toast";
|
||||
import { LinkAimeCard } from "@/lib/LinkNewAccessCode";
|
||||
import { getLachesisInUseCards } from "@/lib/GetUserAccessCode";
|
||||
import SettingsSubMenuNavigation from "@/components/navigationbar/settingsnavigation";
|
||||
|
||||
const GeneralSettings = () => {
|
||||
const { toast } = useToast();
|
||||
const [accessCode, setAccessCode] = useState("");
|
||||
const [isButtonDisabled, setIsButtonDisabled] = useState(true);
|
||||
const [currentAccessCode, setCurrentAccessCode] = useState<string | null>(
|
||||
null
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
// Fetch current access code when component mounts
|
||||
fetchCurrentAccessCode();
|
||||
}, []);
|
||||
|
||||
const fetchCurrentAccessCode = async () => {
|
||||
try {
|
||||
const aimeUser = await getLachesisInUseCards();
|
||||
if (aimeUser) {
|
||||
setCurrentAccessCode(aimeUser.accessCode || null);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error fetching access code:", error);
|
||||
}
|
||||
};
|
||||
|
||||
const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
const value = e.target.value;
|
||||
setAccessCode(value);
|
||||
setIsButtonDisabled(value.length !== 20);
|
||||
};
|
||||
|
||||
const handleSubmit = async (event: React.FormEvent<HTMLFormElement>) => {
|
||||
event.preventDefault();
|
||||
|
||||
const formData = new FormData(event.currentTarget);
|
||||
try {
|
||||
const result = await LinkAimeCard(formData);
|
||||
if (result.success) {
|
||||
toast({
|
||||
title: "Success",
|
||||
description: "Aime Card linked successfully",
|
||||
});
|
||||
setAccessCode("");
|
||||
fetchCurrentAccessCode();
|
||||
} else {
|
||||
toast({
|
||||
title: "Error",
|
||||
description: "Failed to link Aime Card",
|
||||
});
|
||||
}
|
||||
} catch (error: any) {
|
||||
if (error instanceof Error) {
|
||||
if (error.message === "Access Code is already used by another user") {
|
||||
toast({
|
||||
title: "Error",
|
||||
description: "Access Code is already used by another user",
|
||||
});
|
||||
} else if (
|
||||
error.message === "Not in artemis's database, Nice try ^_^"
|
||||
) {
|
||||
toast({
|
||||
title: "Error",
|
||||
description: "Access Code not found in database",
|
||||
});
|
||||
} else if (
|
||||
error.message === "You are currently holding this access code"
|
||||
) {
|
||||
toast({
|
||||
title: "Error",
|
||||
description: "You are currently holding this access code",
|
||||
});
|
||||
setAccessCode("");
|
||||
} else {
|
||||
toast({
|
||||
title: "Error",
|
||||
description: `Failed to link Aime Card: ${error.message}`,
|
||||
});
|
||||
}
|
||||
} else {
|
||||
console.error(error);
|
||||
toast({
|
||||
title: "Error",
|
||||
description: "An unexpected error occurred. Please try again later.",
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Card x-chunk="aimecard">
|
||||
<CardHeader>
|
||||
<CardTitle className="text-2xl">Link New Aime Card</CardTitle>
|
||||
</CardHeader>
|
||||
|
||||
<CardContent>
|
||||
<div className="text-lg mb-4">
|
||||
Current Access Code: {currentAccessCode}
|
||||
</div>
|
||||
<form onSubmit={handleSubmit} className="grid gap-4">
|
||||
<div className="grid gap-2">
|
||||
<Label htmlFor="accessCode">Access Code</Label>
|
||||
<Input
|
||||
name="accessCode"
|
||||
type="text"
|
||||
placeholder="*******************"
|
||||
value={accessCode}
|
||||
onChange={handleInputChange}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<Button type="submit" className="w-full" disabled={isButtonDisabled}>
|
||||
Link
|
||||
</Button>
|
||||
</form>
|
||||
</CardContent>
|
||||
</Card>
|
||||
);
|
||||
};
|
||||
|
||||
export { GeneralSettings };
|
@ -0,0 +1,14 @@
|
||||
import { getAuth } from "@/auth/queries/getauth";
|
||||
import { GeneralSettings } from "./home";
|
||||
|
||||
const ProtectedDashboardPage = async () => {
|
||||
const { user } = await getAuth();
|
||||
|
||||
return (
|
||||
<div>
|
||||
<GeneralSettings />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ProtectedDashboardPage;
|
@ -0,0 +1,62 @@
|
||||
"use server";
|
||||
|
||||
import { getAuth } from "@/auth/queries/getauth";
|
||||
import { artemis, lachesis } from "@/lib/prisma";
|
||||
import { Argon2id } from "oslo/password";
|
||||
|
||||
export const updatePassword = async (
|
||||
currentPassword: string,
|
||||
newPassword: string,
|
||||
confirmNewPassword: string
|
||||
) => {
|
||||
// Check if new passwords match
|
||||
if (newPassword !== confirmNewPassword) {
|
||||
return { error: "New passwords do not match" };
|
||||
}
|
||||
|
||||
const { user } = await getAuth();
|
||||
|
||||
if (!user) {
|
||||
return { error: "User not authenticated" };
|
||||
}
|
||||
|
||||
try {
|
||||
// Fetch user from database
|
||||
const existingUser = await lachesis.user.findUnique({
|
||||
where: {
|
||||
id: user.id,
|
||||
},
|
||||
});
|
||||
|
||||
if (!existingUser) {
|
||||
return { error: "User not found" };
|
||||
}
|
||||
|
||||
// Verify current password
|
||||
const isPasswordValid = await new Argon2id().verify(
|
||||
existingUser.hashedPassword,
|
||||
currentPassword
|
||||
);
|
||||
|
||||
if (!isPasswordValid) {
|
||||
return { error: "Current password is incorrect" };
|
||||
}
|
||||
|
||||
// Hash new password
|
||||
const hashedPassword = await new Argon2id().hash(newPassword);
|
||||
|
||||
// Update user's password
|
||||
await lachesis.user.update({
|
||||
where: {
|
||||
id: user.id,
|
||||
},
|
||||
data: {
|
||||
hashedPassword,
|
||||
},
|
||||
});
|
||||
|
||||
return { success: "Password updated successfully" };
|
||||
} catch (error: any) {
|
||||
return { error: "Failed to update password: " + error.message };
|
||||
}
|
||||
};
|
@ -0,0 +1,10 @@
|
||||
import SecuritySettings from "./security";
|
||||
|
||||
const SecuritySettingsPage = async () => {
|
||||
return (
|
||||
<div className="flex min-h-screen w-full flex-col">
|
||||
<SecuritySettings />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
export default SecuritySettingsPage;
|
@ -0,0 +1,104 @@
|
||||
"use client";
|
||||
import React, { useState, useEffect } from "react";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import {
|
||||
Card,
|
||||
CardContent,
|
||||
CardFooter,
|
||||
CardHeader,
|
||||
CardTitle,
|
||||
} from "@/components/ui/card";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { toast, useToast } from "@/components/ui/use-toast";
|
||||
import { updatePassword } from "./forgotpassword";
|
||||
|
||||
export default function SecuritySettings() {
|
||||
const [currentPassword, setCurrentPassword] = useState("");
|
||||
const [newPassword, setNewPassword] = useState("");
|
||||
const [confirmPassword, setConfirmPassword] = useState("");
|
||||
|
||||
const handleCurrentPasswordChange = (
|
||||
event: React.ChangeEvent<HTMLInputElement>
|
||||
) => {
|
||||
setCurrentPassword(event.target.value);
|
||||
};
|
||||
|
||||
const handleNewPasswordChange = (
|
||||
event: React.ChangeEvent<HTMLInputElement>
|
||||
) => {
|
||||
setNewPassword(event.target.value);
|
||||
};
|
||||
|
||||
const handleConfirmPasswordChange = (
|
||||
event: React.ChangeEvent<HTMLInputElement>
|
||||
) => {
|
||||
setConfirmPassword(event.target.value);
|
||||
};
|
||||
|
||||
const isButtonDisabled = !currentPassword || !newPassword || !confirmPassword;
|
||||
|
||||
const submit = async (data: FormData) => {
|
||||
const { error } = await updatePassword(
|
||||
currentPassword,
|
||||
newPassword,
|
||||
confirmPassword
|
||||
);
|
||||
if (error) {
|
||||
toast({
|
||||
title: "Error",
|
||||
description: error,
|
||||
});
|
||||
} else {
|
||||
toast({
|
||||
title: "Success",
|
||||
description: "Password updated successfully",
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="grid gap-6">
|
||||
<Card x-chunk="passwordreset">
|
||||
<form action={submit}>
|
||||
<CardHeader>
|
||||
<CardTitle>Reset Password</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent className="space-y-4">
|
||||
<Input
|
||||
type="password"
|
||||
name="currentPassword"
|
||||
placeholder="Current Password"
|
||||
value={currentPassword}
|
||||
onChange={handleCurrentPasswordChange}
|
||||
/>
|
||||
<Input
|
||||
type="password"
|
||||
name="newPassword"
|
||||
placeholder="New Password"
|
||||
value={newPassword}
|
||||
onChange={handleNewPasswordChange}
|
||||
/>
|
||||
<Input
|
||||
type="password"
|
||||
name="confirmPassword"
|
||||
placeholder="Confirm Password"
|
||||
value={confirmPassword}
|
||||
onChange={handleConfirmPasswordChange}
|
||||
/>
|
||||
</CardContent>
|
||||
<CardFooter className="border-t flex justify-end px-6 py-4">
|
||||
<Button
|
||||
type="submit"
|
||||
disabled={isButtonDisabled}
|
||||
style={{
|
||||
backgroundColor: isButtonDisabled ? "grey" : "",
|
||||
}}
|
||||
>
|
||||
Save
|
||||
</Button>
|
||||
</CardFooter>
|
||||
</form>
|
||||
</Card>
|
||||
</div>
|
||||
);
|
||||
}
|
14
app/(authenticated)/chunithm/page.tsx
Normal file
14
app/(authenticated)/chunithm/page.tsx
Normal file
@ -0,0 +1,14 @@
|
||||
import React from "react";
|
||||
import ChunithmData from "../../../components/scoreplaylog/page";
|
||||
|
||||
const ChunithmPage = async () => {
|
||||
return (
|
||||
<div>
|
||||
<div className=" ">
|
||||
<ChunithmData />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ChunithmPage;
|
7
app/(authenticated)/home/page.tsx
Normal file
7
app/(authenticated)/home/page.tsx
Normal file
@ -0,0 +1,7 @@
|
||||
import React from "react";
|
||||
|
||||
const Dashboard = () => {
|
||||
return <div>Dashboard</div>;
|
||||
};
|
||||
|
||||
export default Dashboard;
|
24
app/(authenticated)/layout.tsx
Normal file
24
app/(authenticated)/layout.tsx
Normal file
@ -0,0 +1,24 @@
|
||||
import { getAuth } from "@/auth/queries/getauth";
|
||||
import { redirect } from "next/navigation";
|
||||
import HeaderNavigation from "../../components/navigationbar/navigationbar";
|
||||
|
||||
export default async function AuthenticatedLayout({
|
||||
children,
|
||||
}: Readonly<{
|
||||
children: React.ReactNode;
|
||||
}>) {
|
||||
const { user } = await getAuth();
|
||||
|
||||
if (!user) {
|
||||
console.log("you need to be signed in");
|
||||
redirect("/");
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<HeaderNavigation />
|
||||
|
||||
{children}
|
||||
</>
|
||||
);
|
||||
}
|
62
app/(password-reset)/forgot-password/emailforgotpassword.ts
Normal file
62
app/(password-reset)/forgot-password/emailforgotpassword.ts
Normal file
@ -0,0 +1,62 @@
|
||||
"use server";
|
||||
|
||||
import { lachesis } from "@/lib/prisma";
|
||||
import { randomUUID } from "crypto";
|
||||
import { Resend } from "resend";
|
||||
import { redirect } from "next/navigation";
|
||||
|
||||
const resend = new Resend(process.env.NEXT_PUBLIC_RESEND_API_KEY);
|
||||
const DOMAIN = process.env.DOMAIN || "localhost:3000";
|
||||
const PROTOCOL = process.env.NODE_ENV === "production" ? "https" : "http";
|
||||
|
||||
const sendEmail = async (email: string, token: string, userName: string) => {
|
||||
try {
|
||||
const { data } = await resend.emails.send({
|
||||
from: "Password Reset <security@resend.dev>",
|
||||
to: [email],
|
||||
subject: "Reset Password Request",
|
||||
text: `Hello ${userName}, someone (hopefully you) requested a password reset for this account. If you did want to reset your password, please click here: ${PROTOCOL}://${DOMAIN}/password-reset/${token}
|
||||
|
||||
For security reasons, this link is only valid for four hours.
|
||||
|
||||
If you did not request this reset, please ignore this email.`,
|
||||
});
|
||||
|
||||
console.log("Email sent successfully:", data);
|
||||
// Handle success
|
||||
} catch (error) {
|
||||
console.error("Error sending email:", error);
|
||||
// Handle error
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
export async function EmailPasswordResetLink(data: FormData) {
|
||||
const email = data.get("email");
|
||||
if (!email || typeof email !== "string") {
|
||||
return {
|
||||
error: "Invalid email",
|
||||
};
|
||||
}
|
||||
|
||||
const user = await lachesis.user.findUnique({
|
||||
where: { email },
|
||||
});
|
||||
|
||||
if (!user) {
|
||||
return {
|
||||
error: "This email is not registered",
|
||||
};
|
||||
}
|
||||
|
||||
const token = await lachesis.passwordResetToken.create({
|
||||
data: {
|
||||
id: randomUUID(),
|
||||
userId: user.id,
|
||||
token: `${randomUUID()}${randomUUID()}`.replace(/-/g, ""),
|
||||
},
|
||||
});
|
||||
|
||||
await sendEmail(user.email, token.token, user.username);
|
||||
redirect("/forgot-password/success");
|
||||
}
|
43
app/(password-reset)/forgot-password/page.tsx
Normal file
43
app/(password-reset)/forgot-password/page.tsx
Normal file
@ -0,0 +1,43 @@
|
||||
"use client";
|
||||
|
||||
import Link from "next/link";
|
||||
import { useState } from "react";
|
||||
import { ArrowLeftSquare } from "lucide-react";
|
||||
import { Card } from "@/components/ui/card";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { EmailPasswordResetLink } from "./emailforgotpassword";
|
||||
|
||||
export default function ForgotPassword() {
|
||||
const [error, setError] = useState<string>("");
|
||||
|
||||
const submit = async (data: FormData) => {
|
||||
const { error } = await EmailPasswordResetLink(data);
|
||||
setError(error);
|
||||
};
|
||||
|
||||
return (
|
||||
<main className="max-w-xl px-4 mx-auto flex flex-col justify-center h-screen ">
|
||||
<Card className="gap-4 flex flex-col p-6 ">
|
||||
<form action={submit} className="flex flex-col gap-4">
|
||||
{" "}
|
||||
<h1 className="text-2xl font-light">Reset password</h1>
|
||||
<p>
|
||||
Enter your email address to get instructions for resetting your
|
||||
password.
|
||||
</p>
|
||||
<Input name="email" type="email" placeholder="Your email..." />
|
||||
{error && <p className="text-red-500">{error}</p>}
|
||||
<Button type="submit">Reset Password</Button>
|
||||
<Link
|
||||
href="/"
|
||||
className="text-sm text-neutral-700/80 flex items-center"
|
||||
>
|
||||
<ArrowLeftSquare />
|
||||
<span>Return to Login</span>
|
||||
</Link>
|
||||
</form>
|
||||
</Card>
|
||||
</main>
|
||||
);
|
||||
}
|
19
app/(password-reset)/forgot-password/success/page.tsx
Normal file
19
app/(password-reset)/forgot-password/success/page.tsx
Normal file
@ -0,0 +1,19 @@
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Card } from "@/components/ui/card";
|
||||
import Link from "next/link";
|
||||
|
||||
export default async function SuccessPage() {
|
||||
return (
|
||||
<main className="max-w-xl px-4 mx-auto flex flex-col justify-center h-screen ">
|
||||
<Card className="gap-4 flex flex-col p-6">
|
||||
<h1 className="text-2xl font-light mb-4">Password reset</h1>
|
||||
<p className="mb-4">
|
||||
If the email doesn't show up, check your spam folder.
|
||||
</p>
|
||||
<Button type="submit" asChild className="mt-4">
|
||||
<Link href="/">Return to Login</Link>
|
||||
</Button>
|
||||
</Card>
|
||||
</main>
|
||||
);
|
||||
}
|
45
app/(password-reset)/password-reset/[token]/page.tsx
Normal file
45
app/(password-reset)/password-reset/[token]/page.tsx
Normal file
@ -0,0 +1,45 @@
|
||||
"use client";
|
||||
|
||||
import Link from "next/link";
|
||||
import { useState } from "react";
|
||||
import { ArrowLeft } from "lucide-react";
|
||||
import { Card } from "@/components/ui/card";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { resetPassword } from "./token";
|
||||
|
||||
export default function ({ params }: { params: { token: string } }) {
|
||||
const [error, setError] = useState<string>("");
|
||||
|
||||
async function submit(data: FormData) {
|
||||
const { error } = await resetPassword(params.token, data);
|
||||
setError(error || "");
|
||||
}
|
||||
|
||||
return (
|
||||
<main className="max-w-xl px-4 mx-auto flex flex-col justify-center h-screen ">
|
||||
<Card className="gap-4 flex flex-col p-6 ">
|
||||
<form action={submit} className="flex flex-col gap-4">
|
||||
{" "}
|
||||
<h1 className="text-2xl font-light">Choose a new password</h1>
|
||||
<p>You can reset your password here.</p>
|
||||
<Input name="password" type="password" placeholder="Password" />
|
||||
<Input
|
||||
name="confirm"
|
||||
type="password"
|
||||
placeholder="Confirm password"
|
||||
/>
|
||||
{error && <p className="text-red-500 text-sm">{error}</p>}
|
||||
<Button type="submit">Reset Password</Button>
|
||||
<Link
|
||||
href="/"
|
||||
className="text-sm text-neutral-700/80 flex items-center"
|
||||
>
|
||||
<ArrowLeft />
|
||||
<span>Return to Login</span>
|
||||
</Link>
|
||||
</form>
|
||||
</Card>
|
||||
</main>
|
||||
);
|
||||
}
|
67
app/(password-reset)/password-reset/[token]/token.ts
Normal file
67
app/(password-reset)/password-reset/[token]/token.ts
Normal file
@ -0,0 +1,67 @@
|
||||
"use server";
|
||||
|
||||
import { lachesis } from "@/lib/prisma";
|
||||
import { redirect } from "next/navigation";
|
||||
import { Argon2id } from "oslo/password";
|
||||
|
||||
export async function resetPassword(token: string, data: FormData) {
|
||||
const password = data.get("password");
|
||||
const confirmPassword = data.get("confirm");
|
||||
|
||||
if (
|
||||
!password ||
|
||||
typeof password !== "string" ||
|
||||
password !== confirmPassword
|
||||
) {
|
||||
return {
|
||||
error:
|
||||
"The passwords did not match. Please try retyping them and submitting again.",
|
||||
};
|
||||
}
|
||||
|
||||
const passwordResetToken = await lachesis.passwordResetToken.findUnique({
|
||||
where: {
|
||||
token,
|
||||
createdAt: { gt: new Date(Date.now() - 1000 * 60 * 60 * 4) },
|
||||
resetAt: null,
|
||||
},
|
||||
});
|
||||
|
||||
if (!passwordResetToken) {
|
||||
return {
|
||||
error:
|
||||
"Invalid token reset request. Please try resetting your password again.",
|
||||
};
|
||||
}
|
||||
|
||||
const argon2 = new Argon2id();
|
||||
const encrypted = await argon2.hash(password);
|
||||
|
||||
const updateUser = lachesis.user.update({
|
||||
where: { id: passwordResetToken.userId },
|
||||
data: {
|
||||
hashedPassword: encrypted,
|
||||
},
|
||||
});
|
||||
|
||||
const updateToken = lachesis.passwordResetToken.update({
|
||||
where: {
|
||||
id: passwordResetToken.id,
|
||||
},
|
||||
data: {
|
||||
resetAt: new Date(),
|
||||
},
|
||||
});
|
||||
|
||||
try {
|
||||
await lachesis.$transaction([updateUser, updateToken]);
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
return {
|
||||
error:
|
||||
"An unexpected error occurred. Please try again and if the problem persists, contact support.",
|
||||
};
|
||||
}
|
||||
|
||||
redirect("/password-reset/success");
|
||||
}
|
32
app/(password-reset)/password-reset/success/page.tsx
Normal file
32
app/(password-reset)/password-reset/success/page.tsx
Normal file
@ -0,0 +1,32 @@
|
||||
"use client";
|
||||
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Card } from "@/components/ui/card";
|
||||
import Link from "next/link";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { useEffect } from "react";
|
||||
|
||||
export default async function SuccessPage() {
|
||||
const router = useRouter();
|
||||
|
||||
useEffect(() => {
|
||||
setTimeout(() => {
|
||||
router.push("/"); // Redirect to login page
|
||||
}, 5000);
|
||||
});
|
||||
|
||||
return (
|
||||
<main className="max-w-xl px-4 mx-auto flex flex-col justify-center h-screen">
|
||||
<Card className="gap-4 flex flex-col p-6">
|
||||
<h1 className="text-2xl font-light">Your password has been updated.</h1>
|
||||
<p>
|
||||
You will now be redirected to the login page where you can login
|
||||
again.
|
||||
</p>
|
||||
<Button type="submit" asChild>
|
||||
<Link href="/">Return to Login</Link>
|
||||
</Button>
|
||||
</Card>
|
||||
</main>
|
||||
);
|
||||
}
|
38
app/(sharing)/[token]/[id]/page.tsx
Normal file
38
app/(sharing)/[token]/[id]/page.tsx
Normal file
@ -0,0 +1,38 @@
|
||||
import { SharingPlaylogId, getSongsWithTitles } from "@/lib/api";
|
||||
import { shareScore } from "../token";
|
||||
|
||||
export default async function Share({
|
||||
params,
|
||||
}: {
|
||||
params: { token: string; id: string };
|
||||
}) {
|
||||
const { token, id } = params;
|
||||
|
||||
// Verify the token
|
||||
const tokenResult = await shareScore(token);
|
||||
|
||||
if (tokenResult.error) {
|
||||
// Handle error appropriately, e.g., render an error message
|
||||
return <p>{tokenResult.error}</p>;
|
||||
}
|
||||
|
||||
const userId = 10000;
|
||||
const playlogId = parseInt(id);
|
||||
|
||||
const songsData = await getSongsWithTitles(userId);
|
||||
const playlogIds = await SharingPlaylogId(playlogId);
|
||||
|
||||
const matchingSongs = songsData.filter((song) =>
|
||||
playlogIds.includes(song.id)
|
||||
);
|
||||
|
||||
const songTitles = matchingSongs.map((song) => song.title);
|
||||
|
||||
return (
|
||||
<ul>
|
||||
{songTitles.map((title) => (
|
||||
<li key={title}>{title}</li>
|
||||
))}
|
||||
</ul>
|
||||
);
|
||||
}
|
67
app/(sharing)/[token]/token.ts
Normal file
67
app/(sharing)/[token]/token.ts
Normal file
@ -0,0 +1,67 @@
|
||||
"use server";
|
||||
|
||||
import { getAuth } from "@/auth/queries/getauth";
|
||||
import { lachesis } from "@/lib/prisma";
|
||||
import { randomUUID } from "crypto";
|
||||
import { randomBytes } from "crypto";
|
||||
import { redirect } from "next/navigation";
|
||||
|
||||
export async function generateShareToken(id: number): Promise<{
|
||||
token?: string;
|
||||
id?: string;
|
||||
error?: string;
|
||||
}> {
|
||||
const { user } = await getAuth();
|
||||
|
||||
if (!user || !user.id || typeof user.id !== "string") {
|
||||
return {
|
||||
error: "Invalid user or user ID",
|
||||
};
|
||||
}
|
||||
|
||||
const gernatetoken = randomBytes(5).readUInt32BE(0).toString();
|
||||
const token = await lachesis.linkSharingToken.create({
|
||||
data: {
|
||||
playlogId: id,
|
||||
id: randomUUID(),
|
||||
userId: user.id,
|
||||
token: gernatetoken,
|
||||
createdAt: new Date(),
|
||||
},
|
||||
});
|
||||
|
||||
return { token: token.token };
|
||||
}
|
||||
|
||||
export async function shareScore(token: string) {
|
||||
const PublicPage = await lachesis.linkSharingToken.findUnique({
|
||||
where: {
|
||||
token,
|
||||
},
|
||||
});
|
||||
|
||||
if (!PublicPage) {
|
||||
return {
|
||||
error: "Invalid token or token does not exist",
|
||||
};
|
||||
}
|
||||
|
||||
// Check if token has expired
|
||||
const tokenAge =
|
||||
new Date().getTime() - new Date(PublicPage.createdAt).getTime();
|
||||
const tokenAgeLimit = 1000 * 60 * 60 * 24; // 1 day in milliseconds
|
||||
|
||||
if (tokenAge > tokenAgeLimit) {
|
||||
await lachesis.linkSharingToken.update({
|
||||
where: {
|
||||
token,
|
||||
},
|
||||
data: {
|
||||
tokenExpiredAt: new Date(),
|
||||
},
|
||||
});
|
||||
redirect("/");
|
||||
}
|
||||
|
||||
return { success: true };
|
||||
}
|
BIN
app/favicon.ico
Normal file
BIN
app/favicon.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 25 KiB |
76
app/globals.css
Normal file
76
app/globals.css
Normal file
@ -0,0 +1,76 @@
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
@layer base {
|
||||
:root {
|
||||
--background: 0 0% 100%;
|
||||
--foreground: 222.2 84% 4.9%;
|
||||
|
||||
--card: 0 0% 100%;
|
||||
--card-foreground: 222.2 84% 4.9%;
|
||||
|
||||
--popover: 0 0% 100%;
|
||||
--popover-foreground: 222.2 84% 4.9%;
|
||||
|
||||
--primary: 222.2 47.4% 11.2%;
|
||||
--primary-foreground: 210 40% 98%;
|
||||
|
||||
--secondary: 210 40% 96.1%;
|
||||
--secondary-foreground: 222.2 47.4% 11.2%;
|
||||
|
||||
--muted: 210 40% 96.1%;
|
||||
--muted-foreground: 215.4 16.3% 46.9%;
|
||||
|
||||
--accent: 210 40% 96.1%;
|
||||
--accent-foreground: 222.2 47.4% 11.2%;
|
||||
|
||||
--destructive: 0 84.2% 60.2%;
|
||||
--destructive-foreground: 210 40% 98%;
|
||||
|
||||
--border: 214.3 31.8% 91.4%;
|
||||
--input: 214.3 31.8% 91.4%;
|
||||
--ring: 222.2 84% 4.9%;
|
||||
|
||||
--radius: 0.5rem;
|
||||
}
|
||||
|
||||
.dark {
|
||||
--background: 222.2 84% 4.9%;
|
||||
--foreground: 210 40% 98%;
|
||||
|
||||
--card: 222.2 84% 4.9%;
|
||||
--card-foreground: 210 40% 98%;
|
||||
|
||||
--popover: 222.2 84% 4.9%;
|
||||
--popover-foreground: 210 40% 98%;
|
||||
|
||||
--primary: 210 40% 98%;
|
||||
--primary-foreground: 222.2 47.4% 11.2%;
|
||||
|
||||
--secondary: 217.2 32.6% 17.5%;
|
||||
--secondary-foreground: 210 40% 98%;
|
||||
|
||||
--muted: 217.2 32.6% 17.5%;
|
||||
--muted-foreground: 215 20.2% 65.1%;
|
||||
|
||||
--accent: 217.2 32.6% 17.5%;
|
||||
--accent-foreground: 210 40% 98%;
|
||||
|
||||
--destructive: 0 62.8% 30.6%;
|
||||
--destructive-foreground: 210 40% 98%;
|
||||
|
||||
--border: 217.2 32.6% 17.5%;
|
||||
--input: 217.2 32.6% 17.5%;
|
||||
--ring: 212.7 26.8% 83.9%;
|
||||
}
|
||||
}
|
||||
|
||||
@layer base {
|
||||
* {
|
||||
@apply border-border;
|
||||
}
|
||||
body {
|
||||
@apply bg-background text-foreground;
|
||||
}
|
||||
}
|
27
app/layout.tsx
Normal file
27
app/layout.tsx
Normal file
@ -0,0 +1,27 @@
|
||||
import type { Metadata } from "next";
|
||||
import { GeistSans } from "geist/font/sans";
|
||||
import { Toaster } from "@/components/ui/toaster";
|
||||
import "./globals.css";
|
||||
import ReactQueryProvider from "./provider";
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "Lachesis",
|
||||
description: "Artemis score viewer",
|
||||
};
|
||||
|
||||
export default function RootLayout({
|
||||
children,
|
||||
}: Readonly<{
|
||||
children: React.ReactNode;
|
||||
}>) {
|
||||
return (
|
||||
<html lang="en" className={GeistSans.className}>
|
||||
<body className={GeistSans.className}>
|
||||
<ReactQueryProvider>
|
||||
<main className="">{children} </main>
|
||||
</ReactQueryProvider>
|
||||
<Toaster />
|
||||
</body>
|
||||
</html>
|
||||
);
|
||||
}
|
11
app/page.tsx
Normal file
11
app/page.tsx
Normal file
@ -0,0 +1,11 @@
|
||||
import { SignInForm } from "@/auth/components/signin/signin";
|
||||
|
||||
const PublicHomePage = () => {
|
||||
return (
|
||||
<div className="h-screen flex items-center justify-center">
|
||||
<SignInForm />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default PublicHomePage;
|
14
app/provider.tsx
Normal file
14
app/provider.tsx
Normal file
@ -0,0 +1,14 @@
|
||||
"use client";
|
||||
|
||||
import { QueryClientProvider, QueryClient } from "@tanstack/react-query";
|
||||
import { useState } from "react";
|
||||
|
||||
const ReactQueryProvider = ({ children }: { children: React.ReactNode }) => {
|
||||
const [queryClient] = useState(() => new QueryClient());
|
||||
|
||||
return (
|
||||
<QueryClientProvider client={queryClient}>{children}</QueryClientProvider>
|
||||
);
|
||||
};
|
||||
|
||||
export default ReactQueryProvider;
|
11
app/signup/page.tsx
Normal file
11
app/signup/page.tsx
Normal file
@ -0,0 +1,11 @@
|
||||
import { SignUpForm } from "@/auth/components/signup/signup";
|
||||
|
||||
const SignUpPage = () => {
|
||||
return (
|
||||
<div className="h-screen flex items-center justify-center">
|
||||
<SignUpForm />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default SignUpPage;
|
47
auth/components/signin/action.ts
Normal file
47
auth/components/signin/action.ts
Normal file
@ -0,0 +1,47 @@
|
||||
"use server";
|
||||
|
||||
import { cookies } from "next/headers";
|
||||
import { redirect } from "next/navigation";
|
||||
import { Argon2id } from "oslo/password";
|
||||
import { lucia } from "@/lib/lucia";
|
||||
import { lachesis } from "@/lib/prisma";
|
||||
|
||||
const signIn = async (formData: FormData) => {
|
||||
const formDataRaw = {
|
||||
username: formData.get("username") as string,
|
||||
password: formData.get("password") as string,
|
||||
};
|
||||
|
||||
try {
|
||||
const user = await lachesis.user.findUnique({
|
||||
where: { username: formDataRaw.username },
|
||||
});
|
||||
|
||||
if (!user) {
|
||||
return { error: "Incorrect username" };
|
||||
}
|
||||
|
||||
const validPassword = await new Argon2id().verify(
|
||||
user.hashedPassword,
|
||||
formDataRaw.password
|
||||
);
|
||||
|
||||
if (!validPassword) {
|
||||
return { error: "Incorrect password" };
|
||||
}
|
||||
|
||||
const session = await lucia.createSession(user.id, {});
|
||||
const sessionCookie = lucia.createSessionCookie(session.id);
|
||||
|
||||
cookies().set(
|
||||
sessionCookie.name,
|
||||
sessionCookie.value,
|
||||
sessionCookie.attributes
|
||||
);
|
||||
} catch (error: any) {
|
||||
return { error: "Sign-in failed: " + error.message };
|
||||
}
|
||||
redirect("/home");
|
||||
};
|
||||
|
||||
export { signIn };
|
79
auth/components/signin/signin.tsx
Normal file
79
auth/components/signin/signin.tsx
Normal file
@ -0,0 +1,79 @@
|
||||
"use client";
|
||||
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import Link from "next/link";
|
||||
import {
|
||||
Card,
|
||||
CardContent,
|
||||
CardDescription,
|
||||
CardHeader,
|
||||
CardTitle,
|
||||
} from "@/components/ui/card";
|
||||
import { Label } from "@/components/ui/label";
|
||||
|
||||
import { signIn } from "./action";
|
||||
import { toast } from "@/components/ui/use-toast";
|
||||
|
||||
export default function SignInForm() {
|
||||
const submit = async (data: FormData) => {
|
||||
const { error } = await signIn(data);
|
||||
if (error) {
|
||||
toast({
|
||||
title: "Error",
|
||||
description: error,
|
||||
});
|
||||
} else {
|
||||
toast({
|
||||
title: "Success",
|
||||
description: "Account created successfully",
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Card className="mx-auto max-w-sm">
|
||||
<CardHeader>
|
||||
<CardTitle className="text-2xl">Sign in</CardTitle>
|
||||
<CardDescription>
|
||||
Enter your info below to login your account
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<form action={submit} className="grid gap-4">
|
||||
<div className="grid gap-2">
|
||||
<Label htmlFor="username">Username</Label>
|
||||
<Input name="username" type="text" placeholder="clotho" required />
|
||||
</div>
|
||||
|
||||
<div className="grid gap-2">
|
||||
<Label htmlFor="password">Password</Label>
|
||||
<Input
|
||||
name="password"
|
||||
type="password"
|
||||
placeholder="********"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<Link
|
||||
href="/forgot-password"
|
||||
className="ml-auto inline-block text-sm underline"
|
||||
>
|
||||
Forgot your password?
|
||||
</Link>
|
||||
|
||||
<Button type="submit" className="w-full">
|
||||
Sign in
|
||||
</Button>
|
||||
</form>
|
||||
<div className="mt-4 text-center text-sm">
|
||||
Need an account?{" "}
|
||||
<Link href="/signup" className="underline">
|
||||
Sign up
|
||||
</Link>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
export { SignInForm };
|
26
auth/components/signout.ts
Normal file
26
auth/components/signout.ts
Normal file
@ -0,0 +1,26 @@
|
||||
'use server';
|
||||
|
||||
import { cookies } from 'next/headers';
|
||||
import { redirect } from 'next/navigation';
|
||||
import { lucia } from '@/lib/lucia';
|
||||
import { getAuth } from '../queries/getauth';
|
||||
|
||||
export const signOut = async (_formData: FormData) => {
|
||||
const { session } = await getAuth();
|
||||
|
||||
if (!session) {
|
||||
redirect('/');
|
||||
}
|
||||
|
||||
await lucia.invalidateSession(session.id);
|
||||
|
||||
const sessionCookie = lucia.createBlankSessionCookie();
|
||||
|
||||
cookies().set(
|
||||
sessionCookie.name,
|
||||
sessionCookie.value,
|
||||
sessionCookie.attributes
|
||||
);
|
||||
|
||||
redirect('/');
|
||||
};
|
99
auth/components/signup/action.ts
Normal file
99
auth/components/signup/action.ts
Normal file
@ -0,0 +1,99 @@
|
||||
"use server";
|
||||
|
||||
import { generateId } from "lucia";
|
||||
import { cookies } from "next/headers";
|
||||
import { redirect } from "next/navigation";
|
||||
import { Argon2id } from "oslo/password";
|
||||
import { lucia } from "@/lib/lucia";
|
||||
import { lachesis, artemis } from "@/lib/prisma";
|
||||
|
||||
const signUp = async (formData: FormData) => {
|
||||
const formDataRaw = {
|
||||
username: formData.get("username") as string,
|
||||
email: formData.get("email") as string,
|
||||
accessCode: formData.get("accessCode") as string,
|
||||
password: formData.get("password") as string,
|
||||
confirmPassword: formData.get("confirmPassword") as string,
|
||||
};
|
||||
|
||||
if (formDataRaw.password !== formDataRaw.confirmPassword) {
|
||||
return { error: "Passwords do not match" };
|
||||
}
|
||||
|
||||
try {
|
||||
// Check if access code is already used in lachesis database
|
||||
const existingUser = await lachesis.user.findFirst({
|
||||
where: {
|
||||
accessCode: formDataRaw.accessCode,
|
||||
},
|
||||
});
|
||||
|
||||
if (existingUser) {
|
||||
return { error: "Access Code already in use" };
|
||||
}
|
||||
|
||||
// Check if username is already used in lachesis database
|
||||
const existingUsername = await lachesis.user.findFirst({
|
||||
where: {
|
||||
username: formDataRaw.username,
|
||||
},
|
||||
});
|
||||
|
||||
if (existingUsername) {
|
||||
return { error: "Username is currently taken" };
|
||||
}
|
||||
|
||||
const existingEmail = await lachesis.user.findFirst({
|
||||
where: {
|
||||
email: formDataRaw.email,
|
||||
},
|
||||
});
|
||||
|
||||
if (existingEmail) {
|
||||
return { error: "Email is already in use" };
|
||||
}
|
||||
|
||||
// Check if access code exists in artemis database
|
||||
const existingAccessCode = await artemis.aime_card.findFirst({
|
||||
where: {
|
||||
access_code: formDataRaw.accessCode,
|
||||
},
|
||||
});
|
||||
|
||||
if (!existingAccessCode) {
|
||||
return { error: "Not in artemis's database, Nice try ^_^" };
|
||||
}
|
||||
|
||||
const hashedPassword = await new Argon2id().hash(formDataRaw.password);
|
||||
const userId = generateId(15);
|
||||
|
||||
// Create user in the lachesis database
|
||||
await lachesis.user.create({
|
||||
data: {
|
||||
id: userId,
|
||||
username: formDataRaw.username,
|
||||
email: formDataRaw.email,
|
||||
accessCode: formDataRaw.accessCode,
|
||||
hashedPassword,
|
||||
},
|
||||
});
|
||||
|
||||
// Create session and set cookie
|
||||
const session = await lucia.createSession(userId, {});
|
||||
const sessionCookie = lucia.createSessionCookie(session.id);
|
||||
|
||||
cookies().set(
|
||||
sessionCookie.name,
|
||||
sessionCookie.value,
|
||||
sessionCookie.attributes
|
||||
);
|
||||
console.log("Account created");
|
||||
|
||||
// Redirect to home page
|
||||
redirect("/home");
|
||||
} catch (error: any) {
|
||||
return { error: "Account creation failed: " + error.message };
|
||||
}
|
||||
};
|
||||
|
||||
export { signUp };
|
107
auth/components/signup/signup.tsx
Normal file
107
auth/components/signup/signup.tsx
Normal file
@ -0,0 +1,107 @@
|
||||
"use client";
|
||||
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import Link from "next/link";
|
||||
import {
|
||||
Card,
|
||||
CardContent,
|
||||
CardDescription,
|
||||
CardHeader,
|
||||
CardTitle,
|
||||
} from "@/components/ui/card";
|
||||
import { Label } from "@/components/ui/label";
|
||||
import { toast, useToast } from "@/components/ui/use-toast";
|
||||
|
||||
import { signUp } from "./action";
|
||||
import { useState } from "react";
|
||||
|
||||
export default function SignUpForm() {
|
||||
const [error, setError] = useState<string>("");
|
||||
|
||||
const submit = async (data: FormData) => {
|
||||
const { error } = await signUp(data);
|
||||
if (error) {
|
||||
toast({
|
||||
title: "Error",
|
||||
description: error,
|
||||
});
|
||||
} else {
|
||||
toast({
|
||||
title: "Success",
|
||||
description: "Account created successfully",
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Card className="mx-auto max-w-sm">
|
||||
<CardHeader>
|
||||
<CardTitle className="text-2xl">Sign up</CardTitle>
|
||||
<CardDescription>
|
||||
Enter your info below to create your account
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<form action={submit} className="grid gap-4">
|
||||
<div className="grid gap-2">
|
||||
<Label htmlFor="username">Username</Label>
|
||||
<Input name="username" type="text" placeholder="clotho" required />
|
||||
</div>
|
||||
|
||||
<div className="grid gap-2">
|
||||
<Label htmlFor="email">Email</Label>
|
||||
<Input
|
||||
name="email"
|
||||
type="email"
|
||||
placeholder="clotho@fates.com"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="grid gap-2">
|
||||
<Label htmlFor="accessCode">Access Code</Label>
|
||||
<Input
|
||||
name="accessCode"
|
||||
type="text"
|
||||
placeholder="*******************"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="grid gap-2">
|
||||
<Label htmlFor="password">Password</Label>
|
||||
<Input
|
||||
name="password"
|
||||
type="password"
|
||||
placeholder="********"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="grid gap-2">
|
||||
<Label htmlFor="confirmPassword">Confirm Password</Label>
|
||||
<Input
|
||||
name="confirmPassword"
|
||||
type="password"
|
||||
placeholder="********"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
|
||||
<Button type="submit" className="w-full">
|
||||
Sign up
|
||||
</Button>
|
||||
</form>
|
||||
<div className="mt-4 text-center text-sm">
|
||||
Already have an account?{" "}
|
||||
<Link href="/" className="underline">
|
||||
Sign in
|
||||
</Link>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
||||
export { SignUpForm };
|
41
auth/queries/getauth.ts
Normal file
41
auth/queries/getauth.ts
Normal file
@ -0,0 +1,41 @@
|
||||
import { cookies } from "next/headers";
|
||||
import { cache } from "react";
|
||||
|
||||
import type { Session, User } from "lucia";
|
||||
import { lucia } from "@/lib/lucia";
|
||||
|
||||
export const getAuth = cache(
|
||||
async (): Promise<
|
||||
{ user: User; session: Session } | { user: null; session: null }
|
||||
> => {
|
||||
const sessionId = cookies().get(lucia.sessionCookieName)?.value ?? null;
|
||||
if (!sessionId) {
|
||||
return {
|
||||
user: null,
|
||||
session: null,
|
||||
};
|
||||
}
|
||||
|
||||
const result = await lucia.validateSession(sessionId);
|
||||
|
||||
try {
|
||||
if (result.session && result.session.fresh) {
|
||||
const sessionCookie = lucia.createSessionCookie(result.session.id);
|
||||
cookies().set(
|
||||
sessionCookie.name,
|
||||
sessionCookie.value,
|
||||
sessionCookie.attributes
|
||||
);
|
||||
}
|
||||
if (!result.session) {
|
||||
const sessionCookie = lucia.createBlankSessionCookie();
|
||||
cookies().set(
|
||||
sessionCookie.name,
|
||||
sessionCookie.value,
|
||||
sessionCookie.attributes
|
||||
);
|
||||
}
|
||||
} catch {}
|
||||
return result;
|
||||
}
|
||||
);
|
17
components.json
Normal file
17
components.json
Normal file
@ -0,0 +1,17 @@
|
||||
{
|
||||
"$schema": "https://ui.shadcn.com/schema.json",
|
||||
"style": "default",
|
||||
"rsc": true,
|
||||
"tsx": true,
|
||||
"tailwind": {
|
||||
"config": "tailwind.config.ts",
|
||||
"css": "app/globals.css",
|
||||
"baseColor": "slate",
|
||||
"cssVariables": true,
|
||||
"prefix": ""
|
||||
},
|
||||
"aliases": {
|
||||
"components": "@/components",
|
||||
"utils": "@/lib/utils"
|
||||
}
|
||||
}
|
36
components/navigationbar/adminnavigation.tsx
Normal file
36
components/navigationbar/adminnavigation.tsx
Normal file
@ -0,0 +1,36 @@
|
||||
"use client";
|
||||
import Link from "next/link";
|
||||
|
||||
import { usePathname } from "next/navigation";
|
||||
|
||||
const NAV_ITEMS = [
|
||||
{ href: "/admin/home", label: "Home" },
|
||||
{ href: "/admin/unlock", label: "Unlock User" },
|
||||
{ href: "/admin/extraction", label: "Extract Game Files" },
|
||||
];
|
||||
|
||||
const AdminSubNavigation = () => {
|
||||
const pathname = usePathname();
|
||||
|
||||
return (
|
||||
<nav className="grid gap-4 text-sm text-muted-foreground">
|
||||
{NAV_ITEMS.map(({ href, label }) => {
|
||||
const isActive = pathname === href;
|
||||
|
||||
return (
|
||||
<Link
|
||||
key={href}
|
||||
href={href}
|
||||
className={`${
|
||||
isActive ? "font-semibold text-primary" : "text-muted-foreground"
|
||||
} text-sm`}
|
||||
>
|
||||
{label}
|
||||
</Link>
|
||||
);
|
||||
})}
|
||||
</nav>
|
||||
);
|
||||
};
|
||||
|
||||
export default AdminSubNavigation;
|
36
components/navigationbar/desktopNavBar.tsx
Normal file
36
components/navigationbar/desktopNavBar.tsx
Normal file
@ -0,0 +1,36 @@
|
||||
"use client";
|
||||
import Link from "next/link";
|
||||
|
||||
import { usePathname } from "next/navigation";
|
||||
|
||||
const NAV_ITEMS = [
|
||||
{ href: "/home", label: "Home" },
|
||||
{ href: "/chunithm", label: "Chunithm" },
|
||||
{ href: "/maimai", label: "Maimai" },
|
||||
];
|
||||
|
||||
const NavigationMenuDesktop = () => {
|
||||
const pathname = usePathname();
|
||||
|
||||
return (
|
||||
<nav className="hidden flex-col gap-6 text-lg font-medium md:flex md:flex-row md:items-center md:gap-5 md:text-sm lg:gap-6">
|
||||
{NAV_ITEMS.map(({ href, label }) => {
|
||||
const isActive = pathname === href;
|
||||
|
||||
return (
|
||||
<Link
|
||||
key={href}
|
||||
href={href}
|
||||
className={`${
|
||||
isActive ? "font-semibold text-primary" : "text-muted-foreground"
|
||||
} text-sm`}
|
||||
>
|
||||
{label}
|
||||
</Link>
|
||||
);
|
||||
})}
|
||||
</nav>
|
||||
);
|
||||
};
|
||||
|
||||
export default NavigationMenuDesktop;
|
36
components/navigationbar/mobileNavBar.tsx
Normal file
36
components/navigationbar/mobileNavBar.tsx
Normal file
@ -0,0 +1,36 @@
|
||||
"use client";
|
||||
import Link from "next/link";
|
||||
|
||||
import { usePathname } from "next/navigation";
|
||||
|
||||
const NAV_ITEMS = [
|
||||
{ href: "/home", label: "Home" },
|
||||
{ href: "/chunithm", label: "Chunithm" },
|
||||
{ href: "/maimai", label: "Maimai" },
|
||||
];
|
||||
|
||||
const NavigationMenuMobile = () => {
|
||||
const pathname = usePathname();
|
||||
|
||||
return (
|
||||
<nav className="grid gap-6 text-lg font-medium">
|
||||
{NAV_ITEMS.map(({ href, label }) => {
|
||||
const isActive = pathname === href;
|
||||
|
||||
return (
|
||||
<Link
|
||||
key={href}
|
||||
href={href}
|
||||
className={`${
|
||||
isActive ? "font-semibold text-primary" : "text-muted-foreground"
|
||||
} text-sm`}
|
||||
>
|
||||
{label}
|
||||
</Link>
|
||||
);
|
||||
})}
|
||||
</nav>
|
||||
);
|
||||
};
|
||||
|
||||
export default NavigationMenuMobile;
|
94
components/navigationbar/navigationbar.tsx
Normal file
94
components/navigationbar/navigationbar.tsx
Normal file
@ -0,0 +1,94 @@
|
||||
import React from "react";
|
||||
import Link from "next/link";
|
||||
import { CircleUser, Menu, Search } from "lucide-react";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuItem,
|
||||
DropdownMenuLabel,
|
||||
DropdownMenuSeparator,
|
||||
DropdownMenuTrigger,
|
||||
} from "@/components/ui/dropdown-menu";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Sheet, SheetContent, SheetTrigger } from "@/components/ui/sheet";
|
||||
import { getAuth } from "@/auth/queries/getauth";
|
||||
import NavigationMenuDesktop from "./desktopNavBar";
|
||||
import NavigationMenuMobile from "./mobileNavBar";
|
||||
import { signOut } from "@/auth/components/signout";
|
||||
const HeaderNavigation = async () => {
|
||||
const { user } = await getAuth();
|
||||
|
||||
return (
|
||||
<>
|
||||
{user && (
|
||||
<header className="sticky top-0 flex h-16 items-center gap-4 border-b bg-background px-4 md:px-6">
|
||||
<NavigationMenuDesktop />
|
||||
<Sheet>
|
||||
<SheetTrigger asChild>
|
||||
<Button
|
||||
variant="outline"
|
||||
size="icon"
|
||||
className="shrink-0 md:hidden"
|
||||
>
|
||||
<Menu className="h-5 w-5" />
|
||||
<span className="sr-only">Toggle navigation menu</span>
|
||||
</Button>
|
||||
</SheetTrigger>
|
||||
<SheetContent side="left">
|
||||
<NavigationMenuMobile />
|
||||
</SheetContent>
|
||||
</Sheet>
|
||||
<div className="flex w-full items-center gap-4 md:ml-auto md:gap-2 lg:gap-4">
|
||||
<form className="ml-auto flex-1 sm:flex-initial">
|
||||
<div className="relative">
|
||||
<Search className="absolute left-2.5 top-2.5 h-4 w-4 text-muted-foreground" />
|
||||
<Input
|
||||
type="search"
|
||||
placeholder="Search"
|
||||
className="pl-8 sm:w-[300px] md:w-[200px] lg:w-[300px]"
|
||||
/>
|
||||
</div>
|
||||
</form>
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button
|
||||
variant="secondary"
|
||||
size="icon"
|
||||
className="rounded-full"
|
||||
>
|
||||
<CircleUser className="h-5 w-5" />
|
||||
<span className="sr-only">Toggle user menu</span>
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align="end">
|
||||
<DropdownMenuLabel>My Account</DropdownMenuLabel>
|
||||
<DropdownMenuSeparator />
|
||||
<DropdownMenuItem>
|
||||
<Link className="text-primary" href="/settings/home">
|
||||
Settings
|
||||
</Link>
|
||||
</DropdownMenuItem>
|
||||
{user.role === "ADMIN" && (
|
||||
<DropdownMenuItem>
|
||||
<Link className="text-primary" href="/admin/home">
|
||||
Admin
|
||||
</Link>
|
||||
</DropdownMenuItem>
|
||||
)}
|
||||
<DropdownMenuSeparator />
|
||||
<form action={signOut}>
|
||||
<Button className="bg-transparent text-primary hover:bg-transparent pl-2 font-normal">
|
||||
Logout
|
||||
</Button>
|
||||
</form>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
</div>
|
||||
</header>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default HeaderNavigation;
|
35
components/navigationbar/settingsnavigation.tsx
Normal file
35
components/navigationbar/settingsnavigation.tsx
Normal file
@ -0,0 +1,35 @@
|
||||
"use client";
|
||||
import Link from "next/link";
|
||||
|
||||
import { usePathname } from "next/navigation";
|
||||
|
||||
const NAV_ITEMS = [
|
||||
{ href: "/settings/home", label: "General" },
|
||||
{ href: "/settings/security", label: "Security" },
|
||||
];
|
||||
|
||||
const SettingsSubMenuNavigation = () => {
|
||||
const pathname = usePathname();
|
||||
|
||||
return (
|
||||
<nav className="grid gap-4 text-sm text-muted-foreground">
|
||||
{NAV_ITEMS.map(({ href, label }) => {
|
||||
const isActive = pathname === href;
|
||||
|
||||
return (
|
||||
<Link
|
||||
key={href}
|
||||
href={href}
|
||||
className={`${
|
||||
isActive ? "font-semibold text-primary" : "text-muted-foreground"
|
||||
} text-sm`}
|
||||
>
|
||||
{label}
|
||||
</Link>
|
||||
);
|
||||
})}
|
||||
</nav>
|
||||
);
|
||||
};
|
||||
|
||||
export default SettingsSubMenuNavigation;
|
105
components/scoreplaylog/colums.tsx
Normal file
105
components/scoreplaylog/colums.tsx
Normal file
@ -0,0 +1,105 @@
|
||||
"use client";
|
||||
|
||||
import { ColumnDef } from "@tanstack/react-table";
|
||||
import { MoreHorizontal } from "lucide-react";
|
||||
|
||||
import { Button } from "@/components/ui/button";
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuItem,
|
||||
DropdownMenuLabel,
|
||||
DropdownMenuSeparator,
|
||||
DropdownMenuTrigger,
|
||||
} from "@/components/ui/dropdown-menu";
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
import { useState } from "react";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { generateShareToken } from "@/app/(sharing)/[token]/token";
|
||||
export type Song = {
|
||||
title: string;
|
||||
artist: string;
|
||||
score?: number | null;
|
||||
userPlayDate: string | null;
|
||||
isFullCombo: boolean | null;
|
||||
isAllJustice: boolean | null;
|
||||
id: number;
|
||||
};
|
||||
|
||||
export const columns: ColumnDef<Song>[] = [
|
||||
{
|
||||
accessorKey: "title",
|
||||
header: "Title",
|
||||
cell: ({ row }) => (
|
||||
<div>
|
||||
<div className="font-medium ">
|
||||
{row.original.title}
|
||||
<span className="space-x-2 pl-2">
|
||||
{row.original.isFullCombo && (
|
||||
<Badge className="rounded-sm">Full Combo</Badge>
|
||||
)}
|
||||
{row.original.isAllJustice && (
|
||||
<Badge className="rounded-sm bg-yellow-500 text-black">
|
||||
All Justice
|
||||
</Badge>
|
||||
)}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div className="text-sm text-muted-foreground truncate inline-block w-[200px] ">
|
||||
{row.original.artist}
|
||||
</div>
|
||||
</div>
|
||||
),
|
||||
},
|
||||
{
|
||||
accessorKey: "score",
|
||||
header: "Score",
|
||||
cell: ({ row }) => row.original.score?.toLocaleString(),
|
||||
},
|
||||
{
|
||||
accessorKey: "userPlayDate",
|
||||
header: "Date",
|
||||
},
|
||||
{
|
||||
id: "actions",
|
||||
header: () => <div className="text-left pl-2">More</div>,
|
||||
cell: ({ row }) => {
|
||||
const song = row.original;
|
||||
const [error, setError] = useState<string>("");
|
||||
const router = useRouter();
|
||||
|
||||
const handleGenerateShareToken = async () => {
|
||||
const { token, error } = await generateShareToken(row.original.id);
|
||||
if (error) {
|
||||
setError(error);
|
||||
} else {
|
||||
router.push(`/${token}/${row.original.id}`);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button variant="ghost" className="h-8 w-8 p-0">
|
||||
<span className="sr-only">Open menu</span>
|
||||
<MoreHorizontal className="h-4 w-4" />
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align="end">
|
||||
<DropdownMenuLabel>Actions</DropdownMenuLabel>
|
||||
<DropdownMenuItem
|
||||
onClick={() => navigator.clipboard.writeText(song.title)}
|
||||
>
|
||||
Copy song title
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuSeparator />
|
||||
<DropdownMenuItem onClick={handleGenerateShareToken}>
|
||||
View details
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
);
|
||||
},
|
||||
},
|
||||
];
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user