diff --git a/.github/workflows/opencode-review.yml b/.github/workflows/opencode-review.yml new file mode 100644 index 0000000..9ab5501 --- /dev/null +++ b/.github/workflows/opencode-review.yml @@ -0,0 +1,30 @@ +name: opencode-review + +on: + pull_request: + types: [opened, synchronize, reopened, ready_for_review] + +jobs: + review: + runs-on: ubuntu-latest + permissions: + id-token: write + contents: read + pull-requests: read + issues: read + steps: + - uses: actions/checkout@v6 + with: + persist-credentials: false + - uses: anomalyco/opencode/github@latest + env: + ALIBABA_CODING_PLAN_API_KEY: ${{ secrets.ALIBABA_CODING_PLAN_API_KEY }} + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + model: alibaba-coding-plan/kimi-k2.5 + use_github_token: true + prompt: | + Review this pull request: + - Check for code quality issues + - Look for potential bugs + - Suggest improvements diff --git a/.github/workflows/opencode.yml b/.github/workflows/opencode.yml index ef0050e..8f150a8 100644 --- a/.github/workflows/opencode.yml +++ b/.github/workflows/opencode.yml @@ -17,8 +17,8 @@ jobs: permissions: id-token: write contents: read - pull-requests: read - issues: read + pull-requests: write + issues: write steps: - name: Checkout repository uses: actions/checkout@v6 @@ -30,4 +30,4 @@ jobs: env: ALIBABA_CODING_PLAN_API_KEY: ${{ secrets.ALIBABA_CODING_PLAN_API_KEY }} with: - model: alibaba-coding-plan/kimi-k2.5 \ No newline at end of file + model: alibaba-coding-plan/kimi-k2.5 diff --git a/.github/workflows/web-app.yml b/.github/workflows/web-app.yml index e3056d1..0442d64 100644 --- a/.github/workflows/web-app.yml +++ b/.github/workflows/web-app.yml @@ -154,7 +154,7 @@ jobs: uses: docker/build-push-action@v5 with: context: . - file: ./apps/web/Dockerfile + file: ./Dockerfile push: true tags: ${{ steps.meta.outputs.tags }} labels: ${{ steps.meta.outputs.labels }} diff --git a/apps/web/Dockerfile b/Dockerfile similarity index 100% rename from apps/web/Dockerfile rename to Dockerfile diff --git a/apps/feed-updater/package.json b/apps/feed-updater/package.json index 2ac38e6..29c2604 100644 --- a/apps/feed-updater/package.json +++ b/apps/feed-updater/package.json @@ -5,7 +5,8 @@ "type": "module", "scripts": { "build": "bun build ./src/index.ts --outdir ./dist --target bun", - "start": "bun run src/index.ts" + "start": "bun run src/index.ts", + "dev": "bun run src/index.ts" }, "dependencies": { "@reafrac/external-script": "workspace:*" diff --git a/apps/web/server.ts b/apps/web/server.ts index 41f48cb..ab49c71 100644 --- a/apps/web/server.ts +++ b/apps/web/server.ts @@ -75,6 +75,7 @@ import path from 'node:path'; import * as Sentry from '@sentry/tanstackstart-react'; import { refetchFeeds } from '@reafrac/external-script'; +import { runMigrations } from '@reafrac/database'; const ENABLE_FEED_CRON = process.env.ENABLE_FEED_CRON === 'true'; const FEED_CRON_INTERVAL_MS = Number(process.env.FEED_CRON_INTERVAL_MS ?? 30 * 60 * 1000); @@ -482,6 +483,16 @@ async function initializeStaticRoutes(clientDirectory: string): Promise Response | Promise }; try { diff --git a/apps/web/src/routes/__root.tsx b/apps/web/src/routes/__root.tsx index 44d92f5..36c7c0c 100644 --- a/apps/web/src/routes/__root.tsx +++ b/apps/web/src/routes/__root.tsx @@ -102,7 +102,6 @@ function RootDocument({ children }: { children: React.ReactNode }) { ); } -function NotFoundComponent(props: NotFoundRouteProps) { - console.log({ NotFoundProps: props }); +function NotFoundComponent() { return

This setting page doesn't exist!

; } diff --git a/docker-compose.yml b/docker-compose.yml index 3a06878..45fe361 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,99 +1,51 @@ +# Reafrac - RSS Reader +# +# Create a .env file with the following variables: +# POSTGRES_PASSWORD=your-secure-password +# BETTER_AUTH_SECRET=your-auth-secret +# GOOGLE_CLIENT_ID=your-google-client-id (optional) +# GOOGLE_CLIENT_SECRET=your-google-client-secret (optional) +# VITE_SENTRY_DSN=your-sentry-dsn (optional) +# VITE_SENTRY_TRACES_SAMPLE_RATE=0.1 (optional) +# PROXY_URL=your-proxy-url (optional) +# ENABLE_FEED_CRON=true (optional, enables in-process feed fetching) + services: - app: - image: ghcr.io/${GITHUB_USERNAME}/reafrac:latest - container_name: reafrac-app + reafrac: + image: ghcr.io/rulasfia/reafrac:latest + container_name: reafrac restart: unless-stopped ports: - - '3000:3000' - environment: - - NODE_ENV=production - - PORT=3000 - - DATABASE_URL=postgresql://postgres:${POSTGRES_PASSWORD}@postgres:5432/${POSTGRES_DB} - depends_on: - postgres: - condition: service_healthy - networks: - - reafrac-network - healthcheck: - test: ['CMD', 'curl', '-f', 'http://localhost:3000/'] - interval: 30s - timeout: 10s - retries: 3 - start_period: 40s - - # Migration service (run once and exit) - migrate: - image: ghcr.io/${GITHUB_USERNAME}/reafrac:latest - container_name: reafrac-migrate + - '${PORT:-3000}:3000' environment: - - DATABASE_URL=postgresql://postgres:${POSTGRES_PASSWORD}@postgres:5432/${POSTGRES_DB} - command: ['bun', 'run', 'db:migrate'] + - PORT=${PORT:-3000} + - DATABASE_URL=postgres://postgres:${POSTGRES_PASSWORD}@reafrac-pg:5432/reafrac + - VITE_SENTRY_DSN=${VITE_SENTRY_DSN} + - VITE_SENTRY_TRACES_SAMPLE_RATE=${VITE_SENTRY_TRACES_SAMPLE_RATE} + - BETTER_AUTH_SECRET=${BETTER_AUTH_SECRET} + - GOOGLE_CLIENT_ID=${GOOGLE_CLIENT_ID} + - GOOGLE_CLIENT_SECRET=${GOOGLE_CLIENT_SECRET} + - PROXY_URL=${PROXY_URL} + - ENABLE_FEED_CRON=${ENABLE_FEED_CRON} depends_on: - postgres: + reafrac-pg: condition: service_healthy - networks: - - reafrac-network - restart: 'no' # Only run once - postgres: + reafrac-pg: image: postgres:17-alpine - container_name: reafrac-db + container_name: reafrac-pg restart: unless-stopped + volumes: + - reafrac-db:/var/lib/postgresql/data environment: - - POSTGRES_DB=${POSTGRES_DB:-reafrac} - - POSTGRES_USER=${POSTGRES_USER:-postgres} + - POSTGRES_USER=postgres + - POSTGRES_DB=reafrac - POSTGRES_PASSWORD=${POSTGRES_PASSWORD} - - POSTGRES_INITDB_ARGS=--encoding=UTF-8 --lc-collate=C --lc-ctype=C - volumes: - - postgres_data:/var/lib/postgresql/data - - ./migrations:/docker-entrypoint-initdb.d:ro - ports: - - '5432:5432' - networks: - - reafrac-network healthcheck: - test: ['CMD-SHELL', 'pg_isready -U ${POSTGRES_USER:-postgres} -d ${POSTGRES_DB:-reafrac}'] - interval: 10s + test: ['CMD-SHELL', 'pg_isready -U postgres'] + interval: 5s timeout: 5s retries: 5 - start_period: 30s - # Optional: Add resource limits - deploy: - resources: - limits: - memory: 1024M - reservations: - memory: 256M - - # Optional: Add pgAdmin for database management - pgadmin: - image: dpage/pgadmin4:latest - container_name: reafrac-pgadmin - restart: unless-stopped - environment: - - PGADMIN_DEFAULT_EMAIL=${PGADMIN_EMAIL:-admin@example.com} - - PGADMIN_DEFAULT_PASSWORD=${PGADMIN_PASSWORD} - - PGADMIN_CONFIG_SERVER_MODE=False - ports: - - '5050:80' - volumes: - - pgadmin_data:/var/lib/pgadmin - networks: - - reafrac-network - depends_on: - - postgres - profiles: - - admin # Use 'docker-compose --profile admin up' to include this service volumes: - postgres_data: - driver: local - pgadmin_data: - driver: local - -networks: - reafrac-network: - driver: bridge - ipam: - config: - - subnet: 172.20.0.0/16 + reafrac-db: diff --git a/package.json b/package.json index e247e50..bd6ed51 100644 --- a/package.json +++ b/package.json @@ -9,6 +9,7 @@ ], "scripts": { "dev": "turbo run dev", + "dev:proxy": "turbo run dev --filter @reafrac/feed-updater --filter @reafrac/web --filter @reafrac/content-proxy", "build": "turbo run build", "start": "turbo run start", "test": "turbo run test", diff --git a/packages/database/src/index.ts b/packages/database/src/index.ts index 2a2ad53..fcc4c27 100644 --- a/packages/database/src/index.ts +++ b/packages/database/src/index.ts @@ -1,5 +1,6 @@ export * from './schema'; export * from './connection'; +export { runMigrations } from './migrate'; // re-export drizzle-orm to use in other packages. doing this will // prevent type errors 'shouldInlineParams' when importing directly from drizzle-orm export * from 'drizzle-orm'; diff --git a/packages/database/src/migrate.ts b/packages/database/src/migrate.ts new file mode 100644 index 0000000..beb0758 --- /dev/null +++ b/packages/database/src/migrate.ts @@ -0,0 +1,24 @@ +import { migrate } from 'drizzle-orm/postgres-js/migrator'; +import postgres from 'postgres'; +import { drizzle } from 'drizzle-orm/postgres-js'; +import * as schema from './schema'; +import { fileURLToPath } from 'node:url'; +import path from 'node:path'; + +const DB_URL = process.env.DATABASE_URL; +if (!DB_URL) { + throw new Error('DATABASE_URL is not defined'); +} + +export async function runMigrations() { + const migrationClient = postgres(DB_URL!, { max: 1 }); + const db = drizzle(migrationClient, { schema }); + + // Migrations folder is relative to this file's location + const currentDir = path.dirname(fileURLToPath(import.meta.url)); + const migrationsFolder = path.join(currentDir, '../migrations'); + + await migrate(db, { migrationsFolder }); + + await migrationClient.end(); +}