NovaBrand Platform Documentation
Central documentation for the NovaBrand platform — a brand engagement system built on blockchain-based membership programs.
Platform Overview
NovaBrand enables brands to create digital membership programs ("Passes") with on-chain tokens, activations, collectibles, and rewards. The platform consists of:
Key System Components
Backend (Django) — REST APIs, business logic, admin portals, Celery task dispatch. Multiple admin sites: Brand Manager, User Manager, Support, Blockchain, Config.
Frontend (React) — Single-page app deployed to S3, served via CloudFront with environment-specific subdomains (dev/staging/app).
Database (PostgreSQL + RDS) — Core data store with HStore extension. Automated RDS snapshots for backup.
Celery + Redis — Async task processing for blockchain operations (minting, airdrops, token transfers), email dispatch, and scheduled cron jobs. Workers managed via Supervisor on a dedicated EC2 instance.
Blockchain (Flow) — Smart contracts (Cadence) for NFT-based membership tokens. Brands, Passes, and Collectibles are represented on-chain. Backend interacts via flow-py-sdk.
Stripe — Payment processing with webhook integration at /api/v1/payments/webhook/.
Shopify Plugin — Embedded Shopify sales channel app (React 19 + Polaris). Enables merchants to sync products, manage orders, and link their accounts — all within the Shopify Admin.
AWS Services — EC2 (app + worker servers), RDS (PostgreSQL), S3 (static/media), Lambda (image compression, wallet verification, meta tags), CloudFront (CDN), SES (email).
Architecture Diagram
Repositories
| Repository | Stack | Description |
|---|---|---|
novabrand-backend | Django 3.2, Python 3.10 | REST APIs, admin, Celery, blockchain integration |
novabrand-fe | React 18, Vite 6, TypeScript | Frontend SPA, CloudFront-hosted |
novabrand-shopify | React 19, Vite 7, Polaris | Embedded Shopify sales channel app |
novabrand-docs | — | Platform-wide documentation (this repo) |
Technical Architecture & API Reference
1. System Architecture Overview
1.1 High-Level Architecture
NovaBrand is a web platform that connects brands and consumers around digital memberships ("passes"), collectibles, incentives, and commerce. The system is split into:
| Layer | Repository | Technology | Role |
|---|---|---|---|
| Frontend | novabrand-fe | React 18, Vite 6, TypeScript, TanStack Query, Tailwind, MUI | SPA for brands and consumers; all API calls go to the backend base URL. |
| Backend | novabrand-backend | Django 3.2, Django REST Framework, Python 3.10 | REST API under /api/v1/, auth, business logic, integrations. |
| Shopify Plugin | novabrand-shopify | React 19, Vite 7, TypeScript, Shopify Polaris v13, App Bridge v3 | Embedded Shopify sales channel app for product sync, orders, and account linking. |
| External services | — | AWS (S3, SES, Lambda, CloudFront), Stripe, Flow blockchain, Firebase, Sentry | Storage, payments, blockchain, notifications, monitoring. |
1.2 Request Flow
- User opens the app in the browser.
- Frontend resolves the API base URL from
VITE_API_BASE_URLand sends all API requests to that origin. - Backend serves REST endpoints under
https://<backend-host>/api/v1/... - Authenticated requests include the header:
Authorization: Token <token>. - Backend uses Django ORM (PostgreSQL in production, SQLite in dev), optional Redis for cache, Celery for async jobs, and integrates with S3, Stripe, Flow, Shopify, and Firebase as needed.
1.3 Deployment Context
- Frontend and backend are deployed separately (frontend on S3 + CloudFront CDN, backend on EC2 with Nginx + uWSGI).
- Celery workers run on a dedicated EC2 instance, managed via Supervisor.
- CORS, allowed hosts, and API base URL are configured per environment.
Frontend environments (automated via GitHub Actions):
| Branch | Environment | URL |
|---|---|---|
main | Production | app.example.com |
staging | Staging | staging.example.com |
dev | Dev | dev.example.com |
Backend deployment is manual (SSH → git pull → migrate → uWSGI reload).
2. Dependency Graph
2.1 Frontend Dependencies
2.2 Backend Dependencies
3. API Endpoints
All routes below are relative to the backend base URL and the prefix /api/v1/. Unless stated otherwise, endpoints expect Authorization: Token <token> for authenticated access.
3.1 Meta Tags
| Method | Route | View / Handler | Notes |
|---|---|---|---|
| GET | meta-tags/brand/<brand_id>/pass/<pass_id> | GetPassMetatag | Pass meta for SEO |
| GET | meta-tags/brand/<brand_id> | GetBrandMetatag | Brand meta for SEO |
| GET | meta-tags/user-profile/<username> | GetUserMetatag | User profile meta for SEO |
3.2 Brand
| Method | Route | View / Handler | Notes |
|---|---|---|---|
| GET | brand/ | BrandAPIViewSet (list) | List brands; filters: tags, search, is_featured |
| POST | brand/ | BrandAPIViewSet (create) | Create brand (authenticated) |
| GET | brand/<pk>/ | BrandAPIViewSet (retrieve) | Brand detail (public or dashboard) |
| PUT/PATCH | brand/<pk>/ | BrandAPIViewSet (update) | Update brand (admin permission) |
| GET | brand/names/ | BrandNamesListAPIView | List brand names |
| GET | brand/get-ids/ | BrandNamePassNameAPIView | Resolve brand/pass IDs from names |
| GET | brand/tags/ | TagsListAPIView | List tags |
| POST | brand/brand-invitation/ | BrandInvitationAPIView | Create brand invitation |
| POST | brand/buy/ | BuyBrandApiView | Start brand purchase (Stripe checkout) |
| GET | brand/brand-plans/ | BrandPlansListAPIViewSet | List brand plans |
| GET | brand/<brand_id>/all-passes-name | BrandPassesNameListAPIView | Pass names for brand |
| POST | brand/<brand_id>/pass-with-release/ | CreatePassWithReleaseAPIView | Create pass + release in one call |
| GET | brand/<brand_id>/pass/ | ListCreatePass | List/create passes for brand |
| GET | brand/<brand_id>/pass/<pk>/members | PassMembersAPIView | Pass members |
| POST | brand/<brand_id>/pass/<pk>/update-members | AddUpdatePassMembersAPIView | Add/update members |
| GET | brand/<brand_id>/pass/<pk>/check-membership | CheckMembershipAPIView | Check membership |
| GET | brand/<brand_id>/pass/<pk>/download-member-data | ExportMembersData | Export member data |
| POST | brand/pass/<pass_id>/publish/ | PublishPass | Publish pass |
| POST | brand/pass/<pass_id>/settle-purchase/ | SettlePassPurchase | Settle purchase (e.g. tx hash) |
| GET/POST | brand/pass/<pass_id>/release/ | CreateReleaseAPIView | List/create releases |
| GET/PUT/PATCH/DELETE | brand/pass/<pass_id>/release/<pk> | UpdateDeleteReleaseAPIView | Update/delete release |
| POST | brand/pass/<pass_id>/release/<pk>/publish/ | PublishRelease | Publish release |
3.3 Pass (Public / Consumer-Facing)
| Method | Route | View / Handler | Notes |
|---|---|---|---|
| GET | pass/ | PublicPassListRetrieveView (list) | List public passes |
| GET | pass/<pass_id>/ | PublicPassListRetrieveView (retrieve) | Public pass detail |
| GET | pass/<pass_id>/getPurchaseArgs/ | PurchaseArgsForPass | Purchase args (Stripe/Dapper) |
| GET | pass/<pass_id>/collectibles/ | PassCollectiblesViewSet | Collectibles for pass |
| GET | pass/<pass_id>/incentive-collectible-notifications/ | PassIncentivesCollectiblesNotificationsAPI | Incentive/collectible notifications |
| POST | pass/<pass_id>/claim/ | ClaimPass | Claim pass (e.g. event) |
| GET | pass/<pass_id>/event-claim-request/ | PassClaimResponseAPIView | Event claim request |
| POST | pass/<pass_id>/update-event-form/ | UpdatePassClaimFormAPI | Update claim form |
3.4 User
| Method | Route | View / Handler | Notes |
|---|---|---|---|
| GET | user/get-nonce | GetNonce | Get nonce for wallet auth |
| POST | user/verify-signature | VerifySignature | Verify signature; returns token |
| POST | user/auth/ | AuthUserView | Auth (e.g. token validation) |
| GET | user/new-verification-link/ | new_verification_link | Request new verification link |
| GET | user/verify-email/<token>/ | verify_email | Email verification |
| POST | user/set-password/ | set_password | Set password |
| GET | user/self/ | RetrieveUpdateUserProfile | Current user profile |
| PUT/PATCH | user/self/ | RetrieveUpdateUserProfile | Update current user profile |
| GET | user/inventory/ | UserInventoryModelViewSet | User pass inventory |
| GET | user/stats/ | UserStatsAPIView | User stats |
| GET | user/notifications/ | ListUserNotificationsViewSet | List notifications |
| POST | user/notifications/read/ | ReadAllUserNotificationsViewSet | Mark all read |
| GET | user/notifications/unread-count | UserNotificationsUnreadCountViewSet | Unread count |
| POST | user/signup/ | UserSignUpAPIView | Email signup |
| POST | user/login/ | UserLogInAPIView | Email login |
| POST | user/logout/ | LogoutUserAPIView | Logout |
| POST | user/resend-otp/ | ResendOTPAPIView | Resend OTP |
| POST | user/verify-otp/ | VerifyOTPAPIView | Verify OTP |
| POST | user/change-password/ | AuthenticatedUserResetPasswordAPIView | Change password (authenticated) |
| GET | user/<username>/ | PublicUserProfile | Public user profile |
3.5 Activations
Activations are engagement features (polls, airdrops, claim codes, merch drops, social share) scoped by brand or pass.
| Method | Route | View / Handler | Notes |
|---|---|---|---|
| GET | activations/ | ActivationsPublicReadOnlyView | List activations (public) |
| GET | activations/brand/<brand_id>/ | BrandActivationsReadOnlyView | Activations for brand |
| GET | activations/pass/<pass_id>/ | PassCollectibleOrActivationsListView | Activations for pass |
| GET/POST | activations/pass/<pass_id>/drop/airdrop/ | AirdropActivationModelViewSet | Airdrop collectibles |
| GET/POST | activations/pass/<pass_id>/drop/claim_code/ | ClaimCodeModelViewSet | Claim codes |
| POST | activations/pass/<pass_id>/drop/claim_code/<id>/claim/ | ClaimClaimCodeAPIView | Claim with code |
| GET/POST | activations/pass/<pass_id>/drop/merch_drop/ | MerchCollectibleModelViewSet | Merch drops |
| GET/POST | activations/pass/<pass_id>/response/poll/ | PrivatePollCollectibleModelViewSet | Polls |
| GET/POST | activations/pass/<pass_id>/response/social-share/ | CRUDSocialSharingCollectibleModelViewSet | Social share |
| POST | activations/pass/<pass_id>/publish/<id>/ | PublishCollectibleView | Publish collectible |
3.6 Collectible (Public)
| Method | Route | View / Handler | Notes |
|---|---|---|---|
| GET | collectible/ | CollectibleRetrieveView (list) | List collectibles |
| GET | collectible/<id>/ | CollectibleRetrieveView (retrieve) | Collectible detail |
| POST | collectible/<id>/claim-private/ | ClaimPrivateCollectible | Claim private collectible |
| GET | collectible/<id>/download/ | DownloadCollectibleView | Download collectible |
3.7 Activity
| Method | Route | View / Handler | Notes |
|---|---|---|---|
| GET | activity/pass/<pass_id> | PassActivityViewSet | Activity for pass |
| GET | activity/user/<username> | UserActivityViewSet | Activity for user |
3.8 Analytics
| Method | Route | View / Handler | Notes |
|---|---|---|---|
| GET | analytics/brand/<brand_id> | PrivateBrandAnalyticsViewSet | Brand analytics |
| GET | analytics/brand/<brand_id>/get-audience | PrivateAudienceAnalyticsViewSet | Audience analytics |
| GET | analytics/brand/<brand_id>/get-audience/export | AudienceAnalyticsDownloadAPI | Export audience data |
| GET | analytics/brand/<brand_id>/collectibles | PrivateCollectibleAnalyticsView | Collectible analytics |
| GET | analytics/admin-site | GetAdminAnalyticsAPIViewSet | Admin-site analytics |
3.9 Social Media
| Method | Route | View / Handler | Notes |
|---|---|---|---|
| GET | social-media/twitter/callback | TwitterCallbackHandlerAPIView | Twitter OAuth callback |
| GET | social-media/twitter/oauth2 | TwitterOauth2HandlerAPIView | Twitter OAuth2 URL/start |
| GET | social-media/social-auth/get-auth-code | get_auth_code_uri | Get auth code (e.g. Google) |
| POST | social-media/social-auth/google-handshake | handle_google_login | Google sign-in handshake |
3.10 Payments
| Method | Route | View / Handler | Notes |
|---|---|---|---|
| GET | payments/config | stripe_config | Stripe config (public key) |
| GET | payments/create-checkout-session/pass/<pass_id> | CreateCheckoutSession | Create Stripe checkout session |
| GET | payments/success | SuccessView | Checkout success page |
| GET | payments/cancelled | CancelledView | Checkout cancelled |
| POST | payments/webhook/ | StripeCallback | Stripe webhook (signature verified) |
| GET | payments/stripe/oauth/redirect-url | StripeOauthCallback | Stripe Connect OAuth redirect |
| GET | payments/stripe/connect | StripeConnect | Stripe Connect onboarding |
3.11 UGC Campaign
| Method | Route | View / Handler | Notes |
|---|---|---|---|
| GET | campaign/ | ListCreateUGCCampaignView (list) | List UGC campaigns |
| POST | campaign/ | ListCreateUGCCampaignView (create) | Create UGC campaign |
| GET | campaign/all_ugc/ | ListAllUGCCampaignSubmissionsView | All UGC submissions |
| GET | campaign/user/ | UserUGCSubmissionsView | Current user's UGC submissions |
| POST | campaign/bulk-download/ | BulkDownloadMediaView | Bulk download UGC media |
| POST | campaign/submissions/status-update/ | UGCCampaignSubmissionStatusUpdateView | Update submission status |
| POST | campaign/<id>/endcampaign/ | EndUGCCampaignView | End campaign |
3.12 Shopify
| Method | Route | View / Handler | Notes |
|---|---|---|---|
| GET | shopify/connect/ | ShopifyConnectViewSet | Start Shopify OAuth |
| GET | shopify/callback/ | ShopifyCallbackAPIView | OAuth callback |
| POST | shopify/register-webhooks/ | ShopifyRegisterWebhooksAPIView | Register webhooks |
| POST | shopify/sync-products/ | ShopifyProductSyncAPIView | Sync products |
| GET | shopify/search-products/ | ShopifySearchProductsAPIView | Search products |
| GET | shopify/list-products/ | BrandProductListAPIView | List products |
| POST | shopify/disconnect/ | ShopifyDisconnectAPIView | Disconnect store |
| POST | shopify/select-products/ | ShopifySelectProductAPIView | Select products for platform |
| GET | shopify/orders/ | ShopifyOrdersAPIView | List orders |
| POST | shopify/sync-orders/ | ShopifyOrderSyncAPIView | Sync orders |
| GET | shopify/storefront-token/ | ShopifyStorefrontTokenAPIView | Storefront token |
| GET | shopify/public/products/ | ShopifyPublicProductsAPIView | Public products |
| POST | shopify/products/publish/ | ShopifyProductPublishingAPIView | Publish product |
| POST | shopify/products/unpublish/ | ShopifyProductUnpublishingAPIView | Unpublish product |
| GET | shopify/public/orders/ | ShopifyPublicOrdersAPIView | Public orders |
| POST | shopify/public/orders/<id>/fulfill/ | FulfillOrderAPIView | Fulfill order |
| POST | shopify/public/orders/<id>/cancel/ | CancelOrderAPIView | Cancel order |
| POST | shopify/public/orders/<id>/refund/ | RefundOrderAPIView | Refund order |
| GET/POST | shopify/billing/ | ShopifyBillingAPIView | Billing management |
Shopify webhooks (POST, by Shopify): customers/data_request, customers/redact, shop/redact, app/uninstalled, products/create, products/update, products/delete, create-order, orders/updated, orders/paid, orders/cancelled, orders/fulfilled, inventory_items/update, collections/create, collections/update, collections/delete.
3.13 Support & Utilities
| Method | Route | View / Handler | Notes |
|---|---|---|---|
| POST | /support/query/ | SupportQuery | Submit support query |
| GET | /support/get-url-metadata/ | GetUrlMetadata | Fetch URL metadata |
| POST | /utilities/api/v1/upload/ | FileUploadView | Upload file to S3 |
4. Authentication Patterns
4.1 Backend Auth
- Token:
rest_framework.authentication.TokenAuthentication— Header:Authorization: Token <token> - Session: SessionAuthentication (browser/admin)
- Basic: BasicAuthentication (tooling)
- Default permission: IsAuthenticated for most API views. Specific views override to AllowAny.
- Object-level: django-guardian for brand/pass-level checks.
4.2 Frontend Auth
- Token stored in
localStorage utils/ApiHelpers.ts→getAuthHeaders()addsAuthorization: Token <token>to all Axios requests- UserContext holds token, userDetails, and isUserLoggedIn
- Routes wrapped in AuthenticatedRoute require login; BrandRoute enforces brand-admin access
4.3 Auth Flow Summary
| Flow | Frontend | Backend |
|---|---|---|
| Token auth | Store token in localStorage; send Authorization: Token | Validate token; enforce IsAuthenticated or AllowAny |
| Wallet (Dapper) | get-nonce → sign → verify-signature → store token | Issue token on valid signature |
| login/signup + verify-otp or verify-email | Issue token on success | |
| get-auth-code → google-handshake | Create/attach user; return token/session | |
| Webhooks (Stripe/Shopify) | N/A | Verify payload signature; no user token |
5. Inter-App Relationships
5.1 Frontend ↔ Backend
- Single client: One SPA talks to one backend via
VITE_API_BASE_URL. - All server state via API: No direct DB or cache access from the frontend.
- Errors: Backend returns structured error payloads; frontend uses
utils/request.tsandApiHelpers.buildErrorMessage().
5.2 Backend ↔ External Systems
| External System | Used For | Trigger |
|---|---|---|
| PostgreSQL | Primary data store | Every request / Celery task |
| Redis | Cache, Celery broker | Cache, queue |
| AWS S3 | Media, uploads | Upload API, presigned URLs |
| Stripe | Payments, Connect | Checkout, webhook, Connect onboarding |
| Flow Blockchain | NFT mints, ownership | Purchase settlement, wallet flows |
| Shopify | E-commerce, orders, products | OAuth, webhooks, product/order APIs |
| Firebase | Push notifications | Backend sends to FCM |
| SES / Mailjet / Twilio | Email, SMS | Verification, notifications |
5.3 Data Ownership
- Brands are owned by users (brand admin). Passes belong to brands. Releases belong to passes.
- Activations (polls, airdrops, claim codes, merch, social share) are scoped to brands/passes.
- User inventory (purchased/claimed passes) is stored and returned by the backend.
- UGC campaigns are brand-owned; submissions are linked to campaigns and users.
- Shopify data (products, orders) is tied to brand and shop.
Database Schema Documentation
Django 3.2.8 PostgreSQL 11 Apps ~45 Models
Overview
The platform uses PostgreSQL as its primary database, accessed through Django 3.2.8 ORM. The schema spans 11 Django applications containing approximately 45 models. The database uses the HSTORE extension and supports both standard auto-increment integer primary keys and ShortUUID primary keys for certain models.
Base Model Classes
- BaseModelWithCreatedInfo: Adds
created_at(auto) andcreated_by(FK to User) - BaseModelWithCreatedInfoUUID: Same as above + ShortUUID primary key
- BaseImageModel: Adds image_file, image_url, image_hash, and original/small/medium URL variants
- BaseProfileCoverImageModel: Adds profile_image, cover_image with URLs and hashes
App-to-Model Summary
| Django App | Model Count | Purpose |
|---|---|---|
| brand | 20 | Brands, Passes, Releases, Tokens, Events, Claims, Subscriptions, UGC Campaigns, Shopify, Invitations |
| user_management | 5 | User profiles, inventory, achievements, OTP, nonce tracking |
| activations | 6 | Activations, Collectibles, drops (Collectible/Pass/Raffle), Brand Activations |
| payments | 1 | Stripe payment records |
| order | 2 | Purchase orders and pass lock table |
| activity | 1 | Activity feed with generic foreign keys |
| social_media | 2 | Social share responses, Twitter OAuth tracking |
| notifications | 2 | User notifications, notification subscriptions |
| support | 1 | Support/contact queries |
| app_manager | 1 | Custom admin sidebar apps |
| utilities | 5+ | Abstract bases, Address, ProjectConfigurations, Tags |
Core Models
Brand App
The brand app contains the largest models, managing brands, passes (NFT collections), releases, tokens, events, UGC campaigns, Shopify integrations, and subscriptions.
Key Models
- Tags: Tagging system for brands (symbol auto-uppercased, spaces replaced with underscores)
- Brand: Core entity with approval workflow, Stripe/Shopify connections, profile/cover images
- Pass: NFT collections with
unique_togetherconstraint on(brand, name) - PassReleases: Sale/release windows with pricing and time windows
- PassReleaseWhitelist: Whitelisted emails for restricted releases
- PlatformToken: Individual NFT tokens with minting and claiming tracking
- PassBenefits: Benefits associated with pass ownership
- PassEvents: Events linked to pass releases
- PassClaims: Pass claim requests by users
- SubscriptionDetails: Subscription pricing management
- StoreFrontListing: Secondary market listings
- BlockchainTransaction: Audit log for blockchain operations
- UGCCampaign: User-generated content campaigns
- UGCCampaignSubmission: Individual UGC submissions
- BrandInvitation: Brand collaboration invitations (token-based, 48-hour expiry)
- ConnectedStore: Shopify store connections
- Product, ProductVariant, ProductImage: Shopify product sync
- ShopifyOrder: Shopify order webhook data
User Management App
- UserProfile: Extended user profile (1:1 with auth.User), stores wallet, social links, address
- UserInventory: Tracks items owned by users (passes/collectibles), uses GenericForeignKey
- Achievements: Gamification badges
- UserOTPMapping: Email OTP verification (30-min expiry)
- NonceSignatureTracker: Wallet proof nonce/signature pairs
Activations App
- Activations: Master activation type registry
- BrandActivations: Junction table linking Brands to unlocked Activations
- Collectible: Core engagement entity
- CollectibleDrop: Individual collectible deliveries to users
- PassDrop: Pass airdrops
- RaffleDrop: Raffle-based distributions
Payments & Orders
- PaymentRecord: Stripe payment records
- PurchaseOrder: Completed pass purchases
- PassLockTable: Temporary 5-minute lock during purchase
Status Enumerations
| Entity | Statuses |
|---|---|
| Brand | DRAFT(0) APPROVING(1) APPROVED(2) REJECTED(3) SUSPENDED(4) DELETED(5) |
| Pass | DRAFT(0) PUBLISHING(1) PUBLISHED(2) DELETED(3) |
| Release | DRAFT(0) SCHEDULING(1) SCHEDULED(2) ON_SALE(3) SALE_ENDED(4) DELETED(5) |
| Token | CREATED(0) MINTED(1) VIEWED(4) CLAIMED(2) BURN(3) |
| Collectible | DRAFT(0) PUBLISHING(1) PUBLISHED(2) SCHEDULED(3) STARTED(4) COMPLETED(5) ENDED(6) DELETED(7) |
| Drop | SCHEDULED(0) PICKED(1) DROPPING(2) DROPPED(3) FAILED(4) |
Activation Types
airdrop merch_drop claim_code poll social_share ugc_submission
Key Relationship Map
Core Hierarchy
Engagement Chain
Commerce Chain
User Chain
Implementation Notes
- Soft Deletes: Most models use
is_deletedBooleanField rather than actual deletion. Custom managers filter out deleted records by default. - GenericForeignKeys: Used extensively in Activity, Notifications, UserInventory via
content_type + object_idpairs. - Time Representation: Many models store both epoch milliseconds and Django DateTimeFields for blockchain and Django admin compatibility.
- UUID Primary Keys: ShortUUID (22-char) primary keys used in Brand, Pass, Collectible, PaymentRecord, UGCCampaign, etc.
- Image Processing: Models inheriting BaseImageModel automatically generate image hashes and trigger AWS Lambda for image resizing.
- Signals: Several
post_savesignals exist for UserInventory verification, BrandInvitation permissions, retroactive incentive drops, and cache invalidation. - DO_NOTHING ForeignKeys: Many FK relationships use
on_delete=DO_NOTHINGfor audit trail preservation.
Celery Tasks & Cron Jobs Guide
Overview
The platform uses Celery (v5.2.7) with Redis as the message broker to handle asynchronous tasks. The system processes blockchain transactions, email notifications, subscription management, UGC campaign lifecycle events, and media file operations outside the HTTP request-response cycle.
| Setting | Value |
|---|---|
| Celery App | novabrand |
| Broker | Redis at redis://127.0.0.1:6379 |
| Result Backend | None (disabled) |
| Beat Scheduler | django_celery_beat.schedulers:DatabaseScheduler |
| Serialization | JSON (task and result) |
| Workers | 3 concurrent + 1 Beat scheduler (via Supervisor) |
Task Summary
| Task Name | Type | Trigger | Frequency |
|---|---|---|---|
| blockchain_transaction | Celery | Called by views/services with type param | On demand |
| claim_pass_notification_task | Celery | Called after pass airdrop | On demand |
| update_passes_membership_metadata | Celery | Manual or scheduled | On demand |
| make_latest_subscription_details_active | Beat Cron | Celery Beat scheduler | Daily 00:00 UTC |
| email_upcoming_subscription_details_to_users | Beat Cron | Celery Beat scheduler | Daily 00:00 UTC |
| send_email_verification_reminders | Celery | Manual or scheduled | On demand |
| refresh_brand_analytics | Celery | Manual or scheduled | On demand |
| update_ugc_campaign_status | Celery | Manual or scheduled | On demand |
| bulk_create_ugc_submission_notifications | Celery | Called on UGC approve/reject | On demand |
| send_ugc_campaign_started_email | Celery | Called on campaign start | On demand |
| create_and_send_zip_email | Celery | Called on media export request | On demand |
| cleanup_temp_zip_file | Celery | Scheduled 2 days after zip creation | Delayed (48h) |
| generate_watermark_task | Celery | Called on UGC image upload | On demand |
| give_collectible_to_user | Celery | Called after UGC submissions approved | On demand |
| check_retroactive_incentive_drops | Celery | Called when incentive collectible created | On demand |
| manage_product_publication | Celery | Called on Shopify publish/unpublish | On demand |
Cron Jobs (Celery Beat)
Celery Beat schedules are stored in the Django database via django-celery-beat. Currently, only 2 crons are active in production.
| Cron Name | Schedule | Status | Celery Task |
|---|---|---|---|
| Activate Subscription Details | Daily 00:00 | ACTIVE | make_latest_subscription_details_active |
| Email Subscription Changes | Daily 00:00 | ACTIVE | email_upcoming_subscription_details_to_users |
| Purchase Settle | Every 2 min | COMMENTED | blockchain_transaction(settle_purchase) |
| Pass Airdrop | Every 2 min | COMMENTED | blockchain_transaction(airdrop_pass) |
| Collectible Drop | Every 5 min | COMMENTED | blockchain_transaction(airdrop_collectible) |
| Listing Removal | Every 13 min | COMMENTED | blockchain_transaction(remove_listing) |
| Signup Reminders | Daily 12:00 | COMMENTED | blockchain_transaction(signup_reminder) |
How to Enable a Commented Cron
- SSH into the EC2 instance and navigate to the backend app directory.
- Open Django shell:
python manage.py shell - Import and call the setup function for the desired cron.
- Verify in Django Admin > Periodic Tasks that the new cron appears.
- Restart Celery Beat:
sudo supervisorctl restart celerybeat
Detailed Task Reference
blockchain_transaction
Parameters: type (str) — one of 13 sub-types; payload (dict, optional)
Central dispatcher for all blockchain operations on Flow. Uses a match/case statement to route to the appropriate worker function. Each worker runs asynchronously via asyncio.run() and gets a proposer address based on the worker hostname (round-robin across workers).
Blockchain Transaction Sub-Types
| Type Parameter | Description |
|---|---|
create_brand | Creates brand on Flow blockchain after admin approval |
create_pass | Registers pass (NFT collection) on Flow |
schedule_release | Mints and lists pass tokens for scheduled release |
create_collectible | Creates collectible (sub-NFT) on Flow blockchain |
drop | Airdrops collectibles to eligible users from drop table |
claim_token | Marks a token as claimed on-chain (3 retries) |
settle_purchase | Settles pending purchase transactions |
remove_listing | Removes storefront listings (batch of 2) |
airdrop_pass | Airdrops passes to eligible users (cron) |
airdrop_collectible | Processes scheduled collectibles for drop (cron) |
signup_reminder | Sends reminders to users 3 days after claim |
settle_linked_wallet | Settles linked wallet transactions |
create_submission | Creates UGC submission on-chain (3 retries) |
make_latest_subscription_details_active
Cron: Daily at 00:00 UTC — External Dependencies: Stripe API
Activates subscription plan changes scheduled for the current date. Deactivates old subscription details, activates new ones, and modifies Stripe subscription schedules to reflect pricing/interval changes. Uses atomic database transactions. Creates prorated transition phases in Stripe when payment cycles change.
create_and_send_zip_email
Parameters: media_urls (list of S3 URLs), recipient_email (str)
Downloads media files from S3, compresses them into a ZIP archive in memory, uploads the ZIP back to S3, generates a presigned download URL (valid for 2 days), and emails the link to the recipient. Schedules cleanup_temp_zip_file to run 48 hours later.
manage_product_publication
Parameters: store_id, product_ids (list), action ('publish' or 'unpublish')
Publishes or unpublishes products on a Shopify store via the GraphQL Admin API. Processes products concurrently using ThreadPoolExecutor with max 5 workers. Each product has its own retry logic (3 attempts with exponential backoff).
Supervisor Configuration
All Celery processes are managed by Supervisor on the production EC2 instance.
| Process | Command | Log File |
|---|---|---|
| celery-worker-1 | celery -A novabrand worker --loglevel=info --concurrency=1 -n wrkr_1 | /var/log/platform_celeryd1.log |
| celery-worker-2 | celery -A novabrand worker --loglevel=info --concurrency=1 -n wrkr_2 | /var/log/platform_celeryd2.log |
| celery-worker-3 | celery -A novabrand worker --loglevel=info --concurrency=1 -n wrkr_3 | /var/log/platform_celeryd3.log |
| celery-beat | celery -A novabrand beat -l info -S django | /var/log/platform_beat.log |
Key Configuration Details
- Each worker runs with
concurrency=1(single thread per worker). Intentional because blockchain transactions are async and must be isolated. - Workers are named
wrkr_1,wrkr_2,wrkr_3. The blockchain_transaction task uses this hostname to determine which Flow proposer address to use. - Beat runs as a single process with a 10-second startsecs delay. Uses the Django database scheduler.
- All processes run as the
ubuntuuser and auto-restart on failure.
Common Supervisor Commands
# Check all process status
sudo supervisorctl status
# Restart all Celery workers
sudo supervisorctl restart celery-workers:*
# Restart Beat scheduler
sudo supervisorctl restart celerybeat
# Restart a single worker
sudo supervisorctl restart celery-worker-1
# View worker logs (live)
tail -f /var/log/platform_celeryd1.log
# View Beat logs (live)
tail -f /var/log/platform_beat.log
AWS Operations Runbook
Production Environment
Region: us-east-1
EC2 Instances
Application Servers
| Instance Name | Instance Type | OS | Purpose |
|---|---|---|---|
| Platform-Prod | t2.medium | Ubuntu 20.04.6 | Main Backend Application |
| Platform-Dev | t2.medium | Ubuntu 20.04.6 | Dev Backend Application |
| Platform-Staging | t2.medium | Ubuntu 20.04.6 | Staging Backend Application |
Worker Servers (Celery)
| Instance Name | Instance Type | OS | Purpose |
|---|---|---|---|
| worker-machine | t2.medium | Ubuntu 20.04.6 | Celery Background Jobs |
SSH Access
- Log in to the AWS Console
- Navigate to EC2 → Instances
- Select the instance
- Click Connect → EC2 Instance Connect
Application Reload (Without Restarting Server)
touch ~/backend/deployment/uwsgi.ini
Restart Nginx (If Required)
sudo systemctl restart nginx
Restart Celery Workers
sudo supervisorctl restart all
RDS – PostgreSQL Database
| DB Identifier | Engine | Backup Retention | Multi-AZ | Status |
|---|---|---|---|---|
| platform-production | PostgreSQL | 7 days | No | Available |
Health Checks
AWS Console → RDS → Databases → Select DB Instance. Verify: Status, CPU Utilization, Active Connections, Storage Utilization.
Create Manual Snapshot
- RDS → Snapshots
- Click Create Snapshot
- Select Database
- Provide Snapshot Name
Amazon S3 – Storage
| Bucket Name | Environment | Purpose |
|---|---|---|
| platform-static | Production | User uploads & Static files |
AWS Lambda Functions
| Function Name | Runtime | Trigger Type | Purpose |
|---|---|---|---|
| s3-image-variant-generator | Python | Custom Backend Event | Compress & Resize Images (1024px, 512px) + DB Update |
| compress-image-and-videos | Python | S3 ObjectCreated Event | Compress Images (Pillow) & Videos (FFmpeg) |
| wallet-verification-login | Node.js | API Gateway | Generate Secure 32-byte Nonce |
| cloudfront-function | Node.js | CloudFront Origin Response | Set Last-Modified Header from S3 Metadata |
| meta-tag-lambda | Node.js | CloudFront Viewer Request | Inject Dynamic OG/Meta Tags into index.html |
First-Time Server Setup
Nginx
sudo apt-get install nginx -y
sudo mkdir -p /var/www/platform/{media,static}
sudo chown -R ubuntu:www-data /var/www/platform
sudo chmod -R 770 /var/www/platform
python manage.py collectstatic
sudo ln -s ~/backend/deployment/nginx.conf /etc/nginx/sites-enabled/backend
sudo service nginx reload
uWSGI
sudo pip3 install uwsgi
sudo mkdir -p /etc/uwsgi/vassals
sudo ln -s ~/backend/deployment/uwsgi.ini /etc/uwsgi/vassals/backend_uwsgi.ini
# Test: uwsgi --emperor /etc/uwsgi/vassals --uid www-data --gid www-data
# For auto-start, add to /etc/rc.local:
/usr/local/bin/uwsgi --emperor /etc/uwsgi/vassals --uid www-data --gid www-data --daemonize /var/log/uwsgi-emperor.log
SSL (Certbot / LetsEncrypt)
sudo apt install certbot python3-certbot-nginx
sudo certbot --nginx -d <your-sub-domain>
sudo service nginx restart
# Renew: certbot renew
# Dry run: certbot renew --dry-run
Celery Workers (Supervisor)
# 1. Install supervisor
sudo apt-get install supervisor
# 2. Create log files
sudo touch /var/log/platform_celeryd{1,2,3}.log /var/log/platform_beat.log
sudo chown ubuntu:www-data /var/log/platform_celeryd{1,2,3}.log /var/log/platform_beat.log
# 3. Create softlinks to supervisor config
sudo ln -s ~/backend/supervisor_conf/celery.conf /etc/supervisor/conf.d/celery.conf
sudo ln -s ~/backend/supervisor_conf/celery_beat.conf /etc/supervisor/conf.d/celery_beat.conf
# 4. Reload supervisor
sudo supervisorctl reread
sudo supervisorctl update
# 5. Manage workers
sudo supervisorctl start celery-workers:*
sudo supervisorctl status
sudo supervisorctl restart all
Rollback Procedure
git checkout <previous_commit_or_tag>- Run migrations if needed:
python3 manage.py migrate - Reload uWSGI:
touch ~/backend/deployment/uwsgi.ini - Restart Celery:
sudo supervisorctl restart all
Troubleshooting
Python 3.10 Upgrade
sudo add-apt-repository ppa:deadsnakes/ppa
sudo apt-get update
sudo apt-get install python3.10 python3.10-dev python3.10-distutils build-essential
python3.10 -m pip install --upgrade pip
pip install uwsgi
Common issues after upgrade:
- uWSGI using old Python: Use
/usr/local/bin/uwsgi(full path) in/etc/rc.local - Celery using old Python: Edit celery shebang to
#!/usr/bin/python3.10
Operations Checklist
| Pre-Deployment | Post-Deployment |
|---|---|
| Take RDS Snapshot | Reload Application |
| Verify EC2 Instance Health | Verify Logs |
| Check Celery Status | Confirm Background Jobs Running |
| Confirm S3 Availability | Test Critical Endpoints |
macOS Local Development Setup
PostgreSQL Installation
Step 1: Check if PostgreSQL Is Installed
psql --version
If you see a version number, skip ahead. If command not found: psql, proceed below.
Step 2: Install Homebrew (If Not Already Installed)
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
Step 3: Install PostgreSQL
brew install postgresql@18
psql --version
Fix: PATH Not Set
If psql is still not found after installation, add it to your PATH:
echo 'export PATH="/opt/homebrew/opt/postgresql@18/bin:$PATH"' >> ~/.zshrc
source ~/.zshrc
psql --version
Database Setup
Create Database and User
sudo -u postgres -i psql
CREATE DATABASE platform_db;
CREATE USER platform_user WITH PASSWORD 'password';
GRANT ALL PRIVILEGES ON DATABASE platform_db TO platform_user;
CREATE EXTENSION hstore;
GRANT USAGE ON SCHEMA public TO platform_user;
GRANT SELECT ON ALL TABLES IN SCHEMA public TO platform_user;
ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT SELECT ON TABLES TO platform_user;
CREATE EXTENSION IF NOT EXISTS vector;
Restore Database Dump
# Step 1: Drop existing database
psql -U postgres -h localhost -p 5432 -d postgres -c "DROP DATABASE IF EXISTS platform_db;"
# Step 2: Recreate with correct owner
psql -U postgres -h localhost -p 5432 -d postgres -c "CREATE DATABASE platform_db OWNER platform_user;"
# Step 3: Ensure schema ownership
psql -U postgres -h localhost -p 5432 -d platform_db -c "ALTER SCHEMA public OWNER TO platform_user;"
psql -U postgres -h localhost -p 5432 -d platform_db -c "GRANT ALL ON SCHEMA public TO platform_user;"
# Step 4: Restore dump
PGPASSWORD=password pg_restore \
-U platform_user \
-d platform_db \
-h localhost \
-p 5432 \
--no-owner \
--no-privileges \
~/Downloads/platform_db_dump.dump
Python 3.10.8 Setup
# Install Python 3.10
brew install [email protected]
# Verify
python3.10 --version
# Expected: Python 3.10.8
Backend Requirements Installation
# Install ffmpeg globally
brew install ffmpeg
# Required files (place in project):
# requirements.txt -> <project_path>/requirements.txt
# firebase_creds.json -> <project_path>/app/platform/firebase_creds.json
# local_settings.py -> <project_path>/app/platform/local_settings.py
# setup_venv.sh -> <project_path>/scripts/setup_venv.sh
# Give file running permissions
chmod +x ./scripts/setup_venv.sh
# Run migrations (inside app directory)
python3 manage.py migrate
If Permission Issues on Migrations
GRANT USAGE ON SCHEMA public TO platform_user;
GRANT CREATE ON SCHEMA public TO platform_user;
GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA public TO platform_user;
GRANT ALL PRIVILEGES ON ALL SEQUENCES IN SCHEMA public TO platform_user;
Create Log Files
cd /var/log/
sudo touch platform-web2.log platform-web3.log platform-cron.log
sudo chown <user> platform-web2.log platform-web3.log platform-cron.log
Shopify Plugin Guide
Overview
The Shopify plugin is an embedded Shopify sales channel app that allows merchants to connect their Shopify stores with the NovaBrand marketplace. It handles product synchronization, order management, and account linking — all within the Shopify Admin interface.
Tech Stack
React 19 TypeScript Vite 7 Shopify Polaris v13 App Bridge v3 Apollo Client S3 + CloudFront
Architecture
Key Flow
- Installation: Merchant installs from Shopify → backend handles OAuth at
/api/v1/shopify/connect/→ redirects to Shopify permission screen → callback at/api/v1/shopify/callback/ - Account Linking: After OAuth, merchant connects their NovaBrand account via a popup to the dashboard
- Embedded App: Once connected, the app runs inside Shopify Admin with
host+shopURL params, using App Bridge for auth context
App Tabs
| Tab | Component | Description |
|---|---|---|
| Account | AccountManagement.tsx | Connect/disconnect account, view connection status |
| Products | ProductPublishing.tsx | Sync, publish, and unpublish products to marketplace |
| Orders | OrderManagement.tsx | View and manage orders |
Backend Integration
| Endpoint | Purpose |
|---|---|
/shopify/connect/ | Initiate OAuth flow |
/shopify/callback/ | OAuth callback handler |
/shopify/account/disconnect/ | Disconnect merchant account |
/shopify/public/products/ | List synced products |
/shopify/public/orders/ | List orders |
/shopify/products/publish/ | Publish products to marketplace |
/shopify/products/unpublish/ | Unpublish products |
/shopify/register-webhooks/ | Register Shopify webhooks |
/shopify/sales-channel-status/ | Sales channel metrics |
/shopify/product-listings/ | Product listing management |
Webhooks
| Topic | Backend Handler |
|---|---|
app/uninstalled | /shopify/webhook/app/uninstalled |
products/create, update, delete | /shopify/webhook/products/* |
orders/create, updated, paid | /shopify/webhook/orders/* |
inventory_items/update | /shopify/webhook/inventory_items/update |
collections/create, update, delete | /shopify/webhook/collections/* |
| GDPR compliance | customers/data_request, customers/redact, shop/redact |
App Configuration
- API Version: 2025-04
- Embedded: Yes
- POS: No
Required Scopes
read_customers, read_orders, write_orders, read_products, read_publications, write_publications, write_resource_feedbacks, write_script_tags, unauthenticated_write_checkouts, unauthenticated_read_checkouts
Development Setup
git clone [email protected]:org/novabrand-shopify.git
cd novabrand-shopify
yarn install
cp env.example .env # fill in your Shopify API key
yarn dev # starts at http://localhost:3000
Environment Variables
| Variable | Description |
|---|---|
VITE_SHOPIFY_API_KEY | Shopify app API key (from Partner Dashboard) |
VITE_SHOPIFY_APP_URL | App URL for redirects |
VITE_SHOPIFY_REDIRECT_URI | OAuth callback URL (backend) |
VITE_API_BASE_URL | Backend API base URL |
VITE_DEFAULT_SHOP_DOMAIN | Dev only — default shop for testing |
VITE_DASHBOARD_BASE_URL | Dashboard URL (for account linking popup) |
Deployment
| Command | Target |
|---|---|
yarn deploy | Production (S3 + CloudFront) |
yarn deploy-staging | Staging (S3 + CloudFront) |
Both use env-cmd to load .env, build with Vite, upload to S3, and invalidate CloudFront cache.
Billing
| Plan | Price | Limits |
|---|---|---|
| Starter | $9.99/mo | 100 products, 500 orders |
| Professional | $29.99/mo | 500 products, 1,000 orders |
| Enterprise | $99.99/mo | Unlimited |
NovaBrand Platform Documentation — Sample
This is anonymized sample documentation. All identifying information has been replaced.