First commit
This commit is contained in:
commit
56f362d189
|
|
@ -0,0 +1,5 @@
|
|||
node_modules
|
||||
# Keep environment variables out of version control
|
||||
.env
|
||||
|
||||
/src/generated/prisma
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
const { createDefaultPreset } = require("ts-jest");
|
||||
|
||||
const tsJestTransformCfg = createDefaultPreset().transform;
|
||||
|
||||
/** @type {import("jest").Config} **/
|
||||
export default {
|
||||
testEnvironment: "node",
|
||||
transform: {
|
||||
...tsJestTransformCfg,
|
||||
},
|
||||
};
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,41 @@
|
|||
{
|
||||
"name": "server",
|
||||
"version": "1.0.0",
|
||||
"type": "module",
|
||||
"main": "index.ts",
|
||||
"scripts": {
|
||||
"start": "tsx src/index.ts",
|
||||
"dev": "nodemon --exec tsx src/index.ts",
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"description": "",
|
||||
"dependencies": {
|
||||
"@prisma/client": "^6.15.0",
|
||||
"@types/jest": "^30.0.0",
|
||||
"@types/supertest": "^6.0.3",
|
||||
"bcrypt": "^6.0.0",
|
||||
"cors": "^2.8.5",
|
||||
"dotenv": "^17.2.1",
|
||||
"express": "^5.1.0",
|
||||
"express-validator": "^7.2.1",
|
||||
"jest": "^30.1.3",
|
||||
"jsonwebtoken": "^9.0.2",
|
||||
"lodash": "^4.17.21",
|
||||
"lodash.merge": "^4.6.2",
|
||||
"morgan": "^1.10.1",
|
||||
"supertest": "^7.1.4",
|
||||
"ts-jest": "^29.4.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/lodash.merge": "^4.6.9",
|
||||
"@types/node": "^24.3.0",
|
||||
"nodemon": "^3.1.10",
|
||||
"prisma": "^6.15.0",
|
||||
"ts-node": "^10.9.2",
|
||||
"tsx": "^4.20.5",
|
||||
"typescript": "^5.9.2"
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,95 @@
|
|||
-- CreateEnum
|
||||
CREATE TYPE "public"."UPDATE_STATUS" AS ENUM ('IN_PROGRESS', 'LIVE', 'DEPRECATED', 'ARCHIVED');
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "public"."Post" (
|
||||
"id" SERIAL NOT NULL,
|
||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updatedAt" TIMESTAMP(3) NOT NULL,
|
||||
"title" VARCHAR(255) NOT NULL,
|
||||
"content" TEXT,
|
||||
"published" BOOLEAN NOT NULL DEFAULT false,
|
||||
"authorId" TEXT NOT NULL,
|
||||
|
||||
CONSTRAINT "Post_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "public"."User" (
|
||||
"id" TEXT NOT NULL,
|
||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updatedAt" TIMESTAMP(3) NOT NULL,
|
||||
"username" TEXT NOT NULL,
|
||||
"password" TEXT NOT NULL,
|
||||
|
||||
CONSTRAINT "User_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "public"."Product" (
|
||||
"id" TEXT NOT NULL,
|
||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"name" TEXT NOT NULL,
|
||||
"belongsToId" TEXT NOT NULL,
|
||||
|
||||
CONSTRAINT "Product_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "public"."Update" (
|
||||
"id" TEXT NOT NULL,
|
||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updatedAt" TIMESTAMP(3) NOT NULL,
|
||||
"title" VARCHAR(255) NOT NULL,
|
||||
"body" TEXT NOT NULL,
|
||||
"status" "public"."UPDATE_STATUS" NOT NULL DEFAULT 'IN_PROGRESS',
|
||||
"version" TEXT,
|
||||
"asset" TEXT NOT NULL,
|
||||
"productId" TEXT NOT NULL,
|
||||
|
||||
CONSTRAINT "Update_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "public"."UpdatePoint" (
|
||||
"id" TEXT NOT NULL,
|
||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updatedAt" TIMESTAMP(3) NOT NULL,
|
||||
"name" VARCHAR(255) NOT NULL,
|
||||
"description" TEXT NOT NULL,
|
||||
"updateId" TEXT NOT NULL,
|
||||
|
||||
CONSTRAINT "UpdatePoint_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "public"."_UpdateToUser" (
|
||||
"A" TEXT NOT NULL,
|
||||
"B" TEXT NOT NULL,
|
||||
|
||||
CONSTRAINT "_UpdateToUser_AB_pkey" PRIMARY KEY ("A","B")
|
||||
);
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "User_username_key" ON "public"."User"("username");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "_UpdateToUser_B_index" ON "public"."_UpdateToUser"("B");
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "public"."Post" ADD CONSTRAINT "Post_authorId_fkey" FOREIGN KEY ("authorId") REFERENCES "public"."User"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "public"."Product" ADD CONSTRAINT "Product_belongsToId_fkey" FOREIGN KEY ("belongsToId") REFERENCES "public"."User"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "public"."Update" ADD CONSTRAINT "Update_productId_fkey" FOREIGN KEY ("productId") REFERENCES "public"."Product"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "public"."UpdatePoint" ADD CONSTRAINT "UpdatePoint_updateId_fkey" FOREIGN KEY ("updateId") REFERENCES "public"."Update"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "public"."_UpdateToUser" ADD CONSTRAINT "_UpdateToUser_A_fkey" FOREIGN KEY ("A") REFERENCES "public"."Update"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "public"."_UpdateToUser" ADD CONSTRAINT "_UpdateToUser_B_fkey" FOREIGN KEY ("B") REFERENCES "public"."User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
# Please do not edit this file manually
|
||||
# It should be added in your version-control system (e.g., Git)
|
||||
provider = "postgresql"
|
||||
|
|
@ -0,0 +1,82 @@
|
|||
// This is your Prisma schema file,
|
||||
// learn more about it in the docs: https://pris.ly/d/prisma-schema
|
||||
|
||||
// Looking for ways to speed up your queries, or scale easily with your serverless or edge functions?
|
||||
// Try Prisma Accelerate: https://pris.ly/cli/accelerate-init
|
||||
|
||||
generator client {
|
||||
provider = "prisma-client-js"
|
||||
output = "../src/generated/prisma"
|
||||
}
|
||||
|
||||
datasource db {
|
||||
provider = "postgresql"
|
||||
url = env("DATABASE_URL")
|
||||
}
|
||||
|
||||
model Post {
|
||||
id Int @id @default(autoincrement())
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
title String @db.VarChar(255)
|
||||
content String?
|
||||
published Boolean @default(false)
|
||||
author User @relation(fields: [authorId], references: [id])
|
||||
authorId String
|
||||
}
|
||||
|
||||
model User {
|
||||
id String @id @default(uuid())
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
username String @unique
|
||||
password String
|
||||
updates Update[]
|
||||
|
||||
Post Post[]
|
||||
|
||||
Product Product[]
|
||||
}
|
||||
|
||||
model Product {
|
||||
id String @id @default(uuid())
|
||||
createdAt DateTime @default(now())
|
||||
name String
|
||||
belongsTo User @relation(fields: [belongsToId], references: [id])
|
||||
belongsToId String
|
||||
updates Update[]
|
||||
}
|
||||
|
||||
enum UPDATE_STATUS {
|
||||
IN_PROGRESS
|
||||
LIVE
|
||||
DEPRECATED
|
||||
ARCHIVED
|
||||
}
|
||||
|
||||
model Update {
|
||||
id String @id @default(uuid())
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime
|
||||
title String @db.VarChar(255)
|
||||
body String
|
||||
status UPDATE_STATUS @default(IN_PROGRESS)
|
||||
version String?
|
||||
asset String
|
||||
|
||||
productId String
|
||||
product Product @relation(fields: [productId], references: [id])
|
||||
updatePoints UpdatePoint[]
|
||||
|
||||
User User[]
|
||||
}
|
||||
|
||||
model UpdatePoint {
|
||||
id String @id @default(uuid())
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime
|
||||
name String @db.VarChar(255)
|
||||
description String
|
||||
updateId String
|
||||
update Update @relation(fields: [updateId], references: [id])
|
||||
}
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
POST http://localhost:3000/user
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"username": "Piddo",
|
||||
"password": "123456789"
|
||||
}
|
||||
|
||||
POST http://localhost:3000/signin
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"username": "Piddo",
|
||||
"password": "123456789"
|
||||
}
|
||||
|
||||
GET http://localhost:3000/health
|
||||
|
||||
GET http://localhost:3000/api/product
|
||||
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjEwZGQ1MmUzLWY2NjEtNGRkNy1hNjIzLTFkNzY0YjQwMmY0YSIsInVzZXJuYW1lIjoiUGlkZG8iLCJpYXQiOjE3NTY5MTA5NTd9.rtktVNT5uofPuSJ5nkz6J19iCRnC_qQ35oCuN5VgfBI
|
||||
|
|
@ -0,0 +1,50 @@
|
|||
GET http://localhost:3000/api/product
|
||||
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjAyNDAzNGRjLTVjZmEtNDk2Mi1iOTA5LTU2MmUwY2M0OGIyZCIsInVzZXJuYW1lIjoiRXBocmFpbSIsImlhdCI6MTc1NjkwODI5NH0.gLosNyt2gISTlHLO2jRtE1a07lgHJukJ34i7jHOwkAM
|
||||
|
||||
POST http://localhost:3000/user
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"username": "Y",
|
||||
"password": "t@no2500"
|
||||
}
|
||||
|
||||
POST http://localhost:3000/user
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"username": "Paschal",
|
||||
"password": "M@no2500pasc"
|
||||
}
|
||||
|
||||
POST http://localhost:3000/user
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"username": "Ephraim",
|
||||
"password": "M@no474pasc"
|
||||
}
|
||||
|
||||
POST http://localhost:3000/signin
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"username": "kiddoh",
|
||||
"password": "M@no2500"
|
||||
}
|
||||
|
||||
POST http://localhost:3000/signin
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"username": "Paschal",
|
||||
"password": "M@no2500pasc"
|
||||
}
|
||||
|
||||
POST http://localhost:3000/signin
|
||||
Content-Type: application/json
|
||||
{
|
||||
"username": "Ephraim",
|
||||
"password": "M@no474pasc"
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
import merge from "lodash.merge"
|
||||
|
||||
const stage = process.env.STAGE || "local";
|
||||
|
||||
let envConfig;
|
||||
|
||||
if(stage=== "production"){
|
||||
envConfig = require("./prod").default;
|
||||
}
|
||||
else if( stage==="staging") {
|
||||
envConfig = require("./local").default;
|
||||
}
|
||||
else{
|
||||
envConfig = require("./local").default;
|
||||
}
|
||||
|
||||
const defaultConfig = {
|
||||
stage,
|
||||
dbUrl: process.env.DATABASE_URL,
|
||||
jwtSecret:process.env.JWT_SECRET,
|
||||
logging:false,
|
||||
|
||||
}
|
||||
export default merge(defaultConfig, envConfig);
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
import { PrismaClient } from "./generated/prisma";
|
||||
|
||||
const prisma = new PrismaClient();
|
||||
|
||||
export default prisma;
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
import prisma from "../db";
|
||||
import { Request, Response } from "express";
|
||||
|
||||
export const getPosts = async (req:Request, res:Response) => {
|
||||
const user = await prisma.user.findUnique({
|
||||
where: { id: req.user.id },
|
||||
include: { Post: true },
|
||||
});
|
||||
res.json({data: user.Post});
|
||||
}
|
||||
|
||||
export const getPostById = async (req:Request, res:Response) => {
|
||||
const post = await prisma.post.findUnique({
|
||||
where: {id: req.params.id, authorId: req.user.id},
|
||||
});
|
||||
res.json({data: post});
|
||||
}
|
||||
|
||||
export const createPost = async (req:Request, res:Response) => {
|
||||
const post = await prisma.post.create({
|
||||
data: {
|
||||
title: req.body.title,
|
||||
content: req.body.content,
|
||||
authorId: req.user.id,
|
||||
}
|
||||
});
|
||||
res.json({data: post});
|
||||
}
|
||||
|
||||
export const updatePost = async (req:Request, res:Response) => {
|
||||
const post = await prisma.post.update({
|
||||
where: {id: req.params.id, authorId: req.user.id},
|
||||
data: {
|
||||
title: req.body.title,
|
||||
content: req.body.content,
|
||||
}
|
||||
});
|
||||
res.json({data: post});
|
||||
}
|
||||
|
||||
export const deletePost = async (req:Request, res:Response) => {
|
||||
const post = await prisma.post.delete({
|
||||
where: {id: req.params.id, authorId: req.user.id},
|
||||
});
|
||||
res.json({data: post});
|
||||
}
|
||||
|
|
@ -0,0 +1,48 @@
|
|||
import prisma from "../db";
|
||||
import { Request, Response } from "express";
|
||||
|
||||
export const getProducts = async (req:Request, res:Response) => {
|
||||
const user = await prisma.user.findUnique({
|
||||
where: { id: req.user.id },
|
||||
include: { Product: true },
|
||||
});
|
||||
res.json({data: user.Product});
|
||||
}
|
||||
|
||||
export const getProductById = async (req:Request, res:Response) => {
|
||||
const product = await prisma.product.findUnique({
|
||||
where: {id: req.params.id,belongsToId: req.user.id},
|
||||
});
|
||||
res.json({data: product});
|
||||
}
|
||||
|
||||
export const createProduct = async (req:Request, res:Response) => {
|
||||
const product = await prisma.product.create({
|
||||
data: {
|
||||
name: req.body.name,
|
||||
belongsToId: req.user.id,
|
||||
}
|
||||
});
|
||||
res.json({data: product});
|
||||
}
|
||||
|
||||
export const updateProduct = async (req:Request, res:Response) => {
|
||||
const product = await prisma.product.update({
|
||||
where: {id: req.params.id, belongsToId: req.user.id},
|
||||
data: {
|
||||
name: req.body.name,
|
||||
}
|
||||
});
|
||||
res.json({data: product});
|
||||
}
|
||||
|
||||
export const deleteProduct = async (req:Request, res:Response) => {
|
||||
const product = await prisma.product.delete({
|
||||
where: {id: req.params.id, belongsToId: req.user.id},
|
||||
});
|
||||
res.json({data: product});
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,82 @@
|
|||
import prisma from "../db";
|
||||
import { Request, Response } from "express";
|
||||
|
||||
export const getUpdates = async (req:Request, res:Response) => {
|
||||
const products = await prisma.product.findMany({
|
||||
where: {belongsToId: req.user.id},
|
||||
include: {updates: true}
|
||||
});
|
||||
const updates = products.reduce((allUpdates, product) => {
|
||||
return [...allUpdates, ...product.updates]
|
||||
}, []);
|
||||
res.json({data: updates});
|
||||
}
|
||||
|
||||
export const getUpdateById = async (req:Request, res:Response) => {
|
||||
const update = await prisma.update.findUnique({
|
||||
where: {id: req.params.id}
|
||||
});
|
||||
res.json({data: update});
|
||||
}
|
||||
|
||||
export const createUpdate = async (req:Request, res:Response) => {
|
||||
const product = await prisma.product.findUnique({
|
||||
where: {id: req.body.productId}
|
||||
});
|
||||
|
||||
if (!product || product.belongsToId !== req.user.id) {
|
||||
return res.status(401).json({message: "Unauthorized"});
|
||||
}
|
||||
|
||||
const update = await prisma.update.create({
|
||||
data: req.body
|
||||
});
|
||||
res.json({data: update});
|
||||
}
|
||||
|
||||
export const updateUpdate = async (req:Request, res:Response) => {
|
||||
const products = await prisma.product.findMany({
|
||||
where: {belongsToId: req.user.id},
|
||||
include: {updates: true}
|
||||
});
|
||||
|
||||
const updates = products.reduce((allUpdates, product) => {
|
||||
return [...allUpdates, ...product.updates]
|
||||
}, []);
|
||||
|
||||
const match = updates.find(update => update.id === req.params.id);
|
||||
|
||||
if (!match) {
|
||||
return res.status(401).json({message: "Unauthorized"});
|
||||
}
|
||||
|
||||
const updatedUpdate = await prisma.update.update({
|
||||
where: {id: req.params.id},
|
||||
data: req.body
|
||||
});
|
||||
|
||||
res.json({data: updatedUpdate});
|
||||
}
|
||||
|
||||
export const deleteUpdate = async (req:Request, res:Response) => {
|
||||
const products = await prisma.product.findMany({
|
||||
where: {belongsToId: req.user.id},
|
||||
include: {updates: true}
|
||||
});
|
||||
|
||||
const updates = products.reduce((allUpdates, product) => {
|
||||
return [...allUpdates, ...product.updates]
|
||||
}, []);
|
||||
|
||||
const match = updates.find(update => update.id === req.params.id);
|
||||
|
||||
if (!match) {
|
||||
return res.status(401).json({message: "Unauthorized"});
|
||||
}
|
||||
|
||||
const deleted = await prisma.update.delete({
|
||||
where: {id: req.params.id}
|
||||
});
|
||||
|
||||
res.json({data: deleted});
|
||||
}
|
||||
|
|
@ -0,0 +1,68 @@
|
|||
import prisma from "../db";
|
||||
import { Request, Response } from "express";
|
||||
|
||||
export const getUpdatePoints = async (req:Request, res:Response) => {
|
||||
const update = await prisma.update.findUnique({
|
||||
where: {id: req.body.updateId},
|
||||
include: {updatePoints: true}
|
||||
});
|
||||
res.json({data: update.updatePoints});
|
||||
}
|
||||
|
||||
export const getUpdatePointById = async (req:Request, res:Response) => {
|
||||
const updatePoint = await prisma.updatePoint.findUnique({
|
||||
where: {id: req.params.id}
|
||||
});
|
||||
res.json({data: updatePoint});
|
||||
}
|
||||
|
||||
export const createUpdatePoint = async (req:Request, res:Response) => {
|
||||
const update = await prisma.update.findUnique({
|
||||
where: {id: req.body.updateId},
|
||||
include: {product: true}
|
||||
});
|
||||
|
||||
if (!update || update.product.belongsToId !== req.user.id) {
|
||||
return res.status(401).json({message: "Unauthorized"});
|
||||
}
|
||||
|
||||
const updatePoint = await prisma.updatePoint.create({
|
||||
data: req.body
|
||||
});
|
||||
res.json({data: updatePoint});
|
||||
}
|
||||
|
||||
export const updateUpdatePoint = async (req:Request, res:Response) => {
|
||||
const updatePoint = await prisma.updatePoint.findUnique({
|
||||
where: {id: req.params.id},
|
||||
include: {update: {include: {product: true}}}
|
||||
});
|
||||
|
||||
if (!updatePoint || updatePoint.update.product.belongsToId !== req.user.id) {
|
||||
return res.status(401).json({message: "Unauthorized"});
|
||||
}
|
||||
|
||||
const updatedUpdatePoint = await prisma.updatePoint.update({
|
||||
where: {id: req.params.id},
|
||||
data: req.body
|
||||
});
|
||||
|
||||
res.json({data: updatedUpdatePoint});
|
||||
}
|
||||
|
||||
export const deleteUpdatePoint = async (req:Request, res:Response) => {
|
||||
const updatePoint = await prisma.updatePoint.findUnique({
|
||||
where: {id: req.params.id},
|
||||
include: {update: {include: {product: true}}}
|
||||
});
|
||||
|
||||
if (!updatePoint || updatePoint.update.product.belongsToId !== req.user.id) {
|
||||
return res.status(401).json({message: "Unauthorized"});
|
||||
}
|
||||
|
||||
const deleted = await prisma.updatePoint.delete({
|
||||
where: {id: req.params.id}
|
||||
});
|
||||
|
||||
res.json({data: deleted});
|
||||
}
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
import prisma from "../db";
|
||||
import { comparePasswords, createJWT, hashPassword } from "../module/auth";
|
||||
|
||||
export const createNewUser = async (req, res) => {
|
||||
const hash = await hashPassword(req.body.password);
|
||||
|
||||
const user = await prisma.user.create({
|
||||
data: {
|
||||
username: req.body.username,
|
||||
password: hash,
|
||||
}
|
||||
});
|
||||
|
||||
const token = createJWT(user);
|
||||
res.json({ user, token });
|
||||
}
|
||||
|
||||
export const signIn = async (req, res) => {
|
||||
const user = await prisma.user.findUnique({
|
||||
where: {
|
||||
username: req.body.username,
|
||||
}
|
||||
});
|
||||
|
||||
if (!user) {
|
||||
return res.status(401).json({ error: 'User not found' });
|
||||
}
|
||||
|
||||
const isValid = await comparePasswords(req.body.password, user.password);
|
||||
|
||||
if (!isValid) {
|
||||
return res.status(401).json({ error: 'Invalid password' });
|
||||
}
|
||||
|
||||
const token = createJWT(user);
|
||||
res.status(200).json({ token });
|
||||
};
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
import app from './server';
|
||||
import dotenv from 'dotenv';
|
||||
|
||||
dotenv.config();
|
||||
|
||||
app.listen(process.env.PORT || 3000, () => {
|
||||
console.log(`Server is running on port ${process.env.PORT || 3000}`);
|
||||
});
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
import jwt from 'jsonwebtoken';
|
||||
import * as bcrypt from 'bcrypt';
|
||||
|
||||
export const createJWT = (user) => {
|
||||
const token = jwt.sign(
|
||||
{ id: user.id, username: user.username },
|
||||
process.env.JWT_SECRET,
|
||||
);
|
||||
return token;
|
||||
}
|
||||
|
||||
export const comparePasswords = (password: string, hash: string) => {
|
||||
return bcrypt.compare(password, hash);
|
||||
};
|
||||
|
||||
export const hashPassword = (password: string) => {
|
||||
return bcrypt.hash(password, 10);
|
||||
}
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
import { NextFunction, Request, Response } from 'express';
|
||||
import jwt from 'jsonwebtoken';
|
||||
|
||||
export const protect = (req: Request, res: Response, next: NextFunction) => {
|
||||
const bearer = req.headers.authorization;
|
||||
if(!bearer){
|
||||
return res.status(401).json({message: 'Unauthorized'});
|
||||
}
|
||||
|
||||
const [, token] = bearer.split(" ");
|
||||
if(!token){
|
||||
console.log("No token found");
|
||||
return res.status(401).json({message: 'Unauthorized'});
|
||||
}
|
||||
|
||||
const jwtSecret = process.env.JWT_SECRET;
|
||||
if(!jwtSecret){
|
||||
console.log("JWT_SECRET not defined in environment variables");
|
||||
return res.status(500).json({message: 'Internal server error'});
|
||||
}
|
||||
|
||||
try {
|
||||
const payload = jwt.verify(token, jwtSecret);
|
||||
req.user = payload;
|
||||
console.log(payload);
|
||||
next();
|
||||
} catch (error) {
|
||||
console.log("JWT verification failed:", error);
|
||||
return res.status(401).json({message: 'Unauthorized'});
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
import { NextFunction, Request, Response } from 'express';
|
||||
|
||||
export const healthCheck = (req: Request, res: Response, next: NextFunction) => {
|
||||
// res.status(200).json({status: 'OK', message: 'Server is healthy'});
|
||||
console.log(new Date(), 'Health check endpoint hit');
|
||||
next();
|
||||
}
|
||||
|
|
@ -0,0 +1,48 @@
|
|||
import { NextFunction, Request, Response} from 'express';
|
||||
import { body, validationResult } from "express-validator";
|
||||
|
||||
export const validateRequestUpdate =[
|
||||
body("title").isString().isLength({min: 2, max: 255}),
|
||||
body("body").isString().isLength({min: 2}),
|
||||
body("version").optional().isString(),
|
||||
body("asset").isString().isLength({min:1, max:5}),
|
||||
(req: Request, res: Response, next: NextFunction) => {
|
||||
const errors = validationResult(req);
|
||||
if (!errors.isEmpty()){
|
||||
return res.status(400).json({errors: errors.array()});
|
||||
}
|
||||
next();
|
||||
}];
|
||||
|
||||
export const validateRequestProduct =[
|
||||
body("name").isString().isLength({min: 2}),
|
||||
(req: Request, res: Response, next: NextFunction) => {
|
||||
const errors = validationResult(req);
|
||||
if (!errors.isEmpty()){
|
||||
return res.status(400).json({errors: errors.array()});
|
||||
}
|
||||
next();
|
||||
}];
|
||||
|
||||
export const validateRequestPost = [
|
||||
body("title").isString().isLength({min: 2, max: 255}),
|
||||
body("content").optional().isString(),
|
||||
(req: Request, res: Response, next: NextFunction) => {
|
||||
const errors = validationResult(req);
|
||||
if (!errors.isEmpty()){
|
||||
return res.status(400).json({errors: errors.array()});
|
||||
}
|
||||
next();
|
||||
}
|
||||
];
|
||||
|
||||
export const validateRequestUpdatePoint =[
|
||||
body("name").isString().isLength({min: 2, max: 255}),
|
||||
body("description").isString().isLength({min: 2}),
|
||||
(req: Request, res: Response, next: NextFunction) => {
|
||||
const errors = validationResult(req);
|
||||
if (!errors.isEmpty()){
|
||||
return res.status(400).json({errors: errors.array()});
|
||||
}
|
||||
next();
|
||||
}];
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
import { Router } from "express";
|
||||
import { body, validationResult } from "express-validator";
|
||||
import { getProducts, getProductById, createProduct, updateProduct, deleteProduct } from "./handlers/product";
|
||||
import { getPosts, getPostById, createPost, updatePost, deletePost } from "./handlers/post";
|
||||
import { getUpdates, getUpdateById, createUpdate, updateUpdate, deleteUpdate } from "./handlers/update";
|
||||
import { getUpdatePoints, getUpdatePointById, createUpdatePoint, updateUpdatePoint, deleteUpdatePoint } from "./handlers/updatepoint";
|
||||
import { validateRequestProduct, validateRequestUpdate, validateRequestUpdatePoint, validateRequestPost } from "./module/validateMiddleware";
|
||||
|
||||
const router = Router();
|
||||
|
||||
|
||||
router.get("/product", getProducts);
|
||||
|
||||
|
||||
router.get("/product/:id", getProductById);
|
||||
|
||||
router.post("/product",validateRequestProduct, createProduct);
|
||||
router.put("/product/:id",validateRequestProduct,updateProduct);
|
||||
router.delete("/product/:id", deleteProduct);
|
||||
|
||||
router.get("/update", getUpdates);
|
||||
router.get("/update/:id", getUpdateById);
|
||||
router.post("/update",validateRequestUpdate, createUpdate);
|
||||
router.put("/update/:id",validateRequestUpdate, updateUpdate);
|
||||
router.delete("/update/:id", deleteUpdate);
|
||||
|
||||
router.get("/updatepoint", getUpdatePoints);
|
||||
router.get("/updatepoint/:id", getUpdatePointById);
|
||||
router.post("/updatepoint", validateRequestUpdatePoint, createUpdatePoint);
|
||||
router.put("/updatepoint/:id", validateRequestUpdatePoint, updateUpdatePoint);
|
||||
router.delete("/updatepoint/:id", deleteUpdatePoint);
|
||||
|
||||
router.get("/post", getPosts);
|
||||
router.get("/post/:id", getPostById);
|
||||
router.post("/post", validateRequestPost, createPost);
|
||||
router.put("/post/:id", validateRequestPost, updatePost);
|
||||
router.delete("/post/:id", deletePost);
|
||||
|
||||
export default router;
|
||||
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
import cors from 'cors';
|
||||
import express from 'express';
|
||||
import morgan from 'morgan';
|
||||
import { createNewUser, signIn } from './handlers/user';
|
||||
import { protect } from './module/authMiddleware';
|
||||
import { healthCheck } from './module/healthMiddleware';
|
||||
import router from './router';
|
||||
|
||||
|
||||
const app = express();
|
||||
// app.get("/", (req, res) => {
|
||||
// console.log('Request received');
|
||||
// res.status(200);
|
||||
// res.json({ message: 'Hello, Dropping Zone' });
|
||||
// });
|
||||
|
||||
app.use(cors());
|
||||
app.use(morgan('dev'));
|
||||
app.use(express.json());
|
||||
app.use(express.urlencoded({ extended: true }));
|
||||
|
||||
app.get('/health',healthCheck, (req, res) => {
|
||||
res.json({"ping": "pong"});
|
||||
});
|
||||
|
||||
app.use('/api', protect, router);
|
||||
app.post('/user', createNewUser)
|
||||
app.post('/signin', signIn)
|
||||
|
||||
|
||||
export default app;
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"sourceMap": true,
|
||||
"outDir": "dist",
|
||||
"strict": false,
|
||||
"lib":["esnext"],
|
||||
"esModuleInterop": true,
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue