v1.0.10 légal item 3. RGPD requires explicit re-acceptance of any
terms-of-service-class document on material change. Adds a per-user,
per-document, per-version ledger so disputes can be answered with
evidence (timestamp + originating IP + user-agent).
Backend
* migrations/991_terms_acceptance.sql — table terms_acceptances with
UNIQUE (user_id, terms_type, version) so re-accepts are idempotent.
inet column for IP, varchar(512) for UA, both nullable for the
internal seed paths.
* internal/services/terms_service.go — TermsService :
- CurrentTerms map (ISO date version per class) is the single
source of truth ; bump on text edit.
- CurrentVersions(userID) returns versions + the user's
unaccepted set ; userID==Nil ⇒ versions only (anonymous OK).
- Accept(userID, []AcceptInput) : validates each (type, version)
against CurrentTerms (ErrTermsVersionMismatch on stale POST),
writes one row per accept in a single transaction, idempotent
via FirstOrCreate against the unique index.
* internal/handlers/terms_handler.go — REST surface :
- GET /api/v1/legal/terms/current (public, OptionalAuth)
- POST /api/v1/legal/terms/accept (RequireAuth)
- Captures IP via gin's ClientIP() (X-Forwarded-For-aware) and
UA from the request, truncates UA to fit the column.
* routes_legal.go — wires the two endpoints. `current` falls back
to no-middleware when AuthMiddleware is nil so test rigs work.
Frontend
* features/legal/pages/{CGUPage,CGVPage,MentionsPage}.tsx — initial
drafts with version constants matching the backend's CurrentTerms.
Counsel review required before v2.0.0 (text is honest baseline,
not finalised legal copy).
* services/api/legalTerms.ts — fetchCurrentTerms() / acceptTerms() ;
hand-written to keep the consent-modal wiring readable.
* components/TermsAcceptanceModal.tsx — non-dismissable modal that
opens on every authenticated session when the unaccepted set is
non-empty. Per-document checkboxes + single submit ; refusal keeps
the modal open (no decline-and-continue path because the legal
contract requires acceptance to use the platform).
* Mounted in App.tsx alongside CookieBanner ; both must overlay
every screen.
* Lazy-component registry + routes for /legal/{cgu,cgv,mentions}.
Operator workflow when text changes :
1. Edit the text in the relevant page component. Bump the
`*_VERSION` const in that file.
2. Bump CurrentTerms[*] in services/terms_service.go to the same
value.
3. Deploy. Every existing user gets force-prompted on their next
session ; new users prompted at registration.
baseline checks : tsc 0 errors, eslint 754, go build clean.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
62 lines
1.2 KiB
TypeScript
62 lines
1.2 KiB
TypeScript
export {
|
|
// eslint-disable-next-line react-refresh/only-export-components -- lazy-component registry
|
|
createLazyComponent,
|
|
LazyErrorFallback,
|
|
LazyErrorBoundary,
|
|
LazyDashboard,
|
|
LazyChat,
|
|
LazyChatJoin,
|
|
LazyLibrary,
|
|
LazyProfile,
|
|
LazySettings,
|
|
LazyLogin,
|
|
LazyRegister,
|
|
LazyForgotPassword,
|
|
LazyVerifyEmail,
|
|
LazyResetPassword,
|
|
LazySessions,
|
|
LazyNotFound,
|
|
LazyServerError,
|
|
LazyUserProfile,
|
|
LazyRoles,
|
|
LazyTrackDetail,
|
|
LazyPlaylistRoutes,
|
|
LazySharedPlaylistPage,
|
|
LazyAdminDashboard,
|
|
LazyAdminModeration,
|
|
LazyAdminPlatform,
|
|
LazyAdminTransfers,
|
|
LazyAnalytics,
|
|
LazyWebhooks,
|
|
LazyDesignSystemDemo,
|
|
LazySocial,
|
|
LazyFeed,
|
|
LazyDiscover,
|
|
LazyGear,
|
|
LazyLive,
|
|
LazyGoLive,
|
|
LazyListenTogether,
|
|
LazyCloud,
|
|
LazyQueue,
|
|
LazyDeveloper,
|
|
LazyNotifications,
|
|
LazyMarketplace,
|
|
LazySearch,
|
|
LazySellerDashboard,
|
|
LazyWishlist,
|
|
LazyPurchases,
|
|
LazyProductDetail,
|
|
LazyCheckoutComplete,
|
|
LazySubscription,
|
|
LazyDistribution,
|
|
LazyEducation,
|
|
LazySupport,
|
|
LazyLanding,
|
|
LazyDmca,
|
|
LazyDmcaNotice,
|
|
LazyPrivacy,
|
|
LazyCGU,
|
|
LazyCGV,
|
|
LazyMentions,
|
|
} from './lazy-component';
|
|
export type { LazyComponentProps, LazyErrorFallbackProps, LazyErrorBoundaryProps } from './lazy-component';
|