{"id":50292292,"url":"https://github.com/oratis/adex","last_synced_at":"2026-05-28T06:16:06.261Z","repository":{"id":352063863,"uuid":"1212018536","full_name":"oratis/adex","owner":"oratis","description":"AI-powered ad placement and creative automation platform — Next.js 16 + Postgres + GCS on Cloud Run","archived":false,"fork":false,"pushed_at":"2026-05-14T07:52:50.000Z","size":1056,"stargazers_count":5,"open_issues_count":0,"forks_count":3,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-05-14T09:49:40.726Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/oratis.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":"AGENTS.md","dco":null,"cla":null}},"created_at":"2026-04-16T01:34:05.000Z","updated_at":"2026-05-14T07:52:54.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/oratis/adex","commit_stats":null,"previous_names":["oratis/adex"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/oratis/adex","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/oratis%2Fadex","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/oratis%2Fadex/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/oratis%2Fadex/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/oratis%2Fadex/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/oratis","download_url":"https://codeload.github.com/oratis/adex/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/oratis%2Fadex/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33596538,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-05-28T02:00:06.440Z","response_time":99,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-05-28T06:16:04.481Z","updated_at":"2026-05-28T06:16:06.249Z","avatar_url":"https://github.com/oratis.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Adex\n\nAI-powered ad placement and creative automation platform. Adex helps marketers connect ad accounts (Google Ads, with more platforms planned), generate creative assets with AI (image, video, audio via Seedance2), sync performance data, and orchestrate campaigns from a single dashboard.\n\nBuilt on Next.js 16 with the App Router, deployed on Google Cloud Run with Cloud SQL Postgres and Google Cloud Storage for persistent state.\n\n## Features\n\n- **Multi-platform ad account auth** — OAuth2 for Google Ads (MCC supported), token-based config for Meta, TikTok, AppsFlyer, Adjust\n- **Unified performance sync** — One \"Sync Data\" button pulls metrics from Google Ads, Meta, TikTok, AppsFlyer and Adjust into Postgres; dashboard always reads from DB so transient API failures never crash the UI\n- **7-day trend chart** — Dependency-free SVG line chart with spend-by-platform overlay\n- **AI Advisor** — Powered by Claude (Anthropic API) when `ANTHROPIC_API_KEY` is set; falls back to rule-based recommendations otherwise (low ROAS, low CTR, high CPA, scale opportunities)\n- **AI creative generation** — Seedance2 (doubao-seedance-2-0) for text2video/image2video, plus `/api/creatives/generate-copy` for LLM-written headline/description/CTA variants\n- **Campaign + Creative + Budget CRUD** — Edit, pause/resume, delete; attach creatives to campaigns (auto-creates default ad group)\n- **Asset library** — Upload to Google Cloud Storage, folder browsing, multi-select with bulk delete/tag, Google Drive sync\n- **Daily digest email** — Nightly performance summary via SMTP (nodemailer); optional LLM-generated executive summary\n- **Signed session cookies** — HMAC-SHA256 session tokens with TTL; server-side auth guard on dashboard routes\n\n## Tech Stack\n\n- **Frontend / API**: Next.js 16.2 (App Router, Turbopack, standalone output), React 19, Tailwind\n- **Database**: PostgreSQL 16 (Cloud SQL) via Prisma 7 with `@prisma/adapter-pg`\n- **Storage**: Google Cloud Storage (REST API + ADC token from metadata server)\n- **Hosting**: Google Cloud Run (containerized, basePath `/adex`)\n- **AI**: Seedance2 (doubao-seedance-2-0) for video; pluggable provider layer\n\n## Quick Start (local)\n\n```bash\n# 1. install\nnpm install\n\n# 2. configure\ncp .env.example .env\n# edit .env — at minimum set DATABASE_URL pointing at any Postgres instance\n\n# 3. migrate\nnpx prisma migrate deploy\n\n# 4. run\nnpm run dev\n```\n\nOpen \u003chttp://localhost:3000/adex\u003e.\n\nFor local file uploads to GCS, either run with Application Default Credentials (`gcloud auth application-default login`) or set `GOOGLE_ACCESS_TOKEN` in `.env`.\n\n## Deployment to Google Cloud Run\n\nThe repo includes a production-ready `Dockerfile` and `start.sh` (runs `prisma migrate deploy` then boots the standalone server).\n\n```bash\n# Build \u0026 push\ngcloud builds submit --tag gcr.io/YOUR_PROJECT/adex\n\n# Deploy (set env vars via --set-env-vars or Secret Manager)\ngcloud run deploy adex \\\n  --image gcr.io/YOUR_PROJECT/adex \\\n  --region us-central1 \\\n  --allow-unauthenticated \\\n  --add-cloudsql-instances YOUR_PROJECT:REGION:INSTANCE \\\n  --set-env-vars \"DATABASE_URL=postgresql://...,GCS_BUCKET=...,...\"\n```\n\nNotes:\n- The Cloud Run service account needs `roles/storage.objectAdmin` on the GCS bucket and `roles/cloudsql.client` for Cloud SQL.\n- Prisma Migrate does not parse the Cloud SQL Unix-socket DSN format — use the public IP DSN at runtime, with the instance allow-listed for the Cloud Run egress range (or use a connector).\n\n## Project Structure\n\n```\nsrc/\n  app/\n    (dashboard)/         # authed UI routes (basePath /adex)\n    api/\n      assets/            # asset CRUD + GCS upload\n      auth/google/       # OAuth2 connect for Google Ads\n      reports/sync/      # pull metrics into DB\n      seedance2/         # AI video generation\n  lib/\n    prisma.ts            # PrismaClient with pg.Pool adapter, lazy init\n    storage.ts           # GCS upload/delete via REST + ADC\n    auth.ts              # cookie session helpers\n    platforms/           # external API clients (google-ads, seedance2, ...)\nprisma/\n  schema.prisma          # PostgreSQL schema\n  migrations/\n```\n\n## Architecture Notes\n\n- **Persistence first.** No SQLite, no local disk for user data. Everything goes to Cloud SQL or GCS so a Cloud Run cold start never loses anything.\n- **Crash-safe dashboard.** API responses are validated (`res.ok` + `Array.isArray`) before being rendered. A failed external sync surfaces as a banner, not a white screen.\n- **Lazy Prisma client.** `src/lib/prisma.ts` returns a `Proxy` when `DATABASE_URL` is unset, so Next.js build-time page-data collection doesn't need a live DB.\n- **basePath `/adex`.** All routes are mounted under `/adex` to coexist with sibling apps on the same domain.\n\n## Security\n\n- Don't commit `.env`. The repo's `.env.example` documents every variable.\n- OAuth tokens are stored in the `PlatformAuth` table (per user, per platform). Refresh tokens are kept; the callback flow forces `prompt=consent` so a refresh token is always issued.\n- Hardcoded keys / URLs have been scrubbed — all secrets come from environment.\n\n## Continuous Integration\n\nEvery push and PR to `main` runs a GitHub Actions workflow that:\n\n1. installs dependencies with `npm ci`\n2. runs `prisma generate`\n3. runs `eslint` + `tsc --noEmit`\n4. runs `next build` to catch runtime-level regressions\n\nSee `.github/workflows/ci.yml`.\n\n## License\n\n[MIT](./LICENSE) © 2026 Oratis\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Foratis%2Fadex","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Foratis%2Fadex","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Foratis%2Fadex/lists"}