Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
78 changes: 78 additions & 0 deletions .github/workflows/pipeline.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
name: pipeline
on:
push:
branches:
- "deployment"

permissions:
contents: read
packages: write

jobs:
build-and-push-image:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4

- name: Log in to the Container registry
uses: docker/login-action@v3
with:
registry: https://ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Set up QEMU
uses: docker/setup-qemu-action@v3

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3

- name: Build and push frontend Docker image
uses: docker/build-push-action@v6
with:
context: ./client
push: true
tags: |
ghcr.io/manushpatell/campuscart/client:latest
ghcr.io/manushpatell/campuscart/client:${{ github.sha }}
platforms: linux/amd64,linux/arm64
cache-from: type=registry,ref=ghcr.io/manushpatell/campuscart/client:latest
cache-to: type=registry,ref=ghcr.io/manushpatell/campuscart/client:latest,mode=max


- name: Build and push backend Docker image
uses: docker/build-push-action@v6
with:
context: ./backend
push: true
tags: |
ghcr.io/manushpatell/campuscart/backend:latest
ghcr.io/manushpatell/campuscart/backend:${{ github.sha }}
platforms: linux/amd64,linux/arm64
cache-from: type=registry,ref=ghcr.io/manushpatell/campuscart/backend:latest
cache-to: type=registry,ref=ghcr.io/manushpatell/campuscart/backend:latest,mode=max


deploy:
runs-on: ubuntu-latest
needs:
- build-and-push-image
steps:
- name: Checkout code
uses: actions/checkout@v2

- name: create env file
run: |
echo "GIT_COMMIT_HASH=${{ github.sha }}" >> ./envfile


# - name: Docker Stack Deploy
# uses: cssnr/stack-deploy-action@v1
# with:
# name: zenfulstats
# file: docker-stack.yaml
# host: zenful.site
# user: deploy
# ssh_key: ${{ secrets.DEPLOY_SSH_PRIVATE_KEY }}
# env_file: ./envfile
4 changes: 4 additions & 0 deletions backend/.dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
node_modules
dist
Dockerfile
.dockerignore
2 changes: 1 addition & 1 deletion backend/.gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
node_modules
dist/**
dist
.env
25 changes: 25 additions & 0 deletions backend/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
FROM node:20
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .

RUN npm run build

ENV NODE_ENV=production
ENV PORT=3001
EXPOSE 3001

ENV FRONTEND_ORIGIN=http://client:80
ENV DB_PORT=5432
ENV POSTGRES_HOST=host.docker.internal

# Use docker secrets
ENV DB_PASSWORD=mysecretpassword
ENV DB_USERNAME=postgres
ENV ACCESS_TOKEN_SECRET=ASJKHFBA!IASKDJFBAS
ENV REFRESH_TOKEN_SECRET=ASJKHFB@21234314!asdsdaffasIASKDJFBAS


CMD ["npm", "run", "start"]

28 changes: 28 additions & 0 deletions backend/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion backend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
"name": "backend",
"version": "1.0.0",
"main": "dist/app.js",
"type": "module",
"scripts": {
"docker-build:dev": "docker build -t backend-img:0.0.1 -f Dockerfile.dev .",
"docker-run:dev": "docker run -it -p 3000:3000 --name backend-container -v ./:/app -v my-node-modules:/app/node_modules backend-img:0.0.1",
Expand All @@ -26,6 +25,7 @@
"dotenv": "^16.5.0",
"express": "^4.21.2",
"express-async-errors": "^3.1.1",
"express-rate-limit": "^8.0.1",
"jsonwebtoken": "^9.0.2",
"morgan": "^1.10.1",
"multer": "^2.0.2",
Expand Down
27 changes: 18 additions & 9 deletions backend/src/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,19 +10,26 @@ import bodyParser from "body-parser";
import cookieParser from "cookie-parser";

import swaggerUi from "swagger-ui-express";
import { swaggerSpec } from "./swagger.ts";
import { swaggerSpec } from "./swagger";

import userRoutes from "./routes/userRoutes.ts";
import rentalRoutes from "./routes/rentalRoutes.ts";
import authRoutes from "./routes/authRoutes.ts";
import textbookRoutes from "./routes/textbookRoute.ts";
import miscRoutes from "./routes/miscRoutes.ts";
import uploadRoutes from "./routes/uploadRoutes.ts";
import userRoutes from "./routes/userRoutes";
import rentalRoutes from "./routes/rentalRoutes";
import authRoutes from "./routes/authRoutes";
import textbookRoutes from "./routes/textbookRoute";
import miscRoutes from "./routes/miscRoutes";
import uploadRoutes from "./routes/uploadRoutes";

import cors from "cors";
import morgan from "morgan";
import RateLimit from "express-rate-limit";

const limiter = RateLimit({
windowMs: 1 * 60 * 1000,
max: 30,
});

const app = express();
app.use(limiter);
const PORT = process.env.PORT || 3001;
const NODE_ENV = process.env.NODE_ENV || "development";
const FRONTEND_ORIGIN = process.env.FRONTEND_ORIGIN || "http://localhost:4321";
Expand Down Expand Up @@ -59,7 +66,8 @@ app.use((err: Error, req: Request, res: Response, next: NextFunction) => {
}
});

app.use("/docs", swaggerUi.serve, swaggerUi.setup(swaggerSpec));
if (NODE_ENV !== "production")
app.use("/docs", swaggerUi.serve, swaggerUi.setup(swaggerSpec));

app.get("/", (req: Request, res: Response) => {
res.send(
Expand Down Expand Up @@ -94,5 +102,6 @@ app.use((req, res, next) => {

app.listen(PORT, () => {
console.log(`Server running for ${NODE_ENV} at http://localhost:${PORT}`);
console.log(`JsDoc running on http://localhost:${PORT}/docs`);
if (NODE_ENV !== "production")
console.log(`JsDoc running on http://localhost:${PORT}/docs`);
});
13 changes: 8 additions & 5 deletions backend/src/controllers/authController.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { type UserPayload } from "../types/user.ts";
import { type UserPayload } from "../types/user";
import { type Request, type Response } from "express";
import bcrypt from "bcrypt";
import jwt from "jsonwebtoken";
Expand All @@ -8,8 +8,8 @@ import {
deleteToken,
isRefreshTokenRegistered,
deleteTokenById,
} from "../models/tokenModel.ts";
import { findUserByEmail, findUserById } from "../models/userModel.ts";
} from "../models/tokenModel";
import { findUserByEmail, findUserById } from "../models/userModel";

export async function getUserInformation(req: Request, res: Response) {
const { id } = req.user!; // This route is protected which guarantees req.user exists
Expand Down Expand Up @@ -66,13 +66,15 @@ export async function postLoginUser(req: Request, res: Response) {
.cookie("accessToken", accessToken, {
httpOnly: true,
secure: process.env.NODE_ENV === "production",
sameSite: "lax",
sameSite: "none",
path: "/",
expires: new Date(Date.now() + 1800000), // expires in 30 minutes
})
.cookie("refreshToken", refreshToken, {
httpOnly: true,
secure: process.env.NODE_ENV === "production",
sameSite: "lax",
sameSite: "none",
path: "/",
expires: new Date(Date.now() + 604800000), // expires in 7 days
})
.json({});
Expand All @@ -97,6 +99,7 @@ export async function postRefreshToken(req: Request, res: Response) {
jwt.verify(
refreshToken,
process.env.REFRESH_TOKEN_SECRET as string,
//@ts-expect-error Not sure why typescript doesn't like this
(err: unknown, user: UserPayload) => {
if (err) {
res.status(403).json({ error: err });
Expand Down
1 change: 0 additions & 1 deletion backend/src/controllers/textbookController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import {
findAllTextbooks,
findTextbook,
removeTextbook,
removeTextbook,
} from "../models/textbookModel";

export async function getAllTextbooks(req: Request, res: Response) {
Expand Down
8 changes: 4 additions & 4 deletions backend/src/controllers/userController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@ import {
findUserById,
addUser,
findUserByEmail,
} from "../models/userModel.ts";
import { findRentalsFromUser } from "../models/rentalModel.ts";
import { findTextbooksFromUser } from "../models/textbookModel.ts";
import { findMiscFromUser } from "../models/miscModel.ts";
} from "../models/userModel";
import { findRentalsFromUser } from "../models/rentalModel";
import { findTextbooksFromUser } from "../models/textbookModel";
import { findMiscFromUser } from "../models/miscModel";

export async function getUserById(
req: Request,
Expand Down
3 changes: 2 additions & 1 deletion backend/src/middleware/authMiddleware.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { type UserPayload } from "../types/user.ts";
import { type UserPayload } from "../types/user";
import { type Request, type Response, type NextFunction } from "express";
import jwt from "jsonwebtoken";

Expand All @@ -23,6 +23,7 @@ export const authenticateToken = (
jwt.verify(
accessToken,
process.env.ACCESS_TOKEN_SECRET as string,
// eslint-disable-next-line @typescript-eslint/no-explicit-any
(err: any, decoded: any) => {
if (err?.name === "TokenExpiredError") {
res
Expand Down
2 changes: 1 addition & 1 deletion backend/src/models/db.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import postgres from "postgres";

const sql = postgres({
host: "localhost",
host: "host.docker.internal",
port: parseInt(process.env.DB_PORT!),
username: process.env.DB_USERNAME!,
password: process.env.DB_PASSWORD!,
Expand Down
8 changes: 5 additions & 3 deletions backend/src/models/miscModel.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import sql from "./db.ts";
import { User } from "./userModel.ts";
import sql from "./db";
import { User } from "./userModel";

type ListingType = "Selling" | "Wanted";

Expand Down Expand Up @@ -43,7 +43,9 @@ export const addMisc = async (
return result;
};

export const editMisc = async (misc: Omit<Miscellaneous, "date_posted">) => {
export const editMisc = async (
misc: Omit<Miscellaneous, "date_posted" | "photos">,
) => {
const result =
await sql`UPDATE misc SET (title, description, price, listing_type) = (${misc.title}, ${misc.description}, ${misc.price}, ${misc.listing_type}) WHERE id = ${misc.id} AND seller = ${misc.seller}`;
return result;
Expand Down
4 changes: 2 additions & 2 deletions backend/src/models/rentalModel.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import sql from "./db.ts";
import { User } from "./userModel.ts";
import sql from "./db";
import { User } from "./userModel";

export type RentalListing = {
id: string;
Expand Down
6 changes: 3 additions & 3 deletions backend/src/models/textbookModel.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import sql from "./db.ts";
import { User } from "./userModel.ts";
import sql from "./db";
import { User } from "./userModel";

export interface Textbook {
id: number;
id: string;
book_title: string;
author: string;
edition: string;
Expand Down
2 changes: 1 addition & 1 deletion backend/src/models/tokenModel.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import sql from "./db.ts";
import sql from "./db";

export const registerToken = async (refreshToken: string, userId: string) => {
const tokenRow = await sql<{ token: string }[]>`
Expand Down
4 changes: 2 additions & 2 deletions backend/src/models/userModel.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import sql from "./db.ts";
import { RentalListing } from "./rentalModel.ts";
import sql from "./db";
import { RentalListing } from "./rentalModel";

export interface User {
id: string;
Expand Down
4 changes: 2 additions & 2 deletions backend/src/routes/authRoutes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ import {
postLoginUser,
postRefreshToken,
getUserInformation,
} from "../controllers/authController.ts";
import { authenticateToken } from "../middleware/authMiddleware.ts";
} from "../controllers/authController";
import { authenticateToken } from "../middleware/authMiddleware";

const router = express.Router();

Expand Down
Loading