Skip to content
Open
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 .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
# Auto detect text files and perform LF normalization
* text=auto

# Explicitly declare text files you want to always be normalized and converted to native line endings on checkout
*.ts text eol=lf
*.tsx text eol=lf
*.js text eol=lf
*.jsx text eol=lf
*.mjs text eol=lf
*.cjs text eol=lf
*.json text eol=lf
*.md text eol=lf
*.yml text eol=lf
*.yaml text eol=lf
*.toml text eol=lf
*.css text eol=lf
*.scss text eol=lf
*.html text eol=lf
*.xml text eol=lf
*.svg text eol=lf
*.sh text eol=lf

# Lock files should maintain LF
package-lock.json text eol=lf
bun.lockb binary
bun.lock text eol=lf
yarn.lock text eol=lf
pnpm-lock.yaml text eol=lf

# Denote all files that are truly binary and should not be modified
*.png binary
*.jpg binary
*.jpeg binary
*.gif binary
*.ico binary
*.woff binary
*.woff2 binary
*.ttf binary
*.eot binary
*.pdf binary
*.zip binary
*.gz binary

# Windows-specific files
*.bat text eol=crlf
*.cmd text eol=crlf
*.ps1 text eol=crlf
14 changes: 14 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -39,3 +39,17 @@ yarn-error.log*

*storybook.log
storybook-static

# IDE
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
.idea
*.swp
*.swo
*~

# OS
Thumbs.db
15 changes: 15 additions & 0 deletions apps/backend/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# Database Configuration
DATABASE_URL=postgresql://user:password@localhost:5432/eventer

# Supabase Configuration
SUPABASE_URL=https://your-project.supabase.co
SUPABASE_KEY=your-anon-key-here

# Server Configuration
PORT=4000
NODE_ENV=development

# CORS Configuration
# In development: http://localhost:3000
# In production: https://your-domain.com
CORS_ORIGIN=http://localhost:3000
39 changes: 28 additions & 11 deletions apps/backend/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,19 +1,36 @@
FROM oven/bun
FROM oven/bun:1 AS deps
WORKDIR /app

# Copy workspace manifests for dependency caching
COPY package.json bun.lockb turbo.json ./
COPY apps/backend/package.json ./apps/backend/
COPY packages/typescript-config/package.json ./packages/typescript-config/

RUN bun install --frozen-lockfile --production=false

FROM oven/bun:1 AS runner
WORKDIR /app

COPY package.json .
RUN addgroup --system --gid 1001 appgroup && \
adduser --system --uid 1001 --ingroup appgroup appuser

RUN bun install --production
COPY --from=deps /app/node_modules ./node_modules
COPY --from=deps /app/apps/backend/node_modules ./apps/backend/node_modules

COPY src src
COPY tsconfig.json .
COPY drizzle.config.ts .
COPY ./drizzle ./drizzle
COPY apps/backend/src ./apps/backend/src
COPY apps/backend/tsconfig.json ./apps/backend/
COPY apps/backend/drizzle.config.ts ./apps/backend/
COPY apps/backend/drizzle ./apps/backend/drizzle
COPY packages/typescript-config ./packages/typescript-config

# COPY public public
USER appuser

ENV NODE_ENV production
CMD ["bun", "src/index.ts"]
ENV NODE_ENV=production
WORKDIR /app/apps/backend

EXPOSE 8080
EXPOSE 8080

HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \
CMD wget --no-verbose --tries=1 --spider http://localhost:8080/health || exit 1

CMD ["bun", "src/index.ts"]
31 changes: 25 additions & 6 deletions apps/backend/api/index.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,29 @@
// import type { VercelRequest, VercelResponse } from "@vercel/node";
import type { VercelRequest, VercelResponse } from "@vercel/node";
import { app } from "../src";

export const config = {
runtime: "edge", // Required for edge deployment
};
export default async function handler(req: VercelRequest, res: VercelResponse) {
// Convert Vercel request to Web Request
const url = `${req.headers["x-forwarded-proto"] || "http"}://${req.headers.host}${req.url}`;
const headers = new Headers();
Object.entries(req.headers).forEach(([key, value]) => {
if (value) headers.set(key, Array.isArray(value) ? value.join(", ") : value);
});

export default async function handler(request: Request): Promise<Response> {
return app.handle(request);
const request = new Request(url, {
method: req.method,
headers,
body: req.method !== "GET" && req.method !== "HEAD" ? JSON.stringify(req.body) : undefined,
});

// Handle with Elysia
const response = await app.handle(request);

// Convert Web Response to Vercel Response
res.status(response.status);
response.headers.forEach((value, key) => {
res.setHeader(key, value);
});

const body = await response.text();
res.send(body);
}
3 changes: 3 additions & 0 deletions apps/backend/drizzle/0002_tough_nextwave.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
ALTER TABLE "events" ADD COLUMN "created_at" timestamp DEFAULT now() NOT NULL;--> statement-breakpoint
ALTER TABLE "events" ADD COLUMN "updated_at" timestamp DEFAULT now() NOT NULL;--> statement-breakpoint
ALTER TABLE "agenda" ADD CONSTRAINT "agenda_event_id_events_id_fk" FOREIGN KEY ("event_id") REFERENCES "public"."events"("id") ON DELETE cascade ON UPDATE no action;
224 changes: 224 additions & 0 deletions apps/backend/drizzle/meta/0002_snapshot.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,224 @@
{
"id": "c6096878-3f40-4d48-9742-04f69fc39bbc",
"prevId": "ef8332c8-f25a-4dd8-8f56-785c9155e4cf",
"version": "7",
"dialect": "postgresql",
"tables": {
"public.agenda": {
"name": "agenda",
"schema": "",
"columns": {
"id": {
"name": "id",
"type": "text",
"primaryKey": true,
"notNull": true
},
"event_id": {
"name": "event_id",
"type": "text",
"primaryKey": false,
"notNull": true
},
"start": {
"name": "start",
"type": "text",
"primaryKey": false,
"notNull": true
},
"end": {
"name": "end",
"type": "text",
"primaryKey": false,
"notNull": true
},
"person_in_charge": {
"name": "person_in_charge",
"type": "text",
"primaryKey": false,
"notNull": true
},
"duration": {
"name": "duration",
"type": "integer",
"primaryKey": false,
"notNull": true
},
"activity": {
"name": "activity",
"type": "text",
"primaryKey": false,
"notNull": true
},
"remarks": {
"name": "remarks",
"type": "text",
"primaryKey": false,
"notNull": false,
"default": "''"
},
"actual_end_time": {
"name": "actual_end_time",
"type": "text",
"primaryKey": false,
"notNull": false
}
},
"indexes": {},
"foreignKeys": {
"agenda_event_id_events_id_fk": {
"name": "agenda_event_id_events_id_fk",
"tableFrom": "agenda",
"tableTo": "events",
"columnsFrom": ["event_id"],
"columnsTo": ["id"],
"onDelete": "cascade",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {},
"policies": {},
"checkConstraints": {},
"isRLSEnabled": false
},
"public.events": {
"name": "events",
"schema": "",
"columns": {
"id": {
"name": "id",
"type": "text",
"primaryKey": true,
"notNull": true
},
"name": {
"name": "name",
"type": "text",
"primaryKey": false,
"notNull": true
},
"start_date": {
"name": "start_date",
"type": "timestamp",
"primaryKey": false,
"notNull": true
},
"end_date": {
"name": "end_date",
"type": "timestamp",
"primaryKey": false,
"notNull": true
},
"location": {
"name": "location",
"type": "text",
"primaryKey": false,
"notNull": true
},
"description": {
"name": "description",
"type": "text",
"primaryKey": false,
"notNull": false
},
"created_by": {
"name": "created_by",
"type": "text",
"primaryKey": false,
"notNull": true
},
"created_at": {
"name": "created_at",
"type": "timestamp",
"primaryKey": false,
"notNull": true,
"default": "now()"
},
"updated_at": {
"name": "updated_at",
"type": "timestamp",
"primaryKey": false,
"notNull": true,
"default": "now()"
}
},
"indexes": {},
"foreignKeys": {},
"compositePrimaryKeys": {},
"uniqueConstraints": {},
"policies": {},
"checkConstraints": {},
"isRLSEnabled": false
},
"public.users": {
"name": "users",
"schema": "",
"columns": {
"id": {
"name": "id",
"type": "text",
"primaryKey": true,
"notNull": true
},
"name": {
"name": "name",
"type": "text",
"primaryKey": false,
"notNull": true
},
"email": {
"name": "email",
"type": "text",
"primaryKey": false,
"notNull": true
},
"created_at": {
"name": "created_at",
"type": "timestamp",
"primaryKey": false,
"notNull": true,
"default": "now()"
},
"updated_at": {
"name": "updated_at",
"type": "timestamp",
"primaryKey": false,
"notNull": true,
"default": "now()"
},
"avatar_url": {
"name": "avatar_url",
"type": "text",
"primaryKey": false,
"notNull": true,
"default": "''"
}
},
"indexes": {},
"foreignKeys": {},
"compositePrimaryKeys": {},
"uniqueConstraints": {
"users_email_unique": {
"name": "users_email_unique",
"nullsNotDistinct": false,
"columns": ["email"]
}
},
"policies": {},
"checkConstraints": {},
"isRLSEnabled": false
}
},
"enums": {},
"schemas": {},
"sequences": {},
"roles": {},
"policies": {},
"views": {},
"_meta": {
"columns": {},
"schemas": {},
"tables": {}
}
}
Loading
Loading