diff --git a/.env.example b/.env.example index 53e4c22c..fd12addd 100644 --- a/.env.example +++ b/.env.example @@ -4,7 +4,7 @@ DATABASE_PASSWORD=super-secret DATABASE_PORT=3306 DATABASE_SSL=false -APP_DATABASE_NAME=vim +WEB_DATABASE_NAME=vim CMS_DATABASE_NAME=vim_cms CMS_PORT=1337 diff --git a/cms/src/extensions/users-permissions/content-types/user/schema.json b/cms/src/extensions/users-permissions/content-types/user/schema.json new file mode 100644 index 00000000..0ed20df0 --- /dev/null +++ b/cms/src/extensions/users-permissions/content-types/user/schema.json @@ -0,0 +1,76 @@ +{ + "kind": "collectionType", + "collectionName": "up_users", + "info": { + "name": "user", + "description": "", + "singularName": "user", + "pluralName": "users", + "displayName": "User" + }, + "options": { + "draftAndPublish": false + }, + "attributes": { + "username": { + "type": "string", + "minLength": 3, + "unique": true, + "configurable": false, + "required": true + }, + "email": { + "type": "email", + "minLength": 6, + "configurable": false, + "required": true + }, + "provider": { + "type": "string", + "configurable": false + }, + "password": { + "type": "password", + "minLength": 6, + "configurable": false, + "private": true, + "searchable": false + }, + "resetPasswordToken": { + "type": "string", + "configurable": false, + "private": true, + "searchable": false + }, + "confirmationToken": { + "type": "string", + "configurable": false, + "private": true, + "searchable": false + }, + "confirmed": { + "type": "boolean", + "default": false, + "configurable": false + }, + "blocked": { + "type": "boolean", + "default": false, + "configurable": false + }, + "role": { + "type": "relation", + "relation": "manyToOne", + "target": "plugin::users-permissions.role", + "inversedBy": "users", + "configurable": false + }, + "old_pw_hash": { + "type": "string" + }, + "migrated_pw": { + "type": "boolean", + "default": false + } + } +} diff --git a/scripts/user-migration/package-lock.json b/scripts/user-migration/package-lock.json new file mode 100644 index 00000000..50d5c16c --- /dev/null +++ b/scripts/user-migration/package-lock.json @@ -0,0 +1,207 @@ +{ + "name": "user-migration", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "user-migration", + "version": "1.0.0", + "license": "ISC", + "dependencies": { + "axios": "^1.6.8", + "mysql2": "^3.9.3" + } + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" + }, + "node_modules/axios": { + "version": "1.6.8", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.8.tgz", + "integrity": "sha512-v/ZHtJDU39mDpyBoFVkETcd/uNdxrWRrg3bKpOKzXFA6Bvqopts6ALSMU3y6ijYxbw2B+wPrIv46egTzJXCLGQ==", + "dependencies": { + "follow-redirects": "^1.15.6", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/denque": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/denque/-/denque-2.1.0.tgz", + "integrity": "sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==", + "engines": { + "node": ">=0.10" + } + }, + "node_modules/follow-redirects": { + "version": "1.15.6", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz", + "integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/generate-function": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/generate-function/-/generate-function-2.3.1.tgz", + "integrity": "sha512-eeB5GfMNeevm/GRYq20ShmsaGcmI81kIX2K9XQx5miC8KdHaC6Jm0qQ8ZNeGOi7wYB8OsdxKs+Y2oVuTFuVwKQ==", + "dependencies": { + "is-property": "^1.0.2" + } + }, + "node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-property": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-property/-/is-property-1.0.2.tgz", + "integrity": "sha512-Ks/IoX00TtClbGQr4TWXemAnktAQvYB7HzcCxDGqEZU6oCmb2INHuOoKxbtR+HFkmYWBKv/dOZtGRiAjDhj92g==" + }, + "node_modules/long": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/long/-/long-5.2.3.tgz", + "integrity": "sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q==" + }, + "node_modules/lru-cache": { + "version": "8.0.5", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-8.0.5.tgz", + "integrity": "sha512-MhWWlVnuab1RG5/zMRRcVGXZLCXrZTgfwMikgzCegsPnG62yDQo5JnqKkrK4jO5iKqDAZGItAqN5CtKBCBWRUA==", + "engines": { + "node": ">=16.14" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mysql2": { + "version": "3.9.3", + "resolved": "https://registry.npmjs.org/mysql2/-/mysql2-3.9.3.tgz", + "integrity": "sha512-+ZaoF0llESUy7BffccHG+urErHcWPZ/WuzYAA9TEeLaDYyke3/3D+VQDzK9xzRnXpd0eMtRf0WNOeo4Q1Baung==", + "dependencies": { + "denque": "^2.1.0", + "generate-function": "^2.3.1", + "iconv-lite": "^0.6.3", + "long": "^5.2.1", + "lru-cache": "^8.0.0", + "named-placeholders": "^1.1.3", + "seq-queue": "^0.0.5", + "sqlstring": "^2.3.2" + }, + "engines": { + "node": ">= 8.0" + } + }, + "node_modules/named-placeholders": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/named-placeholders/-/named-placeholders-1.1.3.tgz", + "integrity": "sha512-eLoBxg6wE/rZkJPhU/xRX1WTpkFEwDJEN96oxFrTsqBdbT5ec295Q+CoHrL9IT0DipqKhmGcaZmwOt8OON5x1w==", + "dependencies": { + "lru-cache": "^7.14.1" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/named-placeholders/node_modules/lru-cache": { + "version": "7.18.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", + "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", + "engines": { + "node": ">=12" + } + }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "node_modules/seq-queue": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/seq-queue/-/seq-queue-0.0.5.tgz", + "integrity": "sha512-hr3Wtp/GZIc/6DAGPDcV4/9WoZhjrkXsi5B/07QgX8tsdc6ilr7BFM6PM6rbdAX1kFSDYeZGLipIZZKyQP0O5Q==" + }, + "node_modules/sqlstring": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/sqlstring/-/sqlstring-2.3.3.tgz", + "integrity": "sha512-qC9iz2FlN7DQl3+wjwn3802RTyjCx7sDvfQEXchwa6CWOx07/WVfh91gBmQ9fahw8snwGEWU3xGzOt4tFyHLxg==", + "engines": { + "node": ">= 0.6" + } + } + } +} diff --git a/scripts/user-migration/package.json b/scripts/user-migration/package.json new file mode 100644 index 00000000..0ed45fb5 --- /dev/null +++ b/scripts/user-migration/package.json @@ -0,0 +1,15 @@ +{ + "name": "user-migration", + "version": "1.0.0", + "description": "", + "main": "user-migration.js", + "scripts": { + "migrate": "node user-migration.js" + }, + "author": "izzy", + "license": "ISC", + "dependencies": { + "axios": "^1.6.8", + "mysql2": "^3.9.3" + } +} diff --git a/scripts/user-migration/user-migration.js b/scripts/user-migration/user-migration.js new file mode 100644 index 00000000..0065b8c6 --- /dev/null +++ b/scripts/user-migration/user-migration.js @@ -0,0 +1,62 @@ +// example migration +const strapiUrl = "http://localhost:1337/api"; +const axios = require("axios"); + +async function getUsersFromStrapi() { + try { + const response = await axios.get(`${strapiUrl}/users`); + const users = response.data; + console.log(users); + } catch (error) { + console.error("Error fetching users:", error.message); + } +} + +async function createUser(username, email, password, old_password) { + try { + const response = await fetch(`${strapiUrl}/auth/local/register`, { + method: "POST", + headers: { + "Content-Type": "application/json", + Accept: "application/json", + }, + body: JSON.stringify({ + username: username, + email: email, + password: password, + old_pw_hash: old_password, + }), + }); + console.log(response); + + return await response.json(); + } catch (e) { + console.log("something went wrong"); + console.log(e); + return null; + } +} + +const mysql = require("mysql2"); +const connection = mysql.createConnection({ + host: "localhost", + user: "vim", + database: "vim", + password: "super-secret", +}); + +connection.query("SELECT * FROM `vs_users`", function (err, results, fields) { + console.log(results); + + results.forEach((user) => { + createUser(user.user_name, user.email, "123gege321", "old_hash") + .then((a) => { + console.log(a); + }) + .catch((err) => { + console.log(err); + }); + }); + + console.log(fields); +}); diff --git a/web/Dockerfile.dev b/web/Dockerfile.dev index b57c3066..8c92204a 100644 --- a/web/Dockerfile.dev +++ b/web/Dockerfile.dev @@ -17,6 +17,7 @@ COPY next.config.js . COPY tsconfig.json . COPY postcss.config.js . COPY tailwind.config.ts . +COPY .env . ENV NEXT_TELEMETRY_DISABLED 1 diff --git a/web/package-lock.json b/web/package-lock.json index ef37f6cb..23d16a78 100644 --- a/web/package-lock.json +++ b/web/package-lock.json @@ -10,13 +10,18 @@ "dependencies": { "@tabler/icons-react": "^2.45.0", "@tanstack/react-table": "^8.11.7", + "axios": "^1.6.8", + "md5": "^2.3.0", + "mysql2": "^3.9.7", "next": "13.5.6", + "next-auth": "^4.24.7", "qs": "^6.11.2", "react": "^18", "react-dom": "^18", "swiper": "^11.0.5" }, "devDependencies": { + "@types/md5": "^2.3.5", "@types/node": "^20", "@types/qs": "^6.9.11", "@types/react": "^18", @@ -61,7 +66,6 @@ "version": "7.23.2", "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.23.2.tgz", "integrity": "sha512-mM8eg4yl5D6i3lu2QKPuPH4FArvJ8KhTofbE7jwMUv9KX5mBvwPAqnV3MlyBNqdp9RyRKP6Yck8TrfYrPvX3bg==", - "dev": true, "dependencies": { "regenerator-runtime": "^0.14.0" }, @@ -390,6 +394,14 @@ "node": ">= 8" } }, + "node_modules/@panva/hkdf": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@panva/hkdf/-/hkdf-1.1.1.tgz", + "integrity": "sha512-dhPeilub1NuIG0X5Kvhh9lH4iW3ZsHlnzwgwbOlgwQ2wG1IqFzsgHqmKPk3WzsdWAeaxKJxgM0+W433RmN45GA==", + "funding": { + "url": "https://github.com/sponsors/panva" + } + }, "node_modules/@pkgr/core": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.1.1.tgz", @@ -484,6 +496,12 @@ "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", "dev": true }, + "node_modules/@types/md5": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@types/md5/-/md5-2.3.5.tgz", + "integrity": "sha512-/i42wjYNgE6wf0j2bcTX6kuowmdL/6PE4IVitMpm2eYKBUuYCprdcWVK+xEF0gcV6ufMCRhtxmReGfc6hIK7Jw==", + "dev": true + }, "node_modules/@types/node": { "version": "20.8.7", "resolved": "https://registry.npmjs.org/@types/node/-/node-20.8.7.tgz", @@ -1003,6 +1021,11 @@ "has-symbols": "^1.0.3" } }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" + }, "node_modules/autoprefixer": { "version": "10.4.16", "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.16.tgz", @@ -1061,6 +1084,16 @@ "node": ">=4" } }, + "node_modules/axios": { + "version": "1.6.8", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.8.tgz", + "integrity": "sha512-v/ZHtJDU39mDpyBoFVkETcd/uNdxrWRrg3bKpOKzXFA6Bvqopts6ALSMU3y6ijYxbw2B+wPrIv46egTzJXCLGQ==", + "dependencies": { + "follow-redirects": "^1.15.6", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, "node_modules/axobject-query": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-3.2.1.tgz", @@ -1216,6 +1249,14 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, + "node_modules/charenc": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/charenc/-/charenc-0.0.2.tgz", + "integrity": "sha512-yrLQ/yVUFXkzg7EDQsPieE/53+0RlaWTs+wBrvW36cyilJ2SaDWfl4Yj7MtLTXleV9uEKefbAGUPv2/iWSooRA==", + "engines": { + "node": "*" + } + }, "node_modules/chokidar": { "version": "3.5.3", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", @@ -1278,6 +1319,17 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/commander": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", @@ -1293,6 +1345,14 @@ "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", "dev": true }, + "node_modules/cookie": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", + "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==", + "engines": { + "node": ">= 0.6" + } + }, "node_modules/cross-spawn": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", @@ -1307,6 +1367,14 @@ "node": ">= 8" } }, + "node_modules/crypt": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/crypt/-/crypt-0.0.2.tgz", + "integrity": "sha512-mCxBlsHFYh9C+HVpiEacem8FEBnMXgU9gy4zmNC+SXAZNB/1idgp/aulFJ4FgCi7GPEVbfyng092GqL2k2rmow==", + "engines": { + "node": "*" + } + }, "node_modules/cssesc": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", @@ -1384,6 +1452,22 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/denque": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/denque/-/denque-2.1.0.tgz", + "integrity": "sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==", + "engines": { + "node": ">=0.10" + } + }, "node_modules/dequal": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", @@ -2181,6 +2265,25 @@ "integrity": "sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ==", "dev": true }, + "node_modules/follow-redirects": { + "version": "1.15.6", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz", + "integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, "node_modules/for-each": { "version": "0.3.3", "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", @@ -2190,6 +2293,19 @@ "is-callable": "^1.1.3" } }, + "node_modules/form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/fraction.js": { "version": "4.3.7", "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.3.7.tgz", @@ -2258,6 +2374,14 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/generate-function": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/generate-function/-/generate-function-2.3.1.tgz", + "integrity": "sha512-eeB5GfMNeevm/GRYq20ShmsaGcmI81kIX2K9XQx5miC8KdHaC6Jm0qQ8ZNeGOi7wYB8OsdxKs+Y2oVuTFuVwKQ==", + "dependencies": { + "is-property": "^1.0.2" + } + }, "node_modules/get-intrinsic": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.2.tgz", @@ -2495,6 +2619,17 @@ "node": ">= 0.4" } }, + "node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/ignore": { "version": "5.2.4", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", @@ -2634,6 +2769,11 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" + }, "node_modules/is-callable": { "version": "1.2.7", "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", @@ -2775,6 +2915,11 @@ "node": ">=8" } }, + "node_modules/is-property": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-property/-/is-property-1.0.2.tgz", + "integrity": "sha512-Ks/IoX00TtClbGQr4TWXemAnktAQvYB7HzcCxDGqEZU6oCmb2INHuOoKxbtR+HFkmYWBKv/dOZtGRiAjDhj92g==" + }, "node_modules/is-regex": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", @@ -2925,6 +3070,14 @@ "jiti": "bin/jiti.js" } }, + "node_modules/jose": { + "version": "4.15.5", + "resolved": "https://registry.npmjs.org/jose/-/jose-4.15.5.tgz", + "integrity": "sha512-jc7BFxgKPKi94uOvEmzlSWFFe2+vASyXaKUpdQKatWAESU2MWjDfFf0fdfc83CDKcA5QecabZeNLyfhe3yKNkg==", + "funding": { + "url": "https://github.com/sponsors/panva" + } + }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -3060,6 +3213,11 @@ "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", "dev": true }, + "node_modules/long": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/long/-/long-5.2.3.tgz", + "integrity": "sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q==" + }, "node_modules/loose-envify": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", @@ -3075,7 +3233,6 @@ "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, "dependencies": { "yallist": "^4.0.0" }, @@ -3083,6 +3240,16 @@ "node": ">=10" } }, + "node_modules/md5": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/md5/-/md5-2.3.0.tgz", + "integrity": "sha512-T1GITYmFaKuO91vxyoQMFETst+O71VUPEU3ze5GNzDm0OWdP8v1ziTaAEPUr/3kLsY3Sftgz242A1SetQiDL7g==", + "dependencies": { + "charenc": "0.0.2", + "crypt": "0.0.2", + "is-buffer": "~1.1.6" + } + }, "node_modules/merge2": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", @@ -3105,6 +3272,25 @@ "node": ">=8.6" } }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/minimatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", @@ -3132,6 +3318,32 @@ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", "dev": true }, + "node_modules/mysql2": { + "version": "3.9.7", + "resolved": "https://registry.npmjs.org/mysql2/-/mysql2-3.9.7.tgz", + "integrity": "sha512-KnJT8vYRcNAZv73uf9zpXqNbvBG7DJrs+1nACsjZP1HMJ1TgXEy8wnNilXAn/5i57JizXKtrUtwDB7HxT9DDpw==", + "dependencies": { + "denque": "^2.1.0", + "generate-function": "^2.3.1", + "iconv-lite": "^0.6.3", + "long": "^5.2.1", + "lru-cache": "^8.0.0", + "named-placeholders": "^1.1.3", + "seq-queue": "^0.0.5", + "sqlstring": "^2.3.2" + }, + "engines": { + "node": ">= 8.0" + } + }, + "node_modules/mysql2/node_modules/lru-cache": { + "version": "8.0.5", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-8.0.5.tgz", + "integrity": "sha512-MhWWlVnuab1RG5/zMRRcVGXZLCXrZTgfwMikgzCegsPnG62yDQo5JnqKkrK4jO5iKqDAZGItAqN5CtKBCBWRUA==", + "engines": { + "node": ">=16.14" + } + }, "node_modules/mz": { "version": "2.7.0", "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", @@ -3143,6 +3355,25 @@ "thenify-all": "^1.0.0" } }, + "node_modules/named-placeholders": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/named-placeholders/-/named-placeholders-1.1.3.tgz", + "integrity": "sha512-eLoBxg6wE/rZkJPhU/xRX1WTpkFEwDJEN96oxFrTsqBdbT5ec295Q+CoHrL9IT0DipqKhmGcaZmwOt8OON5x1w==", + "dependencies": { + "lru-cache": "^7.14.1" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/named-placeholders/node_modules/lru-cache": { + "version": "7.18.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", + "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", + "engines": { + "node": ">=12" + } + }, "node_modules/nanoid": { "version": "3.3.6", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.6.tgz", @@ -3211,6 +3442,33 @@ } } }, + "node_modules/next-auth": { + "version": "4.24.7", + "resolved": "https://registry.npmjs.org/next-auth/-/next-auth-4.24.7.tgz", + "integrity": "sha512-iChjE8ov/1K/z98gdKbn2Jw+2vLgJtVV39X+rCP5SGnVQuco7QOr19FRNGMIrD8d3LYhHWV9j9sKLzq1aDWWQQ==", + "dependencies": { + "@babel/runtime": "^7.20.13", + "@panva/hkdf": "^1.0.2", + "cookie": "^0.5.0", + "jose": "^4.15.5", + "oauth": "^0.9.15", + "openid-client": "^5.4.0", + "preact": "^10.6.3", + "preact-render-to-string": "^5.1.19", + "uuid": "^8.3.2" + }, + "peerDependencies": { + "next": "^12.2.5 || ^13 || ^14", + "nodemailer": "^6.6.5", + "react": "^17.0.2 || ^18", + "react-dom": "^17.0.2 || ^18" + }, + "peerDependenciesMeta": { + "nodemailer": { + "optional": true + } + } + }, "node_modules/node-releases": { "version": "2.0.13", "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.13.tgz", @@ -3235,6 +3493,11 @@ "node": ">=0.10.0" } }, + "node_modules/oauth": { + "version": "0.9.15", + "resolved": "https://registry.npmjs.org/oauth/-/oauth-0.9.15.tgz", + "integrity": "sha512-a5ERWK1kh38ExDEfoO6qUHJb32rd7aYmPHuyCu3Fta/cnICvYmgd2uhuKXvPD+PXB+gCEYYEaQdIRAjCOwAKNA==" + }, "node_modules/object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", @@ -3360,6 +3623,14 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/oidc-token-hash": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/oidc-token-hash/-/oidc-token-hash-5.0.3.tgz", + "integrity": "sha512-IF4PcGgzAr6XXSff26Sk/+P4KZFJVuHAJZj3wgO3vX2bMdNVp/QXTP3P7CEm9V1IdG8lDLY3HhiqpsE/nOwpPw==", + "engines": { + "node": "^10.13.0 || >=12.0.0" + } + }, "node_modules/once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", @@ -3369,6 +3640,28 @@ "wrappy": "1" } }, + "node_modules/openid-client": { + "version": "5.6.5", + "resolved": "https://registry.npmjs.org/openid-client/-/openid-client-5.6.5.tgz", + "integrity": "sha512-5P4qO9nGJzB5PI0LFlhj4Dzg3m4odt0qsJTfyEtZyOlkgpILwEioOhVVJOrS1iVH494S4Ee5OCjjg6Bf5WOj3w==", + "dependencies": { + "jose": "^4.15.5", + "lru-cache": "^6.0.0", + "object-hash": "^2.2.0", + "oidc-token-hash": "^5.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/panva" + } + }, + "node_modules/openid-client/node_modules/object-hash": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-2.2.0.tgz", + "integrity": "sha512-gScRMn0bS5fH+IuwyIFgnh9zBdo4DV+6GhygmWM9HyNJSgS0hScp1f5vjtm7oIIOiT9trXrShAkLFSc2IqKNgw==", + "engines": { + "node": ">= 6" + } + }, "node_modules/optionator": { "version": "0.9.3", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", @@ -3635,6 +3928,26 @@ "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", "dev": true }, + "node_modules/preact": { + "version": "10.20.1", + "resolved": "https://registry.npmjs.org/preact/-/preact-10.20.1.tgz", + "integrity": "sha512-JIFjgFg9B2qnOoGiYMVBtrcFxHqn+dNXbq76bVmcaHYJFYR4lW67AOcXgAYQQTDYXDOg/kTZrKPNCdRgJ2UJmw==", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/preact" + } + }, + "node_modules/preact-render-to-string": { + "version": "5.2.6", + "resolved": "https://registry.npmjs.org/preact-render-to-string/-/preact-render-to-string-5.2.6.tgz", + "integrity": "sha512-JyhErpYOvBV1hEPwIxc/fHWXPfnEGdRKxc8gFdAZ7XV4tlzyzG847XAyEZqoDnynP88akM4eaHcSOzNcLWFguw==", + "dependencies": { + "pretty-format": "^3.8.0" + }, + "peerDependencies": { + "preact": ">=10" + } + }, "node_modules/prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", @@ -3672,6 +3985,11 @@ "node": ">=6.0.0" } }, + "node_modules/pretty-format": { + "version": "3.8.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-3.8.0.tgz", + "integrity": "sha512-WuxUnVtlWL1OfZFQFuqvnvs6MiAGk9UNsBostyBOB0Is9wb5uRESevA6rnl/rkksXaGX3GzZhPup5d6Vp1nFew==" + }, "node_modules/prop-types": { "version": "15.8.1", "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", @@ -3682,6 +4000,11 @@ "react-is": "^16.13.1" } }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" + }, "node_modules/punycode": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz", @@ -3797,8 +4120,7 @@ "node_modules/regenerator-runtime": { "version": "0.14.0", "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.0.tgz", - "integrity": "sha512-srw17NI0TUWHuGa5CFGGmhfNIeja30WMBfbslPNhf6JrqQlLN5gcrvig1oqPxiVaXb0oW0XRKtH6Nngs5lKCIA==", - "dev": true + "integrity": "sha512-srw17NI0TUWHuGa5CFGGmhfNIeja30WMBfbslPNhf6JrqQlLN5gcrvig1oqPxiVaXb0oW0XRKtH6Nngs5lKCIA==" }, "node_modules/regexp.prototype.flags": { "version": "1.5.1", @@ -3932,6 +4254,11 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, "node_modules/sass": { "version": "1.70.0", "resolved": "https://registry.npmjs.org/sass/-/sass-1.70.0.tgz", @@ -3972,6 +4299,11 @@ "node": ">=10" } }, + "node_modules/seq-queue": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/seq-queue/-/seq-queue-0.0.5.tgz", + "integrity": "sha512-hr3Wtp/GZIc/6DAGPDcV4/9WoZhjrkXsi5B/07QgX8tsdc6ilr7BFM6PM6rbdAX1kFSDYeZGLipIZZKyQP0O5Q==" + }, "node_modules/set-function-length": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.1.1.tgz", @@ -4051,6 +4383,14 @@ "node": ">=0.10.0" } }, + "node_modules/sqlstring": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/sqlstring/-/sqlstring-2.3.3.tgz", + "integrity": "sha512-qC9iz2FlN7DQl3+wjwn3802RTyjCx7sDvfQEXchwa6CWOx07/WVfh91gBmQ9fahw8snwGEWU3xGzOt4tFyHLxg==", + "engines": { + "node": ">= 0.6" + } + }, "node_modules/streamsearch": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz", @@ -4584,6 +4924,14 @@ "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", "dev": true }, + "node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "bin": { + "uuid": "dist/bin/uuid" + } + }, "node_modules/watchpack": { "version": "2.4.0", "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.0.tgz", @@ -4696,8 +5044,7 @@ "node_modules/yallist": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" }, "node_modules/yaml": { "version": "2.3.3", diff --git a/web/package.json b/web/package.json index dfbd1ddd..6dee6144 100644 --- a/web/package.json +++ b/web/package.json @@ -14,13 +14,18 @@ "dependencies": { "@tabler/icons-react": "^2.45.0", "@tanstack/react-table": "^8.11.7", + "axios": "^1.6.8", + "md5": "^2.3.0", + "mysql2": "^3.9.7", "next": "13.5.6", + "next-auth": "^4.24.7", "qs": "^6.11.2", "react": "^18", "react-dom": "^18", "swiper": "^11.0.5" }, "devDependencies": { + "@types/md5": "^2.3.5", "@types/node": "^20", "@types/qs": "^6.9.11", "@types/react": "^18", diff --git a/web/src/app/[...slug]/page.tsx b/web/src/app/[...slug]/page.tsx index 20c0856e..87a6fdd1 100644 --- a/web/src/app/[...slug]/page.tsx +++ b/web/src/app/[...slug]/page.tsx @@ -3,11 +3,19 @@ import { getPageContent } from "@/helpers/getPageContent"; import Card from "@/components/common/Card/Card"; export default async function page({ params }: { params: { slug: string[] } }) { - const { page } = await getPageContent(params.slug); - return ( -
- {/*

{page.pageTitle}

*/} - -
- ); + try { + const { page } = await getPageContent(params.slug); + return ( +
+ {/*

{page.pageTitle}

*/} + +
+ ); + } catch (e) { + return ( +
+

No content defined in CMS.

+
+ ); + } } diff --git a/web/src/app/api/auth/[...nextauth]/route.ts b/web/src/app/api/auth/[...nextauth]/route.ts new file mode 100644 index 00000000..228b250d --- /dev/null +++ b/web/src/app/api/auth/[...nextauth]/route.ts @@ -0,0 +1,75 @@ +import CredentialsProvider from "next-auth/providers/credentials"; +import NextAuth from "next-auth/next"; +import { JWT } from "next-auth/jwt"; +import { Session, User } from "next-auth"; +import { signIn, signUp } from "@/lib/auth/strapi"; +import { findUserByEmail, verifyPassword } from "@/lib/auth/legacy_web"; +import { StrapiSignInResponse } from "@/lib/types"; + +const authOptions = { + secret: process.env.NEXTAUTH_SECRET, + providers: [ + CredentialsProvider({ + name: "Username or Email", + credentials: { + email: { label: "Email", type: "text" }, + password: { label: "Password", type: "password" }, + }, + async authorize(credentials) { + try { + if (credentials?.email == null || credentials.password == null) return null; + + const strapiResponse: StrapiSignInResponse = await signIn(credentials.email, credentials.password); + + if (strapiResponse === null || strapiResponse.error) { + // if strapi cannot authenticate + // look if user is to migrate + const potentialUserForMigration = await findUserByEmail(credentials.email); + const isToMigrate = potentialUserForMigration && verifyPassword(credentials.password, potentialUserForMigration.password); + + if (isToMigrate) { + const migrationResponse = await signUp(potentialUserForMigration.user_name, potentialUserForMigration.email, credentials.password, "internal"); + + return { + jwt: migrationResponse.jwt, + id: String(migrationResponse.user.id), + email: migrationResponse.user.email, + name: migrationResponse.user.username, + }; + } + return null; + } + + return { + jwt: strapiResponse.jwt, + id: String(strapiResponse.user.id), + email: strapiResponse.user.email, + name: strapiResponse.user.username, + }; + } catch { + return null; + } + }, + }), + ], + callbacks: { + session: async ({ session, token }: { session: Session; token: JWT }) => { + session.id = token.id as string; + session.jwt = token.jwt as string; + + return session; + }, + jwt: async ({ token, user }: { token: JWT; user: User }) => { + if (user) { + token.id = user.id; + token.jwt = user.jwt; + token.email = user.email; + } + return token; + }, + }, +}; + +const handler = NextAuth(authOptions); + +export { handler as GET, handler as POST }; diff --git a/web/src/app/layout.tsx b/web/src/app/layout.tsx index 42bc64a4..19aa9da7 100644 --- a/web/src/app/layout.tsx +++ b/web/src/app/layout.tsx @@ -1,8 +1,10 @@ import { Fira_Code } from "next/font/google"; +import { getServerSession } from "next-auth"; import type { Metadata } from "next"; import "../styles/globals.scss"; import qs from "qs"; import Layout from "@/components/Layout/Layout"; +import SessionProvider from "./../components/SessionProvider"; const params = { nested: true, @@ -40,10 +42,13 @@ async function getPageProps() { export default async function RootLayout({ children }: { children: React.ReactNode }) { const pageProps = await getPageProps(); + const session = await getServerSession(); return ( - - {pageProps && {children}} - + + + {pageProps && {children}} + + ); } diff --git a/web/src/app/my-account/page.tsx b/web/src/app/my-account/page.tsx new file mode 100644 index 00000000..68c750b3 --- /dev/null +++ b/web/src/app/my-account/page.tsx @@ -0,0 +1,71 @@ +"use client"; +import { ChangeEvent, FormEvent, useState } from "react"; +import { signIn, signOut, useSession } from "next-auth/react"; +import { getMailValidation } from "@/helpers/validators"; +import Input from "@/components/Inputs/TextInput"; + +type LoginFormData = { + email: string; + password: string; +}; + +export default function MyAccount() { + const { data: session } = useSession(); + const [formData, setFormData] = useState({ + email: "", + password: "", + }); + + const [errorMessage, setErrorMessage] = useState(""); + + const handleInputChange = (e: ChangeEvent) => { + const { name, value } = e.target; + setFormData(prevFormData => ({ ...prevFormData, [name]: value })); + }; + + return ( +
+

My Account

+
+ {session && } + {session?.user == null && ( +
) => { + e.preventDefault(); + try { + const res = await signIn("credentials", { email: formData.email, password: formData.password, redirect: false }); + if (res?.error) throw res.error; + else setErrorMessage(""); + } catch { + setErrorMessage("Invalid email or password"); + } + }} + > + getMailValidation(value)} + /> + + +
+ )} +
+ {errorMessage != "" &&

{errorMessage}

} +

Please note

+ {session?.user &&

{session.user.name}

} +

+ Lorem ipsum dolor sit amet consectetur adipisicing elit. Vel est adipisci quas, aperiam voluptas, omnis tempora blanditiis quae fuga eum ullam + commodi a perspiciatis provident pariatur sunt excepturi doloremque! Commodi! +

+
+
+
+ ); +} diff --git a/web/src/app/registration/page.tsx b/web/src/app/registration/page.tsx index 98a1563d..91e2085d 100644 --- a/web/src/app/registration/page.tsx +++ b/web/src/app/registration/page.tsx @@ -1,5 +1,7 @@ "use client"; -import { ChangeEvent, useState } from "react"; +import { ChangeEvent, FormEvent, useState } from "react"; +import { signIn, useSession } from "next-auth/react"; +import { signUp } from "@/lib/auth/strapi"; import { getMailValidation } from "@/helpers/validators"; import Input from "@/components/Inputs/TextInput"; @@ -13,6 +15,7 @@ type RegistrationFormData = { }; export default function Registration() { + const { data: session } = useSession(); const [formData, setFormData] = useState({ username: "", firstname: "", @@ -22,6 +25,8 @@ export default function Registration() { passwordConfirm: "", }); + const [errorMessage, setErrorMessage] = useState(""); + const handleInputChange = (e: ChangeEvent) => { const { name, value } = e.target; setFormData(prevFormData => ({ ...prevFormData, [name]: value })); @@ -36,12 +41,23 @@ export default function Registration() {

My Account

-
+ ) => { + e.preventDefault(); + const registerresp = await signUp(formData.username, formData.email, formData.password); + if (registerresp.error) { + setErrorMessage(registerresp.error.message); + return; + } + await signIn("credentials", { email: formData.email, password: formData.password, redirect: false }); + }} + > -
+ {/*
-
+
*/}
+ {errorMessage != "" &&

{errorMessage}

}

Please note

- + {session?.user &&

{session.user.name}

}

Lorem ipsum dolor sit amet consectetur adipisicing elit. Vel est adipisci quas, aperiam voluptas, omnis tempora blanditiis quae fuga eum ullam commodi a perspiciatis provident pariatur sunt excepturi doloremque! Commodi! diff --git a/web/src/components/Layout/Navigation/Navigation.tsx b/web/src/components/Layout/Navigation/Navigation.tsx index eb877c7c..dbee41f1 100644 --- a/web/src/components/Layout/Navigation/Navigation.tsx +++ b/web/src/components/Layout/Navigation/Navigation.tsx @@ -1,6 +1,7 @@ "use client"; import Link from "next/link"; import Image from "next/image"; +import { useSession } from "next-auth/react"; import { IconUserCircle } from "@tabler/icons-react"; type NavigationProps = { @@ -17,6 +18,8 @@ export type Page = { }; export default function Navigation({ menu }: NavigationProps) { + const { data: session } = useSession(); + return (

diff --git a/web/src/components/SessionProvider.tsx b/web/src/components/SessionProvider.tsx new file mode 100644 index 00000000..a2a7c217 --- /dev/null +++ b/web/src/components/SessionProvider.tsx @@ -0,0 +1,3 @@ +"use client"; +import { SessionProvider } from "next-auth/react"; +export default SessionProvider; diff --git a/web/src/config/constants.ts b/web/src/config/constants.ts new file mode 100644 index 00000000..113d67b2 --- /dev/null +++ b/web/src/config/constants.ts @@ -0,0 +1,6 @@ +export const CMS_PUBLIC_API_URL = process.env.CMS_PUBLIC_API_URL ?? "http://localhost:1337/api"; +export const CMS_INTERNAL_API_URL = process.env.CMS_INTERNAL_API_URL ?? "http://cms:1337/api"; +export const DATABASE_HOST = process.env.DATABASE_HOST ?? "db"; +export const DATABASE_USER = process.env.DATABASE_USER ?? "vim"; +export const DATABASE_NAME = process.env.DATABASE_NAME ?? "vim"; +export const DATABASE_PASSWORD = process.env.DATABASE_PASSWORD ?? "super-secret"; diff --git a/web/src/config/db.ts b/web/src/config/db.ts new file mode 100644 index 00000000..db2e00ce --- /dev/null +++ b/web/src/config/db.ts @@ -0,0 +1,9 @@ +import * as mysql from "mysql2"; +import { DATABASE_HOST, DATABASE_NAME, DATABASE_PASSWORD, DATABASE_USER } from "./constants"; + +export const dbConnection = mysql.createConnection({ + host: DATABASE_HOST, + user: DATABASE_USER, + database: DATABASE_NAME, + password: DATABASE_PASSWORD, +}); diff --git a/web/src/lib/auth/legacy_web.ts b/web/src/lib/auth/legacy_web.ts new file mode 100644 index 00000000..aa96da78 --- /dev/null +++ b/web/src/lib/auth/legacy_web.ts @@ -0,0 +1,42 @@ +import md5 from "md5"; +import { dbConnection } from "@/config/db"; +// MD5 Hash is stated to be not secure +// This code has to be deleted after majority of users +// migrated to a secure hashing algorithm +// this is fulfilled when users are registered in strapi + +type LegacyUser = { + user_id: number; + user_name: string; + password: string; + first_name: string; + last_name: string; + email: string; + access?: boolean; + homepage?: string; + sponsor_amount?: number; + sponsor_vote_amount?: number; + sponsor_vote_date?: string; + created: string; +}; + +export const findUserByEmail = (email: string): Promise => { + const sql = "SELECT * FROM `vs_users` WHERE email = ?"; + + return new Promise((resolve, reject) => { + dbConnection.query(sql, [email], function (err, results) { + if (err) { + reject(err); + return; + } + + if ((results as LegacyUser[]).length >= 1) { + resolve((results as LegacyUser[])[0]); + } else { + resolve(null); + } + }); + }); +}; + +export const verifyPassword = (givenPassword: string, legacyHashedPassword: string) => md5(givenPassword) === legacyHashedPassword; diff --git a/web/src/lib/auth/strapi.ts b/web/src/lib/auth/strapi.ts new file mode 100644 index 00000000..161a2696 --- /dev/null +++ b/web/src/lib/auth/strapi.ts @@ -0,0 +1,41 @@ +import { CMS_PUBLIC_API_URL, CMS_INTERNAL_API_URL } from "@/config/constants"; + +export const signIn = async (email: string, password: string) => { + try { + const response = await fetch(`${CMS_INTERNAL_API_URL}/auth/local`, { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ + identifier: email, + password: password, + }), + }); + return await response.json(); + } catch (e) { + return null; + } +}; + +export const signUp = async (username: string, email: string, password: string, mode: "internal" | "public" = "public") => { + const strapiApiUrl = mode == "public" ? CMS_PUBLIC_API_URL : CMS_INTERNAL_API_URL; + try { + const response = await fetch(`${strapiApiUrl}/auth/local/register`, { + method: "POST", + headers: { + "Content-Type": "application/json", + Accept: "application/json", + }, + body: JSON.stringify({ + username: username, + email: email, + password: password, + }), + }); + + return await response.json(); + } catch { + return null; + } +}; diff --git a/web/src/lib/types.ts b/web/src/lib/types.ts new file mode 100644 index 00000000..3f1c0af7 --- /dev/null +++ b/web/src/lib/types.ts @@ -0,0 +1,22 @@ +export type StrapiSignInResponse = { + jwt: string; + user: { + id: number; + username: string; + email: string; + provider: string; + confirmed: boolean; + blocked: boolean; + createdAt: string; + updatedAt: string; + }; + error?: { + details: string[]; + }; +}; + +export type SignUpPayload = { + username: string; + email: string; + password: string; +}; diff --git a/web/tsconfig.json b/web/tsconfig.json index 1bf25398..d0797f33 100644 --- a/web/tsconfig.json +++ b/web/tsconfig.json @@ -22,6 +22,6 @@ "@/*": ["./src/*"] } }, - "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], + "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts", "./types/next-auth.d.ts", "types/**/*"], "exclude": ["node_modules"] } diff --git a/web/types/next-auth.d.ts b/web/types/next-auth.d.ts new file mode 100644 index 00000000..d142d582 --- /dev/null +++ b/web/types/next-auth.d.ts @@ -0,0 +1,15 @@ +import "next-auth"; + +declare module "next-auth" { + interface User { + id: string; + email: string; + name: string; + jwt: JWT; + } + + interface Session extends DefaultSession { + id: string; + jwt: string; + } +}