ads

package
v1.801.473 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 5, 2026 License: Apache-2.0 Imports: 20 Imported by: 0

Documentation

Overview

Package ads is your paid ad campaigns, launched and paused from one place.

A campaign carries an objective, a budget and reported spend, and runs on Meta, Google, TikTok, Reddit, LinkedIn or Microsoft with the org's own connector token.

It is also the PAID executor of the go-to-market plane: apps/campaign fans its paid channel out to LaunchPaid/PaidSpend/PausePaid (provider.go), and this surface runs the same campaigns standalone.

The AdCampaign entity is the root of the ad hierarchy (campaign → ad sets → ads): a named campaign on an ad Platform (meta/google/tiktok/x), a lifecycle Status (draft/active/paused/completed), an Objective, and Budget/Spend in minor units (cents). The ad-set and ad legs of the hierarchy are follow-ups that hang off this seam; this is the reviewable domain + campaign CRUD they attach to.

Tenant isolation is enforced SERVER-SIDE on every request: the org is principal.Org(c) — the value SanitizeIdentity minted from the VALIDATED bearer owner claim (HIP-0026) — and NEVER a client-supplied header. Every store query filters WHERE org=?, so one tenant can never read or mutate another's data.

Surface (all org-scoped; /v1 only):

GET    /v1/ads/summary            per-org roll-up (total/active/budget/spend)
GET    /v1/ads/campaigns          list campaigns (?status=)      -> {data:[…]}
POST   /v1/ads/campaigns          create a campaign              -> AdCampaign (201)
GET    /v1/ads/campaigns/:id      campaign detail                -> AdCampaign
PUT    /v1/ads/campaigns/:id      update a campaign              -> AdCampaign
DELETE /v1/ads/campaigns/:id      delete a campaign
POST   /v1/ads/campaigns/:id/launch  run it on the provider      -> AdCampaign

serve.go auto-registers GET /v1/ads/health (this subsystem does not set OwnsHealth, so the generic always-ok liveness route serves it).

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func Mount

func Mount(app cloud.Router, deps cloud.Deps) error

Mount wires the ads surface onto app per HIP-0106. It keeps a package global (mounted) for Shutdown, so it constructs the Service value directly — the same "complex flavour" clients/crm uses.

func PaidSpend

func PaidSpend(ctx context.Context, org string, ref PaidRef) (int64, error)

PaidSpend reads the provider-reported spend (minor units) for a launched ad campaign. Fail-closed on the token; honest 0 when the platform is unsupported.

func PausePaid

func PausePaid(ctx context.Context, org string, ref PaidRef) error

PausePaid pauses a launched ad campaign on its provider. Fail-closed on the token.

func Shutdown

func Shutdown() error

Shutdown closes the ads store. Idempotent.

Types

type AdCampaign added in v1.801.350

type AdCampaign struct {
	ID         string `json:"id"`
	Org        string `json:"-"`
	Name       string `json:"name"`
	Platform   string `json:"platform"`
	Account    string `json:"account,omitempty"`    // provider ad-account ref (Meta act_<id>)
	ExternalID string `json:"externalId,omitempty"` // provider campaign id after a launch
	Status     string `json:"status"`
	Objective  string `json:"objective"`
	Budget     int64  `json:"budget"`
	Spend      int64  `json:"spend"`
	CreatedAt  int64  `json:"createdAt"`
	UpdatedAt  int64  `json:"updatedAt"`
}

AdCampaign is an org-scoped ad campaign — the root of the ad hierarchy (campaign → ad sets → ads; the ad-set/ad legs hang off this seam as follow-ups). Budget and Spend are minor units (cents). Platform is the ad network (meta/google/tiktok/x); Status is the lifecycle (draft/active/paused/completed) — both validated at the write layer against the fixed vocabularies in ads.go.

type PaidPlan

type PaidPlan struct {
	Platform    string
	Account     string // provider ad-account ref (Meta: act_<id> or <id>)
	Name        string
	Objective   string // provider objective enum; defaulted per-provider when empty
	BudgetCents int64
	ScheduleAt  int64
}

PaidPlan is the standalone contract for launching one ad campaign — the campaign paid channel adapts campaign.Plan onto it (apps/wire_seams.go). BudgetCents is the org's own (connector-paid) budget; ScheduleAt an optional start time.

type PaidRef

type PaidRef struct {
	Platform   string
	Account    string
	ExternalID string
	Status     string
	Detail     string
}

PaidRef is a launched ad campaign's durable handle: the provider-side id plus the platform/account needed to read spend or pause it.

func LaunchPaid

func LaunchPaid(ctx context.Context, org string, p PaidPlan) (PaidRef, error)

LaunchPaid creates the ad campaign on its provider using the org's connector token. Fail-closed: it resolves the token BEFORE any provider call. Meta is executed for real; other connected platforms return errUnsupportedPlatform (the connector is verified, only the provider impl is the remaining gap).

type Store

type Store struct {
	// contains filtered or unexported fields
}

Store is the ads database. ONE SQLite file — the system namespace's "ads" — holds every org's records; tenant isolation is the `org` column, enforced on EVERY query. This mirrors clients/crm exactly (the ONE storage pattern). MaxOpenConns(1) serializes writes against the single-writer file.

func (*Store) Close

func (s *Store) Close() error

Close closes the underlying database. Idempotent-safe via sql.DB.

func (*Store) Counts

func (s *Store) Counts(ctx context.Context, org string) (total, active int, budget, spend int64, err error)

Counts returns the per-org campaign roll-up: total campaigns, how many are active, and the summed budget + spend (cents) — a real, non-fabricated summary for the ads module's overview cards.

func (*Store) CreateCampaign

func (s *Store) CreateCampaign(ctx context.Context, c AdCampaign) (AdCampaign, error)

func (*Store) DeleteCampaign

func (s *Store) DeleteCampaign(ctx context.Context, org, id string) (bool, error)

func (*Store) GetCampaign

func (s *Store) GetCampaign(ctx context.Context, org, id string) (AdCampaign, error)

func (*Store) ListCampaigns

func (s *Store) ListCampaigns(ctx context.Context, org, status string, limit int) ([]AdCampaign, error)

ListCampaigns lists the org's campaigns, optionally filtered by status (status=="" means all). Most-recently-updated first.

func (*Store) MarkLaunched

func (s *Store) MarkLaunched(ctx context.Context, org, id, account, externalID string, updatedAt int64) (AdCampaign, error)

MarkLaunched records the provider execution on a stored campaign: its account + external id, and status=active. Org-scoped — a cross-tenant id affects zero rows (errNotFound), never a foreign mutation. This is the ONLY writer of external_id, so the launch link is never clobbered by a user edit.

func (*Store) UpdateCampaign

func (s *Store) UpdateCampaign(ctx context.Context, c AdCampaign) (AdCampaign, error)

UpdateCampaign edits the user-owned fields. external_id is deliberately NOT in the SET list: it is launch-owned (MarkLaunched sets it), so editing a campaign never clobbers the link to its live provider execution.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL