-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathtodo
More file actions
158 lines (139 loc) Β· 12.6 KB
/
Copy pathtodo
File metadata and controls
158 lines (139 loc) Β· 12.6 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
always:
- π keep misc style/ui clean
- π keep code optimised
- π test out before marking done
---
now:
- π GAM + total ad revenue overview & logging
- π AWS Comprehend integration
- π Cloudflare: unblock AI crawling (Security β Bots β AI Scrapers and Crawlers). The zone's managed robots.txt prepends `Disallow: /` for GPTBot/ClaudeBot/CCBot/Bytespider/Amazonbot/meta-externalagent/Google-Extended, ahead of ours. Google-Extended is the costly one β it gates Gemini grounding (AI Overview citations), not just training, and does not affect Search ranking. Blocking the training crawlers can stay if that is the intent. It also emits a second `User-agent: *` group before ours, which can shadow our private-path disallows. Re-verify with `bun run seo:check <domain>`.
- π Cloudflare Email Routing: create support@topiqu.com and privacy@topiqu.com (no contact address exists anywhere in the app yet, incl. the privacy policy)
- π’ AI involvement with article + optional public disclosure (cron = fully AI, manual AI generation/enhancement = partially AI, manual = human)
- π’ implement in clientpreferences humanHourlyRate / humanWordsPerHour
- π‘ document the wildcard TLS mechanism for \*.topiqu.com in MAP.md Β§4 (Cloudflare Origin CA / DNS-01 / tunnel?) β it works, but nothing records how
- π‘ per-account login lockout β Valkey is available; implement email-keyed exp. backoff + Turnstile on auth/callback/credentials + users/totp; per-IP rate-limit alone is botnet/proxy-bypassable
---
test:
- π google ads
- π google analytics
- π Sentry/Better Stack: verify event + session replay delivery (test throw on FE + Nitro)
- π OAuth ?redirect param
- π fix auth errors with zod id param
- π notifications should also be localised
- π production fix email template json not loadings
- π ai cron jobs for article writing and its notifications
- π semantic article dedupe (make sure cronjobs dont repeat the same topic all over again + integrate sentiment suggestion)
- π‘ Localise notifications used in crons/nitro-tasks (think up how to detect language since tasks dont provide event)
- π‘ Localise user PDF export
- π’ centralised ArticleModal component (for ArticleCreate/Edit)
- π’ remake ArticleModal Drafts with ModalMini
---
optional:
- deps to upgrade
- π @sidebase/nuxt-auth 1.2.0 -> 1.3.1 β pulls @auth/core 0.41.3, fixes critical auth bypass GHSA-7rqj-j65f-68wh (exact-pinned, bun update won't do it)
- π‘ @pinia/nuxt 0.11.3 -> 1.0.1
- π’ ua-parser-js -> 2.0.10
- π° @prisma/client 6 -> 7 β NO
- π more color variety
- π‘ background shapes/patterns
- π‘ fonts
- π‘ HP/index page should get a UI rework and move from MVP to actual production ready page
- π’ favorite button for articles (bookmark/save)
- π’ tiptap for comments
---
future:
- π zapier integration
- π‘ automate onboarding flow: wp form -> post fetch on topiqu backend -> vercel api -> create subdomain/return custom domain dns details
- π° (exp. 100 comments reached, 25 articles liked, following 10 authors, positive karma (total likes - total dislikes))...
- π° newsletter for pro+ clients
- π° multi agent writing (seo analyzer, copywriter, reviewer) with tools (debate, vote, merge, post)
- π’ better gamification - badges, levels, site activity points
---
queue (SQS migration):
- β
serverless β self-hosted migration DONE (Dokploy/VPS, 2026-08) β MIGRATION.md deleted, runtime facts now in MAP.md Β§1/Β§4/Β§8
- π decide: queue in Redis Streams vs Postgres SKIP LOCKED β ArticleJob/PdfJob are Prisma models and PG is already managed+backed up; Streams adds a second stateful system and forces persistence config on the cache instance. Decide before writing queue.ts
- π server/utils/queue.ts abstraction (SQS + in-memory) + LocalStack in docker-compose
- π Prisma models: ArticleJob, PdfJob
- π server/worker.ts entrypoint (4 paralelnΓ pollery)
- π SQS queues + DLQ + IAM (infra/CDK)
- π‘ migrate POST /articles/generate β enqueue (202 + jobId)
- π‘ cron generate-article β fan-out only (enqueue N msgs)
- π‘ split notifications worker (DB createMany + SSE publish)
- π‘ unified SSE server-events channel (notifs + job status, cross-replica)
- π‘ useJobStream composable + UI loading/error/retry states
- π‘ GET /users/pdf β enqueue + pdf-worker (S3 upload + signed URL)
- π‘ view counter: enqueue + 5min batch UPDATE worker
- π’ CloudWatch alarms (queue depth, DLQ > 0, retry 3Γ exp backoff)
- π’ vitest workers (in-memory queue) + LocalStack in CI + MAP.md
---
done:
- π nuxt-gtag
- π cliensite language column
- π cliensite customization (themes)
- π implement missing email templates
- π add sessionId for oauth providers in nuxt auth callback
- π useClientSite composable - return language/cliensite details based on host header
- π all clientsite endpoints should use host header instead of clientsite slug/route params
- π AWS S3 Bucket integration
- π AWS SES integration
- π AWS Rekognition integration
- π fix issue in users/[id]/patch : {
"error": true,
"url": "http://localhost:3000/api/users/f0ddd9fb-42af-4fe9-ab4e-4dd9257b632a",
"statusCode": 400,
"statusMessage": "Validation Error",
"message": "Invalid element at key \"id\": expected a Zod schema",
"data": {},
"stack": [
"Invalid element at key \"id\": expected a Zod schema",
"at createError (C:/Users/kurib/rasg-blog/node_modules/h3/dist/index.mjs:71:15)",
"at createValidationError (C:/Users/kurib/rasg-blog/node_modules/h3/dist/index.mjs:237:9)",
"at validateData (C:/Users/kurib/rasg-blog/node_modules/h3/dist/index.mjs:233:11)",
"at readValidatedBody (C:/Users/kurib/rasg-blog/node_modules/h3/dist/index.mjs:454:10)",
"at process.processTicksAndRejections (node:internal/process/task_queues:105:5)",
"at async Object.handler (C:\\Users\\kurib\\rasg-blog\\server\\api\\users\\[id]\\index.patch.ts:17:0)",
"at async file:///C:/Users/kurib/rasg-blog/node_modules/h3/dist/index.mjs:2004:19)",
"at async Object.callAsync (C:/Users/kurib/rasg-blog/node_modules/unctx/dist/index.mjs:72:16)",
"at async Object.callAsync (C:/Users/kurib/rasg-blog/node_modules/unctx/dist/index.mjs:72:16)",
"at async Server.toNodeHandle (C:/Users/kurib/rasg-blog/node_modules/h3/dist/index.mjs:2296:7)"
]
}
- π‘ autorelease articles
- π‘ encapsulate article exports
- π‘ create dropdown menu component w groups
- π‘ account health - sessions, password strength, 2fa, amount of bans, verified email...
- π‘ footer branding for free tier clients
- π‘ fix Comment/Section - sorting options not working, refresh issues (duplicate articles, reactions)
- π° ai sentiment analysis based on comments. (post comment -> onCommentCreated (ai reviews comments, rates it) -> aggregation of all comments and result community/reader perception of articles) - PREMIUM function
- π’ fix header z-index
- π’ lang switcher -> improve styles
- π’ fix background floating objects z-index
- π’ user page - enhance/improve styling & ui for hero section
- π’ rework related articles logic - should have fallback when no tags are present, and select from more tags, not only one
- π° GSC intelligence layer β per-tenant Search Console OAuth feeding the content engine. API is free; the cost is rows and the OAuth verification. Depends on the privacy@/support@ mailboxes above (consent screen requires a contact address + privacy policy).
- per-tenant OAuth (`webmasters.readonly`), not a shared service account β a service account means every client manually adds us as a SC user, which is worse onboarding. Encrypted refresh token on a `SearchConsoleConnection` row keyed by `clientSiteId`; token never reaches a response, a log or the client bundle. Admin of that tenant only.
- **property validation is the security crux**: `sc-domain:client.cz` only for the tenant owning `client.cz`, URL-prefix must equal the tenant's canonical origin, and `sc-domain:topiqu.com` must be refused outright β it spans every tenant subdomain and would leak aggregated data across clients.
- store site/page/query daily rows; derive CTR from clicks/impressions rather than storing it, but *do* store impressions β averaging daily average positions unweighted is arithmetically wrong. Map pageUrl β Article/ArticleTranslation by canonical so the data reaches the editor.
- sync: 16-month backfill in weekly chunks as a background job (never in the OAuth callback); daily cron takes `today-3d` as the last stable day, re-pulls 7 days for late corrections, and re-pulls 30 days weekly. SC days are Pacific, not tenant-local. Idempotent upsert on (clientSiteId, date, pageUrl, query, device, country); monitor like `gam-sync`.
- the value is the decision layer, not a dashboard: emit `{ query, intent, existingArticleId?, recommendation: UPDATE|EXPAND|NEW_ARTICLE|IGNORE, evidence }` so the engine mostly improves existing URLs instead of manufacturing more β that is the brake on shitty scale. Plus cannibalisation detection (two URLs alternating on one query β merge/canonicalise, never a third article) and 30/60/90-day article review.
- caveat for the engine: zero clicks is not evidence of bad content (new domain, no authority, zero-click query), and CTR must not be used to make it copy the style of whatever performed.
- phases: OAuth + property pick β daily ingest β dashboard β URLβArticle mapping β opportunity detector β SC context in the topic picker β automatic 30/60/90 review.
- π° generic canary / feature-flag primitive (modular + scalable) - reusable, not cache-specific. Best practices:
- deterministic bucketing hash(subject + flagKey) % 100 < rollout (NOT Math.random) -> sticky per subject, no flapping; salt by flagKey
- SSR/client consistency: same deterministic hash from a stable subject so server & client agree (avoids hydration mismatch when a flag affects render)
- config separate from code & changeable at runtime -> rollout state in Redis (Upstash), env/in-code default as fallback chain (Redis -> env -> default OFF); kill switch field for instant rollback, no redeploy
- evaluate() returns { enabled, variant, reason: KILL_SWITCH|DENY|ALLOW|PERCENTAGE|DEFAULT } not bool, for observability
- default-safe: unknown flag / provider error -> safe default (OFF for risky new paths); cache outage must not flip a flag on
- pluggable FlagProvider (swap Env/Redis -> LaunchDarkly/OpenFeature later w/o touching call sites); pluggable subject dimension (requestId/sessionId/userId/clientSiteId; per-tenant rollout is the multi-tenant win)
- support weighted multivariant (A/B/C), allow/deny lists
- layout: shared/canary/{types,evaluate}.ts (pure deterministic core, FNV-1a, unit-tested, shared client+server) ; server/utils/canary.ts (provider + 30s in-memory config cache so we don't hit Redis per request + canary() + withShadow()) ; app/composables/useCanary.ts (client eval over SSR-hydrated flags)
- withShadow(flag, subject, { primary, candidate }) -> runs both, returns primary, logs diff = generic version of the cached-vs-live leak check
- observability: emit each evaluation (flag, variant, reason, hashed subject) to logs AND Sentry (breadcrumb/tag or measurement) so we can correlate canary cohort vs rest, and a bad variant surfaces in error tracking
- first consumer: wrap the feed-cache (server/api/articles/by-clientsite/[slug].ts) so canary actually drives the cache we added, not theory
- π° server cache follow-ups - DONE: temp [cache] HIT/MISS logs dropped, bumpGen wired into article mutations via invalidateFeed(). STILL OPEN: broaden anon-only -> all non-privileged readers; size TTLs from real hit rates (INFO commandstats on the valkey container now that Upstash's request counter is gone).
- π‘ cache step 2 (agreed, not started): mount valkey as nitro storage (nitro.storage.cache redis driver) β also fixes ogImage.runtimeCacheStorage running on per-instance memory; then defineCachedEventHandler on the session-free routes, best candidate clients/slug/[slug].ts (raw prisma, publicClientSiteSelect, hit on every SSR render), then gifs/index.get.ts (Giphy quota). ALWAYS write getKey explicitly β the default key comes from the URL and any host-resolved tenant route would collide. APP_ENV=production is configured in Dokploy, so this is unblocked.
- π‘ cache step 3 (agreed, not started): featured/[slug].ts onto cached() with an anon guard mirroring by-clientsite (it calls getEnhancedPrisma(user), so the payload is role-dependent and must not be cached for logged-in callers)
- π’ feedback loop (was this helpful ? π/π)
- π’ article series (how to build a house 1/5) + next part/back button
- π’ replace sweetalert modals with custom ModalMini component
- π‘ Stripe integration for token refill