mirror of
https://github.com/pawelmalak/snippet-box.git
synced 2025-12-21 13:23:05 +01:00
Database changes. Added Snippet model and controller
This commit is contained in:
100
package-lock.json
generated
100
package-lock.json
generated
@@ -58,6 +58,11 @@
|
|||||||
"integrity": "sha512-eZxlbI8GZscaGS7kkc/trHTT5xgrjH3/1n2JDwusC9iahPKWMRvRjJSAN5mCXviuTGQ/lHnhvv8Q1YTpnfz9gA==",
|
"integrity": "sha512-eZxlbI8GZscaGS7kkc/trHTT5xgrjH3/1n2JDwusC9iahPKWMRvRjJSAN5mCXviuTGQ/lHnhvv8Q1YTpnfz9gA==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"@types/bluebird": {
|
||||||
|
"version": "3.5.36",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/bluebird/-/bluebird-3.5.36.tgz",
|
||||||
|
"integrity": "sha512-HBNx4lhkxN7bx6P0++W8E289foSu8kO8GCk2unhuVggO+cE7rh9DhZUyPhUxNRG9m+5B5BTKxZQ5ZP92x/mx9Q=="
|
||||||
|
},
|
||||||
"@types/body-parser": {
|
"@types/body-parser": {
|
||||||
"version": "1.19.1",
|
"version": "1.19.1",
|
||||||
"resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.1.tgz",
|
"resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.1.tgz",
|
||||||
@@ -68,6 +73,14 @@
|
|||||||
"@types/node": "*"
|
"@types/node": "*"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"@types/bson": {
|
||||||
|
"version": "4.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/bson/-/bson-4.2.0.tgz",
|
||||||
|
"integrity": "sha512-ELCPqAdroMdcuxqwMgUpifQyRoTpyYCNr1V9xKyF40VsBobsj+BbWNRvwGchMgBPGqkw655ypkjj2MEF5ywVwg==",
|
||||||
|
"requires": {
|
||||||
|
"bson": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
"@types/connect": {
|
"@types/connect": {
|
||||||
"version": "3.4.35",
|
"version": "3.4.35",
|
||||||
"resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.35.tgz",
|
"resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.35.tgz",
|
||||||
@@ -77,6 +90,14 @@
|
|||||||
"@types/node": "*"
|
"@types/node": "*"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"@types/continuation-local-storage": {
|
||||||
|
"version": "3.2.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/continuation-local-storage/-/continuation-local-storage-3.2.3.tgz",
|
||||||
|
"integrity": "sha512-4LYeWblV+6puK9tFGM7Zr4OLZkVXmaL7hUK6/wHwbfwM+q7v+HZyBWTXkNOiC9GqOxv7ehhi5TMCbebZWeVYtw==",
|
||||||
|
"requires": {
|
||||||
|
"@types/node": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
"@types/express": {
|
"@types/express": {
|
||||||
"version": "4.17.13",
|
"version": "4.17.13",
|
||||||
"resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.13.tgz",
|
"resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.13.tgz",
|
||||||
@@ -100,12 +121,26 @@
|
|||||||
"@types/range-parser": "*"
|
"@types/range-parser": "*"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"@types/lodash": {
|
||||||
|
"version": "4.14.173",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.173.tgz",
|
||||||
|
"integrity": "sha512-vv0CAYoaEjCw/mLy96GBTnRoZrSxkGE0BKzKimdR8P3OzrNYNvBgtW7p055A+E8C31vXNUhWKoFCbhq7gbyhFg=="
|
||||||
|
},
|
||||||
"@types/mime": {
|
"@types/mime": {
|
||||||
"version": "1.3.2",
|
"version": "1.3.2",
|
||||||
"resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.2.tgz",
|
"resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.2.tgz",
|
||||||
"integrity": "sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw==",
|
"integrity": "sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"@types/mongodb": {
|
||||||
|
"version": "3.6.20",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/mongodb/-/mongodb-3.6.20.tgz",
|
||||||
|
"integrity": "sha512-WcdpPJCakFzcWWD9juKoZbRtQxKIMYF/JIAM4JrNHrMcnJL6/a2NWjXxW7fo9hxboxxkg+icff8d7+WIEvKgYQ==",
|
||||||
|
"requires": {
|
||||||
|
"@types/bson": "*",
|
||||||
|
"@types/node": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
"@types/node": {
|
"@types/node": {
|
||||||
"version": "16.9.2",
|
"version": "16.9.2",
|
||||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-16.9.2.tgz",
|
"resolved": "https://registry.npmjs.org/@types/node/-/node-16.9.2.tgz",
|
||||||
@@ -123,6 +158,17 @@
|
|||||||
"integrity": "sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==",
|
"integrity": "sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"@types/sequelize": {
|
||||||
|
"version": "4.28.10",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/sequelize/-/sequelize-4.28.10.tgz",
|
||||||
|
"integrity": "sha512-GKbEbl6uyEYTPvU2JZvmqZHfpwTTjaZvNSd2gFJrhcxUL1bcyG7i+S8Od2L0/+skrk2bBINl7J1Sugo0mgIY3g==",
|
||||||
|
"requires": {
|
||||||
|
"@types/bluebird": "*",
|
||||||
|
"@types/continuation-local-storage": "*",
|
||||||
|
"@types/lodash": "*",
|
||||||
|
"@types/validator": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
"@types/serve-static": {
|
"@types/serve-static": {
|
||||||
"version": "1.13.10",
|
"version": "1.13.10",
|
||||||
"resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.13.10.tgz",
|
"resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.13.10.tgz",
|
||||||
@@ -133,6 +179,20 @@
|
|||||||
"@types/node": "*"
|
"@types/node": "*"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"@types/umzug": {
|
||||||
|
"version": "2.3.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/umzug/-/umzug-2.3.2.tgz",
|
||||||
|
"integrity": "sha512-ozfpVpUwzozBVVII2T1iUTByJ1n+xgi3xpjzUnZ50tnAhBCzQ+RiewGgjyzuK2RAb3cug0mqKTvMbvd336lkDw==",
|
||||||
|
"requires": {
|
||||||
|
"@types/mongodb": "^3.6.20",
|
||||||
|
"@types/sequelize": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"@types/validator": {
|
||||||
|
"version": "13.6.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/validator/-/validator-13.6.3.tgz",
|
||||||
|
"integrity": "sha512-fWG42pMJOL4jKsDDZZREnXLjc3UE0R8LOJfARWYg6U966rxDT7TYejYzLnUF5cvSObGg34nd0+H2wHHU5Omdfw=="
|
||||||
|
},
|
||||||
"abbrev": {
|
"abbrev": {
|
||||||
"version": "1.1.1",
|
"version": "1.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz",
|
||||||
@@ -306,6 +366,11 @@
|
|||||||
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
|
||||||
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="
|
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="
|
||||||
},
|
},
|
||||||
|
"base64-js": {
|
||||||
|
"version": "1.5.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
|
||||||
|
"integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA=="
|
||||||
|
},
|
||||||
"bcrypt-pbkdf": {
|
"bcrypt-pbkdf": {
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz",
|
||||||
@@ -330,6 +395,11 @@
|
|||||||
"inherits": "~2.0.0"
|
"inherits": "~2.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"bluebird": {
|
||||||
|
"version": "3.7.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz",
|
||||||
|
"integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg=="
|
||||||
|
},
|
||||||
"body-parser": {
|
"body-parser": {
|
||||||
"version": "1.19.0",
|
"version": "1.19.0",
|
||||||
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz",
|
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz",
|
||||||
@@ -470,6 +540,23 @@
|
|||||||
"fill-range": "^7.0.1"
|
"fill-range": "^7.0.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"bson": {
|
||||||
|
"version": "4.5.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/bson/-/bson-4.5.2.tgz",
|
||||||
|
"integrity": "sha512-8CEMJpwc7qlQtrn2rney38jQSEeMar847lz0LyitwRmVknAW8iHXrzW4fTjHfyWm0E3sukyD/zppdH+QU1QefA==",
|
||||||
|
"requires": {
|
||||||
|
"buffer": "^5.6.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"buffer": {
|
||||||
|
"version": "5.7.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz",
|
||||||
|
"integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==",
|
||||||
|
"requires": {
|
||||||
|
"base64-js": "^1.3.1",
|
||||||
|
"ieee754": "^1.1.13"
|
||||||
|
}
|
||||||
|
},
|
||||||
"bytes": {
|
"bytes": {
|
||||||
"version": "3.1.0",
|
"version": "3.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz",
|
||||||
@@ -1266,6 +1353,11 @@
|
|||||||
"safer-buffer": ">= 2.1.2 < 3"
|
"safer-buffer": ">= 2.1.2 < 3"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"ieee754": {
|
||||||
|
"version": "1.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz",
|
||||||
|
"integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA=="
|
||||||
|
},
|
||||||
"ignore-by-default": {
|
"ignore-by-default": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz",
|
||||||
@@ -2831,6 +2923,14 @@
|
|||||||
"integrity": "sha512-4xfscpisVgqqDfPaJo5vkd+Qd/ItkoagnHpufr+i2QCHBsNYp+G7UAoyFl8aPtx879u38wPV65rZ8qbGZijalA==",
|
"integrity": "sha512-4xfscpisVgqqDfPaJo5vkd+Qd/ItkoagnHpufr+i2QCHBsNYp+G7UAoyFl8aPtx879u38wPV65rZ8qbGZijalA==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"umzug": {
|
||||||
|
"version": "2.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/umzug/-/umzug-2.3.0.tgz",
|
||||||
|
"integrity": "sha512-Z274K+e8goZK8QJxmbRPhl89HPO1K+ORFtm6rySPhFKfKc5GHhqdzD0SGhSWHkzoXasqJuItdhorSvY7/Cgflw==",
|
||||||
|
"requires": {
|
||||||
|
"bluebird": "^3.7.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
"unbox-primitive": {
|
"unbox-primitive": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.1.tgz",
|
||||||
|
|||||||
11
package.json
11
package.json
@@ -2,14 +2,14 @@
|
|||||||
"name": "snippet-hub",
|
"name": "snippet-hub",
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"description": "",
|
"description": "",
|
||||||
"main": "src/server.ts",
|
"main": "build/server.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"init:client": "npm install --prefix=client",
|
"init:client": "npm install --prefix=client",
|
||||||
"init:server": "npm install",
|
"init:server": "npm install",
|
||||||
"init": "npm-run-all -n init:**",
|
"init": "npm-run-all -n init:**",
|
||||||
"dev:client": "npm start --prefix=client",
|
"dev:client": "npm start --prefix=client",
|
||||||
"dev:server": "nodemon src/server.ts",
|
"dev:server": "nodemon",
|
||||||
"dev": "npm-run-all -n dev:**",
|
"dev": "npm-run-all -n --parallel dev:**",
|
||||||
"build:clear": "rm -rf build",
|
"build:clear": "rm -rf build",
|
||||||
"build:tsc": "tsc",
|
"build:tsc": "tsc",
|
||||||
"build": "npm-run-all -n build:**"
|
"build": "npm-run-all -n build:**"
|
||||||
@@ -20,15 +20,18 @@
|
|||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/express": "^4.17.13",
|
"@types/express": "^4.17.13",
|
||||||
"@types/node": "^16.9.2",
|
"@types/node": "^16.9.2",
|
||||||
|
"@types/validator": "^13.6.3",
|
||||||
"nodemon": "^2.0.12",
|
"nodemon": "^2.0.12",
|
||||||
"npm-run-all": "^4.1.5",
|
"npm-run-all": "^4.1.5",
|
||||||
"ts-node": "^10.2.1",
|
"ts-node": "^10.2.1",
|
||||||
"typescript": "^4.4.3"
|
"typescript": "^4.4.3"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@types/umzug": "^2.3.2",
|
||||||
"dotenv": "^10.0.0",
|
"dotenv": "^10.0.0",
|
||||||
"express": "^4.17.1",
|
"express": "^4.17.1",
|
||||||
"sequelize": "^6.6.5",
|
"sequelize": "^6.6.5",
|
||||||
"sqlite3": "^5.0.2"
|
"sqlite3": "^5.0.2",
|
||||||
|
"umzug": "^2.3.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
13
src/controllers/snippets.ts
Normal file
13
src/controllers/snippets.ts
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
import { Request, Response, NextFunction } from 'express';
|
||||||
|
import { asyncWrapper } from '../middleware';
|
||||||
|
import { SnippetModel } from '../models';
|
||||||
|
|
||||||
|
export const createSnippet = asyncWrapper(
|
||||||
|
async (req: Request, res: Response, next: NextFunction): Promise<void> => {
|
||||||
|
const snippet = await SnippetModel.create(req.body);
|
||||||
|
|
||||||
|
res.status(201).json({
|
||||||
|
data: snippet
|
||||||
|
});
|
||||||
|
}
|
||||||
|
);
|
||||||
@@ -1,9 +1,9 @@
|
|||||||
require('ts-node/register');
|
import path from 'path';
|
||||||
import { Sequelize } from 'sequelize';
|
import { Sequelize } from 'sequelize';
|
||||||
import { Umzug, SequelizeStorage } from 'umzug';
|
import Umzug from 'umzug';
|
||||||
import { Logger } from '../utils';
|
import { Logger } from '../utils';
|
||||||
|
|
||||||
const logger = new Logger();
|
const logger = new Logger('db');
|
||||||
|
|
||||||
// DB config
|
// DB config
|
||||||
export const sequelize = new Sequelize({
|
export const sequelize = new Sequelize({
|
||||||
@@ -14,15 +14,21 @@ export const sequelize = new Sequelize({
|
|||||||
|
|
||||||
// Migrations config
|
// Migrations config
|
||||||
const umzug = new Umzug({
|
const umzug = new Umzug({
|
||||||
migrations: { glob: '**/migrations/*.ts' },
|
migrations: {
|
||||||
context: sequelize.getQueryInterface(),
|
path: path.join(__dirname, './migrations'),
|
||||||
storage: new SequelizeStorage({ sequelize }),
|
params: [sequelize.getQueryInterface()],
|
||||||
logger: undefined
|
pattern: /^\d+[\w-]+\.(js|ts)$/
|
||||||
|
},
|
||||||
|
storage: 'sequelize',
|
||||||
|
storageOptions: {
|
||||||
|
sequelize
|
||||||
|
},
|
||||||
|
logging: false
|
||||||
});
|
});
|
||||||
|
|
||||||
export type Migration = typeof umzug._types.migration;
|
|
||||||
|
|
||||||
export const connectDB = async () => {
|
export const connectDB = async () => {
|
||||||
|
const isDev = process.env.NODE_ENV == 'development';
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Create & connect db
|
// Create & connect db
|
||||||
await sequelize.authenticate();
|
await sequelize.authenticate();
|
||||||
@@ -30,15 +36,22 @@ export const connectDB = async () => {
|
|||||||
|
|
||||||
// Check migrations
|
// Check migrations
|
||||||
const pendingMigrations = await umzug.pending();
|
const pendingMigrations = await umzug.pending();
|
||||||
|
|
||||||
if (pendingMigrations.length > 0) {
|
if (pendingMigrations.length > 0) {
|
||||||
logger.log(`Found pending migrations. Executing...`);
|
logger.log(`Found pending migrations. Executing...`);
|
||||||
|
|
||||||
|
if (isDev) {
|
||||||
|
pendingMigrations.forEach(({ file }) =>
|
||||||
|
logger.log(`Executing ${file} migration`, 'DEV')
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
await umzug.up();
|
await umzug.up();
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
logger.log(`Database connection error`, 'ERROR');
|
logger.log(`Database connection error`, 'ERROR');
|
||||||
|
|
||||||
if (process.env.NODE_ENV == 'development') {
|
if (isDev) {
|
||||||
console.log(err);
|
console.log(err);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import { DataTypes, Model } from 'sequelize';
|
import { DataTypes, Model, QueryInterface } from 'sequelize';
|
||||||
import type { Migration } from '../';
|
|
||||||
import {
|
import {
|
||||||
Snippet,
|
Snippet,
|
||||||
SnippetCreationAttributes
|
SnippetCreationAttributes
|
||||||
@@ -7,16 +6,15 @@ import {
|
|||||||
|
|
||||||
const { INTEGER, STRING, DATE } = DataTypes;
|
const { INTEGER, STRING, DATE } = DataTypes;
|
||||||
|
|
||||||
export const up: Migration = async ({
|
export const up = async (queryInterface: QueryInterface): Promise<void> => {
|
||||||
context: queryInterface
|
|
||||||
}): Promise<void> => {
|
|
||||||
await queryInterface.createTable<Model<Snippet, SnippetCreationAttributes>>(
|
await queryInterface.createTable<Model<Snippet, SnippetCreationAttributes>>(
|
||||||
'snippets',
|
'snippets',
|
||||||
{
|
{
|
||||||
id: {
|
id: {
|
||||||
type: INTEGER,
|
type: INTEGER,
|
||||||
allowNull: false,
|
allowNull: false,
|
||||||
primaryKey: true
|
primaryKey: true,
|
||||||
|
autoIncrement: true
|
||||||
},
|
},
|
||||||
title: {
|
title: {
|
||||||
type: STRING,
|
type: STRING,
|
||||||
@@ -38,8 +36,6 @@ export const up: Migration = async ({
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const down: Migration = async ({
|
export const down = async (queryInterface: QueryInterface): Promise<void> => {
|
||||||
context: queryInterface
|
|
||||||
}): Promise<void> => {
|
|
||||||
await queryInterface.dropTable('snippets');
|
await queryInterface.dropTable('snippets');
|
||||||
};
|
};
|
||||||
|
|||||||
8
src/middleware/asyncWrapper.ts
Normal file
8
src/middleware/asyncWrapper.ts
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
import { Request, Response, NextFunction } from 'express';
|
||||||
|
|
||||||
|
type Foo = (req: Request, res: Response, next: NextFunction) => Promise<void>;
|
||||||
|
|
||||||
|
export const asyncWrapper =
|
||||||
|
(foo: Foo) => (req: Request, res: Response, next: NextFunction) => {
|
||||||
|
return Promise.resolve(foo(req, res, next)).catch(next);
|
||||||
|
};
|
||||||
2
src/middleware/index.ts
Normal file
2
src/middleware/index.ts
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
export * from './asyncWrapper';
|
||||||
|
export * from './requireBody';
|
||||||
16
src/middleware/requireBody.ts
Normal file
16
src/middleware/requireBody.ts
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
import { Request, Response, NextFunction } from 'express';
|
||||||
|
|
||||||
|
export const requireBody =
|
||||||
|
(...fields: string[]) =>
|
||||||
|
(req: Request, res: Response, next: NextFunction): void => {
|
||||||
|
const bodyKeys = Object.keys(req.body);
|
||||||
|
const missingKeys: string[] = [];
|
||||||
|
|
||||||
|
fields.forEach(field => {
|
||||||
|
if (!bodyKeys.includes(field)) {
|
||||||
|
missingKeys.push(field);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
next();
|
||||||
|
};
|
||||||
31
src/models/Snippet.ts
Normal file
31
src/models/Snippet.ts
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
import { Model, DataTypes } from 'sequelize';
|
||||||
|
import { sequelize } from '../db';
|
||||||
|
import { Snippet, SnippetCreationAttributes } from '../typescript/interfaces';
|
||||||
|
|
||||||
|
const { INTEGER, STRING, DATE } = DataTypes;
|
||||||
|
|
||||||
|
interface SnippetInstance
|
||||||
|
extends Model<Snippet, SnippetCreationAttributes>,
|
||||||
|
Snippet {}
|
||||||
|
|
||||||
|
export const SnippetModel = sequelize.define<SnippetInstance>('Snippet', {
|
||||||
|
id: {
|
||||||
|
type: INTEGER,
|
||||||
|
primaryKey: true,
|
||||||
|
autoIncrement: true
|
||||||
|
},
|
||||||
|
title: {
|
||||||
|
type: STRING,
|
||||||
|
allowNull: false
|
||||||
|
},
|
||||||
|
language: {
|
||||||
|
type: STRING,
|
||||||
|
allowNull: false
|
||||||
|
},
|
||||||
|
createdAt: {
|
||||||
|
type: DATE
|
||||||
|
},
|
||||||
|
updatedAt: {
|
||||||
|
type: DATE
|
||||||
|
}
|
||||||
|
});
|
||||||
1
src/models/index.ts
Normal file
1
src/models/index.ts
Normal file
@@ -0,0 +1 @@
|
|||||||
|
export * from './Snippet';
|
||||||
7
src/routes/snippets.ts
Normal file
7
src/routes/snippets.ts
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
import { Router } from 'express';
|
||||||
|
import { createSnippet } from '../controllers/snippets';
|
||||||
|
import { requireBody } from '../middleware';
|
||||||
|
|
||||||
|
export const snippetRouter = Router();
|
||||||
|
|
||||||
|
snippetRouter.route('/').post(requireBody('title', 'language'), createSnippet);
|
||||||
@@ -3,16 +3,22 @@ import express from 'express';
|
|||||||
import { Logger } from './utils';
|
import { Logger } from './utils';
|
||||||
import { connectDB } from './db';
|
import { connectDB } from './db';
|
||||||
|
|
||||||
|
// Routers
|
||||||
|
import { snippetRouter } from './routes/snippets';
|
||||||
|
|
||||||
// Env config
|
// Env config
|
||||||
dotenv.config({ path: './src/config/.env' });
|
dotenv.config({ path: './src/config/.env' });
|
||||||
|
|
||||||
const app = express();
|
const app = express();
|
||||||
const logger = new Logger();
|
const logger = new Logger('server');
|
||||||
const PORT = process.env.PORT;
|
const PORT = process.env.PORT;
|
||||||
|
|
||||||
// App config
|
// App config
|
||||||
app.use(express.json());
|
app.use(express.json());
|
||||||
|
|
||||||
|
// Routes
|
||||||
|
app.use('/api/snippets', snippetRouter);
|
||||||
|
|
||||||
(async () => {
|
(async () => {
|
||||||
await connectDB();
|
await connectDB();
|
||||||
|
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
export type ErrorLevel = 'INFO' | 'ERROR' | 'WARN';
|
export type ErrorLevel = 'INFO' | 'ERROR' | 'WARN' | 'DEV';
|
||||||
|
|||||||
@@ -1,8 +1,16 @@
|
|||||||
import { ErrorLevel } from '../typescript/types/ErrorLevel';
|
import { ErrorLevel } from '../typescript/types/ErrorLevel';
|
||||||
|
|
||||||
export class Logger {
|
export class Logger {
|
||||||
|
private namespace: string;
|
||||||
|
|
||||||
|
constructor(namespace: string) {
|
||||||
|
this.namespace = namespace;
|
||||||
|
}
|
||||||
|
|
||||||
public log(message: string, level: ErrorLevel = 'INFO'): void {
|
public log(message: string, level: ErrorLevel = 'INFO'): void {
|
||||||
console.log(`[${this.generateTimestamp()}] [${level}] ${message}`);
|
console.log(
|
||||||
|
`[${this.generateTimestamp()}] [${level}] ${this.namespace}: ${message}`
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private generateTimestamp(): string {
|
private generateTimestamp(): string {
|
||||||
|
|||||||
@@ -26,7 +26,7 @@
|
|||||||
|
|
||||||
/* Strict Type-Checking Options */
|
/* Strict Type-Checking Options */
|
||||||
"strict": true /* Enable all strict type-checking options. */,
|
"strict": true /* Enable all strict type-checking options. */,
|
||||||
// "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */
|
// "noImplicitAny": true /* Raise error on expressions and declarations with an implied 'any' type. */,
|
||||||
// "strictNullChecks": true, /* Enable strict null checks. */
|
// "strictNullChecks": true, /* Enable strict null checks. */
|
||||||
// "strictFunctionTypes": true, /* Enable strict checking of function types. */
|
// "strictFunctionTypes": true, /* Enable strict checking of function types. */
|
||||||
// "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */
|
// "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */
|
||||||
@@ -68,5 +68,5 @@
|
|||||||
"skipLibCheck": true /* Skip type checking of declaration files. */,
|
"skipLibCheck": true /* Skip type checking of declaration files. */,
|
||||||
"forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */
|
"forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */
|
||||||
},
|
},
|
||||||
"exclude": ["client"]
|
"include": ["src"]
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user