forked from elliotBraem/efizzybot
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #9 from PotLock/feat/frontend
Updates frontend
- Loading branch information
Showing
71 changed files
with
2,186 additions
and
829 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
# See https://fly.io/docs/app-guides/continuous-deployment-with-github-actions/ | ||
|
||
name: Fly Deploy | ||
on: | ||
push: | ||
branches: | ||
- main | ||
jobs: | ||
deploy: | ||
name: Deploy app | ||
runs-on: ubuntu-latest | ||
concurrency: deploy-group # optional: ensure only one action runs at a time | ||
steps: | ||
- uses: actions/checkout@v4 | ||
- uses: superfly/flyctl-actions/setup-flyctl@master | ||
- run: flyctl deploy --remote-only | ||
env: | ||
FLY_API_TOKEN: ${{ secrets.FLY_API_TOKEN }} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,52 +1,73 @@ | ||
FROM oven/bun as deps | ||
## NOTE | ||
# This Dockerfile builds the frontend and backend separately, | ||
# frontend uses npm and backend requires bun. | ||
# This separation is a temporary solution for a Bun issue with rsbuild, | ||
# see: https://github.com/oven-sh/bun/issues/11628 | ||
|
||
# Frontend deps & build stage | ||
FROM node:20 as frontend-builder | ||
WORKDIR /app | ||
|
||
# Copy package files for all workspaces | ||
COPY package.json bun.lockb turbo.json ./ | ||
# Copy frontend package files | ||
COPY frontend/package.json ./frontend/ | ||
COPY backend/package.json ./backend/ | ||
|
||
# Install dependencies | ||
RUN bun install | ||
# Install frontend dependencies | ||
RUN cd frontend && npm install | ||
|
||
# Copy frontend source code | ||
COPY frontend ./frontend | ||
|
||
# Build frontend | ||
RUN cd frontend && npm run build | ||
|
||
# Build stage | ||
FROM oven/bun as builder | ||
# Backend deps & build stage | ||
FROM oven/bun as backend-builder | ||
WORKDIR /app | ||
|
||
# Set NODE_ENV for build process | ||
ENV NODE_ENV="production" | ||
# Copy backend package files | ||
COPY package.json ./ | ||
COPY backend/package.json ./backend/ | ||
|
||
# Copy all files from deps stage including node_modules | ||
COPY --from=deps /app ./ | ||
# Install backend dependencies | ||
RUN cd backend && bun install | ||
|
||
# Copy source code | ||
COPY . . | ||
# Copy backend source code | ||
COPY backend ./backend | ||
|
||
# Build both frontend and backend | ||
RUN bun run build | ||
# Build backend | ||
RUN cd backend && bun run build | ||
|
||
# Production stage | ||
FROM oven/bun as production | ||
WORKDIR /app | ||
|
||
# Create directory for mount with correct permissions | ||
RUN mkdir -p /.data/db /.data/cache && \ | ||
chown -R bun:bun /.data | ||
# Install LiteFS dependencies | ||
RUN apt-get update -y && apt-get install -y ca-certificates fuse3 sqlite3 | ||
|
||
# Copy only necessary files from builder | ||
COPY --from=builder --chown=bun:bun /app/package.json /app/bun.lockb /app/turbo.json ./ | ||
COPY --from=builder --chown=bun:bun /app/node_modules ./node_modules | ||
COPY --from=builder --chown=bun:bun /app/frontend/dist ./frontend/dist | ||
COPY --from=builder --chown=bun:bun /app/backend/dist ./backend/dist | ||
# Copy LiteFS binary | ||
COPY --from=flyio/litefs:0.5 /usr/local/bin/litefs /usr/local/bin/litefs | ||
|
||
# Create directories for mounts with correct permissions | ||
RUN mkdir -p /litefs /var/lib/litefs && \ | ||
chown -R bun:bun /litefs /var/lib/litefs | ||
|
||
# Create volume mount points | ||
# Copy only necessary files from builders | ||
COPY --from=backend-builder --chown=bun:bun /app/package.json ./ | ||
COPY --chown=bun:bun curate.config.json ./ | ||
|
||
COPY --from=frontend-builder --chown=bun:bun /app/frontend/dist ./frontend/dist | ||
COPY --from=backend-builder --chown=bun:bun /app/backend/dist ./backend/dist | ||
|
||
# Set environment variables | ||
ENV DATABASE_URL="file:/.data/db/sqlite.db" | ||
ENV CACHE_DIR="/.data/cache" | ||
ENV DATABASE_URL="file:/litefs/db" | ||
ENV NODE_ENV="production" | ||
|
||
# Expose the port | ||
EXPOSE 3000 | ||
|
||
# Start the application using the production start script | ||
CMD ["bun", "run", "start"] | ||
# Copy LiteFS configuration | ||
COPY --chown=bun:bun litefs.yml /etc/litefs.yml | ||
|
||
# Start LiteFS (runs app with distributed file system for SQLite) | ||
ENTRYPOINT ["litefs", "mount"] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,113 @@ | ||
import { writeFile, mkdir } from "fs/promises"; | ||
import path from "path"; | ||
import { DistributorPlugin } from "types/plugin"; | ||
import { RssService } from "./rss.service"; | ||
import type { RssItem } from "../../services/rss/queries"; | ||
import type { DBOperations } from "../../services/db/operations"; | ||
|
||
export default class RssPlugin implements DistributorPlugin { | ||
name = "@curatedotfun/rss"; | ||
private services: Map<string, RssService> = new Map(); | ||
private dbOps?: DBOperations; | ||
|
||
getServices(): Map<string, RssService> { | ||
return this.services; | ||
} | ||
|
||
constructor(dbOperations?: DBOperations) { | ||
this.dbOps = dbOperations; | ||
} | ||
|
||
async initialize( | ||
feedId: string, | ||
config: Record<string, string>, | ||
): Promise<void> { | ||
if (!config.title) { | ||
throw new Error("RSS plugin requires title"); | ||
} | ||
|
||
const maxItems = config.maxItems ? parseInt(config.maxItems) : 100; | ||
|
||
// Create a new RSS service for this feed | ||
const service = new RssService( | ||
feedId, | ||
config.title, | ||
maxItems, | ||
config.path, | ||
this.dbOps, | ||
); | ||
|
||
this.services.set(feedId, service); | ||
} | ||
|
||
async distribute(feedId: string, content: string): Promise<void> { | ||
const service = this.services.get(feedId); | ||
if (!service) { | ||
throw new Error("RSS plugin not initialized for this feed"); | ||
} | ||
|
||
const item: RssItem = { | ||
title: "New Update", | ||
content, | ||
link: "https://twitter.com/", // TODO: Update with actual link | ||
publishedAt: new Date().toISOString(), | ||
guid: Date.now().toString(), | ||
}; | ||
|
||
// Save to database | ||
service.saveItem(item); | ||
|
||
// Write to file if path is provided (backward compatibility) | ||
const path = service.getPath(); | ||
if (path) { | ||
await this.writeToFile(service, path); | ||
} | ||
} | ||
|
||
private async writeToFile( | ||
service: RssService, | ||
filePath: string, | ||
): Promise<void> { | ||
const items = service.getItems(); | ||
|
||
const feed = `<?xml version="1.0" encoding="UTF-8" ?> | ||
<rss version="2.0"> | ||
<channel> | ||
<title>${service.getTitle()}</title> | ||
<link>https://twitter.com/</link> | ||
<lastBuildDate>${new Date().toUTCString()}</lastBuildDate> | ||
${items | ||
.map( | ||
(item) => ` | ||
<item> | ||
<title>${this.escapeXml(item.title || "")}</title> | ||
<description>${this.escapeXml(item.content)}</description> | ||
<link>${item.link || ""}</link> | ||
<pubDate>${new Date(item.publishedAt).toUTCString()}</pubDate> | ||
<guid>${item.guid || ""}</guid> | ||
</item>`, | ||
) | ||
.join("\n")} | ||
</channel> | ||
</rss>`; | ||
|
||
// Ensure directory exists | ||
const dir = path.dirname(filePath); | ||
await mkdir(dir, { recursive: true }); | ||
await writeFile(filePath, feed, "utf-8"); | ||
} | ||
|
||
private escapeXml(unsafe: string): string { | ||
return unsafe | ||
.replace(/&/g, "&") | ||
.replace(/</g, "<") | ||
.replace(/>/g, ">") | ||
.replace(/"/g, """) | ||
.replace(/'/g, "'"); | ||
} | ||
|
||
async shutdown(): Promise<void> { | ||
// Clear all services when plugin shuts down | ||
this.services.clear(); | ||
} | ||
} |
Oops, something went wrong.