02 — Architecture overview¶
This doc describes the big-picture architecture of the Someli frontend so you can place new code correctly without re-deriving it from scratch.
Two apps in one repo¶
someli-platform/ ← Nuxt 2 / Vue 2 SPA (the "main app")
├── pages/ ← Nuxt routes
├── components/ ← Vue components
├── layouts/ ← Page layouts (default, error, onBoard, …)
├── store/ ← Vuex modules
├── middleware/ ← Nuxt router middleware (axios interceptor, redirects)
├── api/ ← Nuxt server middleware (small Express helpers, OAuth callbacks)
├── plugins/ ← Nuxt plugins (auth, gtm, snowfall, polotno-related, …)
├── helpers/ ← Shared JS utilities
├── constants/ ← PROVIDER, PLAN, PATH, etc.
├── nuxt.config.js ← The single source of truth for app config
├── polotno-bundle.js, .css ← Prebuilt React/Polotno editor bundle (output of polotno-editor/)
└── polotno-editor/ ← Independent React 18 + MobX-State-Tree app (Parcel build)
The Nuxt app is a pure SPA (ssr: false in nuxt.config.js). There is no server-side rendering — Nuxt is used as a development environment, build pipeline, and routing layer, not as an SSR framework.
The Polotno editor is a separate React app that builds with Parcel into a single JS+CSS bundle written into the root of the main repo. Editor pages in the Nuxt app import that bundle as an ES module:
The two projects have separate package.json files, separate node_modules, and separate lockfiles. They share no source code.
Request lifecycle for an authenticated page¶
When a logged-in user navigates to, say, /<accountId>/contentplanner:
Browser
│
▼
Nuxt router middleware chain (defined in nuxt.config.js → router.middleware)
1. auth → middleware/redirect.js (decides where the user belongs)
2. axios → middleware/axios.js (installs an axios interceptor for this nav)
3. validUrl → middleware/validUrl.js (records last-known-good path)
4. appendUTM → middleware/appendUTM.js (decorates outbound links with UTM)
5. appendUrl → middleware/appendUrl.js (rewrites unprefixed URLs to /<accountId>/...)
│
▼
Page component (pages/_accid/contentplanner/...)
│
▼
Component-level data fetch
├─ store.dispatch('api/fetchSubscription')
├─ store.dispatch('api/fetchAccountDetails')
└─ this.$axios.get('/auth/getMyListPosts')
│
▼
axios interceptor (middleware/axios.js)
├─ Adds `token` header (from cookie) for /auth/* URLs
├─ Adds `accountId` header (from cookie.usedetail) for /auth/* URLs
└─ Adds base64(`apptype`) header on every request
│
▼
Backend API at process.env.API_URL
There are four important things to internalise from this picture:
- Identity lives in cookies, not in the Vuex store. The store sometimes mirrors cookie values, but cookies are the source of truth — see
03-auth-and-sessions.mdfor the full inventory. - The axios interceptor only adds auth headers for
/auth/...URLs. Endpoints outside that prefix are unauthenticated from the frontend's perspective. store/api.jsis a 5-minute cache layer in front of the most-used endpoints. Components should prefer dispatchingapi/fetch*actions over calling$axiosdirectly. See05-state-management.md.- Routes are rewritten by
appendUrlmiddleware to inject theaccountId. A bare/contentplannerlink is intentionally allowed — the middleware will redirect to/<accountId>/contentplanner. See04-routing.md.
Layering / module map¶
| Layer | Where it lives | Notes |
|---|---|---|
| Routing | pages/, nuxt.config.js → router.extendRoutes |
File-system routing. pages/_accid/... is the dynamic-account-id segment that holds most authenticated routes. |
| Layouts | layouts/default.vue, error.vue, onBoard.vue, someliNetwork.vue, specialOffer.vue, fourOhOne.vue |
Pages pick a layout via the layout option. |
| Middleware (router) | middleware/ |
Run on every navigation, in the order declared in nuxt.config.js. Specific pages can also opt in to per-page middleware (e.g. editor pages declare middleware: 'auth'). |
| Middleware (server) | api/index.js, api/twitter-middleware.js, api/test-middleware.js |
Express handlers mounted by serverMiddleware in nuxt.config.js. Not the backend — small helper endpoints only. The only currently-live endpoint in api/index.js is GET /api/youtube-videos; the rest is commented-out scaffolding. |
| Plugins | plugins/ |
Initialise singletons (auth, GTM, Clarity, vue-snotify, Polotno-adjacent client-only libs, …). Many are declared ssr: false / mode: 'client' because we don't SSR. |
| State | store/ |
Vuex 3 in non-strict mode (export const strict = false). Modules: post/, dashboard/, chat/, leaderboard/, plan/, reach/, common/, onboarding, api, modules/sitemap. |
| Components | components/ |
Auto-registered (components: true in nuxt.config.js). Subdirectories group related components (Calender/, Charts/, Dashboard/, Buttons/, Cards/, Sidebars/, …). |
| Helpers | helpers/helper.js, helpers/circleNetworkHelper.js, helpers/mixins/, helpers/models/ |
Pure utilities and shared mixins. Notably, helpers/helper.js owns the post-status state machine (renderPostStatus, calStatus) and the billing-plan filter (getPlanList). See 13-post-lifecycle.md and 17-helpers.md. |
| Constants | constants/constants.js, constants/authProviders.js |
PROVIDER, PLAN, PATH, INVALID_RESPONSE, etc. |
| Static assets | static/, assets/ |
static/ is served verbatim (/owl.carousel.min.js, /common.js, /identity.js, /universal.tag.js, …). assets/ is processed by the build (CSS, SCSS, images). |
| Editor | polotno-editor/ |
Independent React app. See 06-polotno-integration.md. |
Cross-cutting concerns¶
Authentication¶
@nuxtjs/auth v4 with the local strategy. Login endpoint is webauthenticate; the user-fetch endpoint is me. Logout is disabled at the strategy level — the app calls app.$auth.logout() directly and clears cookies plus localStorage itself. See 03-auth-and-sessions.md.
Account scoping¶
After login, every meaningful URL is prefixed with the user's accountId. The appendUrl middleware actively rewrites unprefixed URLs in a hardcoded allow-list (/contentplanner, /dashboard/*, /billing, /userSetting, …) to insert the account ID. See 04-routing.md.
Caching¶
store/api.js is the single most important state file in the project. It wraps ~12 backend endpoints in cache-with-TTL actions (fetchSubscription, fetchUserDetails, fetchSettings, fetchPosts(page), fetchFavPosts(page), …). The default TTL is 5 minutes. See 05-state-management.md.
Third-party integrations¶
Most external services are integrated by either (a) a global <script> tag in nuxt.config.js → head.script (jQuery, Bootstrap, Stripe, Paddle, Clarity, slick-carousel, Flagmeister, …), or (b) a Nuxt plugin in plugins/ (GTM via @nuxtjs/gtm, Hotjar, Fullstory, Clarity, Salesforce IQ, …). See 08-integrations.md.
Where to start when you have a feature to build¶
Read in this order:
- The page file in
pages/_accid/...(or wherever it belongs). - The Vuex module(s) it dispatches against.
store/api.jsto see if the backend call is already wrapped there.middleware/redirect.jsif the feature has any post-login or auth-state implication.- The relevant component subdirectory in
components/.
When in doubt, grep for an analogous existing feature and copy its structure rather than inventing a new one.