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
47 changes: 47 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,50 @@
## [1.0.4](https://github.com/virus231/tech-stack/compare/v1.0.3...v1.0.4) (2025-09-16)

### 🐛 Bug Fixes

* vercel errors ([b876f83](https://github.com/virus231/tech-stack/commit/b876f8364df89e899b3de2f1198cb2e52f425d2c))

## [1.0.3](https://github.com/virus231/tech-stack/compare/v1.0.2...v1.0.3) (2025-09-16)

### 🐛 Bug Fixes

* build errors ([3bc9330](https://github.com/virus231/tech-stack/commit/3bc93306569646c43b9b423df532418270eba82b))

## [1.0.2](https://github.com/virus231/tech-stack/compare/v1.0.1...v1.0.2) (2025-09-16)

### 🐛 Bug Fixes

* config ([654ae13](https://github.com/virus231/tech-stack/commit/654ae1339e140a9cb4c6921366186f5aef5fabeb))

## [1.0.1](https://github.com/virus231/tech-stack/compare/v1.0.0...v1.0.1) (2025-09-16)

### 🐛 Bug Fixes

* vercel config ([b667eb5](https://github.com/virus231/tech-stack/commit/b667eb50b47784a3a90f3544eb42e7ee177f2aaf))

## 1.0.0 (2025-09-16)

### ✨ Features

* add CI/CD workflow with semantic-release ([1001430](https://github.com/virus231/tech-stack/commit/10014303229e59c9f15646e0b7adeb7a414592cc))
* add Git Flow CI/CD with develop branch support ([6c7dab9](https://github.com/virus231/tech-stack/commit/6c7dab90595c3f93c89badb95ae092a447536c52))
* add Vercel deployment configuration ([27ed269](https://github.com/virus231/tech-stack/commit/27ed269e88f759e76e19bd8a238a403e825c1010))

### 🐛 Bug Fixes

* ci ([050bbcc](https://github.com/virus231/tech-stack/commit/050bbccbfa8fe73440d3499ed2bafd2870444456))
* ci cd ([25950ae](https://github.com/virus231/tech-stack/commit/25950ae294a01c088d76b91ffd58ed505155d9b6))
* express, ci cd ([8791d34](https://github.com/virus231/tech-stack/commit/8791d349906f80f641973a691f97e70f01077cf5))

### ⚙️ Continuous Integrations

* prisma fix ([6a81c73](https://github.com/virus231/tech-stack/commit/6a81c7329d494963dd7b9f7c10c8e42a46490661))

### ♻️ Chores

* **release:** 1.0.0-beta.1 [skip ci] ([0a95188](https://github.com/virus231/tech-stack/commit/0a95188fabe98dbf94d1d9856477b3b18e1bde87))
* **release:** 1.0.0-beta.2 [skip ci] ([eb0889f](https://github.com/virus231/tech-stack/commit/eb0889f827661cb37b875cd9b12cf479a4bb8afc))

## [1.0.0-beta.2](https://github.com/virus231/tech-stack/compare/v1.0.0-beta.1...v1.0.0-beta.2) (2025-09-16)

### ✨ Features
Expand Down
37 changes: 33 additions & 4 deletions backend/api/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,35 @@
import { VercelRequest, VercelResponse } from '@vercel/node';
import app from '../src/app';
export default async function handler(req: any, res: any) {
// Health check
if (req.url === '/api/health') {
return res.status(200).json({
status: 'OK',
timestamp: new Date().toISOString(),
environment: 'production',
database: 'configured'
});
}

export default function handler(req: VercelRequest, res: VercelResponse) {
return app(req, res);
// Database test endpoint
if (req.url === '/api/db-test') {
try {
return res.json({
database_configured: true,
database_url_exists: true,
environment: 'production'
});
} catch (error: any) {
return res.status(500).json({
error: 'Database test failed',
message: error?.message || 'Unknown error'
});
}
}

// Default API response
return res.json({
message: 'API is working',
method: req.method,
url: req.url,
endpoints: ['/api/health', '/api/db-test']
});
}
30 changes: 30 additions & 0 deletions backend/prisma/migrations/20250916210727_init/migration.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
-- CreateTable
CREATE TABLE "public"."User" (
"id" SERIAL NOT NULL,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
"email" TEXT NOT NULL,
"name" TEXT,
"password" TEXT NOT NULL,

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

-- CreateTable
CREATE TABLE "public"."Post" (
"id" SERIAL NOT NULL,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
"title" TEXT NOT NULL,
"content" TEXT NOT NULL,
"description" TEXT,
"authorId" INTEGER NOT NULL,

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

-- CreateIndex
CREATE UNIQUE INDEX "User_email_key" ON "public"."User"("email");

-- AddForeignKey
ALTER TABLE "public"."Post" ADD CONSTRAINT "Post_authorId_fkey" FOREIGN KEY ("authorId") REFERENCES "public"."User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
3 changes: 3 additions & 0 deletions backend/prisma/migrations/migration_lock.toml
Original file line number Diff line number Diff line change
@@ -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"
30 changes: 27 additions & 3 deletions backend/prisma/schema.prisma
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
// 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"
}
Expand All @@ -12,3 +9,30 @@ datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}

// User model for authentication
model User {
id Int @id @default(autoincrement())
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
email String @unique
name String?
password String

// Relations
posts Post[]
}

// Post model for blog posts
model Post {
id Int @id @default(autoincrement())
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
title String
content String
description String?

// Relations
authorId Int
author User @relation(fields: [authorId], references: [id], onDelete: Cascade)
}
51 changes: 27 additions & 24 deletions backend/src/app.ts
Original file line number Diff line number Diff line change
@@ -1,57 +1,60 @@
import express, { Application } from 'express';
import cors from 'cors';
import helmet from 'helmet';
import morgan from 'morgan';
import { config } from '@/config/config';
import { config } from "@/config/config";
import cors from "cors";
import express, { type Express } from "express";
import helmet from "helmet";
import morgan from "morgan";

const app: Application = express();
const app: Express = express();

// Security middleware
app.use(helmet());

// CORS configuration
app.use(cors({
origin: config.corsOrigin,
credentials: true,
}));
app.use(
cors({
origin: config.corsOrigin,
credentials: true,
})
);

// Logging middleware
app.use(morgan('combined'));
app.use(morgan("combined"));

// Body parsing middleware
app.use(express.json({ limit: '10mb' }));
app.use(express.urlencoded({ extended: true, limit: '10mb' }));
app.use(express.json({ limit: "10mb" }));
app.use(express.urlencoded({ extended: true, limit: "10mb" }));

// Health check endpoint
app.get('/health', (_req, res) => {
app.get("/health", (_req, res) => {
res.status(200).json({
status: 'OK',
status: "OK",
timestamp: new Date().toISOString(),
environment: config.nodeEnv,
});
});

// API routes
app.use('/api', (_req, res) => {
res.json({ message: 'API is working' });
app.use("/api", (_req, res) => {
res.json({ message: "API is working" });
});

// 404 handler
app.use((req, res) => {
res.status(404).json({
error: 'Route not found',
error: "Route not found",
message: `Cannot ${req.method} ${req.originalUrl}`,
});
});

// Global error handler
app.use((err: Error, _req: express.Request, res: express.Response, _next: express.NextFunction) => {
console.error('Error:', err);
app.use((err: Error, _req: any, res: any, _next: any) => {
console.error("Error:", err);

res.status(500).json({
error: 'Internal server error',
message: config.nodeEnv === 'development' ? err.message : 'Something went wrong',
error: "Internal server error",
message:
config.nodeEnv === "development" ? err.message : "Something went wrong",
});
});

export default app;
export default app;
20 changes: 12 additions & 8 deletions backend/src/config/config.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { config as dotenvConfig } from 'dotenv';
import { config as dotenvConfig } from "dotenv";

// Load environment variables
dotenvConfig();
Expand All @@ -11,19 +11,23 @@ interface Config {
corsOrigin: string;
}

const defaultPort = 3001;
const portString = process.env.PORT || "3001";
const port = Number(portString) || defaultPort;

export const config: Config = {
port: parseInt(process.env.PORT || '3000', 10),
nodeEnv: process.env.NODE_ENV || 'development',
databaseUrl: process.env.DATABASE_URL || '',
jwtSecret: process.env.JWT_SECRET || 'fallback-secret-key',
corsOrigin: process.env.CORS_ORIGIN || 'http://localhost:3000',
port: port,
nodeEnv: process.env.NODE_ENV || "development",
databaseUrl: process.env.DATABASE_URL || "",
jwtSecret: process.env.JWT_SECRET || "fallback-secret-key",
corsOrigin: process.env.CORS_ORIGINS || "http://localhost:3000",
};

// Validate required environment variables
const requiredEnvVars = ['DATABASE_URL'];
const requiredEnvVars = ["DATABASE_URL"];

for (const envVar of requiredEnvVars) {
if (!process.env[envVar]) {
throw new Error(`Missing required environment variable: ${envVar}`);
}
}
}
19 changes: 4 additions & 15 deletions vercel.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,29 +6,18 @@
"use": "@vercel/next"
},
{
"src": "backend/src/**/*.ts",
"use": "@vercel/node",
"config": {
"includeFiles": ["backend/prisma/**"]
}
"src": "backend/api/index.ts",
"use": "@vercel/node"
}
],
"routes": [
{
"src": "/api/(.*)",
"dest": "backend/src/server.ts"
"dest": "backend/api/index.ts"
},
{
"src": "/(.*)",
"dest": "frontend/$1"
}
],
"env": {
"DATABASE_URL": "@database_url",
"JWT_SECRET": "@jwt_secret",
"NODE_ENV": "production"
},
"installCommand": "pnpm install",
"buildCommand": "pnpm build",
"outputDirectory": "frontend/.next"
]
}