Skip to content

Commit

Permalink
Merge branch 'feature/auth' of https://github.com/noahk004/ui-compone…
Browse files Browse the repository at this point in the history
…nt-manager into feature/auth
  • Loading branch information
noahk004 committed Jan 18, 2025
2 parents a36b930 + 590d6f0 commit 08c3096
Show file tree
Hide file tree
Showing 16 changed files with 1,405 additions and 93 deletions.
31 changes: 20 additions & 11 deletions apps/api/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,20 +9,29 @@
"test": "jest --coverage=true"
},
"dependencies": {

"@prisma/client": "^5.10.2",
"@types/bcrypt": "^5.0.2",
"@types/cookie-parser": "^1.4.8",
"@types/cors": "^2.8.17",
"@types/jsonwebtoken": "^9.0.7",
"bcrypt": "^5.1.1",
"cookie-parser": "^1.4.7",
"cors": "^2.8.5",
"dotenv": "^16.4.5",
"express": "^4.18.3",
"jsonwebtoken": "^9.0.2",
"@aws-sdk/client-s3": "^3.717.0",
"@aws-sdk/s3-request-presigner": "^3.717.0",
"@prisma/client": "^6.1.0",
"dotenv": "^16.0.3",
"express": "^4.21.2"
"@aws-sdk/s3-request-presigner": "^3.717.0"
},
"devDependencies": {
"@types/express": "^5.0.0",
"@types/jest": "^29.5.14",
"@types/express": "^4.17.21",
"@types/jest": "^29.5.12",
"jest": "^29.7.0",
"nodemon": "^3.1.9",
"prisma": "^6.1.0",
"ts-jest": "^29.2.5",
"nodemon": "^3.1.0",
"prisma": "^5.10.2",
"ts-jest": "^29.1.2",
"ts-node": "^10.9.2",
"typescript": "^5.7.2"
"typescript": "^5.3.3"
}
}
}
8 changes: 8 additions & 0 deletions apps/api/prisma/migrations/20241223212140_test/migration.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
-- CreateTable
CREATE TABLE "Messages" (
"id" TEXT NOT NULL,
"content" TEXT NOT NULL,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,

CONSTRAINT "Messages_pkey" PRIMARY KEY ("id")
);
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
/*
Warnings:
- You are about to drop the `Messages` table. If the table is not empty, all the data it contains will be lost.
*/
-- DropTable
DROP TABLE "Messages";

-- CreateTable
CREATE TABLE "Users" (
"id" TEXT NOT NULL,
"username" TEXT NOT NULL,
"password" TEXT NOT NULL,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,

CONSTRAINT "Users_pkey" PRIMARY KEY ("id")
);
Original file line number Diff line number Diff line change
@@ -1,10 +1,22 @@
/*
Warnings:
- You are about to drop the `Users` table. If the table is not empty, all the data it contains will be lost.
*/
-- DropTable
DROP TABLE "Users";

-- CreateTable
CREATE TABLE "User" (
"id" SERIAL NOT NULL,
"username" TEXT NOT NULL,
"email" TEXT NOT NULL,
"password" TEXT NOT NULL,
"salt" TEXT NOT NULL,
"profileImage" TEXT,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,

CONSTRAINT "User_pkey" PRIMARY KEY ("id")
);
Expand All @@ -13,50 +25,50 @@ CREATE TABLE "User" (
CREATE TABLE "Component" (
"id" SERIAL NOT NULL,
"title" TEXT NOT NULL,
"description" TEXT,
"description" TEXT NOT NULL,
"type" TEXT NOT NULL,
"codeContentLink" TEXT NOT NULL,
"isPrivate" BOOLEAN NOT NULL DEFAULT false,
"userId" INTEGER NOT NULL,
"codeSource" TEXT NOT NULL,
"isPrivate" BOOLEAN NOT NULL,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
"userId" INTEGER NOT NULL,

CONSTRAINT "Component_pkey" PRIMARY KEY ("id")
);

-- CreateTable
CREATE TABLE "Tag" (
"id" SERIAL NOT NULL,
"name" TEXT NOT NULL,
CREATE TABLE "Like" (
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"userId" INTEGER NOT NULL,
"componentId" INTEGER NOT NULL,

CONSTRAINT "Tag_pkey" PRIMARY KEY ("id")
CONSTRAINT "Like_pkey" PRIMARY KEY ("userId","componentId")
);

-- CreateTable
CREATE TABLE "ComponentTag" (
CREATE TABLE "Download" (
"id" SERIAL NOT NULL,
"downloadedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"userId" INTEGER NOT NULL,
"componentId" INTEGER NOT NULL,
"tagId" INTEGER NOT NULL,

CONSTRAINT "ComponentTag_pkey" PRIMARY KEY ("componentId","tagId")
CONSTRAINT "Download_pkey" PRIMARY KEY ("id")
);

-- CreateTable
CREATE TABLE "Like" (
"userId" INTEGER NOT NULL,
"componentId" INTEGER NOT NULL,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
CREATE TABLE "Tag" (
"id" SERIAL NOT NULL,
"name" TEXT NOT NULL,

CONSTRAINT "Like_pkey" PRIMARY KEY ("userId","componentId")
CONSTRAINT "Tag_pkey" PRIMARY KEY ("id")
);

-- CreateTable
CREATE TABLE "Download" (
"id" SERIAL NOT NULL,
"userId" INTEGER NOT NULL,
CREATE TABLE "ComponentTag" (
"componentId" INTEGER NOT NULL,
"downloadedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"tagId" INTEGER NOT NULL,

CONSTRAINT "Download_pkey" PRIMARY KEY ("id")
CONSTRAINT "ComponentTag_pkey" PRIMARY KEY ("componentId","tagId")
);

-- CreateIndex
Expand All @@ -65,18 +77,9 @@ CREATE UNIQUE INDEX "User_username_key" ON "User"("username");
-- CreateIndex
CREATE UNIQUE INDEX "User_email_key" ON "User"("email");

-- CreateIndex
CREATE UNIQUE INDEX "Tag_name_key" ON "Tag"("name");

-- AddForeignKey
ALTER TABLE "Component" ADD CONSTRAINT "Component_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE;

-- AddForeignKey
ALTER TABLE "ComponentTag" ADD CONSTRAINT "ComponentTag_componentId_fkey" FOREIGN KEY ("componentId") REFERENCES "Component"("id") ON DELETE RESTRICT ON UPDATE CASCADE;

-- AddForeignKey
ALTER TABLE "ComponentTag" ADD CONSTRAINT "ComponentTag_tagId_fkey" FOREIGN KEY ("tagId") REFERENCES "Tag"("id") ON DELETE RESTRICT ON UPDATE CASCADE;

-- AddForeignKey
ALTER TABLE "Like" ADD CONSTRAINT "Like_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE;

Expand All @@ -88,3 +91,9 @@ ALTER TABLE "Download" ADD CONSTRAINT "Download_userId_fkey" FOREIGN KEY ("userI

-- AddForeignKey
ALTER TABLE "Download" ADD CONSTRAINT "Download_componentId_fkey" FOREIGN KEY ("componentId") REFERENCES "Component"("id") ON DELETE RESTRICT ON UPDATE CASCADE;

-- AddForeignKey
ALTER TABLE "ComponentTag" ADD CONSTRAINT "ComponentTag_componentId_fkey" FOREIGN KEY ("componentId") REFERENCES "Component"("id") ON DELETE RESTRICT ON UPDATE CASCADE;

-- AddForeignKey
ALTER TABLE "ComponentTag" ADD CONSTRAINT "ComponentTag_tagId_fkey" FOREIGN KEY ("tagId") REFERENCES "Tag"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
122 changes: 118 additions & 4 deletions apps/api/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,127 @@
import express, { Request, Response } from "express";
const cors = require("cors");
import express, { Request, Response, RequestHandler } from "express";
import { PrismaClient } from "@prisma/client";
import bcrypt from "bcrypt";
import dotenv from "dotenv";
import jwt from "jsonwebtoken";
import cookieParser from 'cookie-parser';

dotenv.config();

const prisma = new PrismaClient();
const app = express();
const PORT = process.env.PORT || 5000;

app.use(cors());
app.use(express.json());
app.use(cookieParser());

const loginHandler: RequestHandler = async (req: Request, res: Response) => {
try {
const { username, password } = req.body;

if (!username || !password) {
res.status(400).json({
error: "Username and password are required",
});
return;
}

const user = await prisma.user.findUnique({
where: { username }
});

if (!user) {
res.status(401).json({ error: "Invalid credentials" });
return;
}

const isValidPassword = await bcrypt.compare(password, user.password);

if (!isValidPassword) {
res.status(401).json({ error: "Invalid credentials" });
return;
}

const token = jwt.sign(
{ userId: user.id, username: user.username },
process.env.JWT_SECRET!,
{ expiresIn: "24h" }
);

res.cookie('token', token, {
httpOnly: true,
secure: process.env.NODE_ENV === 'production',
sameSite: 'none',
maxAge: 24 * 60 * 60 * 1000
});

res.json({
message: 'Login successful'
});

} catch (error) {
console.error('Login error:', error);
res.status(500).json({ error: 'Internal server error' });
}
};

const signupHandler: RequestHandler = async (req: Request, res: Response) => {
try {
const { email, username, password } = req.body;

if (!email || !username || !password) {
res.status(400).json({ error: 'All fields are required' });
return;
}

const existingUser = await prisma.user.findFirst({
where: {
OR: [
{ email },
{ username }
]
}
});

if (existingUser) {
res.status(400).json({
error: existingUser.email === email
? 'Email already in use'
: 'Username already taken'
});
return;
}

const salt = await bcrypt.genSalt(10);
const hashedPassword = await bcrypt.hash(password, salt);

const user = await prisma.user.create({
data: {
email,
username,
password: hashedPassword,
salt
}
});

res.status(201).json({
message: 'User created successfully',
user: {
id: user.id,
username: user.username,
email: user.email
}
});

} catch (error) {
console.error('Signup error:', error);
res.status(500).json({ error: 'Internal server error' });
}
};

app.get("/", (req: Request, res: Response) => {
res.send("this works yippee");
});
app.post("/login", loginHandler);
app.post("/signup", signupHandler);

app.listen(PORT, () => {
console.log(
Expand Down
30 changes: 30 additions & 0 deletions apps/api/src/middleware/auth.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { Request, Response, NextFunction, RequestHandler } from 'express';
import jwt from 'jsonwebtoken';

interface AuthRequest extends Request {
user?: {
userId: string;
username: string;
};
}

interface JwtPayload {
userId: string;
username: string;
}

export const authenticateToken: RequestHandler = (req: AuthRequest, res: Response, next: NextFunction) => {
const token = req.cookies.token;

if (!token) {
return res.status(401).json({ error: 'Authentication required' });
}

try {
const user = jwt.verify(token, process.env.JWT_SECRET!) as JwtPayload;
req.user = user;
next();
} catch (error) {
return res.status(403).json({ error: 'Invalid token' });
}
};
6 changes: 5 additions & 1 deletion apps/web/next.config.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
import type { NextConfig } from "next";
import MiniCssExtractPlugin from "mini-css-extract-plugin";

const nextConfig: NextConfig = {
/* config options here */
webpack: (config) => {
config.plugins.push(new MiniCssExtractPlugin());
return config;
}
};

export default nextConfig;
1 change: 1 addition & 0 deletions apps/web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
"@types/react-dom": "^19",
"eslint": "^9",
"eslint-config-next": "15.1.0",
"mini-css-extract-plugin": "^2.9.2",
"postcss": "^8",
"tailwindcss": "^3.4.1",
"typescript": "^5"
Expand Down
Loading

0 comments on commit 08c3096

Please sign in to comment.