mirror of
https://github.com/Dvorinka/MyClubServer.git
synced 2026-06-04 18:52:56 +00:00
379 lines
13 KiB
Plaintext
379 lines
13 KiB
Plaintext
%%{init: {"theme":"forest","securityLevel":"loose","flowchart":{"curve":"linear","useMaxWidth":true,"nodeSpacing":40,"rankSpacing":50},"themeCSS":"svg { font-family: Inter, ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, Helvetica, Arial; } .edgePath path { stroke-opacity:.6 } .ext > rect, .ext > polygon, .ext > path { stroke: #7e57c2; } .db > rect, .db > polygon, .db > path { fill: #e3f2fd; stroke: #1e88e5; } .svc > rect, .svc > polygon, .svc > path { fill: #e8f5e9; stroke: #43a047; } .fe > rect, .fe > polygon, .fe > path { fill: #fff8e1; stroke: #f9a825; } .ctrl > rect, .ctrl > polygon, .ctrl > path { fill: #f3e5f5; stroke: #8e24aa; } .mid > rect, .mid > polygon, .mid > path { fill: #e0f2f1; stroke: #00897b; } .model > rect, .model > polygon, .model > path { fill: #ede7f6; stroke: #5e35b1; } .route > rect, .route > polygon, .route > path { fill: #e8eaf6; stroke: #3f51b5; } .cluster rect { rx:8; ry:8 }" }}%%
|
|
flowchart TB
|
|
|
|
%% ========================= Docker & Runtime =========================
|
|
subgraph DOCKER["Docker Compose (Local Dev/Prod)"]
|
|
direction TB
|
|
docker_net([Bridge Network: fotbal-network]):::svc
|
|
docker_vol1["Volume: postgres_data"]:::svc
|
|
docker_vol2["Bind: ./uploads -> /app/uploads"]:::svc
|
|
docker_vol3["Bind: ./cache -> /app/cache"]:::svc
|
|
|
|
subgraph docker_backend["backend (Go) container"]
|
|
direction TB
|
|
be_8080["Expose 8080:8080"]
|
|
be_env[".env + overrides"]
|
|
be_cmd["command: ./main"]
|
|
end
|
|
|
|
subgraph docker_frontend["frontend (Nginx) container"]
|
|
direction TB
|
|
fe_3000["Expose 3000:80"]
|
|
fe_env[".env.frontend"]
|
|
end
|
|
|
|
subgraph docker_db["postgres:15-alpine"]
|
|
direction TB
|
|
db_5432["Expose 5432:5432"]
|
|
db_env["POSTGRES_* env"]
|
|
end
|
|
|
|
docker_net --- docker_backend
|
|
docker_net --- docker_frontend
|
|
docker_net --- docker_db
|
|
docker_vol1 --- docker_db
|
|
docker_vol2 --- docker_backend
|
|
docker_vol3 --- docker_backend
|
|
end
|
|
|
|
user_browser((User Browser)):::ext
|
|
user_browser ==>|HTTP 80| fe_3000
|
|
user_browser -.->|dev direct :8080| be_8080
|
|
|
|
%% ========================= Backend (Go/Gin) =========================
|
|
subgraph BACKEND["Backend Service (Golang + Gin) :8080"]
|
|
direction TB
|
|
cfg["Config (internal/config.Config)<br/>- APP_ENV/PORT/DEBUG<br/>- DATABASE_URL (GORM)<br/>- JWT_SECRET/EXP<br/>- ALLOWED_ORIGINS (CORS)<br/>- UPLOAD_DIR/MAX_UPLOAD_SIZE<br/>- SMTP_* (Email)<br/>- FRONTEND_BASE_URL<br/>- PUBLIC_API_BASE_URL<br/>- ERROR_INGEST_URL/TOKEN<br/>- FACR_SCRAPER_BASE_URL<br/>- UMAMI_*<br/>- CLAMAV_* (optional)"]
|
|
logger[Logger (pkg/logger)]
|
|
db_init[[InitDB() + AutoMigrate()]]:::db
|
|
email_svc[EmailService (pkg/email)]:::svc
|
|
|
|
subgraph middleware[Middleware]
|
|
direction TB
|
|
mw_reqid[RequestID]
|
|
mw_logger[RequestLogger]
|
|
mw_recovery[CustomRecoveryWithReporter]
|
|
mw_errstatus[ErrorStatusReporter]
|
|
mw_sanitize[SanitizeHeaders]
|
|
mw_dbctx[DBContext (req ctx timeout)]
|
|
mw_size[RequestSizeLimit (2MB)]
|
|
mw_ct[ValidateContentType]
|
|
mw_csrf[CSRFProtection (protected)]
|
|
mw_rate[RateLimit (per-route)]
|
|
mw_sec[SecurityHeaders + AssetCacheControl]
|
|
end
|
|
|
|
prometheus["GET /metrics (promhttp)"]
|
|
static1["Static: /uploads -> UPLOAD_DIR"]
|
|
static2["Static: /cache -> ./cache"]
|
|
static3["Static: /dist -> ./static"]
|
|
static4["Static: /premium-assets -> ./pro"]
|
|
|
|
subgraph router["Router"]
|
|
direction TB
|
|
api_grp["/api/v1"]:::route
|
|
root_grp["/root/"]:::route
|
|
end
|
|
|
|
subgraph controllers[Controllers]
|
|
direction TB
|
|
c_auth["AuthController<br/>/login,/logout,/register,/me<br/>/password-reset"]
|
|
c_contact["ContactController<br/>/contact + newsletter + admin forwarding"]
|
|
c_pass[PasswordController]
|
|
c_ai["AIController<br/>/ai/blog,/ai/about,/ai/css,/ai/instagram"]
|
|
c_score["ScoreboardController<br/>/public + admin timer/sponsors/qr"]
|
|
c_about[AboutController]
|
|
c_gallery["GalleryController<br/>/Zonerama profile/albums/picks"]
|
|
c_files["FilesController<br/>/list/unused/duplicates/usage<br/>/scan/refresh-tracking/delete"]
|
|
c_notify[NotificationsController]
|
|
c_email["EmailController<br/>/open.gif/click/unsubscribe/stats"]
|
|
c_prefetch["PrefetchController<br/>/status/trigger"]
|
|
c_seo["SEOController<br/>/seo (public) + robots.txt + sitemap"]
|
|
c_nav["NavigationController<br/>/navigation + social-links + admin CRUD"]
|
|
c_poll["PollController<br/>/public vote/results + admin"]
|
|
c_sw["SweepstakesController<br/>/public current/visual + admin CRUD/finalize"]
|
|
c_cloth["ClothingController<br/>/public + admin CRUD"]
|
|
c_pec["PageElementConfigController<br/>/public + admin CRUD/batch"]
|
|
c_article["ArticleController<br/>/create + match-link"]
|
|
c_base["BaseController<br/>/health, uploads, categories, teams, players, matches, standings, zonerama, settings, shortlinks(public)"]
|
|
c_myu["MyUIbrixController<br/>/validate,/preview,/optimize"]
|
|
c_editor["EditorPreviewController<br/>/preview state + variants"]
|
|
c_short["ShortLinkController<br/>/public create + admin + redirect /s/:code"]
|
|
c_comment["CommentController<br/>/public list + CRUD + reactions<br/>ban/unban/report (admin)"]
|
|
c_eng["EngagementController<br/>/rewards/leaderboard/profile/actions"]
|
|
c_facr["FACRController<br/>/facr club search/info/table"]
|
|
c_yt["YouTubeController<br/>/youtube/videos"]
|
|
c_umami["UmamiController<br/>/config + admin initialize/stats"]
|
|
c_error["ErrorController<br/>/errors ingest + admin + external"]
|
|
end
|
|
|
|
subgraph services[Services & Jobs]
|
|
direction TB
|
|
s_errrep[ErrorReporter]
|
|
s_prefetch["Prefetcher<br/>StartPrefetcher(target)"]
|
|
s_nlsched[NewsletterScheduler]
|
|
s_nlauto["NewsletterAutomation<br/>weekly, reminders, results"]
|
|
s_sweep[SweepstakesScheduler]
|
|
s_umami[UmamiService]
|
|
s_facr[FACRService]
|
|
s_cache[CacheService]
|
|
s_logo[LogoCache]
|
|
s_filetrk[FileTracker]
|
|
s_imgopt[ImageOptimizer]
|
|
s_bad[BadWords/Spam]
|
|
s_setup[SetupService]
|
|
end
|
|
|
|
subgraph models[Models (GORM)]
|
|
direction LR
|
|
m_settings[Settings]
|
|
m_user[User]
|
|
m_article[Article]
|
|
m_scoreboard[ScoreboardState]
|
|
m_compalias[CompetitionAlias]
|
|
m_team[Team]
|
|
m_player[Player]
|
|
m_contact_cat[ContactCategory]
|
|
m_contact[Contact]
|
|
m_contact_msg[ContactMessage]
|
|
m_news[NewsletterSubscription]
|
|
m_sponsor[Sponsor]
|
|
m_cloth[Clothing]
|
|
m_poll[Poll + PollOption + PollVote]
|
|
m_nav[NavigationItem + SocialLink]
|
|
m_pageel[PageElementConfig]
|
|
m_short[ShortLink + LinkClick]
|
|
m_comment[Comment + Reaction + Ban + UnbanRequest + Report]
|
|
m_profile[UserProfile]
|
|
m_points[PointsTransaction]
|
|
m_ach[Achievement + UserAchievement]
|
|
m_reward[RewardItem + RewardRedemption]
|
|
m_over[MatchOverride + TeamLogoOverride]
|
|
m_sweep[Sweepstake + Prize + Entry + Winner]
|
|
m_up[UploadedFile + FileUsage]
|
|
m_error[ErrorEvent]
|
|
end
|
|
|
|
%% wiring inside backend
|
|
cfg --> db_init
|
|
cfg --> email_svc
|
|
router --> middleware
|
|
api_grp --> controllers
|
|
root_grp --> c_seo
|
|
root_grp --> c_short
|
|
api_grp --> c_auth
|
|
api_grp --> c_contact
|
|
api_grp --> c_pass
|
|
api_grp --> c_ai
|
|
api_grp --> c_score
|
|
api_grp --> c_about
|
|
api_grp --> c_gallery
|
|
api_grp --> c_files
|
|
api_grp --> c_notify
|
|
api_grp --> c_email
|
|
api_grp --> c_prefetch
|
|
api_grp --> c_seo
|
|
api_grp --> c_nav
|
|
api_grp --> c_poll
|
|
api_grp --> c_sw
|
|
api_grp --> c_cloth
|
|
api_grp --> c_pec
|
|
api_grp --> c_article
|
|
api_grp --> c_base
|
|
api_grp --> c_myu
|
|
api_grp --> c_editor
|
|
api_grp --> c_short
|
|
api_grp --> c_comment
|
|
api_grp --> c_eng
|
|
api_grp --> c_facr
|
|
api_grp --> c_yt
|
|
api_grp --> c_umami
|
|
api_grp --> c_error
|
|
|
|
%% controllers -> models
|
|
controllers -->|GORM| models
|
|
db_init ==>|Postgres conn| DB["PostgreSQL :5432 / fotbal_club"]:::db
|
|
models ==>|tables| DB
|
|
|
|
%% services wiring
|
|
s_prefetch -.->|GET public endpoints| api_grp
|
|
s_nlsched --> s_nlauto
|
|
s_nlauto --> email_svc
|
|
s_sweep --> email_svc
|
|
s_filetrk --> m_up
|
|
s_imgopt --> m_up
|
|
s_errrep --> c_error
|
|
s_umami --> c_umami
|
|
s_facr --> c_facr
|
|
email_svc -->|SMTP| smtp[(SMTP Provider)]:::ext
|
|
|
|
%% externals
|
|
facr_ext["FACR Scraper :8081"]:::ext
|
|
errors_ingest["Error Receiver: errors.tdvorak.dev/api/v1/errors or local :8083"]:::ext
|
|
errors_admin["Error Review Admin UI/API: errors.tdvorak.dev"]:::ext
|
|
umami_ext["Umami Analytics server"]:::ext
|
|
|
|
s_facr <---> facr_ext
|
|
s_errrep --> errors_ingest
|
|
c_error <---> errors_admin
|
|
s_umami <---> umami_ext
|
|
|
|
%% static serving
|
|
static1 --- user_browser
|
|
static2 --- user_browser
|
|
static3 --- user_browser
|
|
static4 --- user_browser
|
|
|
|
%% metrics
|
|
prometheus --- user_browser
|
|
end
|
|
|
|
user_browser ==>|HTTP /api/v1| api_grp
|
|
user_browser ==>|HTTP /robots.txt, /sitemap.xml, /s/:code| root_grp
|
|
|
|
%% ========================= Frontend (React) =========================
|
|
subgraph FRONTEND[Frontend (React + ChakraUI)]
|
|
direction TB
|
|
fe_router[React Router (src/App.tsx)]:::fe
|
|
|
|
subgraph fe_public[Public Pages]
|
|
direction LR
|
|
p_home[HomePage /]
|
|
p_blog[BlogPage /blog]
|
|
p_newslist[ArticlesListPage]
|
|
p_article["ArticleDetailPage /news/:slug | /articles/:id"]
|
|
p_about[AboutPage /o-klubu]
|
|
p_club[ClubPage /klub]
|
|
p_calendar[CalendarPage /kalendar]
|
|
p_actcal[ActivitiesCalendarPage /aktivity]
|
|
p_tables[TablesPage /tabulky]
|
|
p_matches[MatchesPage /zapasy]
|
|
p_match[MatchDetailPage /zapas/:id]
|
|
p_players[PlayersPage /hraci]
|
|
p_player[PlayerDetailPage /hraci/:id]
|
|
p_sponsors[SponsorsPage /sponzori]
|
|
p_contact[ContactPage /kontakt]
|
|
p_gallery[GalleryPage /galerie]
|
|
p_album[AlbumDetailPage /galerie/album/:id]
|
|
p_videos[VideosPage /videa]
|
|
p_clothing[ClothingPage /obleceni]
|
|
p_polls[PollsPage /ankety]
|
|
p_search[SearchPage /hledat]
|
|
p_short[ShortRedirectPage /s/:code]
|
|
p_over_sb[OverlayScoreboardPage /overlay/scoreboard]
|
|
p_over_sp[OverlaySponsorsPage /overlay/sponsors]
|
|
p_cookies[CookiePolicyPage]
|
|
p_terms[TermsPage]
|
|
p_privacy[PrivacyPolicyPage]
|
|
p_notfound[NotFoundPage *]
|
|
end
|
|
|
|
subgraph fe_auth[Auth & Setup]
|
|
direction LR
|
|
p_login[AuthPage /login]
|
|
p_register[RegisterPage /register]
|
|
p_forgot[ForgotPasswordPage /forgot-password]
|
|
p_reset[ResetPasswordPage /reset-password]
|
|
p_setup[SetupPage /setup]
|
|
p_style[StylePreviewPage /setup/styl]
|
|
p_news_unsub[NewsletterUnsubscribePage]
|
|
p_news_prefs[NewsletterPreferencesPage]
|
|
end
|
|
|
|
subgraph fe_admin[Admin Pages]
|
|
direction LR
|
|
a_dashboard[AdminDashboardPage]
|
|
a_docs[AdminDocsPage]
|
|
a_about[AboutAdminPage]
|
|
a_videos[AdminVideosPage]
|
|
a_gallery[GalleryAdminPage]
|
|
a_merch[AdminMerchPage]
|
|
a_sponsors[SponsorsAdminPage]
|
|
a_matches[MatchesAdminPage]
|
|
a_players[PlayersAdminPage]
|
|
a_teams[TeamsAdminPage]
|
|
a_users[UsersAdminPage]
|
|
a_banners[BannersAdminPage]
|
|
a_messages[MessagesAdminPage]
|
|
a_settings[SettingsAdminPage]
|
|
a_newsletter[NewsletterAdminPage]
|
|
a_polls[PollsAdminPage]
|
|
a_comp[CompetitionAliasesAdminPage]
|
|
a_prefetch[PrefetchAdminPage]
|
|
a_scoreboard[ScoreboardAdminPage]
|
|
a_score_remote[MobileScoreboardControlPage]
|
|
a_analytics[AnalyticsAdminPage]
|
|
a_shortlinks[ShortlinksAdminPage]
|
|
a_files[FilesAdminPage]
|
|
a_contacts[ContactsAdminPage]
|
|
a_navigation[NavigationAdminPage]
|
|
a_comments[CommentsAdminPage]
|
|
a_engagement[EngagementAdminPage]
|
|
a_sweep[SweepstakesAdminPage]
|
|
a_sweep_visual[SweepstakeVisualPage]
|
|
a_adminreset[AdminResetPasswordPage]
|
|
end
|
|
|
|
%% FE -> BE API mappings (high level)
|
|
fe_router -->|services/api.ts| api_grp
|
|
p_blog -->|GET /articles| api_grp
|
|
p_article -->|GET /articles/slug/:slug, /articles/:id<br/>POST /articles/:id/read| api_grp
|
|
p_home -->|GET /articles/featured, /matches, /standings, /settings, /navigation| api_grp
|
|
p_matches -->|GET /matches,/standings| api_grp
|
|
p_match -->|GET /matches/:id| api_grp
|
|
p_players -->|GET /players| api_grp
|
|
p_player -->|GET /players/:id| api_grp
|
|
p_gallery -->|GET /gallery/albums| api_grp
|
|
p_album -->|GET /gallery/albums/:id| api_grp
|
|
p_videos -->|GET /youtube/videos| api_grp
|
|
p_clothing -->|GET /clothing| api_grp
|
|
p_polls -->|GET /polls| api_grp
|
|
p_contact -->|POST /contact| api_grp
|
|
p_over_sb -->|GET /scoreboard (public)| api_grp
|
|
p_over_sp -->|GET /scoreboard/sponsors| api_grp
|
|
p_short -->|GET /s/:code (root)| root_grp
|
|
|
|
%% Admin flows
|
|
a_articles[ArticlesAdminPage] -->|POST/PUT/DELETE /articles<br/>/link-match| api_grp
|
|
a_matches -->|GET /admin/matches| api_grp
|
|
a_comments -->|GET/PATCH /admin/comments| api_grp
|
|
a_navigation -->|CRUD /admin/navigation| api_grp
|
|
a_files -->|GET/DELETE /admin/files| api_grp
|
|
a_scoreboard -->|GET/PUT /admin/scoreboard + timer| api_grp
|
|
a_score_remote -->|POST timer controls| api_grp
|
|
a_newsletter -->|send/test/preview/status| api_grp
|
|
a_sweep -->|CRUD /admin/sweepstakes| api_grp
|
|
a_sweep_visual -->|GET /admin/sweepstakes/:id/visual| api_grp
|
|
a_analytics -->|/admin/umami| api_grp
|
|
|
|
%% FE error reporting & analytics
|
|
fe_router -->|POST /errors (ErrorReporter)| api_grp
|
|
fe_router -->|GET /umami/config| api_grp
|
|
|
|
end
|
|
|
|
%% ========================= Ports & CORS =========================
|
|
subgraph PORTS[Ports & CORS]
|
|
direction LR
|
|
port_be[Backend :8080]
|
|
port_fe[Frontend :3000 -> :80]
|
|
port_db[Postgres :5432]
|
|
cors["CORS AllowedOrigins<br/>- http://localhost:3000<br/>- http://localhost:8080<br/>+ FrontendBaseURL origin<br/>+ * optional in dev"]
|
|
end
|
|
port_be --- docker_backend
|
|
port_fe --- docker_frontend
|
|
port_db --- docker_db
|
|
cors -. controls .- router
|
|
|
|
%% Legend
|
|
subgraph LEGEND[Legend]
|
|
direction LR
|
|
L1[External Service]:::ext
|
|
L2[Database/Table]:::db
|
|
L3[Service/Daemon]:::svc
|
|
L4[Controller]:::ctrl
|
|
L5[Middleware]:::mid
|
|
L6[Model]:::model
|
|
L7[Route Group]:::route
|
|
end
|