尚未偵測到更新。GitHub 發布版本後會顯示在這裡。
個人 GitHub 帳號下公開儲存庫的擁有者可以登入並驗證所有權,自行管理介紹,無需管理員核准。
登入並產生驗證檔案,在 30 分鐘內將其提交至此儲存庫的預設分支。其他分支或分叉儲存庫中的檔案無效。然後在下方檢查檔案,即可無需管理員核准直接管理專案。請保留檔案以維持管理權限。
如需登入,將前往登入頁面。登入後請回到此處,再次點擊所選的驗證按鈕。
在專案資料夾中執行 /nomorevibe verify,即可驗證所有權並自行管理介紹。
一分鐘完成驗證AI-powered daily news portal, turborepo monorepo with Fastify API and Next.js frontend
翻譯準備中
取自儲存庫 README
正在載入完整 README。 以下為已儲存的摘錄。
AI-generated daily briefing that turns hundreds of news sources into one reliable read, in Portuguese and English.
Captured from production at 1440px. The full set — 12 routes across three widths, plus dark mode — is in docs/v2/baseline-v2/.
Following the news means opening a dozen sites and still not knowing what mattered. The volume is not the reader's problem to solve, but it is the one every feed hands them.檢視完整 README ↗AI-generated daily briefing that turns hundreds of news sources into one reliable read, in Portuguese and English.
English | Português
Live demo · API reference · Documentation
| Home | News archive | Daily briefing |
|---|---|---|
Captured from production at 1440px. The full set — 12 routes across three widths, plus dark mode — is in docs/v2/baseline-v2/.
Following the news means opening a dozen sites and still not knowing what mattered. The volume is not the reader's problem to solve, but it is the one every feed hands them.
Newra News collects hundreds of articles a day from NewsData.io and eleven Brazilian and international RSS feeds, removes duplicates, and writes a single daily briefing from the fifteen most recent stories. Every briefing cites the articles it was built from and states, on the page, that it was generated by AI. The archive stays browsable by date, category, and full-text search.
The point is not the automation — it is that a reader can trust what comes out of it. Sources are named, the generation date is visible, the model is disclosed, and everything the pipeline touched is still one click away.
Because the briefing goes live with no human in between, the system checks its own work. Two gates decide whether the day's briefing should be published at all, a suite of invariants asks whether the housekeeping that was supposed to happen actually did, and a heartbeat outside the stack e-mails the owner when today's briefing is missing or a page does not show it.
| Layer | Technologies |
|---|---|
| Frontend | Next.js 14.2 (App Router), React 18.3, TypeScript 5.9, Tailwind CSS 4.3, Base UI, TanStack Query 5.103, next-intl 3.26 |
| Backend | Fastify 4.29, Node.js 22, TypeScript 5.9, Zod 3.25, pino 9, next-auth 4.24 with a JWT shared between both apps |
| Database | PostgreSQL 16, Prisma ORM 5.22 |
| AI and data | Google Gemini (primary), Groq (fallback), NewsData.io, RSS feeds, Resend |
| Quality | Vitest 2.1, Testing Library, Playwright 1.63, ESLint, Prettier, Gitleaks, CodeQL, Dependabot, Lighthouse CI |
| Infra | Turborepo, pnpm workspaces, Vercel, Render, Neon, GitHub Actions |
flowchart LR
CRON["Daily cron 08:00 BRT"] --> ING[Ingest and normalize]
SRC["NewsData.io + 11 RSS feeds"] --> ING
ING --> DEDUP[Deduplicate]
DEDUP --> DB[(PostgreSQL)]
DEDUP --> SEL[Select 15 most recent]
SEL --> GIN{Entry gate}
GIN --> AI["Gemini, Groq fallback"]
AI --> GOUT{Output gate}
GOUT --> ART[Daily briefing]
ART --> MAIL[Newsletter]
MAIL --> INV[Invariant checks]
DB --> API[Fastify API]
API --> WEB[Next.js web]
ART -.->|refreshes the day's pages| WEB
The pipeline runs once a day at 08:00 BRT, triggered by a Vercel Cron job that wakes the API with a health check before firing. An internal Fastify scheduler points at the same instant, but it only runs if the process happens to be up — on the free tier it often is not, so it is a happy path rather than a safety net. It is idempotent per day: a second trigger on a day that already succeeded reports that it did nothing rather than running again. The web app reads the API through static generation with ISR, so a page that has already been built stays up even while the API is unavailable — and once the run succeeds, the cron route invalidates the day's pages, requests each of them, and checks that they carry the new run before it returns.
Six full diagrams are in docs/diagrams/: system architecture, frontend route map, session and authorization flow, entity relationships, pipeline sequence, and daily data flow.
newra-news/
├── apps/
│ ├── web/ # Frontend — Next.js 14 (App Router)
│ └── api/ # Backend — Fastify + TypeScript
├── packages/
│ ├── database/ # Prisma schema, client, migrations, seed
│ ├── types/ # TypeScript types shared across apps
│ ├── eslint-config/ # Shared ESLint configuration
│ └── tsconfig/ # Base TypeScript configuration
├── docs/ # Setup, architecture, API, diagrams, V2 and observability plans
├── scripts/ # Local bootstrap, dev database lifecycle, guard mutations
└── docker-compose.yml # PostgreSQL 16 + pgAdmin for local development
git clone https://github.com/tavinholoco/newra-news.git
cd newra-news
pnpm install
Three files, one per scope. Copy each example and fill it in — no real values are committed.
cp .env.example .env
cp apps/api/.env.example apps/api/.env
cp apps/web/.env.example apps/web/.env.local
Root — .env:
| Variable | Description | Required |
|---|---|---|
DATABASE_URL | PostgreSQL connection string used by Prisma | Yes |
Backend — apps/api/.env:
| Variable | Description | Required |
|---|---|---|
DATABASE_URL | PostgreSQL connection string | Yes |
GEMINI_API_KEY | Google Gemini key, the primary model for the briefing | Yes |
GROQ_API_KEY | Groq key, used when Gemini fails | Yes |
JOB_SECRET | Shared secret that authorizes the pipeline trigger endpoint | Yes |
AUTH_JWT_SECRET | Secret shared with the web app, which signs the JWTs the API validates. Must be identical in both | Yes for accounts |
NEWSDATA_API_KEY | NewsData.io key. Without it the pipeline runs on RSS feeds only | No |
PORT | API port. Defaults to 3001 | No |
HOST | Bind address. Defaults to 0.0.0.0 | No |
NODE_ENV | development, production, or test | No |
CORS_ORIGIN | Allowed origin. Defaults to http://localhost:3000 | No |
API_PUBLIC_URL | Public API address announced by the OpenAPI document | No |
GEMINI_MODEL | Gemini model id. Defaults to gemini-2.5-flash | No |
GROQ_MODEL | Groq model id. Defaults to openai/gpt-oss-20b | No |
CRON_SCHEDULE | Internal scheduler expression. Defaults to 0 8 * * * | No |
CRON_TIMEZONE | Scheduler timezone. Defaults to America/Sao_Paulo | No |
RESEND_API_KEY | Resend key. Without it the newsletter step is skipped | No |
SITE_URL | Public frontend URL used in newsletter links | No |
NEWSLETTER_FROM |
Frontend — apps/web/.env.local:
| Variable | Description | Required |
|---|---|---|
NEXT_PUBLIC_API_URL | API base URL. Defaults to http://localhost:3001/api | Yes in production |
NEXTAUTH_SECRET | next-auth session secret | Yes for accounts |
AUTH_JWT_SECRET | Same value as the API variable of the same name | Yes for accounts |
NEXTAUTH_URL | Site base URL, required for OAuth in production | Yes in production |
NEXT_PUBLIC_SITE_URL | Public URL used in SEO metadata. Derived from Vercel when absent | No |
GOOGLE_CLIENT_ID | Google OAuth client id | No |
GOOGLE_CLIENT_SECRET | Google OAuth client secret | No |
GITHUB_CLIENT_ID | GitHub OAuth client id | No |
GITHUB_CLIENT_SECRET | GitHub OAuth client secret | No |
CRON_SECRET | Token Vercel injects to authorize the cron route | No |
BACKEND_JOB_URL | Pipeline endpoint the cron route calls | No |
BACKEND_JOB_SECRET | Secret sent to that endpoint. Matches JOB_SECRET | No |
docker compose up -d
pnpm db:generate
pnpm db:migrate
pnpm db:seed
pnpm dev
The web app runs on http://localhost:3000 and the API on http://localhost:3001. On a fresh machine, ./scripts/dev-bootstrap.sh does every step above in one idempotent run.
| Command | Description |
|---|---|
pnpm dev | Runs web and API in development mode |
pnpm build | Production build across the monorepo |
pnpm lint | ESLint across the monorepo |
pnpm test | Vitest for backend and frontend. Needs no database |
pnpm db:generate | Generates the Prisma client |
pnpm db:migrate | Applies Prisma migrations in development |
pnpm db:migrate:deploy | Applies pending migrations without prompting |
pnpm db:migrate:status | Shows which migrations are applied |
pnpm db:studio | Opens Prisma Studio |
pnpm db:seed | Seeds the database with realistic data |
pnpm --filter @newranews/api test:coverage | API tests with the coverage threshold |
pnpm --filter @newranews/web test:coverage | Web tests with the coverage threshold |
pnpm guard:mutations | Breaks each guarded behaviour on purpose and checks that its guard fails |
pnpm --filter @newranews/api archive:hygiene | Measures text hygiene of the archive against production |
pnpm --filter @newranews/api gates:rehearse | Replays the two pipeline gates against the stored briefings |
pnpm --filter @newranews/web test:e2e | Playwright end-to-end specs |
pnpm --filter @newranews/web visual:baseline | Captures the visual baseline of the public routes |
pnpm --filter @newranews/web admin:capture | Captures the three admin tabs at 375 and 1440px, light and dark, with a locally forged session |
pnpm --filter @newranews/web lighthouse:audit | Lighthouse audit against production |
pnpm test
2,553 unit and integration tests across 194 suites — 1,529 for the API in 100 suites, 1,024 for the web app in 94. The suite needs no database and no network: the backend uses fastify.inject(), the frontend uses Testing Library on jsdom. Coverage is enforced at a 70% floor for lines, statements, functions, and branches in both apps, and CI fails below it.
Part of the suite guards the project against itself: tests that enumerate a surface and demand a decision for each item — every route has a line in docs/api.md and in the authorization matrix, every count written in prose matches the code it describes, every page has a rendering mode. pnpm guard:mutations breaks each guarded behaviour on purpose and checks that the guard actually fails.
End-to-end coverage is separate: one Playwright spec file per flow — visitor, archive, account, newsletter and authorization. They run against production through the Smoke E2E workflow on every push to main, and are deliberately not part of pnpm test. The signed-in flows (account and admin) are skipped by decision: running them would put the production session secret in CI. The login path is checked every day by a probe instead — /api/health/auth, which asks the API to accept a token signed by the web app.
pnpm --filter @newranews/web test:e2e
| Component | Where | How |
|---|---|---|
| Web | Vercel | Deploys on push to main. Static generation with ISR, plus a cron job hitting /api/cron/daily-news at 11:00 UTC that refreshes the day's pages once the run succeeds |
| API | Render | render.yaml blueprint, free plan, health check at /api/health |
| Database | Neon | Managed PostgreSQL. Migrations applied by the Migrate workflow, never from a local machine |
Seven GitHub Actions workflows back this up: CI (lint, tests with coverage, build, and a pnpm audit that fails on any high advisory in a production dependency), Gitleaks (secret scanning of every pull request and of every pushed range, merges included), CodeQL (static analysis of both apps), Smoke E2E (production flows after each deploy), Lighthouse CI (weekly, failing below 90 in any category), Migrate, and Heartbeat (daily at 12:40 UTC, from outside: the site, the API — telling a hosting suspension apart —, today's briefing, both Homes showing it, the login probe, and a push within the last 45 days — a failed run is the e-mail). Every workflow declares a minimal GITHUB_TOKEN permission set, and every third-party action is pinned to a full commit SHA — the accepted advisories, each with a reason, a date and a review trigger, live in docs/security-advisories.md.
The interactive Swagger UI is served in development only, at /api/docs. In production the OpenAPI document is still generated, but the UI is not registered — the contract lives in docs/api.md, guarded against drift by a test.
The V2 plan is documented in docs/Newra-News-V2-Frontend-Redesign-Plan.md, the observability plan in docs/Newra-News-Observability-Plan.md, and the phase-by-phase history in docs/progress.md.
Contributions are welcome.
git checkout -b feat/my-change
pnpm test
Branch naming, commit conventions, and the pull request checklist are in CONTRIBUTING.md. Local setup, environment variables, and troubleshooting are in docs/setup.md.
Distributed under the MIT License. See LICENSE for details.
Pedro Levi Dias — Fullstack Developer
| Sender address, on a domain verified in Resend |
| No |
ADMIN_EMAILS | Comma-separated emails that get the ADMIN role on sign-up | No |
LOG_LEVEL | Structured log level: fatal, error, warn, info, debug, trace, silent. Defaults to info (silent in tests) | No |