diff --git a/typescript/lunargame/api/db/migrations/create_account.js b/typescript/lunargame/api/db/migrations/create_account.js new file mode 100644 index 0000000..61201d0 --- /dev/null +++ b/typescript/lunargame/api/db/migrations/create_account.js @@ -0,0 +1,26 @@ +// in case i want to change it +// it's alwys a pain to change it everywhere +const tableName = 'account' + +exports.up = knex => { + return knex.schema.createTable(tableName, table => { + // this is the id of the simulation + table.increments() + + // the name of the user + table.text('name').notNullable() + + // the email of the user + table.text('email').notNullable() + + // the password of the user + table.text('password').notNullable() + + // the password encription + table.text('password_encription').notNullable() + }) +} + +exports.down = knex => { + return knex.schema.dropTable(tableName) +} diff --git a/typescript/lunargame/api/db/migrations/create_simulation.js b/typescript/lunargame/api/db/migrations/create_simulation.js deleted file mode 100644 index c775814..0000000 --- a/typescript/lunargame/api/db/migrations/create_simulation.js +++ /dev/null @@ -1,17 +0,0 @@ -// in case i want to change it -// it's alwys a pain to change it everywhere -const tableName = 'simulation' - -exports.up = function up(knex) { - return knex.schema.createTable(tableName, table => { - // this is the id of the simulation - table.increments() - - // this is the actual name of the simulation - table.text('name').notNull() - }) -} - -exports.down = function down(knex) { - return knex.schema.dropTable(tableName) -} diff --git a/typescript/lunargame/api/knexfile.ts b/typescript/lunargame/api/knexfile.ts index 7c87542..7af3558 100644 --- a/typescript/lunargame/api/knexfile.ts +++ b/typescript/lunargame/api/knexfile.ts @@ -10,9 +10,12 @@ const dbFolder = resolve(__dirname, 'db') const testFolder = resolve(__dirname, 'test') // This is used in all configs -const migrations: Config['migrations'] = { - directory: resolve(dbFolder, 'migrations'), - tableName: 'migrations' +const commonConfig: Partial = { + migrations: { + directory: resolve(dbFolder, 'migrations'), + tableName: 'migrations' + }, + useNullAsDefault: true } // This is the confg we are going to esport @@ -25,7 +28,7 @@ const config: Partial> = { connection: { filename: resolve(dbFolder, dbName) }, - migrations, + ...commonConfig, seeds: { directory: resolve(dbFolder, 'seeds') } @@ -35,7 +38,7 @@ const config: Partial> = { connection: { filename: resolve(testFolder, dbName) }, - migrations, + ...commonConfig, seeds: { directory: resolve(testFolder, 'seeds') } diff --git a/typescript/lunargame/api/package-lock.json b/typescript/lunargame/api/package-lock.json index 6c475db..4de6711 100644 --- a/typescript/lunargame/api/package-lock.json +++ b/typescript/lunargame/api/package-lock.json @@ -231,10 +231,6 @@ "minimist": "^1.2.0" } }, - "@eix/utils": { - "version": "github:eix-js/utils#a21d3b755e965e3c1b6022d0ccc8db457e5a706c", - "from": "github:eix-js/utils" - }, "@jest/console": { "version": "24.7.1", "resolved": "https://registry.npmjs.org/@jest/console/-/console-24.7.1.tgz", @@ -569,6 +565,12 @@ "@types/node": "*" } }, + "@types/cookiejar": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@types/cookiejar/-/cookiejar-2.1.1.tgz", + "integrity": "sha512-aRnpPa7ysx3aNW60hTiCtLHlQaIFsXFCgQlpakNgDNVFzbtusSY8PwjAQgRWfSk0ekNoBjO51eQRB6upA9uuyw==", + "dev": true + }, "@types/cookies": { "version": "0.7.2", "resolved": "https://registry.npmjs.org/@types/cookies/-/cookies-0.7.2.tgz", @@ -790,6 +792,25 @@ "integrity": "sha512-l42BggppR6zLmpfU6fq9HEa2oGPEI8yrSPL3GITjfRInppYFahObbIQOQK3UGxEnyQpltZLaPe75046NOZQikw==", "dev": true }, + "@types/superagent": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/@types/superagent/-/superagent-4.1.3.tgz", + "integrity": "sha512-vy2licJQwOXrTAe+yz9SCyUVXAkMgCeDq9VHzS5CWJyDU1g6CI4xKb4d5sCEmyucjw5sG0y4k2/afS0iv/1D0Q==", + "dev": true, + "requires": { + "@types/cookiejar": "*", + "@types/node": "*" + } + }, + "@types/supertest": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/@types/supertest/-/supertest-2.0.8.tgz", + "integrity": "sha512-wcax7/ip4XSSJRLbNzEIUVy2xjcBIZZAuSd2vtltQfRK7kxhx5WMHbLHkYdxN3wuQCrwpYrg86/9byDjPXoGMA==", + "dev": true, + "requires": { + "@types/superagent": "*" + } + }, "@types/tough-cookie": { "version": "2.3.5", "resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-2.3.5.tgz", @@ -1542,6 +1563,11 @@ "safe-buffer": "~5.1.1" } }, + "cookiejar": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/cookiejar/-/cookiejar-2.1.2.tgz", + "integrity": "sha512-Mw+adcfzPxcPeI+0WlvRrr/3lGVO0bD75SxX6811cxSh1Wbxx7xZBGK1eVtDf6si8rg2lhnUjsVLMFMfbRIuwA==" + }, "cookies": { "version": "0.7.3", "resolved": "https://registry.npmjs.org/cookies/-/cookies-0.7.3.tgz", @@ -2244,6 +2270,11 @@ "mime-types": "^2.1.12" } }, + "formidable": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/formidable/-/formidable-1.2.1.tgz", + "integrity": "sha512-Fs9VRguL0gqGHkXS5GQiMCr1VhZBxz0JnJs4JmMp/2jL18Fmbzvv7vOFRU+U8TBkHEE/CX1qDXzJplVULgsLeg==" + }, "fragment-cache": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz", @@ -4707,6 +4738,11 @@ "to-regex": "^3.0.2" } }, + "mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==" + }, "mime-db": { "version": "1.40.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.40.0.tgz", @@ -5590,8 +5626,7 @@ "process-nextick-args": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", - "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", - "dev": true + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" }, "prompts": { "version": "2.2.1", @@ -5708,7 +5743,6 @@ "version": "2.3.6", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", - "dev": true, "requires": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", @@ -6360,7 +6394,6 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dev": true, "requires": { "safe-buffer": "~5.1.0" } @@ -6392,6 +6425,32 @@ "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", "dev": true }, + "superagent": { + "version": "3.8.3", + "resolved": "https://registry.npmjs.org/superagent/-/superagent-3.8.3.tgz", + "integrity": "sha512-GLQtLMCoEIK4eDv6OGtkOoSMt3D+oq0y3dsxMuYuDvaNUvuT8eFBuLmfR0iYYzHC1e8hpzC6ZsxbuP6DIalMFA==", + "requires": { + "component-emitter": "^1.2.0", + "cookiejar": "^2.1.0", + "debug": "^3.1.0", + "extend": "^3.0.0", + "form-data": "^2.3.1", + "formidable": "^1.2.0", + "methods": "^1.1.1", + "mime": "^1.4.1", + "qs": "^6.5.1", + "readable-stream": "^2.3.5" + } + }, + "supertest": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/supertest/-/supertest-4.0.2.tgz", + "integrity": "sha512-1BAbvrOZsGA3YTCWqbmh14L0YEq0EGICX/nBnfkfVJn7SrxQV1I3pMYjSzG9y/7ZU2V9dWqyqk2POwxlb09duQ==", + "requires": { + "methods": "^1.1.2", + "superagent": "^3.8.3" + } + }, "supports-color": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", @@ -6829,8 +6888,7 @@ "util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", - "dev": true + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" }, "util.promisify": { "version": "1.0.0", diff --git a/typescript/lunargame/api/package.json b/typescript/lunargame/api/package.json index 8c3cb37..fe68fc1 100644 --- a/typescript/lunargame/api/package.json +++ b/typescript/lunargame/api/package.json @@ -3,8 +3,9 @@ "version": "1.0.0", "scripts": { "start": "nodemon", - "reset:db": "knex migrate:rollback && knex migrate:latest && knex seed:run", - "test": "cross-env NODE_ENV=test && jest --runInBand" + "reset-db": "knex migrate:rollback && knex migrate:latest && knex seed:run", + "reset-db:test": "(knex migrate:rollback --env test) && (knex migrate:latest --env test) && (knex seed:run --env test)", + "test": "cross-env NODE_ENV=test && jest" }, "main": "index.js", "private": true, @@ -20,6 +21,7 @@ "@types/koa__cors": "^2.2.3", "@types/node": "^12.0.10", "@types/nodemailer": "^6.2.0", + "@types/supertest": "^2.0.8", "@types/uuid": "^3.4.5", "cross-env": "^5.2.0", "jest": "^24.8.0", @@ -30,7 +32,6 @@ "typescript": "^3.5.2" }, "dependencies": { - "@eix/utils": "github:eix-js/utils", "@koa/cors": "^3.0.0", "@sendgrid/mail": "^6.4.0", "bcryptjs": "^2.4.3", @@ -44,6 +45,7 @@ "koa-session-knex-store": "^1.1.2", "nodemailer": "^6.2.1", "pg": "^7.11.0", + "supertest": "^4.0.2", "uuid": "^3.3.2" } } diff --git a/typescript/lunargame/api/src/common/validation/middleware/validate.ts b/typescript/lunargame/api/src/common/validation/middleware/validate.ts index ae22dbc..5089995 100644 --- a/typescript/lunargame/api/src/common/validation/middleware/validate.ts +++ b/typescript/lunargame/api/src/common/validation/middleware/validate.ts @@ -2,6 +2,14 @@ import { ObjectSchema } from 'joi' import { Middleware } from 'koa' import { HttpError } from '../../../modules/network/classes/HttpError' +/** + * Middlware to validate a joi schema against a request + * + * @param schema The joi shcema to use for the validation + * @param field The field to validate the schema against + * + * @throws HttpError if the validation fails + */ export const validate = ( schema: ObjectSchema, field: 'params' | 'body' | 'query' diff --git a/typescript/lunargame/api/src/index.ts b/typescript/lunargame/api/src/index.ts index 75dc738..d5400f4 100644 --- a/typescript/lunargame/api/src/index.ts +++ b/typescript/lunargame/api/src/index.ts @@ -1,29 +1,7 @@ -import Koa from 'koa' -import cors from '@koa/cors' -import parser from 'koa-bodyparser' - -import { config } from 'dotenv' -import { handleError } from './modules/network/middleware/errorHandler' -import { handleSessions } from './modules/network/middleware/handleSessions' -import { router } from './modules/core/router' - -config() +import { app } from './server' const port = process.env.PORT -const app = new Koa() -app.keys = [process.env.secret || 'secret'] - -app.use( - cors({ - credentials: true - }) -) - .use(handleError()) - .use(handleSessions(app)) - .use(parser()) - .use(router.middleware()) - -app.listen(Number(port), () => { +export const server = app.listen(Number(port), () => { console.log(`Listening on port ${port}`) }) diff --git a/typescript/lunargame/api/src/modules/auth/routes/authRoute.test.ts b/typescript/lunargame/api/src/modules/auth/routes/authRoute.test.ts new file mode 100644 index 0000000..5a68404 --- /dev/null +++ b/typescript/lunargame/api/src/modules/auth/routes/authRoute.test.ts @@ -0,0 +1,26 @@ +import supertest from 'supertest' +import { app } from '../../../server' +import { loggedInAgent } from '../../../../test/utils/loggedInAgent' + +describe('The /auth route', () => { + let request = supertest(app.callback()) + + test('should return undefined if the user was not logged in', async () => { + const res = await request.get('/auth') + + expect(res.body.uid).toBe(undefined) + }) + + test.only('should return the uid form the session while logged in', async () => { + const uid = 7 + + const [agent, cookie] = await loggedInAgent( + supertest.agent(app.callback()), + uid + ) + + const res = await agent.get('/auth').set('cookie', cookie) + + expect(res.body.uid).toBe(uid) + }) +}) diff --git a/typescript/lunargame/api/src/modules/auth/routes/authRoute.ts b/typescript/lunargame/api/src/modules/auth/routes/authRoute.ts new file mode 100644 index 0000000..cc193dc --- /dev/null +++ b/typescript/lunargame/api/src/modules/auth/routes/authRoute.ts @@ -0,0 +1,20 @@ +import Router from 'koa-router' + +const router = new Router() + +router.get('/', (context, next) => { + context.body = { + uid: context.session.uid + } + + return next() +}) + +router.post('/login', (context, next) => { + context.session.uid = context.request.body.uid + context.body = {} + + return next() +}) + +export default router diff --git a/typescript/lunargame/api/src/modules/auth/schemas/LoginBody.ts b/typescript/lunargame/api/src/modules/auth/schemas/LoginBody.ts new file mode 100644 index 0000000..c06ae04 --- /dev/null +++ b/typescript/lunargame/api/src/modules/auth/schemas/LoginBody.ts @@ -0,0 +1,7 @@ +import Joi from 'joi' +import { name, password } from './authFields' + +export const LoginBodySchema = Joi.object({ + name, + password +}) diff --git a/typescript/lunargame/api/src/modules/auth/schemas/authFields.ts b/typescript/lunargame/api/src/modules/auth/schemas/authFields.ts new file mode 100644 index 0000000..52c32e4 --- /dev/null +++ b/typescript/lunargame/api/src/modules/auth/schemas/authFields.ts @@ -0,0 +1,20 @@ +import Joi from 'joi' + +export const name = Joi.string() + .alphanum() + .min(3) + .max(30) + .lowercase() + .required() + +export const email = Joi.string() + .email() + .min(3) + .max(30) + .required() + +export const password = Joi.string() + .min(3) + .max(50) + .alphanum() + .required() diff --git a/typescript/lunargame/api/src/modules/core/router.ts b/typescript/lunargame/api/src/modules/core/router.ts index 8cf3c62..49e1cc3 100644 --- a/typescript/lunargame/api/src/modules/core/router.ts +++ b/typescript/lunargame/api/src/modules/core/router.ts @@ -1,7 +1,8 @@ import Router from 'koa-router' +import AuthRouter from '../auth/routes/authRoute' const router = new Router() -router.use('/game') +router.use('/auth', AuthRouter.middleware()) export { router } diff --git a/typescript/lunargame/api/src/modules/network/middleware/handleSessions.ts b/typescript/lunargame/api/src/modules/network/middleware/handleSessions.ts index 6793beb..aafc605 100644 --- a/typescript/lunargame/api/src/modules/network/middleware/handleSessions.ts +++ b/typescript/lunargame/api/src/modules/network/middleware/handleSessions.ts @@ -4,7 +4,7 @@ import knexSessionStore from 'koa-session-knex-store' import { connection } from '../../db/connection' // The store sessions are saved to -const store = knexSessionStore(connection, { +export const sessionStore = knexSessionStore(connection, { createtable: true }) @@ -16,13 +16,12 @@ const store = knexSessionStore(connection, { export const handleSessions = (app: Koa): Middleware => session( { - key: 'something', maxAge: 1000 * 60 * 60 * (24 * 7), overwrite: true, signed: true, rolling: true, renew: false, - store, + store: sessionStore, domain: 'localhost' }, app diff --git a/typescript/lunargame/api/src/server.ts b/typescript/lunargame/api/src/server.ts new file mode 100644 index 0000000..fb580e6 --- /dev/null +++ b/typescript/lunargame/api/src/server.ts @@ -0,0 +1,26 @@ +import Koa from 'koa' +import cors from '@koa/cors' +import parser from 'koa-bodyparser' + +import { config } from 'dotenv' +import { handleError } from './modules/network/middleware/errorHandler' +import { handleSessions } from './modules/network/middleware/handleSessions' +import { router } from './modules/core/router' + +config() + +const app = new Koa() + +app.keys = [process.env.secret || 'secret'] + +app.use( + cors({ + credentials: true + }) +) + .use(handleError()) + .use(parser()) + .use(handleSessions(app)) + .use(router.middleware()) + +export { app } diff --git a/typescript/lunargame/api/test/utils/loggedInAgent.ts b/typescript/lunargame/api/test/utils/loggedInAgent.ts new file mode 100644 index 0000000..34fe56f --- /dev/null +++ b/typescript/lunargame/api/test/utils/loggedInAgent.ts @@ -0,0 +1,24 @@ +import supertest from 'supertest' + +/** + * Helper to get a supertest agent wich is logged in + * + * @param agent The agent to make the request with + * @param uid The uid to use to login + */ +export const loggedInAgent = async ( + agent: supertest.SuperTest, + uid: number +) => { + const response = await agent.post('/auth/login').send({ + uid + }) + + // the cookie to send back + // needs to be set manually due to a bug with jest + const cookie = response.header['set-cookie'] + + expect(response.status).toBe(200) + + return [agent, cookie] +} diff --git a/typescript/lunargame/api/tsconfig.json b/typescript/lunargame/api/tsconfig.json index b930e8c..9d203bd 100644 --- a/typescript/lunargame/api/tsconfig.json +++ b/typescript/lunargame/api/tsconfig.json @@ -8,6 +8,6 @@ "lib": ["es2015", "dom", "dom.iterable", "esnext"], "target": "esnext" }, - "include": ["src"], + "include": ["src", "test"], "exclude": ["node_modules"] }