Tabor Ads is an ad analytics SaaS platform. It combines pixel-based impression tracking, team workspaces, campaigns with multiple ads, an analytics dashboard, and real-time traffic simulation.
- Responsive public landing page with plan details.
- Sign-up and login with email/password or Google OAuth.
- Signed email verification before workspace tools are enabled.
- Automatic linking of Google identities to existing accounts with the same email address.
- Isolated workspaces, email invitations, and per-member campaign/metrics permissions.
- Profile page with plan details, Premium expiration date, quota usage, account information, and password creation/change.
- PT-BR and EN interfaces; the public language preference is stored in the browser, while the authenticated preference is also saved to the user profile.
- Standard campaigns with multiple ads and a unique tracking pixel for each ad.
- Unlimited simulation campaigns that do not count toward plan quotas.
- Dashboard with date range, campaign, and ad filters, totals, browser breakdown, period comparison, and daily trends.
- WebSocket updates through private workspace and campaign channels.
- Embedded Mercado Pago checkout with credit card and Pix payments on the same page.
- Global success and error feedback through toast notifications.
Public tracking currently measures impressions. Click and conversion tracking are not part of this version yet.
| Feature | Free | Premium |
|---|---|---|
| Active standard campaigns | 3 | 20 |
| Ads per standard campaign | 1 | 10 |
| Workspace members | 1 | 5 |
| Dashboard | On-demand snapshot | Real-time |
| Real-time simulator | Yes | Yes |
simulation campaigns do not count toward the campaign quota. When the simulator is enabled, all simulation campaigns receive events every second for up to three minutes. Each campaign receives a different, stable weight for the duration of the session, preventing their series from converging on nearly identical volumes. Starting a new session redistributes the weights.
- Laravel 13 and PHP 8.4
- Vue 3, Vue Router, Pinia, Vue I18n, Chart.js, and Laravel Echo
- MySQL 8
- Laravel Reverb for WebSockets
- Laravel Socialite for Google OAuth
- Mercado Pago embedded checkout
- Docker Compose and Nginx
- PHPUnit and Laravel Pint
Browser
`-- frontend :3000 (Nginx + Vue SPA)
|-- /api, /t, and /broadcasting -> webserver :8080
`-- /app/{reverb-key} -> reverb :8080
webserver (Nginx/FastCGI)
`-- backend (Laravel PHP-FPM)
`-- db (MySQL 8)
Laravel handles authentication, authorization, domain logic, tracking, analytics, billing, webhooks, and broadcasting. Reverb runs within the same Laravel application and publishes only to authorized private channels.
Prerequisite: Docker Desktop with Docker Compose.
- Copy the example environment file:
Copy-Item .env.example .env- Fill in the optional Google and Mercado Pago credentials.
- Start the environment:
docker compose up -d --build- Open:
- Application:
http://127.0.0.1:3000 - API health check:
http://127.0.0.1:8080/up
Migrations run automatically when the backend container starts. The local database is persisted in data/, which must not be committed.
To monitor the services:
docker compose ps
docker compose logs -f backend reverb frontendThe .env.example file documents every variable used by Docker Compose. The main categories are:
- Application:
APP_KEY,APP_URL,FRONTEND_URL,SESSION_DOMAIN. - MySQL:
DB_NAME,DB_USER,DB_PASSWORD,DB_ROOT_PASSWORD. - Google OAuth:
GOOGLE_CLIENT_ID,GOOGLE_CLIENT_SECRET,GOOGLE_REDIRECT_URI. - Transactional email:
MAIL_MAILER,MAIL_HOST,MAIL_PORT,MAIL_USERNAME,MAIL_PASSWORD, and sender identity. - Mercado Pago:
MERCADO_PAGO_PUBLIC_KEY,MERCADO_PAGO_ACCESS_TOKEN,MERCADO_PAGO_WEBHOOK_SECRET, prices, and notification URL. - Reverb:
REVERB_APP_ID,REVERB_APP_KEY,REVERB_APP_SECRET, host, port, and scheme.
Never commit .env, access tokens, private keys, or MySQL volume data.
Tabor Ads uses Laravel Mail through SMTP for account verification and workspace invitations. The recommended provider is Resend, although any compatible SMTP service can be used.
APP_URL must be the public HTTPS address of the application (for example, https://tabor-ads.example.com), because Laravel uses it to generate the signed email-verification links. FRONTEND_URL must point to the same public frontend origin.
For Resend in production, verify the sending domain, create an API key, and configure:
MAIL_MAILER=smtp
MAIL_SCHEME=null
MAIL_HOST=smtp.resend.com
MAIL_PORT=587
MAIL_TIMEOUT=10
MAIL_USERNAME=resend
MAIL_PASSWORD=re_your_api_key
MAIL_FROM_ADDRESS=notifications@your-domain.com
MAIL_FROM_NAME="Tabor Ads"
MAIL_LOGO_URL=https://your-domain.com/brand/tabor-ads-logo.svgMAIL_LOGO_URL is optional, but it should use a publicly accessible HTTPS URL when emails are opened in Gmail, Outlook, or another remote client. This is especially useful during local development, since those clients cannot load an image from 127.0.0.1.
The sender address must belong to the domain verified with the email provider. New email/password accounts receive a signed verification link that expires after 60 minutes. Workspace invitation links expire after seven days and preserve the permissions selected by the workspace owner. Google accounts are considered verified by the identity provider.
Create a Web application OAuth client in Google Cloud and configure:
GOOGLE_CLIENT_ID=
GOOGLE_CLIENT_SECRET=
GOOGLE_REDIRECT_URI=http://127.0.0.1:3000/api/auth/google/callbackThe URI registered with Google must match GOOGLE_REDIRECT_URI exactly, including its scheme, host, port, and path. In production, replace it with the public HTTPS domain.
When Google returns an email address that already exists, the identity is linked to the current account without removing its password. Users whose accounts were created through Google can set a password later on the profile page.
Use the Public Key and Access Token from the same application and environment (test or production):
MERCADO_PAGO_BASE_URL=https://api.mercadopago.com
MERCADO_PAGO_PUBLIC_KEY=
MERCADO_PAGO_ACCESS_TOKEN=
MERCADO_PAGO_WEBHOOK_SECRET=
MERCADO_PAGO_PREMIUM_MONTHLY_AMOUNT=1.90
MERCADO_PAGO_PREMIUM_ANNUAL_AMOUNT=9.90
MERCADO_PAGO_CURRENCY=BRL
MERCADO_PAGO_NOTIFICATION_URL=https://ads.your-domain.com/api/webhooks/mercadopagoCredit card data is tokenized by the Mercado Pago SDK in the browser; the card number, expiration date, and CVV never pass through Laravel. Pix payments are created through the authenticated API, and the QR code is displayed inside the checkout form. Neither flow redirects users to a Mercado Pago login page.
Prices are never accepted from the frontend: the backend selects the amount based on the validated billing period.
- Monthly Premium: R$1.90 for one month of access.
- Annual Premium: R$9.90 for twelve months of access.
- Both are one-time Pix or credit card payments with manual renewal in this version.
Configure the Mercado Pago payment webhook:
POST https://ads.your-domain.com/api/webhooks/mercadopago
When configured, the webhook validates the request signature, processes events idempotently, and retrieves the payment again before changing the local subscription. Approved payments activate or extend Premium; cancellations, refunds, and chargebacks remove the associated access period.
Mercado Pago cannot deliver webhooks to a local or private URL. For testing, use an HTTPS tunnel and set MERCADO_PAGO_NOTIFICATION_URL to its public URL.
Run the frontend with Vite:
Set-Location frontend
npm ci
npm run devCreate a production build:
Set-Location frontend
npm run buildRun Laravel tests and style checks:
docker compose exec -T backend php artisan test
docker compose exec -T backend vendor/bin/pint --testValidate the complete container setup:
docker compose config --quiet
docker compose build backend frontendThe Continuous Integration workflow runs on pushes and pull requests targeting main and develop:
- PHP 8.4, Composer, Laravel Pint, and the complete PHPUnit test suite.
- Node.js 22,
npm ci, and the Vite production build. - Docker Compose validation and builds of the main images.
The Deploy to OCI Production workflow starts only after CI succeeds on main, or manually through workflow_dispatch. It:
- Connects to the server over SSH.
- Deploys the exact commit SHA validated by CI.
- Confirms that the production
.envfile exists. - Validates the Compose configuration and recreates only the required services.
- Checks
http://127.0.0.1:8080/up. - Rolls back to the previous commit if the health check fails.
Required secrets in the GitHub Production environment:
OCI_SERVER_IPOCI_SERVER_USEROCI_SSH_KEY
The server must have Git, Docker with Compose, read access to the repository, and a persistent ~/impression-track/.env file. The SSH user must be able to run sudo docker compose non-interactively. The public domain and TLS termination must point to the local services exposed by Compose.
.github/workflows/ CI and OCI deployment
api/ Laravel application, migrations, and tests
config/nginx.conf API Nginx/FastCGI configuration
frontend/ Vue landing page and application
data/ Git-ignored local MySQL volume
docker-compose.yml Integrated environment
The previous PHP and WebSocket implementations have been removed. The single backend source is api/, and Laravel Reverb is the official real-time server.