diff --git a/.github/workflows/node.js.yml b/.github/workflows/node.js.yml index 1f2bf92..f7e60f2 100644 --- a/.github/workflows/node.js.yml +++ b/.github/workflows/node.js.yml @@ -39,7 +39,7 @@ jobs: echo "${{ secrets.SSH_PRIVATE_KEY }}" > ~/.ssh/id_ed25519 echo "${{ secrets.SSH_KNOWN_HOSTS }}" > ~/.ssh/known_hosts - name: Deploy with rsync - run: rsync -rav --delete . musicbox@${{ secrets.MUSICBOX_SERVER_IP }}:/home/musicbox/api/production/ + run: rsync -rav --delete --filter 'protect /config.js' --filter 'protect /node_modules/' . musicbox@${{ secrets.MUSICBOX_SERVER_IP }}:/home/musicbox/api/production/ - name: Install dependencies run: | ssh musicbox@${{ secrets.MUSICBOX_SERVER_IP }} <<'ENDSSH' diff --git a/.gitignore b/.gitignore index ad46b30..b0a879a 100644 --- a/.gitignore +++ b/.gitignore @@ -59,3 +59,5 @@ typings/ # next.js build output .next + +config.js diff --git a/config.js.example b/config.js.example new file mode 100644 index 0000000..205f43e --- /dev/null +++ b/config.js.example @@ -0,0 +1,12 @@ +const config = { + db: { + host: "127.0.0.1", + user: "username", + password: "hunter2", + database: "ircbot", + connectTimeout: 60000 + }, + listPerPage: 100, +}; + +module.exports = config; diff --git a/helper.js b/helper.js new file mode 100644 index 0000000..328680d --- /dev/null +++ b/helper.js @@ -0,0 +1,15 @@ +function getOffset(currentPage = 1, listPerPage) { + return (currentPage - 1) * [listPerPage]; +} + +function emptyOrRows(rows) { + if (!rows) { + return []; + } + return rows; +} + +module.exports = { + getOffset, + emptyOrRows +} diff --git a/package-lock.json b/package-lock.json index bb1af41..6b3e212 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,8 @@ "version": "0.0.0", "license": "MIT", "dependencies": { - "express": "^4.19.2" + "express": "^4.19.2", + "mysql2": "^3.11.0" }, "devDependencies": { "mocha": "^10.2.0", @@ -97,6 +98,14 @@ "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", "dev": true }, + "node_modules/aws-ssl-profiles": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/aws-ssl-profiles/-/aws-ssl-profiles-1.1.1.tgz", + "integrity": "sha512-+H+kuK34PfMaI9PNU/NSjBKL5hh/KDM9J72kwYeYEm0A8B1AC4fuCy3qsjnA7lxklgyXsB68yn8Z2xoZEjgwCQ==", + "engines": { + "node": ">= 6.0.0" + } + }, "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", @@ -388,6 +397,14 @@ "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/depd": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", @@ -670,6 +687,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-caller-file": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", @@ -921,6 +946,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-unicode-supported": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", @@ -976,6 +1006,19 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "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/media-typer": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", @@ -1108,6 +1151,63 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" }, + "node_modules/mysql2": { + "version": "3.11.0", + "resolved": "https://registry.npmjs.org/mysql2/-/mysql2-3.11.0.tgz", + "integrity": "sha512-J9phbsXGvTOcRVPR95YedzVSxJecpW5A5+cQ57rhHIFXteTP10HCs+VBjS7DHIKfEaI1zQ5tlVrquCd64A6YvA==", + "dependencies": { + "aws-ssl-profiles": "^1.1.1", + "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/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/mysql2/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/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/negotiator": { "version": "0.6.3", "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", @@ -1362,6 +1462,11 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" }, + "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/serialize-javascript": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz", diff --git a/package.json b/package.json index 051c5dd..715b4c9 100644 --- a/package.json +++ b/package.json @@ -13,6 +13,7 @@ "supertest": "^6.3.4" }, "dependencies": { - "express": "^4.19.2" + "express": "^4.19.2", + "mysql2": "^3.11.0" } } diff --git a/routes/users.js b/routes/users.js new file mode 100644 index 0000000..7c3a3b2 --- /dev/null +++ b/routes/users.js @@ -0,0 +1,15 @@ +const express = require('express'); +const router = express.Router(); +const users = require('../services/users'); + +/* GET users */ +router.get('/', async function(req, res, next) { + try { + res.json(await users.getMultiple(req.query.page)); + } catch (err) { + console.error(`Error while getting users `, err.message); + next(err); + } +}); + +module.exports = router; diff --git a/server.js b/server.js index a12a560..9e2b997 100644 --- a/server.js +++ b/server.js @@ -3,14 +3,25 @@ const app = express(); const port = process.env.PORT || 5000; +const usersRouter = require("./routes/users"); + app.get("/", (req, res) => { return res.status(200).send({ message: "Hello World!", }); }); +app.use("/users", usersRouter); + +app.use((err, req, res, next) => { + const statusCode = err.statusCode || 500; + console.error(err.message, err.stack); + res.status(statusCode).json({ message: err.message }); + return; +}); + app.listen(port, () => { console.log("Listening on " + port); }); -module.exports = app; \ No newline at end of file +module.exports = app; diff --git a/services/db.js b/services/db.js new file mode 100644 index 0000000..566aed2 --- /dev/null +++ b/services/db.js @@ -0,0 +1,13 @@ +const mysql = require('mysql2/promise'); +const config = require('../config'); + +async function query(sql, params) { + const connection = await mysql.createConnection(config.db); + const [results, ] = await connection.execute(sql, params); + + return results; +} + +module.exports = { + query +} diff --git a/services/users.js b/services/users.js new file mode 100644 index 0000000..e3ff7b8 --- /dev/null +++ b/services/users.js @@ -0,0 +1,22 @@ +const db = require('./db'); +const helper = require('../helper'); +const config = require('../config'); + +async function getMultiple(page = 1){ + const offset = helper.getOffset(page, config.listPerPage); + const rows = await db.query( + `SELECT userId, nickname + FROM user LIMIT ${offset},${config.listPerPage}` + ); + const data = helper.emptyOrRows(rows); + const meta = {page}; + + return { + data, + meta + } +} + +module.exports = { + getMultiple +}