From 6f378d25a138d08e73396be9222ce32f70041afe Mon Sep 17 00:00:00 2001 From: Wind-Explorer Date: Fri, 21 Jun 2024 16:10:29 +0800 Subject: [PATCH] Basic user CR --- server/.env.dev | 7 +- server/index.js | 22 +++-- server/models/User.js | 36 +++++++ server/models/index.js | 42 +++++++++ server/package.json | 5 +- server/pnpm-lock.yaml | 206 +++++++++++++++++++++++++++++++++++++++++ server/routes/users.js | 33 +++++-- 7 files changed, 331 insertions(+), 20 deletions(-) create mode 100644 server/models/User.js create mode 100644 server/models/index.js diff --git a/server/.env.dev b/server/.env.dev index 747c68a..d943730 100644 --- a/server/.env.dev +++ b/server/.env.dev @@ -1,2 +1,7 @@ APP_PORT = 5183 -CLIENT_URL = "http://localhost:5173" \ No newline at end of file +CLIENT_URL = "http://localhost:5173" +DB_HOST = "hostname" +DB_PORT = 3306 +DB_USER = "username" +DB_PWD = "password" +DB_NAME = "FSDP_ECOCONNECT_DB" \ No newline at end of file diff --git a/server/index.js b/server/index.js index efb6255..76b0249 100644 --- a/server/index.js +++ b/server/index.js @@ -1,9 +1,10 @@ -import express from "express"; -import dotenv from "dotenv"; -import cors from "cors"; +const express = require("express"); +const dotenv = require("dotenv"); +const cors = require("cors"); +const db = require("./models"); // Routes -import usersRoute from "./routes/users.js"; +const usersRoute = require("./routes/users.js"); dotenv.config(); @@ -24,6 +25,13 @@ app.get("/", (req, res) => { app.use("/users", usersRoute); -app.listen(PORT, () => { - console.log(`ecoconnect server running at http://localhost:${PORT}/`); -}); +db.sequelize + .sync({ alter: true }) + .then(() => { + app.listen(PORT, () => { + console.log(`ecoconnect server running at http://localhost:${PORT}/`); + }); + }) + .catch((err) => { + console.log(err); + }); diff --git a/server/models/User.js b/server/models/User.js new file mode 100644 index 0000000..a1a4d9a --- /dev/null +++ b/server/models/User.js @@ -0,0 +1,36 @@ +module.exports = (sequelize, DataTypes) => { + const User = sequelize.define( + "User", + { + id: { + type: DataTypes.INTEGER(), + allowNull: false, + primaryKey: true, + }, + firstName: { + type: DataTypes.STRING(100), + allowNull: false, + }, + lastName: { + type: DataTypes.STRING(100), + allowNull: false, + }, + email: { + type: DataTypes.STRING(69), + allowNull: false, + }, + phoneNumber: { + type: DataTypes.STRING(8), + allowNull: false, + }, + passwordHash: { + type: DataTypes.STRING(255), + allowNull: false, + }, + }, + { + tableName: "users", + } + ); + return User; +}; diff --git a/server/models/index.js b/server/models/index.js new file mode 100644 index 0000000..e7ce57e --- /dev/null +++ b/server/models/index.js @@ -0,0 +1,42 @@ +"use strict"; +const fs = require("fs"); +const path = require("path"); +const Sequelize = require("sequelize"); +const process = require("process"); +const basename = path.basename(__filename); +const db = {}; +require("dotenv").config(); +// Create sequelize instance using config +let sequelize = new Sequelize( + process.env.DB_NAME, + process.env.DB_USER, + process.env.DB_PWD, + { + host: process.env.DB_HOST, + port: process.env.DB_PORT, + dialect: "mysql", + logging: false, + timezone: "+08:00", + } +); +fs.readdirSync(__dirname) + .filter((file) => { + return ( + file.indexOf(".") !== 0 && file !== basename && file.slice(-3) === ".js" + ); + }) + .forEach((file) => { + const model = require(path.join(__dirname, file))( + sequelize, + Sequelize.DataTypes + ); + db[model.name] = model; + }); +Object.keys(db).forEach((modelName) => { + if (db[modelName].associate) { + db[modelName].associate(db); + } +}); +db.sequelize = sequelize; +db.Sequelize = Sequelize; +module.exports = db; diff --git a/server/package.json b/server/package.json index 8b61379..cffb45b 100644 --- a/server/package.json +++ b/server/package.json @@ -3,7 +3,6 @@ "version": "1.0.0", "private": true, "description": "Backend server for ecoconnect.", - "type": "module", "main": "index.js", "scripts": { "start": "nodemon index.js" @@ -14,6 +13,8 @@ "cors": "^2.8.5", "dotenv": "^16.4.5", "express": "^4.19.2", - "nodemon": "^3.1.3" + "mysql2": "^3.10.1", + "nodemon": "^3.1.3", + "sequelize": "^6.37.3" } } diff --git a/server/pnpm-lock.yaml b/server/pnpm-lock.yaml index 2b579d0..7a906a0 100644 --- a/server/pnpm-lock.yaml +++ b/server/pnpm-lock.yaml @@ -14,12 +14,38 @@ dependencies: express: specifier: ^4.19.2 version: 4.19.2 + mysql2: + specifier: ^3.10.1 + version: 3.10.1 nodemon: specifier: ^3.1.3 version: 3.1.3 + sequelize: + specifier: ^6.37.3 + version: 6.37.3(mysql2@3.10.1) packages: + /@types/debug@4.1.12: + resolution: {integrity: sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==} + dependencies: + '@types/ms': 0.7.34 + dev: false + + /@types/ms@0.7.34: + resolution: {integrity: sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g==} + dev: false + + /@types/node@20.14.6: + resolution: {integrity: sha512-JbA0XIJPL1IiNnU7PFxDXyfAwcwVVrOoqyzzyQTyMeVhBzkJVMSkC1LlVsRQ2lpqiY4n6Bb9oCS6lzDKVQxbZw==} + dependencies: + undici-types: 5.26.5 + dev: false + + /@types/validator@13.12.0: + resolution: {integrity: sha512-nH45Lk7oPIJ1RVOF6JgFI6Dy0QpHEzq4QecZhvguxYPDwT8c93prCMqAtiIttm39voZ+DDR+qkNnMpJmMBRqag==} + dev: false + /accepts@1.3.8: resolution: {integrity: sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==} engines: {node: '>= 0.6'} @@ -180,6 +206,11 @@ packages: gopd: 1.0.1 dev: false + /denque@2.1.0: + resolution: {integrity: sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==} + engines: {node: '>=0.10'} + dev: false + /depd@2.0.0: resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==} engines: {node: '>= 0.8'} @@ -195,6 +226,10 @@ packages: engines: {node: '>=12'} dev: false + /dottie@2.0.6: + resolution: {integrity: sha512-iGCHkfUc5kFekGiqhe8B/mdaurD+lakO9txNnTvKtA6PISrw86LgqHvRzWYPyoE2Ph5aMIrCw9/uko6XHTKCwA==} + dev: false + /ee-first@1.1.1: resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} dev: false @@ -308,6 +343,12 @@ packages: resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} dev: false + /generate-function@2.3.1: + resolution: {integrity: sha512-eeB5GfMNeevm/GRYq20ShmsaGcmI81kIX2K9XQx5miC8KdHaC6Jm0qQ8ZNeGOi7wYB8OsdxKs+Y2oVuTFuVwKQ==} + dependencies: + is-property: 1.0.2 + dev: false + /get-intrinsic@1.2.4: resolution: {integrity: sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==} engines: {node: '>= 0.4'} @@ -378,10 +419,22 @@ packages: safer-buffer: 2.1.2 dev: false + /iconv-lite@0.6.3: + resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} + engines: {node: '>=0.10.0'} + dependencies: + safer-buffer: 2.1.2 + dev: false + /ignore-by-default@1.0.1: resolution: {integrity: sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==} dev: false + /inflection@1.13.4: + resolution: {integrity: sha512-6I/HUDeYFfuNCVS3td055BaXBwKYuzw7K3ExVMStBowKo9oOAMJIXIHvdyR3iboTCp1b+1i5DSkIZTcwIktuDw==} + engines: {'0': node >= 0.4.0} + dev: false + /inherits@2.0.4: resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} dev: false @@ -415,6 +468,28 @@ packages: engines: {node: '>=0.12.0'} dev: false + /is-property@1.0.2: + resolution: {integrity: sha512-Ks/IoX00TtClbGQr4TWXemAnktAQvYB7HzcCxDGqEZU6oCmb2INHuOoKxbtR+HFkmYWBKv/dOZtGRiAjDhj92g==} + dev: false + + /lodash@4.17.21: + resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} + dev: false + + /long@5.2.3: + resolution: {integrity: sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q==} + dev: false + + /lru-cache@7.18.3: + resolution: {integrity: sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==} + engines: {node: '>=12'} + dev: false + + /lru-cache@8.0.5: + resolution: {integrity: sha512-MhWWlVnuab1RG5/zMRRcVGXZLCXrZTgfwMikgzCegsPnG62yDQo5JnqKkrK4jO5iKqDAZGItAqN5CtKBCBWRUA==} + engines: {node: '>=16.14'} + dev: false + /media-typer@0.3.0: resolution: {integrity: sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==} engines: {node: '>= 0.6'} @@ -453,6 +528,16 @@ packages: brace-expansion: 1.1.11 dev: false + /moment-timezone@0.5.45: + resolution: {integrity: sha512-HIWmqA86KcmCAhnMAN0wuDOARV/525R2+lOLotuGFzn4HO+FH+/645z2wx0Dt3iDv6/p61SIvKnDstISainhLQ==} + dependencies: + moment: 2.30.1 + dev: false + + /moment@2.30.1: + resolution: {integrity: sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==} + dev: false + /ms@2.0.0: resolution: {integrity: sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==} dev: false @@ -465,6 +550,27 @@ packages: resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} dev: false + /mysql2@3.10.1: + resolution: {integrity: sha512-6zo1T3GILsXMCex3YEu7hCz2OXLUarxFsxvFcUHWMpkPtmZLeTTWgRdc1gWyNJiYt6AxITmIf9bZDRy/jAfWew==} + engines: {node: '>= 8.0'} + dependencies: + denque: 2.1.0 + generate-function: 2.3.1 + iconv-lite: 0.6.3 + long: 5.2.3 + lru-cache: 8.0.5 + named-placeholders: 1.1.3 + seq-queue: 0.0.5 + sqlstring: 2.3.3 + dev: false + + /named-placeholders@1.1.3: + resolution: {integrity: sha512-eLoBxg6wE/rZkJPhU/xRX1WTpkFEwDJEN96oxFrTsqBdbT5ec295Q+CoHrL9IT0DipqKhmGcaZmwOt8OON5x1w==} + engines: {node: '>=12.0.0'} + dependencies: + lru-cache: 7.18.3 + dev: false + /negotiator@0.6.3: resolution: {integrity: sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==} engines: {node: '>= 0.6'} @@ -517,6 +623,10 @@ packages: resolution: {integrity: sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==} dev: false + /pg-connection-string@2.6.4: + resolution: {integrity: sha512-v+Z7W/0EO707aNMaAEfiGnGL9sxxumwLl2fJvCQtMn9Fxsg+lPpPkdcyBSv/KFgpGdYkMfn+EI1Or2EHjpgLCA==} + dev: false + /picomatch@2.3.1: resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} engines: {node: '>=8.6'} @@ -563,6 +673,10 @@ packages: picomatch: 2.3.1 dev: false + /retry-as-promised@7.0.4: + resolution: {integrity: sha512-XgmCoxKWkDofwH8WddD0w85ZfqYz+ZHlr5yo+3YUCfycWawU56T5ckWXsScsj5B8tqUcIG67DxXByo3VUgiAdA==} + dev: false + /safe-buffer@5.2.1: resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} dev: false @@ -598,6 +712,69 @@ packages: - supports-color dev: false + /seq-queue@0.0.5: + resolution: {integrity: sha512-hr3Wtp/GZIc/6DAGPDcV4/9WoZhjrkXsi5B/07QgX8tsdc6ilr7BFM6PM6rbdAX1kFSDYeZGLipIZZKyQP0O5Q==} + dev: false + + /sequelize-pool@7.1.0: + resolution: {integrity: sha512-G9c0qlIWQSK29pR/5U2JF5dDQeqqHRragoyahj/Nx4KOOQ3CPPfzxnfqFPCSB7x5UgjOgnZ61nSxz+fjDpRlJg==} + engines: {node: '>= 10.0.0'} + dev: false + + /sequelize@6.37.3(mysql2@3.10.1): + resolution: {integrity: sha512-V2FTqYpdZjPy3VQrZvjTPnOoLm0KudCRXfGWp48QwhyPPp2yW8z0p0sCYZd/em847Tl2dVxJJ1DR+hF+O77T7A==} + engines: {node: '>=10.0.0'} + peerDependencies: + ibm_db: '*' + mariadb: '*' + mysql2: '*' + oracledb: '*' + pg: '*' + pg-hstore: '*' + snowflake-sdk: '*' + sqlite3: '*' + tedious: '*' + peerDependenciesMeta: + ibm_db: + optional: true + mariadb: + optional: true + mysql2: + optional: true + oracledb: + optional: true + pg: + optional: true + pg-hstore: + optional: true + snowflake-sdk: + optional: true + sqlite3: + optional: true + tedious: + optional: true + dependencies: + '@types/debug': 4.1.12 + '@types/validator': 13.12.0 + debug: 4.3.5(supports-color@5.5.0) + dottie: 2.0.6 + inflection: 1.13.4 + lodash: 4.17.21 + moment: 2.30.1 + moment-timezone: 0.5.45 + mysql2: 3.10.1 + pg-connection-string: 2.6.4 + retry-as-promised: 7.0.4 + semver: 7.6.2 + sequelize-pool: 7.1.0 + toposort-class: 1.0.1 + uuid: 8.3.2 + validator: 13.12.0 + wkx: 0.5.0 + transitivePeerDependencies: + - supports-color + dev: false + /serve-static@1.15.0: resolution: {integrity: sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==} engines: {node: '>= 0.8.0'} @@ -643,6 +820,11 @@ packages: semver: 7.6.2 dev: false + /sqlstring@2.3.3: + resolution: {integrity: sha512-qC9iz2FlN7DQl3+wjwn3802RTyjCx7sDvfQEXchwa6CWOx07/WVfh91gBmQ9fahw8snwGEWU3xGzOt4tFyHLxg==} + engines: {node: '>= 0.6'} + dev: false + /statuses@2.0.1: resolution: {integrity: sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==} engines: {node: '>= 0.8'} @@ -667,6 +849,10 @@ packages: engines: {node: '>=0.6'} dev: false + /toposort-class@1.0.1: + resolution: {integrity: sha512-OsLcGGbYF3rMjPUf8oKktyvCiUxSbqMMS39m33MAjLTC1DVIH6x3WSt63/M77ihI09+Sdfk1AXvfhCEeUmC7mg==} + dev: false + /touch@3.1.1: resolution: {integrity: sha512-r0eojU4bI8MnHr8c5bNo7lJDdI2qXlWWJk6a9EAFG7vbhTjElYhBVS3/miuE0uOuoLdb8Mc/rVfsmm6eo5o9GA==} hasBin: true @@ -684,6 +870,10 @@ packages: resolution: {integrity: sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==} dev: false + /undici-types@5.26.5: + resolution: {integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==} + dev: false + /unpipe@1.0.0: resolution: {integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==} engines: {node: '>= 0.8'} @@ -694,7 +884,23 @@ packages: engines: {node: '>= 0.4.0'} dev: false + /uuid@8.3.2: + resolution: {integrity: sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==} + hasBin: true + dev: false + + /validator@13.12.0: + resolution: {integrity: sha512-c1Q0mCiPlgdTVVVIJIrBuxNicYE+t/7oKeI9MWLj3fh/uq2Pxh/3eeWbVZ4OcGW1TUf53At0njHw5SMdA3tmMg==} + engines: {node: '>= 0.10'} + dev: false + /vary@1.1.2: resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==} engines: {node: '>= 0.8'} dev: false + + /wkx@0.5.0: + resolution: {integrity: sha512-Xng/d4Ichh8uN4l0FToV/258EjMGU9MGcA0HV2d9B/ZpZB3lqQm7nkOdZdm5GhKtLLhAE7PiVQwN4eN+2YJJUg==} + dependencies: + '@types/node': 20.14.6 + dev: false diff --git a/server/routes/users.js b/server/routes/users.js index c07ba83..bf73398 100644 --- a/server/routes/users.js +++ b/server/routes/users.js @@ -1,16 +1,29 @@ -import { Router } from "express"; -const router = Router(); +const express = require("express"); +const { Op } = require("sequelize"); +const { User } = require("../models"); +const router = express.Router(); -let usersList = []; - -router.post("/", (req, res) => { +router.post("/", async (req, res) => { let data = req.body; - usersList.push(data); - res.json(data); + let result = await User.create(data); + res.json(result); }); -router.get("/", (req, res) => { - res.json(usersList); +router.get("/", async (req, res) => { + let condition = {}; + let search = req.query.search; + if (search) { + condition[Op.or] = [ + { title: { [Op.like]: `%${search}%` } }, + { description: { [Op.like]: `%${search}%` } }, + ]; + } + + let list = await User.findAll({ + where: condition, + order: [["createdAt", "DESC"]], + }); + res.json(list); }); -export default router; +module.exports = router;