@meith/web 0.16.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE.md +165 -0
- package/app/(auth)/layout.tsx +6 -0
- package/app/(auth)/login/page.tsx +106 -0
- package/app/(auth)/login/verify/page.tsx +55 -0
- package/app/(auth)/register/page.tsx +83 -0
- package/app/(auth)/reset/confirm/page.tsx +45 -0
- package/app/(auth)/reset/page.tsx +22 -0
- package/app/(auth)/verify/page.tsx +44 -0
- package/app/(auth)/verify/resend/page.tsx +51 -0
- package/app/(board)/[slug]/feed.xml/route.ts +48 -0
- package/app/(board)/[slug]/new/page.tsx +112 -0
- package/app/(board)/[slug]/page.tsx +403 -0
- package/app/(board)/discover/[view]/page.tsx +115 -0
- package/app/(board)/discover/page.tsx +5 -0
- package/app/(board)/layout.tsx +22 -0
- package/app/(board)/member/[id]/page.tsx +187 -0
- package/app/(board)/member/[id]/reputation/page.tsx +195 -0
- package/app/(board)/member/by-name/[name]/page.tsx +32 -0
- package/app/(board)/messages/[id]/page.tsx +103 -0
- package/app/(board)/messages/compose/page.tsx +60 -0
- package/app/(board)/messages/layout.tsx +5 -0
- package/app/(board)/messages/page.tsx +179 -0
- package/app/(board)/modcp/forums/page.tsx +59 -0
- package/app/(board)/modcp/ip/page.tsx +158 -0
- package/app/(board)/modcp/layout.tsx +5 -0
- package/app/(board)/modcp/log/page.tsx +98 -0
- package/app/(board)/modcp/page.tsx +112 -0
- package/app/(board)/moderation/layout.tsx +5 -0
- package/app/(board)/moderation/page.tsx +111 -0
- package/app/(board)/moderation/reports/page.tsx +193 -0
- package/app/(board)/moderation/warn/page.tsx +158 -0
- package/app/(board)/notifications/layout.tsx +5 -0
- package/app/(board)/notifications/page.tsx +144 -0
- package/app/(board)/notifications/preferences/page.tsx +65 -0
- package/app/(board)/online/page.tsx +119 -0
- package/app/(board)/page.tsx +195 -0
- package/app/(board)/plugins/[key]/[[...path]]/page.tsx +72 -0
- package/app/(board)/privacy/page.tsx +12 -0
- package/app/(board)/report/page.tsx +71 -0
- package/app/(board)/rules/page.tsx +12 -0
- package/app/(board)/search/[token]/page.tsx +130 -0
- package/app/(board)/search/page.tsx +289 -0
- package/app/(board)/stats/page.tsx +202 -0
- package/app/(board)/subscriptions/layout.tsx +5 -0
- package/app/(board)/subscriptions/page.tsx +142 -0
- package/app/(board)/terms/page.tsx +12 -0
- package/app/(board)/thread/[slug]/edit/page.tsx +107 -0
- package/app/(board)/thread/[slug]/feed.xml/route.ts +42 -0
- package/app/(board)/thread/[slug]/page.tsx +707 -0
- package/app/(board)/thread/[slug]/poll/page.tsx +41 -0
- package/app/(board)/thread/[slug]/post/[postId]/history/page.tsx +160 -0
- package/app/(board)/thread/[slug]/reply/page.tsx +136 -0
- package/app/(board)/unsubscribe/page.tsx +73 -0
- package/app/(board)/usercp/avatar/page.tsx +56 -0
- package/app/(board)/usercp/contacts/page.tsx +141 -0
- package/app/(board)/usercp/email/confirm/page.tsx +52 -0
- package/app/(board)/usercp/layout.tsx +23 -0
- package/app/(board)/usercp/options/page.tsx +56 -0
- package/app/(board)/usercp/page.tsx +71 -0
- package/app/(board)/usercp/profile/page.tsx +81 -0
- package/app/(board)/usercp/security/page.tsx +238 -0
- package/app/(board)/usercp/security/verify/page.tsx +92 -0
- package/app/(board)/usercp/signature/page.tsx +67 -0
- package/app/admin/antispam/page.tsx +155 -0
- package/app/admin/api/marketplace/screenshot/route.ts +68 -0
- package/app/admin/api/plugins/[key]/[...path]/route.ts +27 -0
- package/app/admin/api-tokens/page.tsx +129 -0
- package/app/admin/content/announcements/page.tsx +110 -0
- package/app/admin/content/attachments/page.tsx +199 -0
- package/app/admin/content/navigation/page.tsx +123 -0
- package/app/admin/content/page.tsx +170 -0
- package/app/admin/forums/[id]/page.tsx +96 -0
- package/app/admin/forums/[id]/permissions/page.tsx +108 -0
- package/app/admin/forums/page.tsx +45 -0
- package/app/admin/groups/[id]/page.tsx +133 -0
- package/app/admin/groups/memberships/page.tsx +56 -0
- package/app/admin/groups/page.tsx +104 -0
- package/app/admin/groups/promotions/page.tsx +143 -0
- package/app/admin/layout.tsx +86 -0
- package/app/admin/log/page.tsx +116 -0
- package/app/admin/page.tsx +206 -0
- package/app/admin/plugins/[key]/[[...path]]/page.tsx +276 -0
- package/app/admin/plugins/browse/page.tsx +70 -0
- package/app/admin/plugins/page.tsx +165 -0
- package/app/admin/security/page.tsx +124 -0
- package/app/admin/settings/page.tsx +190 -0
- package/app/admin/system/page.tsx +303 -0
- package/app/admin/themes/[key]/page.tsx +110 -0
- package/app/admin/themes/browse/page.tsx +70 -0
- package/app/admin/themes/page.tsx +163 -0
- package/app/admin/users/[id]/merge/page.tsx +136 -0
- package/app/admin/users/[id]/page.tsx +208 -0
- package/app/admin/users/mail/page.tsx +68 -0
- package/app/admin/users/page.tsx +260 -0
- package/app/admin/users/prune/page.tsx +186 -0
- package/app/api/health/route.ts +11 -0
- package/app/api/metrics/route.ts +66 -0
- package/app/api/plugins/[key]/[...path]/route.ts +27 -0
- package/app/api/read/all/route.ts +15 -0
- package/app/api/read/forum/[id]/route.ts +29 -0
- package/app/api/read/thread/[id]/route.ts +40 -0
- package/app/api/ready/route.ts +63 -0
- package/app/api/system/tick/route.ts +77 -0
- package/app/api/v1/[...path]/route.ts +208 -0
- package/app/api/v1/openapi.json/route.ts +12 -0
- package/app/apple-icon.ts +11 -0
- package/app/atom.xml/route.ts +7 -0
- package/app/attachment/[id]/route.ts +10 -0
- package/app/attachment/[id]/thumb/route.ts +10 -0
- package/app/auth/passkey/options/route.ts +145 -0
- package/app/auth/passkey/verify/route.ts +241 -0
- package/app/auth/resume/route.ts +55 -0
- package/app/auth/sso/[provider]/callback/route.ts +173 -0
- package/app/auth/sso/[provider]/go/route.ts +57 -0
- package/app/auth/sso/[provider]/route.ts +105 -0
- package/app/avatar/[id]/route.ts +31 -0
- package/app/brand/icon-192.png/route.ts +7 -0
- package/app/brand/icon-512.png/route.ts +7 -0
- package/app/brand/icon-maskable.png/route.ts +7 -0
- package/app/demo/stripe/[[...path]]/route.ts +27 -0
- package/app/error.tsx +15 -0
- package/app/feed.xml/route.ts +7 -0
- package/app/forumdisplay.php/page.tsx +11 -0
- package/app/group/[id]/badge/[scheme]/route.ts +26 -0
- package/app/icon.ts +13 -0
- package/app/install/page.tsx +221 -0
- package/app/jump/page.tsx +28 -0
- package/app/layout.tsx +124 -0
- package/app/logo/[scheme]/route.ts +24 -0
- package/app/manifest.ts +43 -0
- package/app/member.php/page.tsx +11 -0
- package/app/memberlist.php/page.tsx +11 -0
- package/app/not-found.tsx +14 -0
- package/app/opengraph-image.ts +11 -0
- package/app/redirect/page.tsx +44 -0
- package/app/robots.txt/route.ts +35 -0
- package/app/showthread.php/page.tsx +11 -0
- package/app/sitemap/[page]/route.ts +64 -0
- package/app/sitemap.xml/route.ts +31 -0
- package/app/twitter-image.ts +11 -0
- package/app/viewforum.php/page.tsx +11 -0
- package/app/viewtopic.php/page.tsx +11 -0
- package/bin/forum-web.mjs +241 -0
- package/components.json +21 -0
- package/instrumentation.ts +18 -0
- package/next.config.mjs +182 -0
- package/package.json +99 -0
- package/postcss.config.mjs +7 -0
- package/proxy.ts +160 -0
- package/src/components/account/active-sessions.tsx +87 -0
- package/src/components/account/avatar-form.tsx +119 -0
- package/src/components/account/credential-proof-form.tsx +76 -0
- package/src/components/account/logout-form.tsx +14 -0
- package/src/components/account/notification-forms.tsx +157 -0
- package/src/components/account/passkey-enrol.tsx +81 -0
- package/src/components/account/passkey-proof-button.tsx +47 -0
- package/src/components/account/push-device-form.tsx +193 -0
- package/src/components/account/relation-forms.tsx +69 -0
- package/src/components/account/reputation-forms.tsx +124 -0
- package/src/components/account/security-activity.tsx +46 -0
- package/src/components/account/sign-in-methods.tsx +166 -0
- package/src/components/account/subscription-forms.tsx +151 -0
- package/src/components/account/two-factor-forms.tsx +226 -0
- package/src/components/account/usercp-forms.tsx +387 -0
- package/src/components/account/usercp-nav.tsx +14 -0
- package/src/components/account/usercp-shell.tsx +34 -0
- package/src/components/admin/admin-forms.tsx +92 -0
- package/src/components/admin/admin-nav.tsx +52 -0
- package/src/components/admin/admin-undo.tsx +32 -0
- package/src/components/admin/api-token-forms.tsx +89 -0
- package/src/components/admin/badge-forms.tsx +97 -0
- package/src/components/admin/branding-forms.tsx +102 -0
- package/src/components/admin/content-forms.tsx +712 -0
- package/src/components/admin/form-bits.tsx +11 -0
- package/src/components/admin/forum-forms.tsx +524 -0
- package/src/components/admin/forum-tree.tsx +454 -0
- package/src/components/admin/group-forms.tsx +646 -0
- package/src/components/admin/mail-test-card.tsx +90 -0
- package/src/components/admin/marketplace-forms.tsx +30 -0
- package/src/components/admin/marketplace-listing.tsx +123 -0
- package/src/components/admin/navigation-forms.tsx +229 -0
- package/src/components/admin/navigation-tree.tsx +442 -0
- package/src/components/admin/oklch-picker.tsx +180 -0
- package/src/components/admin/plugin-forms.tsx +225 -0
- package/src/components/admin/settings-form.tsx +148 -0
- package/src/components/admin/system-forms.tsx +169 -0
- package/src/components/admin/theme-changes.tsx +354 -0
- package/src/components/admin/theme-forms.tsx +636 -0
- package/src/components/admin/theme-palette.tsx +119 -0
- package/src/components/admin/theme-preview.tsx +461 -0
- package/src/components/admin/user-bulk-toolbar.tsx +99 -0
- package/src/components/admin/user-forms.tsx +488 -0
- package/src/components/auth/auth-page.tsx +45 -0
- package/src/components/auth/form-controls.tsx +117 -0
- package/src/components/auth/login-form.tsx +47 -0
- package/src/components/auth/passkey-client.ts +171 -0
- package/src/components/auth/passkey-second-factor.tsx +69 -0
- package/src/components/auth/passkey-sign-in.tsx +61 -0
- package/src/components/auth/register-form.tsx +136 -0
- package/src/components/auth/resend-verification-form.tsx +34 -0
- package/src/components/auth/reset-confirm-form.tsx +43 -0
- package/src/components/auth/reset-request-form.tsx +35 -0
- package/src/components/auth/second-factor-form.tsx +51 -0
- package/src/components/auth/sso-buttons.tsx +47 -0
- package/src/components/board/live-region.tsx +74 -0
- package/src/components/board/search-off-notice.tsx +18 -0
- package/src/components/board/section-page.tsx +114 -0
- package/src/components/content/attachment-field.tsx +51 -0
- package/src/components/content/composer-field.ts +31 -0
- package/src/components/content/composer-ids.ts +6 -0
- package/src/components/content/composer-intents.tsx +27 -0
- package/src/components/content/composer-recovery.tsx +215 -0
- package/src/components/content/edit-post-form.tsx +121 -0
- package/src/components/content/markdown-editor.tsx +590 -0
- package/src/components/content/markdown-syntax.ts +53 -0
- package/src/components/content/mention-token.ts +23 -0
- package/src/components/content/multiquote-button.tsx +32 -0
- package/src/components/content/multiquote-selection.tsx +105 -0
- package/src/components/content/multiquote.ts +125 -0
- package/src/components/content/new-thread-form.tsx +169 -0
- package/src/components/content/poll-edit-form.tsx +96 -0
- package/src/components/content/poll.tsx +89 -0
- package/src/components/content/quote-in-place.tsx +51 -0
- package/src/components/content/reply-form.tsx +113 -0
- package/src/components/content/thanks-button.tsx +65 -0
- package/src/components/content/thread-rating.tsx +143 -0
- package/src/components/install/install-form.tsx +497 -0
- package/src/components/legal/legal-document.tsx +22 -0
- package/src/components/messages/message-forms.tsx +152 -0
- package/src/components/moderation/inline-moderation-form.tsx +158 -0
- package/src/components/moderation/modcp-nav.tsx +15 -0
- package/src/components/moderation/modcp-shell.tsx +41 -0
- package/src/components/moderation/queue-form.tsx +96 -0
- package/src/components/moderation/report-forms.tsx +120 -0
- package/src/components/moderation/signature-lock-form.tsx +83 -0
- package/src/components/moderation/thread-surgery-form.tsx +96 -0
- package/src/components/moderation/thread-tools-form.tsx +137 -0
- package/src/components/moderation/warning-forms.tsx +125 -0
- package/src/components/profile/custom-field.tsx +79 -0
- package/src/components/shell/appearance-icons.tsx +35 -0
- package/src/components/shell/board-notice.tsx +21 -0
- package/src/components/shell/copy-record.ts +20 -0
- package/src/components/shell/copy.tsx +17 -0
- package/src/components/shell/crash-notice.tsx +86 -0
- package/src/components/shell/demo-banner.tsx +49 -0
- package/src/components/shell/group-name-style.tsx +6 -0
- package/src/components/shell/measure.ts +1 -0
- package/src/components/shell/notification-menu.tsx +361 -0
- package/src/components/shell/offline-notice.tsx +29 -0
- package/src/components/shell/onboarding-banner.tsx +39 -0
- package/src/components/shell/page-shell.tsx +167 -0
- package/src/components/shell/panel-list.tsx +95 -0
- package/src/components/shell/panel-nav.tsx +40 -0
- package/src/components/shell/panel-overview.tsx +81 -0
- package/src/components/shell/panel-page.tsx +96 -0
- package/src/components/shell/panel-pagination.tsx +28 -0
- package/src/components/shell/panel-shell.tsx +31 -0
- package/src/components/shell/theme-runtime-style.tsx +6 -0
- package/src/components/shell/theme-switcher.tsx +97 -0
- package/src/components/shell/timezone-probe.tsx +45 -0
- package/src/components/shell/view-tabs.tsx +60 -0
- package/src/config.ts +13 -0
- package/src/server/absent-services.ts +29 -0
- package/src/server/admin-actions.ts +171 -0
- package/src/server/admin-settings-actions.ts +83 -0
- package/src/server/admin-undo-actions.ts +70 -0
- package/src/server/admin-undo.ts +75 -0
- package/src/server/admin.ts +112 -0
- package/src/server/announcements.ts +82 -0
- package/src/server/antispam.ts +310 -0
- package/src/server/api/content.ts +486 -0
- package/src/server/api/http.ts +119 -0
- package/src/server/api/members.ts +109 -0
- package/src/server/api/messages.ts +113 -0
- package/src/server/api/registry.ts +30 -0
- package/src/server/api/search.ts +98 -0
- package/src/server/api/subscriptions.ts +106 -0
- package/src/server/api-auth.ts +66 -0
- package/src/server/api-token-actions.ts +85 -0
- package/src/server/api-tokens-admin.ts +95 -0
- package/src/server/appearance-actions.ts +39 -0
- package/src/server/attachment-download.ts +43 -0
- package/src/server/attachment-embed.ts +63 -0
- package/src/server/attachment-upload-actions.ts +92 -0
- package/src/server/attachments.ts +257 -0
- package/src/server/auth-actions.ts +479 -0
- package/src/server/auth-config.ts +61 -0
- package/src/server/auth-events.ts +92 -0
- package/src/server/auth-form-state.ts +14 -0
- package/src/server/auth-mail.ts +104 -0
- package/src/server/avatars.ts +83 -0
- package/src/server/board-latest-actions.ts +7 -0
- package/src/server/board-latest.tsx +103 -0
- package/src/server/board-offline.ts +23 -0
- package/src/server/board-url.ts +14 -0
- package/src/server/brand-assets.ts +151 -0
- package/src/server/brand-mark.tsx +110 -0
- package/src/server/branding-actions.ts +53 -0
- package/src/server/branding.ts +101 -0
- package/src/server/cache-targets.ts +13 -0
- package/src/server/code-highlighting.ts +33 -0
- package/src/server/container.ts +402 -0
- package/src/server/content-actions.ts +459 -0
- package/src/server/content-admin-actions.ts +492 -0
- package/src/server/content-admin.ts +95 -0
- package/src/server/content-security-policy.ts +35 -0
- package/src/server/context.ts +32 -0
- package/src/server/cookies.ts +139 -0
- package/src/server/credential-proof-actions.ts +42 -0
- package/src/server/credential-proof.ts +35 -0
- package/src/server/current-location.ts +19 -0
- package/src/server/demo-stripe.ts +104 -0
- package/src/server/demo-uploads.ts +28 -0
- package/src/server/demo.ts +80 -0
- package/src/server/discovery.ts +104 -0
- package/src/server/embeds.ts +113 -0
- package/src/server/error-notice.tsx +41 -0
- package/src/server/federation-actions.ts +113 -0
- package/src/server/federation.ts +138 -0
- package/src/server/feed-builder.ts +72 -0
- package/src/server/feed-routes.ts +122 -0
- package/src/server/fixture-actor-source.ts +62 -0
- package/src/server/fixture-forum-repo.ts +59 -0
- package/src/server/fixture-member-profile-repo.ts +30 -0
- package/src/server/fixture-post-repo.ts +108 -0
- package/src/server/fixture-thread-repo.ts +98 -0
- package/src/server/form-state-reporter.ts +34 -0
- package/src/server/form-values.ts +23 -0
- package/src/server/forum-admin-actions.ts +353 -0
- package/src/server/forum-admin.ts +98 -0
- package/src/server/group-admin-actions.ts +341 -0
- package/src/server/group-admin.ts +98 -0
- package/src/server/group-badge-actions.ts +61 -0
- package/src/server/group-badge.ts +62 -0
- package/src/server/group-identity.ts +104 -0
- package/src/server/i18n-catalogs.ts +42 -0
- package/src/server/i18n.ts +69 -0
- package/src/server/image-upload.ts +109 -0
- package/src/server/inline-moderation-actions.ts +129 -0
- package/src/server/install-actions.ts +124 -0
- package/src/server/install.ts +281 -0
- package/src/server/legacy-redirect.ts +83 -0
- package/src/server/legal.ts +43 -0
- package/src/server/location-header.ts +37 -0
- package/src/server/mail-brand.ts +43 -0
- package/src/server/mail-health.ts +41 -0
- package/src/server/mail-send.ts +44 -0
- package/src/server/mail-test-actions.ts +67 -0
- package/src/server/mail-test.ts +56 -0
- package/src/server/markdown-pipeline.ts +31 -0
- package/src/server/marketplace-actions.ts +47 -0
- package/src/server/marketplace-admin.ts +260 -0
- package/src/server/mention-actions.ts +21 -0
- package/src/server/mention-search.ts +35 -0
- package/src/server/message-actions.ts +110 -0
- package/src/server/messages.ts +141 -0
- package/src/server/modcp.ts +125 -0
- package/src/server/moderation-actions.ts +165 -0
- package/src/server/navigation-admin-actions.ts +217 -0
- package/src/server/navigation.ts +112 -0
- package/src/server/nonce.ts +13 -0
- package/src/server/notification-actions.ts +84 -0
- package/src/server/notification-audience.ts +10 -0
- package/src/server/notification-menu-actions.ts +82 -0
- package/src/server/notification-menu.ts +96 -0
- package/src/server/notifications.ts +93 -0
- package/src/server/onboarding-actions.ts +27 -0
- package/src/server/onboarding.ts +89 -0
- package/src/server/page-metadata.ts +14 -0
- package/src/server/passkey-challenge.ts +42 -0
- package/src/server/plugin-admin-actions.ts +232 -0
- package/src/server/plugin-admin.ts +276 -0
- package/src/server/plugin-host.ts +143 -0
- package/src/server/plugin-pages.ts +192 -0
- package/src/server/plugin-panel.ts +49 -0
- package/src/server/plugin-routes.ts +298 -0
- package/src/server/plugin-view.tsx +58 -0
- package/src/server/poll-actions.ts +104 -0
- package/src/server/poll-scope.ts +52 -0
- package/src/server/post-notifications.ts +71 -0
- package/src/server/post-scope.ts +86 -0
- package/src/server/presence.ts +177 -0
- package/src/server/profile-fields.ts +112 -0
- package/src/server/push-actions.ts +113 -0
- package/src/server/push.ts +40 -0
- package/src/server/redirect-back.ts +15 -0
- package/src/server/registration.ts +7 -0
- package/src/server/relation-actions.ts +89 -0
- package/src/server/relations.ts +45 -0
- package/src/server/reply-core.ts +173 -0
- package/src/server/report-actions.ts +165 -0
- package/src/server/report-scope.ts +21 -0
- package/src/server/reputation-actions.ts +146 -0
- package/src/server/reputation-target.ts +49 -0
- package/src/server/reputation.ts +70 -0
- package/src/server/request-fingerprint.ts +65 -0
- package/src/server/safe-path.ts +16 -0
- package/src/server/same-origin.ts +45 -0
- package/src/server/search-page.ts +313 -0
- package/src/server/search.ts +45 -0
- package/src/server/secret-auth.ts +20 -0
- package/src/server/see-other.ts +11 -0
- package/src/server/seed-board.ts +417 -0
- package/src/server/session-actions.ts +78 -0
- package/src/server/session-cookies.ts +124 -0
- package/src/server/session-key.ts +17 -0
- package/src/server/settings.ts +47 -0
- package/src/server/signatures.ts +62 -0
- package/src/server/sso-handshake.ts +79 -0
- package/src/server/stats.ts +63 -0
- package/src/server/subscription-actions.ts +161 -0
- package/src/server/surgery-actions.ts +180 -0
- package/src/server/syndication.ts +77 -0
- package/src/server/system-admin-actions.ts +134 -0
- package/src/server/system-admin.ts +90 -0
- package/src/server/test-container.ts +114 -0
- package/src/server/theme-admin-actions.ts +242 -0
- package/src/server/theme-admin.ts +203 -0
- package/src/server/theme-runtime.ts +89 -0
- package/src/server/theme-style.ts +279 -0
- package/src/server/theme.ts +52 -0
- package/src/server/thread-core.ts +207 -0
- package/src/server/thread-rating-actions.ts +55 -0
- package/src/server/thread-tool-actions.ts +154 -0
- package/src/server/two-factor-actions.ts +178 -0
- package/src/server/two-factor.ts +86 -0
- package/src/server/upgrade-notice.ts +41 -0
- package/src/server/user-admin-actions.ts +572 -0
- package/src/server/user-admin.ts +165 -0
- package/src/server/usercp-actions.ts +263 -0
- package/src/server/usercp-mail.ts +120 -0
- package/src/server/viewer-preferences.ts +64 -0
- package/src/server/warning-actions.ts +122 -0
- package/src/styles/globals.css +969 -0
- package/src/theme/contract.fixture.ts +793 -0
- package/src/view/account-copy.ts +351 -0
- package/src/view/admin-content-copy.ts +66 -0
- package/src/view/admin-copy.ts +106 -0
- package/src/view/admin-forum-tree-copy.ts +40 -0
- package/src/view/admin-group-copy.ts +80 -0
- package/src/view/admin-log.ts +58 -0
- package/src/view/admin-nav.ts +164 -0
- package/src/view/admin-navigation-copy.ts +58 -0
- package/src/view/admin-panel-copy.ts +194 -0
- package/src/view/admin-settings.ts +173 -0
- package/src/view/admin-theme-copy.ts +229 -0
- package/src/view/admin-user-copy.ts +96 -0
- package/src/view/attachments.ts +51 -0
- package/src/view/auth-copy.ts +147 -0
- package/src/view/board-index.ts +167 -0
- package/src/view/board-latest.ts +101 -0
- package/src/view/breadcrumb.ts +50 -0
- package/src/view/contacts.ts +85 -0
- package/src/view/content-copy.ts +118 -0
- package/src/view/contrast.ts +286 -0
- package/src/view/copy.ts +101 -0
- package/src/view/count.ts +8 -0
- package/src/view/discovery-view.ts +74 -0
- package/src/view/editor-toolbar.ts +84 -0
- package/src/view/error-notice.ts +52 -0
- package/src/view/feed.ts +130 -0
- package/src/view/forum-cursor.ts +54 -0
- package/src/view/forum-display.ts +152 -0
- package/src/view/forum-jump.ts +60 -0
- package/src/view/forum-notice.ts +24 -0
- package/src/view/inline-moderation.ts +87 -0
- package/src/view/install-copy.ts +95 -0
- package/src/view/legal.ts +58 -0
- package/src/view/locale.ts +40 -0
- package/src/view/marketplace-panel.ts +51 -0
- package/src/view/member-identity.ts +23 -0
- package/src/view/member-profile.ts +65 -0
- package/src/view/messages.ts +222 -0
- package/src/view/metadata.ts +75 -0
- package/src/view/modcp-nav.ts +76 -0
- package/src/view/moderation-copy.ts +84 -0
- package/src/view/moderation-queue.ts +67 -0
- package/src/view/navigation-arrange.ts +188 -0
- package/src/view/navigation.ts +173 -0
- package/src/view/notification-menu.ts +202 -0
- package/src/view/notifications.ts +84 -0
- package/src/view/onboarding.ts +26 -0
- package/src/view/pager.ts +191 -0
- package/src/view/panel-nav.ts +203 -0
- package/src/view/plugin-panel.ts +42 -0
- package/src/view/post-form.ts +61 -0
- package/src/view/post-link.ts +29 -0
- package/src/view/presence.ts +99 -0
- package/src/view/promotion-rules.ts +52 -0
- package/src/view/redirect-notice.ts +21 -0
- package/src/view/reputation.ts +107 -0
- package/src/view/search-controls.ts +103 -0
- package/src/view/search-results.ts +291 -0
- package/src/view/security-activity.ts +73 -0
- package/src/view/setting-groups.ts +43 -0
- package/src/view/shell.ts +164 -0
- package/src/view/slug-id.ts +12 -0
- package/src/view/sso-hand-off.ts +44 -0
- package/src/view/sso-notices.ts +48 -0
- package/src/view/subscriptions.ts +76 -0
- package/src/view/theme-draft.ts +167 -0
- package/src/view/theme-preference.ts +30 -0
- package/src/view/theme-tokens.ts +333 -0
- package/src/view/thread-view.ts +280 -0
- package/src/view/time.ts +31 -0
- package/src/view/timezone.ts +35 -0
- package/src/view/two-factor.ts +6 -0
- package/src/view/usercp-nav.ts +103 -0
- package/src/view/usercp.ts +196 -0
- package/src/view/warnings.ts +128 -0
- package/src/view/word-filter.ts +5 -0
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import 'server-only'
|
|
2
|
+
|
|
3
|
+
import { cache } from 'react'
|
|
4
|
+
|
|
5
|
+
import { CacheTags } from '@meith/core'
|
|
6
|
+
import { getDb, PostgresSettingsRepository } from '@meith/db'
|
|
7
|
+
import { drivers } from '@meith/drivers'
|
|
8
|
+
import { isLogoKey, type LogoScheme, logoPath, saveSettings } from '@meith/settings'
|
|
9
|
+
|
|
10
|
+
import { forgetImage, storeImage } from './image-upload'
|
|
11
|
+
import { getSettings } from './settings'
|
|
12
|
+
import { currentColourScheme } from './theme'
|
|
13
|
+
|
|
14
|
+
export const LOGO_FIELD = 'logo'
|
|
15
|
+
|
|
16
|
+
export { isLogoScheme, type LogoScheme } from '@meith/settings'
|
|
17
|
+
|
|
18
|
+
const SETTING_FOR: Record<LogoScheme, 'board.logo_light' | 'board.logo_dark'> = {
|
|
19
|
+
light: 'board.logo_light',
|
|
20
|
+
dark: 'board.logo_dark',
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
async function writeSetting(key: string, value: string): Promise<void> {
|
|
24
|
+
const result = await saveSettings(
|
|
25
|
+
new PostgresSettingsRepository(getDb()),
|
|
26
|
+
{ [key]: value },
|
|
27
|
+
await getSettings(),
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
if (result.changed.length > 0) {
|
|
31
|
+
await drivers().cache.invalidateTags([CacheTags.settings(), ...result.invalidates])
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export async function saveLogo(scheme: LogoScheme, file: File): Promise<void> {
|
|
36
|
+
const key = await storeImage('board', `logo-${scheme}`, file)
|
|
37
|
+
|
|
38
|
+
const previous = (await getSettings()).get(SETTING_FOR[scheme])
|
|
39
|
+
await writeSetting(SETTING_FOR[scheme], key)
|
|
40
|
+
|
|
41
|
+
if (previous !== key) await forgetImage(previous)
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export async function removeLogo(scheme: LogoScheme): Promise<void> {
|
|
45
|
+
const previous = (await getSettings()).get(SETTING_FOR[scheme])
|
|
46
|
+
await writeSetting(SETTING_FOR[scheme], '')
|
|
47
|
+
|
|
48
|
+
await forgetImage(previous)
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export async function logoKey(scheme: LogoScheme): Promise<string | null> {
|
|
52
|
+
const key = (await getSettings()).get(SETTING_FOR[scheme])
|
|
53
|
+
return isLogoKey(key) ? key : null
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export const logoSrc = logoPath
|
|
57
|
+
|
|
58
|
+
export interface BoardLogo {
|
|
59
|
+
readonly src: string
|
|
60
|
+
readonly darkSrc: string | null
|
|
61
|
+
readonly alt: string
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export function resolveLogo(input: {
|
|
65
|
+
readonly lightKey: string | null
|
|
66
|
+
readonly darkKey: string | null
|
|
67
|
+
readonly scheme: 'light' | 'dark' | 'system'
|
|
68
|
+
readonly alt: string
|
|
69
|
+
readonly boardTitle: string
|
|
70
|
+
}): BoardLogo | null {
|
|
71
|
+
const alt = input.alt.trim() === '' ? input.boardTitle : input.alt.trim()
|
|
72
|
+
|
|
73
|
+
const light = input.lightKey ?? input.darkKey
|
|
74
|
+
const dark = input.darkKey ?? input.lightKey
|
|
75
|
+
if (light === null || dark === null) return null
|
|
76
|
+
|
|
77
|
+
const lightSrc = logoSrc(input.lightKey === null ? 'dark' : 'light', light)
|
|
78
|
+
const darkSrc = logoSrc(input.darkKey === null ? 'light' : 'dark', dark)
|
|
79
|
+
|
|
80
|
+
if (input.scheme === 'light') return { src: lightSrc, darkSrc: null, alt }
|
|
81
|
+
if (input.scheme === 'dark') return { src: darkSrc, darkSrc: null, alt }
|
|
82
|
+
|
|
83
|
+
return { src: lightSrc, darkSrc: darkSrc === lightSrc ? null : darkSrc, alt }
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export const currentLogo = cache(async (boardTitle: string): Promise<BoardLogo | null> => {
|
|
87
|
+
const [settings, scheme, lightKey, darkKey] = await Promise.all([
|
|
88
|
+
getSettings(),
|
|
89
|
+
currentColourScheme(),
|
|
90
|
+
logoKey('light'),
|
|
91
|
+
logoKey('dark'),
|
|
92
|
+
])
|
|
93
|
+
|
|
94
|
+
return resolveLogo({
|
|
95
|
+
lightKey,
|
|
96
|
+
darkKey,
|
|
97
|
+
scheme,
|
|
98
|
+
alt: settings.get('board.logo_alt'),
|
|
99
|
+
boardTitle,
|
|
100
|
+
})
|
|
101
|
+
})
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { CacheTags } from '@meith/core'
|
|
2
|
+
|
|
3
|
+
export type CacheTagFactory = () => string
|
|
4
|
+
|
|
5
|
+
export const CLEARABLE_CACHES: Readonly<Record<string, CacheTagFactory>> = {
|
|
6
|
+
forums: CacheTags.forumTree,
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export function clearableTag(what: unknown): string | null {
|
|
10
|
+
if (typeof what !== 'string') return null
|
|
11
|
+
const factory = CLEARABLE_CACHES[what]
|
|
12
|
+
return factory === undefined ? null : factory()
|
|
13
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import 'server-only'
|
|
2
|
+
|
|
3
|
+
import { codeHighlighter } from '@meith/drivers/highlighting'
|
|
4
|
+
import { escapeAttribute, unescapeHtml } from '@meith/markdown'
|
|
5
|
+
|
|
6
|
+
const CODE_BLOCK =
|
|
7
|
+
/<pre class="md-code"><code(?: class="md-code-lang-([a-z0-9+#._-]+)")?>([\s\S]*?)\n<\/code><\/pre>/g
|
|
8
|
+
|
|
9
|
+
export async function highlightCodeBlocks(html: string): Promise<string> {
|
|
10
|
+
if (!html.includes('md-code')) return html
|
|
11
|
+
|
|
12
|
+
const matches = [...html.matchAll(CODE_BLOCK)]
|
|
13
|
+
if (matches.length === 0) return html
|
|
14
|
+
|
|
15
|
+
const replacements = new Map<string, string>()
|
|
16
|
+
|
|
17
|
+
for (const match of matches) {
|
|
18
|
+
const whole = match[0]
|
|
19
|
+
if (replacements.has(whole)) continue
|
|
20
|
+
|
|
21
|
+
const code = unescapeHtml(match[2] ?? '')
|
|
22
|
+
const highlighted = await codeHighlighter.highlight(code, match[1])
|
|
23
|
+
if (highlighted === null) continue
|
|
24
|
+
|
|
25
|
+
replacements.set(
|
|
26
|
+
whole,
|
|
27
|
+
`<pre class="md-code" data-lang="${escapeAttribute(match[1]!)}"><code class="md-code-lang-${escapeAttribute(match[1]!)}">${highlighted}</code></pre>`,
|
|
28
|
+
)
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
if (replacements.size === 0) return html
|
|
32
|
+
return html.replace(CODE_BLOCK, (whole) => replacements.get(whole) ?? whole)
|
|
33
|
+
}
|
|
@@ -0,0 +1,402 @@
|
|
|
1
|
+
import 'server-only'
|
|
2
|
+
|
|
3
|
+
import forumConfig from '@board/config'
|
|
4
|
+
|
|
5
|
+
import {
|
|
6
|
+
type AccountStore,
|
|
7
|
+
type BanLookup,
|
|
8
|
+
BanService,
|
|
9
|
+
createMemoryStore,
|
|
10
|
+
enrolmentLookup,
|
|
11
|
+
IdentityService,
|
|
12
|
+
type MemberProfileRepository,
|
|
13
|
+
type MemberSettingsRepository,
|
|
14
|
+
SessionService,
|
|
15
|
+
} from '@meith/accounts'
|
|
16
|
+
import type { AdminLogRepository, AdminSessionRepository } from '@meith/admin'
|
|
17
|
+
import type { AttachmentRepository } from '@meith/attachments'
|
|
18
|
+
import {
|
|
19
|
+
type ActorSource,
|
|
20
|
+
type AuthorizationSource,
|
|
21
|
+
Authorizer,
|
|
22
|
+
type BypassEvent,
|
|
23
|
+
InMemoryAuthorizationSource,
|
|
24
|
+
} from '@meith/authorization'
|
|
25
|
+
import type { AvatarRepository } from '@meith/avatars'
|
|
26
|
+
import { env, logger } from '@meith/core'
|
|
27
|
+
import {
|
|
28
|
+
ActorBuilder,
|
|
29
|
+
createPostgresAccountStore,
|
|
30
|
+
getDb,
|
|
31
|
+
PostgresAdminLogRepository,
|
|
32
|
+
PostgresAdminSessionRepository,
|
|
33
|
+
PostgresAttachmentRepository,
|
|
34
|
+
PostgresAuthorizationSource,
|
|
35
|
+
PostgresAvatarRepository,
|
|
36
|
+
PostgresBanRepository,
|
|
37
|
+
PostgresDraftRepository,
|
|
38
|
+
PostgresForumRepository,
|
|
39
|
+
PostgresInlineModerationRepository,
|
|
40
|
+
PostgresMemberProfileRepository,
|
|
41
|
+
PostgresMemberSettingsRepository,
|
|
42
|
+
PostgresMessageRepository,
|
|
43
|
+
PostgresModCpRepository,
|
|
44
|
+
PostgresModerationQueueRepository,
|
|
45
|
+
PostgresNotificationRepository,
|
|
46
|
+
PostgresPollRepository,
|
|
47
|
+
PostgresPostRepository,
|
|
48
|
+
PostgresPostWriteRepository,
|
|
49
|
+
PostgresProfileFieldRepository,
|
|
50
|
+
PostgresReadStateRepository,
|
|
51
|
+
PostgresRelationRepository,
|
|
52
|
+
PostgresReportRepository,
|
|
53
|
+
PostgresReputationRepository,
|
|
54
|
+
PostgresSignatureRepository,
|
|
55
|
+
PostgresSubscriptionRepository,
|
|
56
|
+
PostgresThreadRepository,
|
|
57
|
+
PostgresThreadSurgeryRepository,
|
|
58
|
+
PostgresThreadToolsRepository,
|
|
59
|
+
PostgresThreadViewBuffer,
|
|
60
|
+
PostgresThreadWriteRepository,
|
|
61
|
+
PostgresWarningRepository,
|
|
62
|
+
} from '@meith/db'
|
|
63
|
+
import { demoResetTask } from '@meith/demo'
|
|
64
|
+
import type { DraftRepository } from '@meith/drafts'
|
|
65
|
+
import { drivers } from '@meith/drivers'
|
|
66
|
+
import { imageProcessor } from '@meith/drivers/images'
|
|
67
|
+
import { CachedForumRepository, type ForumRepository } from '@meith/forums'
|
|
68
|
+
import type { MessageRepository } from '@meith/messages'
|
|
69
|
+
import type {
|
|
70
|
+
InlineModerationRepository,
|
|
71
|
+
ModCpRepository,
|
|
72
|
+
ModerationQueueRepository,
|
|
73
|
+
ReportRepository,
|
|
74
|
+
ThreadSurgeryRepository,
|
|
75
|
+
ThreadToolsRepository,
|
|
76
|
+
WarningBanPort,
|
|
77
|
+
WarningRepository,
|
|
78
|
+
} from '@meith/moderation'
|
|
79
|
+
import type { NotificationRepository } from '@meith/notifications'
|
|
80
|
+
import type { PollRepository, ThreadRatingRepository } from '@meith/polls'
|
|
81
|
+
import type { PostRepository, PostWriteRepository } from '@meith/posts'
|
|
82
|
+
import type { ProfileFieldRepository } from '@meith/profile-fields'
|
|
83
|
+
import type { RelationRepository } from '@meith/relations'
|
|
84
|
+
import type { ReputationRepository } from '@meith/reputation'
|
|
85
|
+
import { buildSchedulerBundle } from '@meith/runtime'
|
|
86
|
+
import type { SubscriptionRepository } from '@meith/subscriptions'
|
|
87
|
+
import type { TaskDefinition, TaskRepository } from '@meith/tasks'
|
|
88
|
+
import type {
|
|
89
|
+
ReadStateRepository,
|
|
90
|
+
ReplyWriteRepository,
|
|
91
|
+
ThreadRepository,
|
|
92
|
+
ThreadWriteRepository,
|
|
93
|
+
} from '@meith/threads'
|
|
94
|
+
|
|
95
|
+
import { ABSENT_SERVICES } from './absent-services'
|
|
96
|
+
import {
|
|
97
|
+
AUTH_CONFIG,
|
|
98
|
+
boardAuthConfig,
|
|
99
|
+
boardSessionConfig,
|
|
100
|
+
REMEMBER_DAYS,
|
|
101
|
+
SESSION_LIFETIME_DAYS,
|
|
102
|
+
} from './auth-config'
|
|
103
|
+
import { clearUploadedFiles } from './demo-uploads'
|
|
104
|
+
import { FixtureActorSource } from './fixture-actor-source'
|
|
105
|
+
import { FixtureForumRepository } from './fixture-forum-repo'
|
|
106
|
+
import { FixtureMemberProfileRepository } from './fixture-member-profile-repo'
|
|
107
|
+
import { FixturePostRepository } from './fixture-post-repo'
|
|
108
|
+
import { FixtureThreadRepository } from './fixture-thread-repo'
|
|
109
|
+
import { translatorForLocale } from './i18n-catalogs'
|
|
110
|
+
import { boardRendering } from './markdown-pipeline'
|
|
111
|
+
import { activeDefinitions } from './plugin-host'
|
|
112
|
+
import { FIXTURE_DATA_VERSION, SEED_BOARD, SEED_GROUP } from './seed-board'
|
|
113
|
+
|
|
114
|
+
export interface Container {
|
|
115
|
+
readonly authorizer: Authorizer
|
|
116
|
+
readonly authorizationSource: AuthorizationSource
|
|
117
|
+
readonly actorSource: ActorSource
|
|
118
|
+
readonly identity: IdentityService
|
|
119
|
+
readonly sessions: SessionService
|
|
120
|
+
readonly banLookup: BanLookup | null
|
|
121
|
+
readonly forums: ForumRepository
|
|
122
|
+
readonly threads: ThreadRepository
|
|
123
|
+
readonly threadWrites: (ThreadWriteRepository & ReplyWriteRepository) | null
|
|
124
|
+
readonly postWrites: PostWriteRepository | null
|
|
125
|
+
readonly moderationQueue: ModerationQueueRepository | null
|
|
126
|
+
readonly reports: ReportRepository | null
|
|
127
|
+
readonly threadTools: ThreadToolsRepository | null
|
|
128
|
+
readonly threadSurgery: ThreadSurgeryRepository | null
|
|
129
|
+
readonly inlineModeration: InlineModerationRepository | null
|
|
130
|
+
readonly warnings: WarningRepository | null
|
|
131
|
+
readonly warningBans: WarningBanPort | null
|
|
132
|
+
readonly modcp: ModCpRepository | null
|
|
133
|
+
readonly notifications: NotificationRepository | null
|
|
134
|
+
readonly subscriptions: SubscriptionRepository | null
|
|
135
|
+
readonly memberSettings: MemberSettingsRepository | null
|
|
136
|
+
readonly profileFields: ProfileFieldRepository | null
|
|
137
|
+
readonly messages: MessageRepository | null
|
|
138
|
+
readonly relations: RelationRepository | null
|
|
139
|
+
readonly reputation: ReputationRepository | null
|
|
140
|
+
readonly polls: (PollRepository & ThreadRatingRepository) | null
|
|
141
|
+
readonly drafts: DraftRepository | null
|
|
142
|
+
readonly signatures: PostgresSignatureRepository | null
|
|
143
|
+
readonly adminSessions: AdminSessionRepository | null
|
|
144
|
+
readonly adminLog: AdminLogRepository | null
|
|
145
|
+
readonly attachments: AttachmentRepository | null
|
|
146
|
+
readonly avatars: AvatarRepository | null
|
|
147
|
+
readonly accountStore: AccountStore
|
|
148
|
+
readonly posts: PostRepository
|
|
149
|
+
readonly readState: ReadStateRepository | null
|
|
150
|
+
readonly memberProfiles: MemberProfileRepository
|
|
151
|
+
readonly threadViews: ThreadViewRecorder | null
|
|
152
|
+
readonly fixtureDataVersion: number | null
|
|
153
|
+
readonly scheduler: SchedulerBundle | null
|
|
154
|
+
readonly dataSource: 'fixture' | 'postgres'
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
export interface ThreadViewRecorder {
|
|
158
|
+
record(threadId: number): Promise<void>
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
export interface SchedulerBundle {
|
|
162
|
+
readonly repository: TaskRepository
|
|
163
|
+
readonly tasks: readonly TaskDefinition[]
|
|
164
|
+
readonly onTaskFailure: (taskId: string, error: unknown) => void
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
const GLOBAL_KEY = Symbol.for('@meith/forum.container')
|
|
168
|
+
|
|
169
|
+
type GlobalWithContainer = typeof globalThis & {
|
|
170
|
+
[GLOBAL_KEY]?: Container
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
function build(): Container {
|
|
174
|
+
const log = logger({ module: 'authorization' })
|
|
175
|
+
|
|
176
|
+
const onBypass = (event: BypassEvent): void => {
|
|
177
|
+
log.info(
|
|
178
|
+
{
|
|
179
|
+
kind: event.kind,
|
|
180
|
+
userId: event.userId,
|
|
181
|
+
action: event.action,
|
|
182
|
+
forumId: event.forumId ?? null,
|
|
183
|
+
},
|
|
184
|
+
'authorization bypass',
|
|
185
|
+
)
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
if (env.DATA_SOURCE === 'postgres') {
|
|
189
|
+
return buildPostgres(onBypass)
|
|
190
|
+
}
|
|
191
|
+
return buildFixture(onBypass)
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
function buildFixture(onBypass: (e: BypassEvent) => void): Container {
|
|
195
|
+
const authorizationSource = new InMemoryAuthorizationSource(SEED_BOARD)
|
|
196
|
+
const store: AccountStore = createMemoryStore()
|
|
197
|
+
return {
|
|
198
|
+
authorizationSource,
|
|
199
|
+
authorizer: new Authorizer(authorizationSource, { onBypass }),
|
|
200
|
+
actorSource: new FixtureActorSource(store),
|
|
201
|
+
forums: cached(new FixtureForumRepository()),
|
|
202
|
+
threads: new FixtureThreadRepository(),
|
|
203
|
+
...ABSENT_SERVICES,
|
|
204
|
+
posts: new FixturePostRepository(),
|
|
205
|
+
memberProfiles: new FixtureMemberProfileRepository(),
|
|
206
|
+
fixtureDataVersion: FIXTURE_DATA_VERSION,
|
|
207
|
+
...identityServices(store),
|
|
208
|
+
accountStore: store,
|
|
209
|
+
dataSource: 'fixture',
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
function cached(inner: ForumRepository): ForumRepository {
|
|
214
|
+
return new CachedForumRepository(inner, drivers().cache)
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
function identityServices(
|
|
218
|
+
store: AccountStore,
|
|
219
|
+
bans?: BanLookup,
|
|
220
|
+
): {
|
|
221
|
+
identity: IdentityService
|
|
222
|
+
sessions: SessionService
|
|
223
|
+
banLookup: BanLookup | null
|
|
224
|
+
} {
|
|
225
|
+
return {
|
|
226
|
+
identity: new IdentityService({
|
|
227
|
+
store,
|
|
228
|
+
config: AUTH_CONFIG,
|
|
229
|
+
secondFactor: enrolmentLookup(store.twoFactor),
|
|
230
|
+
...(bans === undefined ? {} : { bans }),
|
|
231
|
+
}),
|
|
232
|
+
sessions: new SessionService({
|
|
233
|
+
store,
|
|
234
|
+
rememberDays: REMEMBER_DAYS,
|
|
235
|
+
sessionLifetimeDays: SESSION_LIFETIME_DAYS,
|
|
236
|
+
}),
|
|
237
|
+
banLookup: bans ?? null,
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
function buildPostgres(onBypass: (e: BypassEvent) => void): Container {
|
|
242
|
+
const db = getDb()
|
|
243
|
+
const authorizationSource = new PostgresAuthorizationSource(db)
|
|
244
|
+
const store: AccountStore = createPostgresAccountStore(db)
|
|
245
|
+
const threadViews = new PostgresThreadViewBuffer(db)
|
|
246
|
+
const warningRepo = new PostgresWarningRepository(db)
|
|
247
|
+
return {
|
|
248
|
+
authorizationSource,
|
|
249
|
+
authorizer: new Authorizer(authorizationSource, { onBypass }),
|
|
250
|
+
actorSource: new ActorBuilder(db, { guestGroupId: 1 }),
|
|
251
|
+
forums: cached(new PostgresForumRepository(db)),
|
|
252
|
+
threads: new PostgresThreadRepository(db),
|
|
253
|
+
threadWrites: new PostgresThreadWriteRepository(db, boardRendering),
|
|
254
|
+
postWrites: new PostgresPostWriteRepository(db, boardRendering),
|
|
255
|
+
moderationQueue: new PostgresModerationQueueRepository(db),
|
|
256
|
+
reports: new PostgresReportRepository(db),
|
|
257
|
+
threadTools: new PostgresThreadToolsRepository(db),
|
|
258
|
+
threadSurgery: new PostgresThreadSurgeryRepository(db),
|
|
259
|
+
inlineModeration: new PostgresInlineModerationRepository(db),
|
|
260
|
+
warnings: warningRepo,
|
|
261
|
+
modcp: new PostgresModCpRepository(db),
|
|
262
|
+
notifications: new PostgresNotificationRepository(db),
|
|
263
|
+
subscriptions: new PostgresSubscriptionRepository(db),
|
|
264
|
+
memberSettings: new PostgresMemberSettingsRepository(db),
|
|
265
|
+
profileFields: new PostgresProfileFieldRepository(db),
|
|
266
|
+
messages: new PostgresMessageRepository(db),
|
|
267
|
+
relations: new PostgresRelationRepository(db),
|
|
268
|
+
reputation: new PostgresReputationRepository(db),
|
|
269
|
+
polls: new PostgresPollRepository(db),
|
|
270
|
+
drafts: new PostgresDraftRepository(db),
|
|
271
|
+
signatures: new PostgresSignatureRepository(db),
|
|
272
|
+
adminSessions: new PostgresAdminSessionRepository(db),
|
|
273
|
+
adminLog: new PostgresAdminLogRepository(db),
|
|
274
|
+
attachments: new PostgresAttachmentRepository(db),
|
|
275
|
+
avatars: new PostgresAvatarRepository(db),
|
|
276
|
+
warningBans: {
|
|
277
|
+
async ban(input) {
|
|
278
|
+
await new BanService({
|
|
279
|
+
bans: new PostgresBanRepository(db),
|
|
280
|
+
bannedGroupId: SEED_GROUP.banned,
|
|
281
|
+
}).ban(input)
|
|
282
|
+
},
|
|
283
|
+
},
|
|
284
|
+
posts: new PostgresPostRepository(db),
|
|
285
|
+
readState: new PostgresReadStateRepository(db),
|
|
286
|
+
memberProfiles: new PostgresMemberProfileRepository(db),
|
|
287
|
+
threadViews,
|
|
288
|
+
fixtureDataVersion: null,
|
|
289
|
+
accountStore: store,
|
|
290
|
+
...identityServices(store, new PostgresBanRepository(db)),
|
|
291
|
+
scheduler: withDemoReset(
|
|
292
|
+
buildSchedulerBundle({
|
|
293
|
+
queue: drivers().queue,
|
|
294
|
+
db,
|
|
295
|
+
mail: drivers().mail,
|
|
296
|
+
themeKey: forumConfig.defaultTheme,
|
|
297
|
+
themeTokens: Object.fromEntries(
|
|
298
|
+
Object.values(forumConfig.themes).map((theme) => [theme.key, theme.tokens]),
|
|
299
|
+
),
|
|
300
|
+
files: drivers().files,
|
|
301
|
+
images: imageProcessor,
|
|
302
|
+
plugins: activeDefinitions(),
|
|
303
|
+
translatorForLocale,
|
|
304
|
+
}),
|
|
305
|
+
db,
|
|
306
|
+
),
|
|
307
|
+
dataSource: 'postgres',
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
/**
|
|
312
|
+
* The reset is registered here rather than in `buildSchedulerBundle` because
|
|
313
|
+
* this is the only composition root with a cache to clear afterwards. The
|
|
314
|
+
* worker's `NextCacheDriver` is a different process holding a different map, so
|
|
315
|
+
* a reset it ran would leave this process serving the forum tree of a board
|
|
316
|
+
* that no longer exists — for up to the tree's 60-second TTL, on the one page
|
|
317
|
+
* every visitor lands on.
|
|
318
|
+
*/
|
|
319
|
+
function withDemoReset(bundle: SchedulerBundle, db: ReturnType<typeof getDb>): SchedulerBundle {
|
|
320
|
+
if (!env.DEMO_MODE) return bundle
|
|
321
|
+
|
|
322
|
+
return {
|
|
323
|
+
...bundle,
|
|
324
|
+
tasks: [
|
|
325
|
+
...bundle.tasks,
|
|
326
|
+
demoResetTask({
|
|
327
|
+
db,
|
|
328
|
+
cache: drivers().cache,
|
|
329
|
+
clearUploads: () => clearUploadedFiles(),
|
|
330
|
+
plugins: activeDefinitions(),
|
|
331
|
+
}),
|
|
332
|
+
],
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
export function getContainer(): Container {
|
|
337
|
+
const g = globalThis as GlobalWithContainer
|
|
338
|
+
const cached = g[GLOBAL_KEY] as Partial<Container> | undefined
|
|
339
|
+
if (
|
|
340
|
+
!cached ||
|
|
341
|
+
typeof cached.threads?.locate !== 'function' ||
|
|
342
|
+
typeof cached.posts?.listThread !== 'function' ||
|
|
343
|
+
typeof cached.posts?.findVisibleById !== 'function' ||
|
|
344
|
+
cached.readState === undefined ||
|
|
345
|
+
cached.threadViews === undefined ||
|
|
346
|
+
cached.threadWrites === undefined ||
|
|
347
|
+
cached.postWrites === undefined ||
|
|
348
|
+
cached.moderationQueue === undefined ||
|
|
349
|
+
cached.reports === undefined ||
|
|
350
|
+
cached.threadTools === undefined ||
|
|
351
|
+
cached.threadSurgery === undefined ||
|
|
352
|
+
cached.inlineModeration === undefined ||
|
|
353
|
+
cached.warnings === undefined ||
|
|
354
|
+
cached.warningBans === undefined ||
|
|
355
|
+
cached.modcp === undefined ||
|
|
356
|
+
cached.notifications === undefined ||
|
|
357
|
+
cached.subscriptions === undefined ||
|
|
358
|
+
cached.memberSettings === undefined ||
|
|
359
|
+
cached.profileFields === undefined ||
|
|
360
|
+
cached.messages === undefined ||
|
|
361
|
+
cached.relations === undefined ||
|
|
362
|
+
cached.reputation === undefined ||
|
|
363
|
+
cached.polls === undefined ||
|
|
364
|
+
cached.drafts === undefined ||
|
|
365
|
+
cached.signatures === undefined ||
|
|
366
|
+
cached.adminSessions === undefined ||
|
|
367
|
+
cached.adminLog === undefined ||
|
|
368
|
+
cached.attachments === undefined ||
|
|
369
|
+
cached.avatars === undefined ||
|
|
370
|
+
typeof cached.memberProfiles?.findPublicById !== 'function' ||
|
|
371
|
+
typeof cached.accountStore?.identities?.listForUser !== 'function' ||
|
|
372
|
+
typeof cached.accountStore?.passkeys?.listForUser !== 'function' ||
|
|
373
|
+
(cached.dataSource === 'fixture' && cached.fixtureDataVersion !== FIXTURE_DATA_VERSION) ||
|
|
374
|
+
(cached.dataSource === 'postgres' && typeof cached.readState?.forUser !== 'function')
|
|
375
|
+
) {
|
|
376
|
+
g[GLOBAL_KEY] = build()
|
|
377
|
+
}
|
|
378
|
+
return g[GLOBAL_KEY] as Container
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
export function getAuthorizer(): Authorizer {
|
|
382
|
+
return getContainer().authorizer
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
export async function configuredIdentity(): Promise<IdentityService> {
|
|
386
|
+
const { accountStore, banLookup } = getContainer()
|
|
387
|
+
return new IdentityService({
|
|
388
|
+
store: accountStore,
|
|
389
|
+
config: await boardAuthConfig(),
|
|
390
|
+
secondFactor: enrolmentLookup(accountStore.twoFactor),
|
|
391
|
+
...(banLookup === null ? {} : { bans: banLookup }),
|
|
392
|
+
})
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
export async function configuredSessions(): Promise<SessionService> {
|
|
396
|
+
const identity = await configuredIdentity()
|
|
397
|
+
return new SessionService({
|
|
398
|
+
store: getContainer().accountStore,
|
|
399
|
+
...(await boardSessionConfig()),
|
|
400
|
+
assertSignInAllowed: (account) => identity.assertSignInAllowed(account),
|
|
401
|
+
})
|
|
402
|
+
}
|