AFFAANhandClaude Opus 5 62c8281ed0 Merge Phase 1 campaign management
Campaign records, approval workflow, budget guardrails against the ad
account minimum, and Meta ad preview. Creates nothing in Meta.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-02 23:35:04 +05:30
2026-08-01 10:28:22 +05:30
2026-08-01 10:28:22 +05:30
2026-08-01 10:28:22 +05:30
2026-08-01 10:28:22 +05:30
2026-08-01 10:28:22 +05:30
2026-08-01 10:28:22 +05:30
2026-08-01 10:28:22 +05:30
2026-08-01 10:28:22 +05:30
2026-08-01 10:28:22 +05:30
2026-08-01 10:28:22 +05:30
2026-08-01 10:28:22 +05:30

MaskanX Backend

Independent Python and FastAPI backend for MaskanX.

Service Contract

  • Default API URL: http://127.0.0.1:8088
  • PostgreSQL database: campaign
  • Frontend origin: configured with MASKANX_CORS_ORIGINS
  • Maskan CRM: connected through its versioned REST API and optional MCP tools

This repository never connects directly to the Maskan CRM PostgreSQL database.

Local Setup

  1. Create the database:

    CREATE DATABASE campaign;
    
  2. Install dependencies and configure the environment:

    python -m venv .venv
    .\.venv\Scripts\python.exe -m pip install -e ".[dev]"
    npm install
    Copy-Item .env.local.example .env.local
    
  3. Migrate, seed, and start:

    npm run local:migrate
    npm run local:seed
    npm run local
    

Open API documentation at http://127.0.0.1:8088/docs when documentation is enabled.

Upgrading from JSON storage

Chats and cron jobs were originally stored as JSON files in the working directory (~/.adclaw/chats.json and ~/.adclaw/jobs.json). PostgreSQL is now the default backend, so those records are no longer read and existing chats, sessions, and scheduled jobs appear to be missing even though nothing was deleted.

Copy them into PostgreSQL once:

npm run local:import-json -- --dry-run   # report what would be imported
npm run local:import-json                # perform the import

The import upserts by id and never deletes, so it is safe to re-run. The JSON files are left untouched as a backup.

Campaigns

Campaign records live in PostgreSQL. Apply the schema before first use:

npm run local:migrate

Set META_ADS_ACCESS_TOKEN in Settings > Environments, and enable the meta_ads MCP client if you also want campaign tools in chat.

What Phase 1 does and does not do

Phase 1 owns campaign records, the approval workflow, budget guardrails and ad preview. It creates nothing in Meta. The only Meta calls are two reads:

  • GET /act_<id> for balance, spend and min_daily_budget
  • GET /act_<id>/generatepreviews to render ad previews

There is deliberately no launch or sync endpoint. A campaign moves draft -> pending_approval -> approved and stops there; pushing objects to Meta arrives in Phase 2.

Ad previews are rendered from a creative spec and create no Meta objects, so the wizard can show exactly how an ad will look across desktop feed, mobile feed, Instagram feed, Instagram story, Facebook story and right column before anything exists.

Budget minimums

Each ad account publishes a min_daily_budget in minor units. The API rejects a lower daily budget with 422 and a readable message rather than letting Meta reject it opaquely later. If the account cannot be read (no token, rate limit), the check is skipped and a warning is logged: the budget guardrail is not enforced in that case.

Delivery requires a payment method

Meta will not deliver ads until a funding source is attached to the ad account. This cannot be done from MaskanX — the Marketing API does not allow creating funding instruments, so it must be done in Meta Business Manager. The Campaigns page shows a warning when no payment method is present.

Clearing and restoring credentials

Stop MaskanX before clearing credentials. A running app holds its configuration in memory and can rewrite config.json and providers.json after the clear, silently restoring the values that were just removed. The clear script now verifies the result and exits non-zero if anything secret-looking survives.

Credentials live in three places, and all of them are cleared:

  • ~/.adclaw.secret/providers.json - LLM provider keys
  • ~/.adclaw.secret/envs.json - integration keys
  • ~/.adclaw/companies/<id>/ - a per-company copy of both, plus LinkedIn OAuth

Because each company keeps its own copy, an intact company store can be used to recover the active one:

npm run secrets:restore -- --from default --dry-run
npm run secrets:restore -- --from default

Credential files are copied wholesale; config.json is patched surgically, so only missing MCP env values and disabled clients are restored and company-specific settings are preserved. A backup is written to ~/.adclaw.secret/restore-backups/ first.

Commands

Command Purpose
npm start Start using .env or injected environment variables
npm run build Build a Python wheel in dist/
npm run local Local API with reload
npm run local:migrate Apply local PostgreSQL migrations
npm run local:migrate:undo Roll back the newest migration
npm run local:migrate:undo:all Roll back every migration
npm run local:seed Add starter data
npm run local:seed:undo Remove starter data
npm run local:import-json Import legacy JSON chats/cron jobs into PostgreSQL
npm run secrets:dry-run Report which credentials a clear would remove
npm run secrets:clear Clear runtime credentials (creates a backup)
npm run secrets:clear:all Also blank secret lines in .env files (creates a backup)
npm run secrets:delete:all Clear everything including .env files (creates a backup)
npm run secrets:delete:all:no-backup Same, with no backup - irreversible
npm run secrets:restore Restore credentials from an intact company store
npm run local:reset Rebuild and seed the local schema
npm run dev Development API with reload
npm run dev:migrate Apply development migrations
npm run dev:reset Reset the development schema
npm test Run Pytest using .env.testing
npm run test:migrate Apply test database migrations
npm run test:reset Reset the test schema
npm run prod Start the production API
npm run prod:migrate Apply production migrations
npm run prod:seed Apply production starter data

The command names match the platform deployment contract. They invoke the native MaskanX Python migration runner, not Sequelize.

Docker

Copy-Item .env.local.example .env
docker compose up -d --build

For a managed PostgreSQL deployment, provide DATABASE_URL through the hosting secret manager and run npm run prod:migrate as a release step.

S
Description
No description provided
Readme
7.6 MiB
Languages
Python 97.7%
HTML 1.1%
JavaScript 0.5%
Shell 0.4%
Dockerfile 0.2%
Other 0.1%