08 — Third-party integrations¶
A reference for the external services this app talks to and how they're wired in. When a feature touches "social X" or "billing", read the relevant section here first.
Social network providers¶
Provider IDs¶
The backend identifies social providers by integer. The IDs are defined in constants/constants.js:
Note that 5 is intentionally skipped. Do not assume sequential numbering. Verify with the team what the missing 5 was — it's likely a deprecated provider whose ID is reserved for backend compatibility.
The IDs surface in many places: payloads to /sociallink, /auth/socialAccountStatus, tokenRefresh, etc. Always pull from the constant — do not hardcode.
Per-provider OAuth wiring¶
Each provider has its own auth flow. Source-of-truth files:
| Provider | Login action | Server middleware | Notes |
|---|---|---|---|
Standard @nuxtjs/auth flow + /sociallink |
— | Token-refresh dispatched via tokenRefresh after sociallink. Provider ID 1. |
|
twitterLogin + twitterRLAuth actions |
api/twitter-middleware.js, api/twitter.js |
OAuth v1 (consumer key/secret) + OAuth v2 (client id/secret). Both sets of credentials are required (TWITTER_CUSTOMER_* and TWITTER_CLIENT_*). |
|
| Linked through Facebook (Meta) auth | — | Provider ID 3. Reconnection logic checks linked accounts before re-auth — see commit log for "improve Meta account reconnection logic". | |
linkedInLogin, linkedinauth, linkedinuserdtl actions |
— | Hits /linkedInRegister, /linkedinauth, /auth/linkedinuserdetail. Provider ID 4. |
|
| TikTok | (Action exists in store; verify which action) | — | Provider ID 6. Verify with the team which login action handles TikTok. |
The general flow shape is:
1. Frontend opens provider OAuth dialog
2. Provider redirects back to /callback with code/token
3. Frontend POSTs to /<provider>Register or /<provider>Auth
4. Backend creates or links the social account, returns user data + token
5. Frontend sets cookies and calls $auth.loginWith('local', { …, social: true })
6. middleware/redirect.js takes over (see 03-auth-and-sessions.md)
Server middleware for Twitter¶
api/twitter-middleware.js and api/twitter.js are Nuxt server middleware mounted under /api/twitter in nuxt.config.js → serverMiddleware. They handle the OAuth callback — Twitter requires a server-side step the frontend can't do alone (consumer-secret signing for OAuth 1.0a). This is the only provider whose OAuth callback runs through the Nuxt server, not just the frontend.
api/index.js contains a lot of commented-out passport/Twitter scaffolding. It is dead code retained for reference; the live Twitter integration is in the dedicated middleware files.
Token refresh¶
After a sociallink succeeds, store/index.js POSTs to /tokenRefresh for Facebook (provider 1) and Instagram (provider 3) to refresh the long-lived token, then calls /auth/socialAccountStatus to mark the link as active. Other providers (Twitter, LinkedIn, TikTok) refresh through different paths or rely on backend-side scheduled refresh — verify with the team which.
Billing¶
Stripe¶
- Public keys live in
STRIPE_PUBLIC_KEY_TEST/STRIPE_PUBLIC_KEY_LIVEenv vars. - The Stripe.js script is loaded from CDN in
nuxt.config.js → head.script: - The npm packages
stripeand@stripe/stripe-jsare both installed;@stripe/stripe-jsis the client-side init wrapper used by checkout flows.
Paddle¶
- Token:
PADDLE_CLIENT_TOKEN(live) /PADDLE_TEST_CLIENT_TOKEN(sandbox). - Environment selector:
PADDLE_ENV/PADDLE_TEST_ENV. - The Paddle JS SDK is loaded from CDN:
- The npm package
@paddle/paddle-jsis also installed and used by the Vue components that initiate checkout.
Plan filtering and noId¶
Plans returned by the backend are filtered client-side in helpers/helper.js → getPlanList(userData, planList, isFromBilling, avoidIsNew) before being shown to the user. The filter has three branches:
isBNI(!!userData?.noId) — users whosenoIdis truthy are treated as a special "BNI" cohort. They never see the launch plan (PLAN.LAUNCH = 7) or the $1 plan (PLAN.ONE_DOLLAR = 28); only yearly-billing or trial-eligible plans are surfaced.isNew(accountcreatedafter2023-12-27T12:00:00Z) — users who signed up after this hardcoded cutoff get the "new user" plan list (trial-eligible, displayName variants, plan id15always shown).- everyone else — old user list (trial-eligible or yearly-billing).
The cutoff date is hardcoded. Verify with the team before changing — it likely corresponds to a marketing/pricing relaunch and changing it has revenue implications.
Stripe vs Paddle — which is active?¶
Verify with the team. Both SDKs are present and both env vars are required by the build. The likely picture is that one is the active billing provider while the other is being phased in or out — but the code retains both surfaces. The paddleTxnId cookie in middleware/redirect.js (see 03-auth-and-sessions.md) suggests Paddle is at least partially live.
AWS¶
aws-sdk v2 (aws-sdk@2.1691.0) is installed and registered as a Nuxt plugin (plugins/aws_sdk.js). It is used for direct-to-S3 uploads — typically large media files via FilePond. Verify with the team which credentials/IAM strategy the frontend uses (signed URLs from the backend? a federated identity?). The frontend should not hold a long-lived AWS access key; if you find one, that's a security issue.
Email (SendGrid)¶
@sendgrid/mail@8.1.4 is installed but its usage from the frontend appears limited (frontend rarely sends mail directly). The dependency is plausibly required by a server middleware. Verify with the team — the most likely shape is that the backend handles transactional mail and SendGrid in this repo is for a server-middleware action like the commented-out password-reset flow in store/index.js → sendEmail.
YouTube¶
The /api/youtube-videos server middleware (api/index.js) proxies the YouTube Data API v3 to fetch videos for a configured channel:
const url = `https://www.googleapis.com/youtube/v3/search?order=date&part=snippet&channelId=${YOUTUBE_CHANNEL_ID}&key=${YOUTUBE_API_KEY}`
This is a frontend-side proxy because the API key is a build-time value. Note: the key is bundled into the server-middleware code at build time, but the middleware runs in the Nuxt Node process, not the browser — so the key is not exposed to clients. Don't move this call into a Vue component.
Google services¶
- Google Tag Manager —
@nuxtjs/gtmmodule,GTM_IDenv (defaults toGTM-KBZLX362). Page tracking is enabled (pageTracking: true). - Microsoft Clarity — inline script in
nuxt.config.js → head.script,CLARITY_PROJECT_IDenv (defaults tou1v818dob2). Plugin file atplugins/clarity.js. - Google Fonts — CSS link in
nuxt.config.js → head.link(Inter, Source Sans Pro).
Analytics & support widgets¶
| Service | Wired in | Notes |
|---|---|---|
| Google Tag Manager | @nuxtjs/gtm module |
GTM_ID env. pageViewEventName: 'page-view'. |
| Microsoft Clarity | nuxt.config.js → head, plugins/clarity.js |
CLARITY_PROJECT_ID env. |
| Hotjar | plugins/hotjar.js, plugins/hotjar2.js |
hotjar2 plugin is currently commented out in nuxt.config.js. Verify which is canonical. |
| Fullstory | plugins/fullstory.js |
Plugin file present; verify if registered/enabled. |
| Salesforce IQ | plugins/sales_iq.js |
Verify registration. |
| Chaskiq | Loaded via env (CHASKIQ_KEY, CHASKIQ_DOMAIN) |
Support widget. Verify loader location. |
| FirstPromoter | Inline script in nuxt.config.js → head.script |
Affiliate tracking with cid: '27t8fy1e'. |
| FlagMeister | plugins/flagmeister.client.js, CDN script |
Country flags / geo widget. |
When adding a new analytics or widget integration, follow the established split:
- Inline
<script>tags or third-party CDNs →nuxt.config.js → head.script/head.link. - Initialisation that needs to run in the Vue/Nuxt context → a
plugins/<name>.jsfile withmode: 'client'orssr: false.
CMS / content tools¶
| Tool | Use | Notes |
|---|---|---|
| TinyMCE | @tinymce/tinymce-vue@3.2.8 |
Rich-text editing. Verify API key handling — TinyMCE 6+ requires one. |
| Konva | konva@9.3.16, plugins/konva.js |
Canvas drawing. Also a Polotno transitive dep. |
| Plyr | plyr@3.8.4, plugins/plyr.client.js |
Video player. |
| FilePond | filepond@4.31.4 and several plugins |
File upload. Used with image preview/transform/validate. |
| FullCalendar | @fullcalendar/*@~6.1.14 |
Content planner calendar. |
| Chart.js | chart.js@3.8.0 + vue-chartjs@4.0.0 |
Dashboard charts. Custom wrappers in components/Charts/. |
| Bootstrap | bootstrap-vue@2.22.0 + Bootstrap 5 from CDN |
UI components. Note the dual usage — bootstrap-vue ships with Bootstrap 4 conventions, while CDN Bootstrap 5 also loads. Some component classes need disambiguation. |
| BlueprintJS popover2 | @blueprintjs/popover2@2.1.11 |
Used by the Polotno editor; also imported via CSS in editor host pages. |
AI providers (in the Polotno editor)¶
The Nuxt app does not call AI providers directly. The editor bundle does. Keys live in polotno-editor/.env:
| Provider | Variable | Used for |
|---|---|---|
| Gemini | GEMINI_AI_KEY |
Text generation. |
| Vertex AI | PROJECT_ID, LANGUAGE_MODEL, VERTEX_ACCESS_TOKEN |
Text generation (alternative). |
| Stability AI | STABILITY_AI |
Image generation. |
| Leonardo AI | LEONARDO_AI |
Image generation. |
If a developer reports "AI doesn't work" in the editor, check polotno-editor/.env, not the root .env.
CRM webhook¶
CRM_webhook_url and CRM_CHAT_ID env vars suggest a CRM webhook integration. Verify with the team which CRM (HubSpot? Salesforce? Custom?) and where it's invoked — likely from plugins/sales_iq.js or one of the support widgets.
Affiliate tracking (FirstPromoter)¶
The FirstPromoter inline script in nuxt.config.js → head.script initialises with cid: '27t8fy1e'. The script registers a window.fpr global that other code can call to track conversions. Verify with the team where conversion calls originate — they're not in this repo; they're either backend webhooks or marketing-page calls.
Adding a new integration: checklist¶
- Decide where it loads (CDN script tag →
nuxt.config.js → head, npm + initialisation →plugins/). - Add the env var(s) to
DockerfileARGs and the.env-from-args block in stages 1 and/or 2 as appropriate. - Update
01-local-setup.mdwith the new variable. - If the integration writes cookies, document them in
03-auth-and-sessions.md. - If it's a client-only library, set
ssr: falseormode: 'client'on the plugin registration. Otherwise the Nuxt build still tries to evaluate the plugin in node and may fail at build time on browser-only globals.