Compare commits

...
Author SHA1 Message Date
Hampus 8f4c966d3a fix(desktop): keep the KDE tray icon full size (#3496) 2026-10-15 16:49:56 +02:00
Hampus c424656bad feat(desktop): crisp taskbar badges and a dark badge option (#3495) 2026-10-15 16:49:34 +02:00
Hampus 4b8ce8bf63 fix(desktop): trust CAs in the windows intermediate store (#3494) 2026-10-15 16:48:55 +02:00
Hampus 4f88872a3c feat(reports): keep reports until resolved, then by outcome (#3493) 2026-10-15 16:40:51 +02:00
Hampus f5c30fdf8f feat(enforcement): record decisions and send notices (#3492) 2026-10-15 07:12:46 +02:00
Hampus da1842ecab fix(app): keep the red taskbar badge on windows (#3491) 2026-10-15 04:27:13 +02:00
Hampus 8c38e06a15 fix(voice): release the mic after a region hot-swap (#3490) 2026-10-15 04:26:44 +02:00
Hampus c454add5c8 fix(desktop): match KDE tray icon size to Breeze 22px grid (#3489) 2026-10-15 04:11:58 +02:00
Hampus 1f4f3318dc feat(api): add libredesk webhook adapter (#3488) 2026-10-15 03:54:32 +02:00
Hampus 105bd49c9e feat(desktop): let users install updates on demand (#3487) 2026-10-15 03:50:03 +02:00
Hampus 9213a765b1 fix(api): include personal notes in dm search scopes (#3486) 2026-10-15 02:43:35 +02:00
Hampus 7c4180c2be fix(app): show the search bar in personal notes (#3485) 2026-10-15 02:36:57 +02:00
Hampus fe4bb4386e fix(app): limit /tts playback to the viewed channel (#3484) 2026-10-15 00:35:06 +02:00
Hampus 674b4d26d1 feat(desktop): add a setting and flag to disable updates (#3483) 2026-10-15 00:33:11 +02:00
Hampus 3a74a6f97a fix(docs): finish the api_public origins footnote (#3482) 2026-10-14 23:58:05 +02:00
Hampus a7ba56b140 feat(app): add help for messaging paused accounts (#3481) 2026-10-14 22:45:26 +02:00
Hampus a39410623e fix(admin): reload pages when an auth proxy blocks navigation (#3480) 2026-10-14 21:18:48 +02:00
Hampus a4802c6004 docs(guilds): document template forum channel fields (#3479) 2026-10-14 20:49:00 +02:00
Hampus 812919759a fix(installer): stop upgrades hanging after the uploads copy (#3478) 2026-10-14 20:37:03 +02:00
Hampus b3f373c94c feat(desktop): tray unread dot and monochrome tray icon (#3477) 2026-10-14 20:00:40 +02:00
Hampus 36095ab66f feat(admin): upload instance branding images (#3476) 2026-10-14 19:26:31 +02:00
Hampus 52c19e67cb fix(app): underline embed links when links are underlined (#3475) 2026-10-14 19:21:31 +02:00
Hampus 4e3299a91c fix(app): make the channel welcome line generic (#3474) 2026-10-14 18:54:05 +02:00
Hampus 99be186fa1 fix(guild): import all template channel types, fetch on desktop (#3473) 2026-10-14 18:39:09 +02:00
Hampus 00249f9b4d fix(deploy): drop healthcheck start_interval for older engines (#3472) 2026-10-14 17:57:34 +02:00
Hampus 7c16d424ce fix(app): show download icon for shell updates (#3471) 2026-10-14 17:48:41 +02:00
Hampus 5f5e26a8d4 fix(config): default self-hosted instances to production (#3470) 2026-10-14 17:14:35 +02:00
Hampus 8c05497c37 fix(auth): stop flagging dev instance signups as staff (#3469) 2026-10-14 17:09:14 +02:00
Hampus d2f19660e5 fix(app): skip zod eval probe blocked by csp (#3468) 2026-10-14 16:41:47 +02:00
Hampus d5ca5d8293 fix(auth): fix approval signups and auto-join invites (#3467) 2026-10-14 16:41:33 +02:00
Hampus e403fb6cce feat(desktop): add FLUXER_DISABLE_MODULE_UPDATES opt-out (#3466) 2026-10-14 16:30:34 +02:00
Hampus e8dff6a8a1 fix(api): page postgres kv full scans by keyset (#3465) 2026-10-14 16:27:27 +02:00
Hampus 2b656a1186 chore(deps): bump rustls in desktop native and fuzz locks (#3464) 2026-10-14 16:07:15 +02:00
Hampus 381699623e fix(app): stop own profile preview trapping mobile web (#3463) 2026-10-14 16:04:09 +02:00
Hampus d7bbe933fa fix(app): unify guild acronyms and shrink them to fit (#3462) 2026-10-14 16:02:18 +02:00
Hampus 069b13c382 chore(deps): bump rustls to 0.23.46 (#3461) 2026-10-14 15:59:23 +02:00
Fenane 538b5d69bb fix(app): convert emoji shortcodes in a single transform pass (#3459) 2026-10-14 15:53:22 +02:00
Hampus 3c8fe74660 fix(app): refetch oauth bot destinations after account switch (#3460) 2026-10-14 15:39:33 +02:00
Hampus c0f825ae2a fix(app): explain software screen share encoding on linux (#3458) 2026-10-14 14:47:44 +02:00
Hampus 8d8a29377c fix(ci): retry the release history read in desktop builds (#3457) 2026-10-14 13:47:38 +02:00
ArcticWarmth abcb5cb814 feat(admin): move pending registrations to their own page (#1388) 2026-10-14 13:45:27 +02:00
Hampus 97b2ac91ca fix(desktop): capture screen share audio from chosen devices (#3456) 2026-10-14 02:48:19 +02:00
Hampus 123c4d5bbf fix(api): link github push commits by their full hash (#3455) 2026-10-14 02:28:06 +02:00
Hampus 6e2bf3cbe6 feat(api): purge media through the controller edge api (#3454) 2026-10-14 02:12:31 +02:00
Hampus f509bef1fb fix(app): keep push-to-talk release delays above 500 ms (#3453) 2026-10-14 00:37:40 +02:00
Hampus 959b03b5f4 fix(desktop): shell-compatible module updates, no linux walls (#3452) 2026-10-13 22:07:00 +02:00
Hampus f501d34179 fix(voice): allow self-mute while push-to-talk is on (#3451) 2026-10-13 22:02:40 +02:00
Fenane 3657438467 fix(app): show username in UserAreaAccountSwitchMenu (#3447)
Signed-off-by: dearfenane <[email protected]>
2026-10-13 21:00:19 +02:00
Hampus 2976b1842b fix(i18n): use емоджі consistently in Ukrainian (#3450) 2026-10-13 20:56:40 +02:00
Hampus 8cac3b35c9 fix(desktop): reuse window capture items on Windows 10 (#3449) 2026-10-13 17:57:09 +02:00
Hampus d514501d57 fix(app-proxy): tell the web client its release channel (#3448) 2026-10-13 15:15:11 +02:00
Hampus 83bb9d89c8 fix(media-proxy): serve transforms whole, never as ranges (#3446) 2026-10-13 05:26:21 +02:00
Hampus 0117605572 fix(api): copy objects without server-side copy on one bucket (#3445) 2026-10-13 04:42:53 +02:00
Hampus 72e14c17ba fix(voice): keep the playback boost while the window is hidden (#3444) 2026-10-13 04:36:23 +02:00
Hampus aa0ebdb97e fix(desktop): build mac native modules for macOS 13 (#3443) 2026-10-13 03:49:39 +02:00
Hampus 3d4b2933e1 fix(setup): show the setup token step in the wizard (#3442) 2026-10-13 03:22:42 +02:00
Hampus 922ff5ea64 fix(app): keep forum channels after a warm account switch (#3441) 2026-10-13 02:23:24 +02:00
Hampus fce3200265 fix(app): keep the AbortSignal polyfill in the bundle (#3440) 2026-10-13 01:50:06 +02:00
Hampus 3b35db2681 fix(app): load on browsers without AbortSignal.any (#3439) 2026-10-13 00:28:09 +02:00
Hampus c7da0cf33d style(deploy): format the helm values schema (#3438) 2026-10-13 00:23:05 +02:00
Hampus 78272521bb feat(setup): add a setup token for first-run setup (#3437) 2026-10-13 00:17:24 +02:00
Hampus 8e39380ee6 feat(deploy): add a complete fluxer helm chart (#3436) 2026-10-13 00:03:55 +02:00
Hampus fb58447ba1 fix(app): lock out Dark Reader on the app and docs (#3435) 2026-10-12 23:58:09 +02:00
Hampus 2ffe8f3387 fix(desktop): fresh manual update check, 90 second polling (#3434) 2026-10-12 21:39:10 +02:00
Hampus 4194525933 chore(unfurl): update ammonia to 4.2.3 (#3433) 2026-10-12 20:30:16 +02:00
Hampus 44799dafa2 fix(app): follow username-only sign-in in every auth flow (#3432) 2026-10-12 20:25:57 +02:00
Hampus 30b32c9a35 fix(app): cap the height of list picker modals (#3431) 2026-10-12 20:25:28 +02:00
Hampus d1b4b82895 fix(app): require a username on username-only instances (#3430) 2026-10-12 20:10:05 +02:00
Hampus 9bc43c0c08 docs(operator): document app support for self-hosted instances (#3429) 2026-10-12 19:58:56 +02:00
Hampus 13960a0bb3 fix(flatpak): drop the unused secrets service access (#3428) 2026-10-12 17:37:56 +02:00
Hampus 64cb159d65 fix(app): make the invite splash cover the whole page (#3427) 2026-10-12 17:13:46 +02:00
Hampus d2173fe879 fix(app): size channel icons in the inbox headers (#3426) 2026-10-12 16:51:17 +02:00
omster 49eb8cdd81 feat(app): add keyboard hint in the account switcher (#3425) 2026-10-12 16:48:10 +02:00
omster b4fc1241ae feat(app): render domain label in account overlay (#3424) 2026-10-12 16:48:05 +02:00
Hampus 54f1b37666 fix(app): keep zoomed media viewer images sharp (#3423) 2026-10-12 16:19:52 +02:00
Hampus 8bda151596 fix(api): resolve s3 copy source under endpoint path (#3422) 2026-10-12 16:02:52 +02:00
Hampus ab19c0d1ac fix(desktop): never queue local proxy requests (#3421) 2026-10-12 15:39:30 +02:00
Hampus 68cb2a4bbb fix(api): resolve client ip from peer when untrusted (#3420) 2026-10-12 15:34:36 +02:00
Hampus 17746fcdc4 feat(api): add managed guild invite pauses (#3419) 2026-10-12 15:14:03 +02:00
Hampus c890956e4f feat(api): include date of birth in registration activity (#3418) 2026-10-12 15:13:37 +02:00
Aru Moon 8a5d4a8bdb fix(app): send current client locale on login/registration (#3412) 2026-10-12 14:26:11 +02:00
Hampus 0ba15ee46a fix(api): delete guilds in a background job (#3417) 2026-10-12 14:11:06 +02:00
Hampus 85d9ef7386 fix(app): hash emitted license asset names (#3416) 2026-10-12 13:58:28 +02:00
Hampus f8d85f0e5e test(cache): freeze clock for ttl resolution (#3415) 2026-10-12 13:37:56 +02:00
Hampus 7f674474b5 fix(app): include record utils in image builds (#3414) 2026-10-12 13:29:06 +02:00
Hampus c9fdf8fead chore(repo): prune dead code and duplicate abstractions (#3413) 2026-10-12 13:09:42 +02:00
Hampus c17091768c feat(app): pick screen share bitrate up to the limit (#3411) 2026-10-12 02:17:53 +02:00
Hampus e0640d68ff feat(limits): add max_screen_share_bitrate limit (#3410) 2026-10-12 02:16:40 +02:00
Hampus 3292dc5f89 test(media-proxy): drop animated jxl fixture case (#3409) 2026-10-12 00:17:29 +02:00
Hampus 86e29b61e3 fix(media-proxy): serve large simple jxl, fit full size (#3408) 2026-10-12 00:05:14 +02:00
Qsie f8d3c70a91 chore(svc): replace deprecated fetch_update with try_update (#3407)
Signed-off-by: Qsie <[email protected]>
2026-10-11 23:30:31 +02:00
Hampus 74c6fad260 fix(billing): stack gifts from paid period end after trial ends (#3406) 2026-10-11 20:08:19 +02:00
Hampus bcb30eea88 fix(voice): one-way audio and stream loss in p2p calls (#3405) 2026-10-11 19:27:24 +02:00
Hampus d59ddc2214 fix(app): wait for the session before redirecting from /app (#3404) 2026-10-11 18:47:50 +02:00
Hampus e87b593f46 fix(media-proxy): fail slow external fetches sooner (#3403) 2026-10-11 18:47:11 +02:00
Hampus 5a2d81ebbb fix(media-proxy): bound animated transforms by output pixels (#3402) 2026-10-11 18:46:34 +02:00
Hampus 50c28aa8fa fix(admin): use instance branding on self-hosted (#3401) 2026-10-11 15:57:12 +02:00
Hampus af7724dbf6 fix(push): drop Fluxer badge on self-hosted instances (#3400) 2026-10-11 15:56:53 +02:00
Hampus be314788b3 fix(app): pixel-align mobile status indicator notch (#3399) 2026-10-11 15:56:15 +02:00
Hampus 5b3f69bf27 fix(app): only remember a language the user picked (#3398) 2026-10-11 15:55:55 +02:00
Hampus d7aa6ec2aa fix(app): use instance branding in self-hosted copy (#3397) 2026-10-11 15:55:15 +02:00
Hampus 20e3474393 fix(app-proxy): serve instance branding before boot (#3396) 2026-10-11 15:54:40 +02:00
Hampus 3694300d86 fix(api): show premium badge for premium override (#3395) 2026-10-11 14:55:43 +02:00
Hampus 54ec74e4f9 fix(app): load quick css from the account scope after sign-in (#3394) 2026-10-11 14:49:15 +02:00
Hampus aa3b058ce6 fix(media-proxy): serve originals and speed up slow transforms (#3393) 2026-10-11 14:41:01 +02:00
Hampus 44bc29b101 fix(media-proxy): let video work fit small memory budgets (#3392) 2026-10-11 14:14:59 +02:00
Hampus b07dcc368d fix(app): read instance theme color without an active runtime (#3391) 2026-10-11 13:37:05 +02:00
Hampus 0967bf3136 fix(api): SSO discovery, channel create and deleted bots (#3390) 2026-10-11 13:31:18 +02:00
Hampus edc913096a fix(selfhost): branding image origins and theme colour (#3389) 2026-10-11 13:31:05 +02:00
Hampus cca8227ca6 fix(email): test saved SMTP config and add TLS modes (#3388) 2026-10-11 13:30:00 +02:00
Hampus 45b78a13c2 fix(media): stream large files and honour size settings (#3387) 2026-10-11 13:28:24 +02:00
Hampus 6acd7f3d22 fix(admin): save premium limits and model changes (#3386) 2026-10-11 13:26:39 +02:00
Hampus 7d2c51a57f fix(media-proxy): budget native image work by pod memory (#3385) 2026-10-11 02:07:19 +02:00
Hampus 575a5adbef refactor(pkgs): share vitest config and dedupe helpers (#3384) 2026-10-10 23:23:34 +02:00
Hampus 5695510980 refactor(app): merge duplicated components and stores (#3383) 2026-10-10 23:17:29 +02:00
Hampus c16d414d93 refactor(api): collapse single-impl interfaces and facades (#3382) 2026-10-10 23:04:42 +02:00
Hampus e7b13fd19f refactor(gateway): share shard helpers and test fixtures (#3381) 2026-10-10 23:04:30 +02:00
Hampus f22261f218 build(static): fetch spellcheck dictionaries at build (#3380) 2026-10-10 22:46:10 +02:00
Hampus 901ed623f6 refactor(i18n): store reviewed-unchanged ids as a flat list (#3379) 2026-10-10 22:44:42 +02:00
Hampus 1198b61648 refactor(i18n): load server catalogs from weblate json (#3378) 2026-10-10 22:35:45 +02:00
Hampus d564ca5691 refactor(i18n): drop line numbers from po references (#3377) 2026-10-10 22:24:51 +02:00
Hampus b7e92cbfe0 fix(repo): drop orphaned error code and dead voice clause (#3376) 2026-10-10 22:18:12 +02:00
Hampus 1cd62eb661 chore(repo): remove dead code and useless tests (#3375) 2026-10-10 21:43:29 +02:00
Hampus 7e177a1c90 fix(flatpak): allow GNOME idle monitor for away detection (#3374) 2026-10-10 21:17:58 +02:00
Hampus 78471d8a85 fix(app): apply push inactive timeout before desktop goes idle (#3373) 2026-10-10 21:17:37 +02:00
Tarek 6b6ae13919 fix(app): apply instance branding to system DM (#3355) 2026-10-10 20:36:02 +02:00
Hampus 7197371123 fix(gateway): resend thread member lists on resume and leave (#3372) 2026-10-10 19:17:08 +02:00
Hampus 70fd9e6331 refactor(api): drop ignored location param from thread routes (#3371) 2026-10-10 17:57:15 +02:00
Hampus c14b7d4cb3 fix(members): show mobile badges and fresh profiles in lists (#3370) 2026-10-10 17:00:30 +02:00
Hampus 0f0ff5f3df feat(schema): add swipe right to left action setting (#3369) 2026-10-10 16:36:13 +02:00
Hampus b4c614d21a fix(threads): show presence in thread member list (#3368) 2026-10-10 15:58:59 +02:00
Hampus bef5e33eec feat(voice): add opt-in peer-to-peer calls and country rollouts (#3367) 2026-10-10 15:45:17 +02:00
Xeon e5a568acca fix(flatpak): Add permissions necessary for security key/card SSO (#3366) 2026-10-10 14:30:11 +02:00
Hampus a208b56354 feat(app): let Windows users switch back to Fluxer Sans (#3365) 2026-10-10 03:15:30 +02:00
Hampus 71be6c1de4 build(fonts): restore symmetric smoothing for Plex at every size (#3364) 2026-10-10 03:15:00 +02:00
Hampus eae905b1da fix(app): use the Windows system font for UI text on Windows (#3363) 2026-10-10 03:14:25 +02:00
Hampus 10cd4f96a4 fix(ui): let report modal steps scroll when zoomed in (#3362) 2026-10-10 01:46:24 +02:00
Hampus 9d74daf8ad fix(reports): drop the in-app DSA link, link copyright help (#3361) 2026-10-10 01:46:19 +02:00
Hampus 6f344def93 test(api): backdate seeded evidence in report deletion tests (#3360) 2026-10-10 01:23:10 +02:00
Hampus 72f9f9c396 fix(settings): group account security into clear cards (#3359) 2026-10-10 00:59:33 +02:00
Hampus c8755885d4 fix(privacy): clarify the privacy settings page (#3358) 2026-10-10 00:58:55 +02:00
Hampus 12d503d4c5 feat(app): show current DM settings and keep them by default (#3357) 2026-10-10 00:58:10 +02:00
Hampus 2f6201bcbd feat(app): guide privacy review with dots and an inline alert (#3356) 2026-10-10 00:57:26 +02:00
Hampus ec681e6061 fix(desktop): retry an update that never installed (#3354) 2026-10-09 15:03:36 +02:00
Hampus 0fc6fad11d fix(ui): report the shown carousel step so report flows react (#3353) 2026-10-09 14:53:08 +02:00
Hampus eb329f2cd0 fix(app): add missing data-flx attributes (#3351) 2026-10-09 14:18:01 +02:00
Hampus 9a20821332 feat(app,api): let group DM owners allow mature content (#3350) 2026-10-09 14:12:13 +02:00
Hampus 4749eb7f86 ci(desktop): use a clean profile for each Windows update check (#3349) 2026-10-09 08:32:45 +02:00
Hampus 2d83fd2a93 ci(desktop): keep the logs from Windows update checks (#3348) 2026-10-09 08:22:21 +02:00
Hampus d180af6ba2 fix(desktop): clean up release check runs on Windows (#3347) 2026-10-09 06:53:50 +02:00
Hampus 4810eb3e24 feat(ci): run the release check on built Windows shells (#3346) 2026-10-09 05:40:01 +02:00
Hampus 48a19dedd8 fix(desktop): stop requiring the removed win-toast at startup (#3345) 2026-10-09 05:39:56 +02:00
Hampus cadf7ea532 feat(schema): add double tap action to synced preferences (#3344) 2026-10-09 04:09:24 +02:00
Hampus fc60d05f17 feat(app): load heavy desktop assets on demand, update in place (#3343) 2026-10-09 03:53:57 +02:00
Hampus 5072488f1e feat(desktop): prefer the bundled renderer, own update prompts (#3341) 2026-10-09 03:01:15 +02:00
Hampus 7a5b9a0ded docs(threads): publish threads, forums and media channels (#3342) 2026-10-09 02:44:06 +02:00
Hampus 4ce4d4e09a feat(ci): bundle the renderer in the desktop shell (#3339) 2026-10-09 02:31:37 +02:00
Hampus 82abd03a37 feat(reports): delete reports and configure retention (#3338) 2026-10-09 01:41:47 +02:00
Hampus 93d1f8af46 refactor: remove dead code (#3337) 2026-10-09 01:41:26 +02:00
Hampus a19002652b docs: use British English throughout (#3336) 2026-10-09 01:40:55 +02:00
Hampus 6534ee4300 feat(admin): require a resolution and fix panel layout and copy (#3335) 2026-10-09 01:40:34 +02:00
Hampus 4db13b0375 fix(app,api): kick reasons, blocked counts and locale fallbacks (#3334) 2026-10-09 01:40:08 +02:00
Hampus a5c1362f4a fix(self-hosting): use instance contacts and live product name (#3333) 2026-10-09 01:39:35 +02:00
Hampus 454fefdb92 chore(moderation): remove the built-in NCMEC integration (#3332) 2026-10-09 01:39:04 +02:00
Hampus f390e610b9 fix(tooling): repair test, i18n and docs verification gates (#3331) 2026-10-09 01:38:34 +02:00
Hampus ece622e800 feat(reports): add server-driven report flows (#3330) 2026-10-09 01:38:00 +02:00
Hampus bc17922b02 fix(gateway): wire presence grace, request limits and voice sync (#3329) 2026-10-09 01:37:25 +02:00
Hampus a32d8e4f52 fix(i18n): use the web term for instance in id, tr and vi (#3328) 2026-10-09 01:36:57 +02:00
Hampus 12daeb758d fix(app): retry failed images at once when back online (#3327) 2026-10-08 23:39:18 +02:00
Hampus fff67ed9af fix(desktop): fetch theme CSS via the local resource proxy (#3326) 2026-10-08 23:21:27 +02:00
Hampus d0b4816a5f fix(media-proxy): allow the desktop app origin and preflight (#3325) 2026-10-08 23:21:10 +02:00
Hampus 0febad324c fix(repo): match editorconfig to each formatter (#3324) 2026-10-08 22:07:46 +02:00
Hampus dcc3273889 feat(app): ask everyone to review who can message them (#3323) 2026-10-08 20:47:44 +02:00
Hampus e9a4f33a7e feat(api): track privacy setup and open community DMs by default (#3322) 2026-10-08 20:47:22 +02:00
Hampus ae820ba4ea chore(i18n): refresh catalog references (#3320) 2026-10-08 20:34:03 +02:00
Hampus 94f1239050 fix(premium): reflect limit config flags in plan comparisons (#3319) 2026-10-08 20:33:20 +02:00
Hampus acc1392a53 fix(ui): reopen a popout clicked while it is closing (#3318) 2026-10-08 20:33:04 +02:00
Hampus a6a9789091 fix(ui): stop clicks passing through the user area footer (#3317) 2026-10-08 20:32:49 +02:00
Hampus fdb7410976 fix(threads): light up joined threads in muted communities (#3316) 2026-10-08 20:32:33 +02:00
Hampus b146b8ee33 fix(desktop): trust locally added CAs in Chromium on Linux (#3315) 2026-10-08 20:21:04 +02:00
Hampus 4b1afc953f fix(app): redraw the favicon badge when branding changes (#3314) 2026-10-08 20:20:49 +02:00
Hampus 4ecbe9603c fix(api): store branding images as media references (#3313) 2026-10-08 20:20:34 +02:00
Hampus 847d449882 chore(i18n): refresh catalog references (#3312) 2026-10-08 20:06:41 +02:00
Hampus bef20cba85 fix(links): warn on unhandled middle clicks and desktop links (#3311) 2026-10-08 20:05:54 +02:00
Jiralite ebf91ccc76 fix(links): warn on embed link middle clicks (#3305) 2026-10-08 20:05:27 +02:00
Hampus abfaff16c2 fix(guild): fit text icon initials to the icon by measured width (#3310) 2026-10-08 20:05:09 +02:00
Hampus 89fa895a0a fix(forum): keep closed posts visible without a reload (#3309) 2026-10-08 20:04:53 +02:00
Hampus 334fef52e1 fix(desktop): keep slow connects racing instead of aborting them (#3308) 2026-10-08 20:04:39 +02:00
Hampus aa42bf13ce fix(threads): announce threads above the latest five messages (#3307) 2026-10-08 20:04:23 +02:00
Hampus c486c2d3d0 revert(admin): restore user type toggles (#3306) 2026-10-08 20:04:01 +02:00
Hampus 0ec1cee99e test(gateway): keep the recheck timer out of the eunit process (#3304) 2026-10-08 18:14:47 +02:00
Hampus ccbe4857a1 docs(auth): document the desktop handoff deep link and deny (#3303) 2026-10-08 18:00:17 +02:00
Hampus 7f8b0afedb chore(i18n): refresh catalog references (#3302) 2026-10-08 17:58:50 +02:00
Jiralite e94b7d7ad8 fix(threads): add a debug thread button (#3276) 2026-10-08 17:57:47 +02:00
Hampus 27baa38fd1 fix(threads): open the thread menu on browser right-click (#3301) 2026-10-08 17:45:33 +02:00
Hampus 4af10dd3e3 fix(threads): keep header actions clear of the channel name (#3300) 2026-10-08 17:44:48 +02:00
Hampus 269d33cab2 fix(threads): confirm before leaving a private thread (#3299) 2026-10-08 17:44:00 +02:00
Hampus 170e12595b fix(threads): let the thread members popout scroll (#3298) 2026-10-08 17:42:56 +02:00
Hampus b82bbaa2dd fix(ui): drop the switch label tab stop with no focus ring (#3297) 2026-10-08 17:42:11 +02:00
Hampus b8f3dbddbf fix(threads): keep Tab and focus inside the thread create pane (#3296) 2026-10-08 17:41:14 +02:00
Hampus 663572fd10 fix(forum): keep attachments on forum and media posts (#3295) 2026-10-08 17:40:29 +02:00
Hampus 5033cf7203 fix(app): follow theme library changes made in other windows (#3294) 2026-10-08 17:39:34 +02:00
Hampus 0fcab2f8f0 fix(app): hold the switched view until its route commits (#3293) 2026-10-08 17:38:36 +02:00
Hampus c3d790f664 fix(app): refocus the editor before text context menu actions (#3292) 2026-10-08 17:37:40 +02:00
Hampus 0906a85d6f fix(app): keep mature content consent across account switches (#3291) 2026-10-08 17:36:55 +02:00
Hampus 4da8dec4ac fix(app): ignore leftover mentions in unseen channels (#3290) 2026-10-08 17:35:59 +02:00
Hampus d6e3254a16 feat(app): show instance branding in switchers and pickers (#3289) 2026-10-08 17:34:56 +02:00
Hampus 6dc698ff5e fix(app): follow instance branding in tab title and favicon (#3288) 2026-10-08 17:33:28 +02:00
Hampus 39e48458da fix(app): keep a global define from breaking bundled modules (#3287) 2026-10-08 17:32:40 +02:00
Hampus c5453f83e8 feat(desktop): sign in with the browser without typing a code (#3286) 2026-10-08 17:31:45 +02:00
Hampus 510cc1b916 fix(desktop): show the text context menu on password fields (#3285) 2026-10-08 17:30:11 +02:00
Hampus e7859fefb1 fix(desktop): leave full screen before hiding on macOS close (#3284) 2026-10-08 17:28:58 +02:00
Hampus 46a356cc40 fix(media): keep self-host media loading after restarts (#3283) 2026-10-08 17:28:18 +02:00
Hampus bd56174870 fix(desktop): frame streamed request bodies for every method (#3282) 2026-10-08 17:26:41 +02:00
Hampus 965bb5f634 fix(desktop): trust the system CA store in main requests (#3281) 2026-10-08 17:26:12 +02:00
Hampus 7937d5f802 fix(desktop): update from the splash only after the user clicks (#3280) 2026-10-08 17:25:34 +02:00
Hampus f1373a63c3 feat(ci): publish renderer modules without a shell build (#3279) 2026-10-08 17:23:59 +02:00
Hampus e0d7625c39 feat(api): hand desktop sign-in back with a deep link grant (#3278) 2026-10-08 17:23:28 +02:00
Hampus f8505d19f7 feat(threads): ship threads and the Plutonium page to everyone (#3277) 2026-10-08 16:30:39 +02:00
Hampus 1515a8487f fix(desktop): fall back across resolved addresses on connect (#3275) 2026-10-08 05:08:00 +02:00
Hampus 7a4aa42d4b feat(premium): native Nordic prices and subscribing during grace (#3274) 2026-10-08 04:21:54 +02:00
Hampus 0ef1cd14c3 feat(desktop): show download progress and diagnostics on splash (#3273) 2026-10-08 03:06:06 +02:00
Hampus 795e190bda fix(desktop): let the Theme Studio popout resolve its runtime (#3272) 2026-10-08 03:05:37 +02:00
Hampus d00389ab30 fix(desktop): reach split-horizon instances on their LAN IP (#3271) 2026-10-08 03:05:15 +02:00
Hampus 3071bc7525 fix(desktop): honour the system proxy in main-process requests (#3270) 2026-10-08 03:04:55 +02:00
Hampus 6de6be2358 fix(threads): open the thread menu on thread chip right-click (#3269) 2026-10-08 03:04:35 +02:00
Hampus c51c222c47 fix(voice): stop stale mute echoes from toggling mute (#3268) 2026-10-08 03:04:10 +02:00
Hampus e218ba21de fix(app): load saved account avatars from their own instance (#3267) 2026-10-08 03:03:47 +02:00
Hampus de358c0def fix(app): let action toolbar overrides win over its defaults (#3266) 2026-10-08 03:03:26 +02:00
Hampus 4b88d64b2d fix(app): show the official badge and a visible account menu (#3265) 2026-10-08 03:03:05 +02:00
Hampus ee1c861eb2 perf(desktop): cache display media and retain shown images (#3264) 2026-10-08 03:02:41 +02:00
Hampus 6a24ac3f4e fix(desktop): fetch media bytes via the local resource proxy (#3263) 2026-10-08 03:02:15 +02:00
Hampus 8b312c610e fix(app): tag only desktop proxy uploads with a local upload id (#3262) 2026-10-08 00:40:51 +02:00
Hampus 2e3e3a1536 fix(desktop): fix manual update wording and retry Velopack (#3261) 2026-10-08 00:04:58 +02:00
Hampus 5c63d81ffd fix(app): show the setup wizard on new instances, not sign-in (#3260) 2026-10-07 23:28:04 +02:00
Hampus c402c52a8a fix(app): read instance config safely in startup skeletons (#3259) 2026-10-07 23:27:59 +02:00
Hampus 317fedeef9 fix(ci): resolve a bare drive WORKDIR to the drive root (#3258) 2026-10-07 21:44:18 +02:00
Hampus 002502a7fa feat(admin): one threads toggle, hide hosted-only rollouts (#3257) 2026-10-07 21:32:45 +02:00
Hampus 1997850d55 fix(threads): let administrators grant thread permissions (#3256) 2026-10-07 21:32:39 +02:00
Hampus d346eb0e88 fix(desktop): generate build channel in tests, bump yoke-derive (#3255) 2026-10-07 19:57:20 +02:00
Hampus 7c5fa2180a feat(desktop): bundled renderer, modules and instance accounts (#3254) 2026-10-07 16:48:07 +02:00
Hampus c748c8af4e feat(channels): redesign the create channel modal (#3253) 2026-10-07 15:26:11 +02:00
Hampus ba59a13149 feat(threads): thread and forum UI on web (#3252) 2026-10-07 15:25:43 +02:00
Hampus 3f4160b138 feat(threads): thread and forum API, admin, schemas and docs (#3251) 2026-10-07 15:25:12 +02:00
Hampus 5f4295e399 feat(threads): gateway, messages and push thread support (#3250) 2026-10-07 15:24:40 +02:00
Hampus 4e730832c7 fix(installer): pull images before the first start (#3248) 2026-10-07 02:37:35 +02:00
Hampus d7c00d4556 fix(schema): keep template topics optional after trimming (#3247) 2026-10-07 01:42:37 +02:00
Hampus 154b65afe5 docs(self-hosting): fix LiveKit CSP and backup guidance (#3246) 2026-10-07 00:09:39 +02:00
Hampus fcc2a3f64b docs(github): keep vulnerability reports out of chats (#3245) 2026-10-06 23:25:53 +02:00
Hampus 80456861ac fix(api): accept long forum topics in imported templates (#3244) 2026-10-06 22:46:47 +02:00
Kai ComptonandHampus 0c4f016ba2 feat(config): read secrets from NAME_FILE variables (#1421)
Co-authored-by: Hampus <[email protected]>
2026-10-06 21:43:08 +02:00
Hampus cc5545c333 fix(api): sync stripe customer email on change (#3243) 2026-10-06 21:38:25 +02:00
Hampus 6e28092cdc fix(app): keep mention highlight when mentions are suppressed (#3242) 2026-10-06 20:58:28 +02:00
Hampus 8b6910d505 chore(github): send bug reports and ideas to feedback.fluxer.com (#3241) 2026-10-06 19:14:26 +02:00
Hampus d87e31efaf fix(app): drop the reply when its target message is deleted (#3237) 2026-10-06 02:14:00 +02:00
Hampus 6618a6baf4 fix(installer): replace a stale installer before upgrading (#3235) 2026-10-05 21:50:16 +02:00
Hampus 22b8f5454b fix(app): skip forwarded messages when editing with arrow up (#3234) 2026-10-05 21:34:05 +02:00
Hampus 801bd3f106 fix(app): cycle dms in sidebar order with the keyboard (#3233) 2026-10-05 21:17:40 +02:00
Hampus e26c8c870d feat(api): archive and schedule automated message deletion (#3231) 2026-10-05 21:03:29 +02:00
Hampus f4e545e090 fix(app): reorder dm list immediately on pin and unpin (#3230) 2026-10-05 20:45:50 +02:00
Hampus 2006fc0d8d feat(push): allow listed hosts to resolve to private addresses (#3228) 2026-10-05 20:11:08 +02:00
Hampus d456048e69 fix(svc): respect FLUXER_POSTGRES_SSL=false with a Postgres URL (#3227) 2026-10-05 20:00:50 +02:00
Hampus fd35b4da24 fix(api): stop counting one refund twice against the allowance (#3226) 2026-10-05 17:41:28 +02:00
Hampus 283d179b05 fix(app): strip youtube is= share tracking param (#3225) 2026-10-05 17:33:35 +02:00
Hampus 3093e7334b feat(api): derive stable placeholder names for hidden profiles (#3224) 2026-10-05 16:57:25 +02:00
Hampus 02c82f0038 fix(api): limit report auto-resolution on scheduled deletion (#3221) 2026-10-05 14:16:08 +02:00
Hampus e1eecc3b6c feat(auth): add username sign-in mode and recovery kits (#3215) 2026-10-05 14:10:07 +02:00
Hampus bf3d73a5f7 fix(ci): drop removed preapproval docs and format a test (#3220) 2026-10-05 13:36:33 +02:00
Hampus 05257d6439 feat(api): add moderation events and visibility actions (#3219) 2026-10-05 13:24:19 +02:00
Hampus 532e828fe6 perf(app): restore preloading channels and guilds on hover (#3208) 2026-10-04 19:46:49 +02:00
Hampus 12a407aca8 fix(api): drop localized card checks and require pix for brazil (#3203) 2026-10-04 18:03:14 +02:00
Hampus 5ca458dada fix(mentions): ignore @everyone and @here in one-to-one DMs (#3199) 2026-10-04 16:59:04 +02:00
Hampus 0aeff01c2d feat(api): scope forwarded client ip trust per caller (#3198) 2026-10-04 16:36:41 +02:00
Hampus 2ac164d5b8 fix(voice): keep the mic graph on the real audio clock (#3197) 2026-10-04 16:18:34 +02:00
Hampus 14d475df9a fix(app): explain how direct input and desktop shortcuts relate (#3196) 2026-10-04 15:12:52 +02:00
Hampus f3c777b244 fix(gateway,api): reach NATS over IPv4 and survive boot races (#3189) 2026-10-04 03:06:12 +02:00
Hampus 1544e58e76 fix(desktop): show unsupported when non-GNOME portal bind fails (#3188) 2026-10-04 02:38:06 +02:00
Hampus 5d0c9c7cbe fix(desktop): stub the build channel in the Linux session test (#3186) 2026-10-04 01:14:18 +02:00
Hampus 8f58fcc4c4 feat(desktop): portal-based Linux global shortcuts and PTT (#3185) 2026-10-04 01:08:51 +02:00
Hampus 5799ef705d fix(app): send expired sessions to login on oauth authorize (#3181) 2026-10-03 20:39:34 +02:00
omster 0de7dde1ce feat(app): reveal external link destinations on hover (#3176) 2026-10-03 19:44:03 +02:00
Hampus 7b39e5a79d fix(api): treat typographic quotes as exact phrase search (#3180) 2026-10-03 19:43:08 +02:00
Hampus 583c791016 fix(app): keep the updater polling after async native results (#3179) 2026-10-03 19:42:41 +02:00
Hampus bc5dcdfe21 feat(app): show paused-messaging banner across the app (#3178) 2026-10-03 19:31:43 +02:00
Hampus 973aaced96 chore(static): drop unused fluxer_static assets (#3175) 2026-10-03 18:22:03 +02:00
Hampus 3e9ee908f8 fix(ci): accept the static image's compound license label (#3174) 2026-10-03 18:20:58 +02:00
Hampus e7347b582c chore(license): relicense artwork and move non-free media out (#3173) 2026-10-03 17:53:42 +02:00
Hampus 71b7cffabc fix(i18n): more natural French paused-messaging notice (#3172) 2026-10-03 17:04:40 +02:00
Hampus da9e9ff0be chore: tidy request handling across services (#3168) 2026-10-03 15:36:33 +02:00
Hampus c6941d5905 style: run rustfmt on attachment url signature tests (#3166) 2026-10-03 15:00:45 +02:00
Hampus a3cf960660 docs(discovery): document the channel preview route (#3165) 2026-10-03 14:48:09 +02:00
Hampus 7eebfca20b feat(blocklist): add url-domain host patterns (#3164) 2026-10-03 14:46:08 +02:00
Hampus e9167d96ec feat(admin): add optional expiry to admin IP bans (#3163) 2026-10-03 14:41:00 +02:00
Hampus 1664050ef7 fix(app): use sidebar channel icons in forwarded-from source (#3162) 2026-10-03 14:25:56 +02:00
7261 changed files with 787168 additions and 8100742 deletions

No files matched your search

-19
View File
@@ -28,7 +28,6 @@ services:
FLUXER_GATEWAY_STATIC_CDN_ENDPOINT: "http://localhost:${FLUXER_DEV_PROXY_PORT:-8088}"
FLUXER_ADMIN_OAUTH_REDIRECT_URI: "http://localhost:${FLUXER_DEV_PROXY_PORT:-8088}/admin/oauth2_callback"
FLUXER_PASSKEY_ADDITIONAL_ALLOWED_ORIGINS: "http://localhost,http://localhost:${FLUXER_DEV_PROXY_PORT:-8088}"
PUBLIC_BOOTSTRAP_API_PUBLIC_ENDPOINT: "http://localhost:${FLUXER_DEV_PROXY_PORT:-8088}/api"
volumes:
- ..:/workspaces/fluxer:cached
- type: volume
@@ -86,11 +85,6 @@ services:
target: /workspaces/fluxer/packages/hono/node_modules
volume:
nocopy: true
- type: volume
source: package-hono-types-node-modules
target: /workspaces/fluxer/packages/hono_types/node_modules
volume:
nocopy: true
- type: volume
source: package-i18n-node-modules
target: /workspaces/fluxer/packages/i18n/node_modules
@@ -166,21 +160,11 @@ services:
target: /workspaces/fluxer/fluxer_api/pkgs/http_client/node_modules
volume:
nocopy: true
- type: volume
source: fluxer-api-initialization-node-modules
target: /workspaces/fluxer/fluxer_api/pkgs/initialization/node_modules
volume:
nocopy: true
- type: volume
source: fluxer-api-kv-client-node-modules
target: /workspaces/fluxer/fluxer_api/pkgs/kv_client/node_modules
volume:
nocopy: true
- type: volume
source: fluxer-api-locale-node-modules
target: /workspaces/fluxer/fluxer_api/pkgs/locale/node_modules
volume:
nocopy: true
- type: volume
source: fluxer-api-media-proxy-utils-node-modules
target: /workspaces/fluxer/fluxer_api/pkgs/media_proxy_utils/node_modules
@@ -355,7 +339,6 @@ volumes:
package-errors-node-modules:
package-geo-utils-node-modules:
package-hono-node-modules:
package-hono-types-node-modules:
package-i18n-node-modules:
package-instance-bootstrap-node-modules:
package-ip-utils-node-modules:
@@ -371,9 +354,7 @@ volumes:
fluxer-api-email-node-modules:
fluxer-api-geoip-node-modules:
fluxer-api-http-client-node-modules:
fluxer-api-initialization-node-modules:
fluxer-api-kv-client-node-modules:
fluxer-api-locale-node-modules:
fluxer-api-media-proxy-utils-node-modules:
fluxer-api-mime-utils-node-modules:
fluxer-api-nats-node-modules:
+5 -3
View File
@@ -38,6 +38,9 @@
/fluxer_app/src/features/theme/styles/generated/
/fluxer_app/src/features/ui/components/SVGMasks.tsx
/fluxer_app/src/features/ui/constants/AvatarStatusGeometry.ts
/fluxer_static/desktop/spellcheck/dictionaries/*/*
!/fluxer_static/desktop/spellcheck/dictionaries/[email protected]/LICENSE
!/fluxer_static/desktop/spellcheck/dictionaries/[email protected]/LICENSE
/fluxer_gateway/priv/
/fluxer_media_proxy/fuzz/artifacts/
@@ -45,11 +48,10 @@
/packages/markdown_parser/rust/fuzz/artifacts/
/packages/markdown_parser/rust/fuzz/corpus/
/fluxer_media_proxy/.benchmark-cache/
/fluxer_media_proxy/bench-results/
/app-dist-output/
/artifacts/
/desktop-shared-assets/
/desktop-modules/
/s3_payload/
/upload_staging/
+27
View File
@@ -9,6 +9,33 @@ max_line_length = 120
indent_style = tab
indent_size = 2
[*.rs]
indent_style = space
indent_size = 4
max_line_length = 100
[*.{erl,hrl,app.src,escript}]
indent_style = space
indent_size = 4
max_line_length = 96
[fluxer_gateway/{rebar.config,elvis.config,.erlfmt}]
indent_style = space
indent_size = 4
max_line_length = 96
[*.{c,h,py}]
indent_style = space
indent_size = 4
[fluxer_app/rust/libfluxwebp/**.{c,h}]
indent_style = space
indent_size = 2
[*.{md,mdx,proto,tpl}]
indent_style = space
indent_size = 2
[*.{yml,yaml,Dockerfile}]
indent_style = space
indent_size = 2
+6
View File
@@ -1,6 +1,12 @@
* text=auto
fluxer_static/** -text -diff
fluxer_static/**/*.md text diff
fluxer_static/*.sh text eol=lf diff
fluxer_static/*.sha256 text eol=lf diff
packages/fonts/files/** -text -diff
fluxer_app/src/features/voice/utils/noise_suppression/deepfilternet3/*.wasm -text -diff
fluxer_app/src/features/voice/utils/noise_suppression/deepfilternet3/*.tar.gz -text -diff
fluxer_app/src/features/i18n/locales/** linguist-generated
**/weblate/locales/** linguist-generated
fluxer_api/src/api/openapi/openapi.json linguist-generated
fluxer_admin/openapi-admin.json linguist-generated
+9 -9
View File
@@ -1,24 +1,24 @@
# Contributing to Fluxer
This policy applies to all issues, discussions, commits and pull requests.
This policy applies to all commits and pull requests.
## Scope
To prevent spam, only approved contributors may submit pull requests.
To request approval, comment on an existing issue and ask to implement it. For work that extends beyond a defect fix, open a [discussion](https://github.com/orgs/fluxerapp/discussions) first.
To request approval, comment on the [feedback.fluxer.com](https://feedback.fluxer.com) post you want to implement and ask to work on it. For work that extends beyond a defect fix, post a feature request there first.
Every pull request must:
- Target the repository's default branch.
- Include a closing reference for each repository issue it resolves.
- Link each feedback.fluxer.com post it resolves.
- Receive approval from a maintainer before it is merged.
Place each closing reference on a separate line:
Place each link on a separate line:
```text
Closes #123
Closes #456
Resolves https://feedback.fluxer.com/p/123
Resolves https://feedback.fluxer.com/p/456
```
## Authorship
@@ -78,11 +78,11 @@ Complete every section of the pull request template. Clearly describe:
## Reports and other contributions
Use the [bug report form](https://github.com/fluxerapp/fluxer/issues/new?template=bug-report.yaml) to report reproducible defects.
Report bugs and request features at [feedback.fluxer.com](https://feedback.fluxer.com).
Report security vulnerabilities privately through the channels specified in the [security policy](https://github.com/fluxerapp/fluxer/blob/main/.github/SECURITY.md). Do not report vulnerabilities in public issues or discussions.
Report security vulnerabilities privately through [fluxer.app/security](https://fluxer.app/security). Never post them publicly.
Use [discussions](https://github.com/orgs/fluxerapp/discussions) for feature proposals and self-hosting questions.
Read the [operator documentation](https://fluxer.dev) for self-hosting questions.
Submit translations through [Weblate](https://weblate.fluxer.tools), not through pull requests.
-41
View File
@@ -1,41 +0,0 @@
# yaml-language-server: $schema=https://www.schemastore.org/github-discussion.json
body:
- type: markdown
attributes:
value: |
Search existing discussions before posting a feature proposal.
Report vulnerabilities through the [private form](https://github.com/fluxerapp/fluxer/security/advisories/new) or <[email protected]>.
- type: textarea
id: problem
attributes:
label: Current problem
description: State what you are trying to do and what prevents it.
validations:
required: true
- type: textarea
id: proposal
attributes:
label: Proposed change
description: State the expected behaviour.
validations:
required: true
- type: textarea
id: notes
attributes:
label: Additional information
description: Optional. Include constraints, trade-offs, related discussions, screenshots or mockups.
validations:
required: false
- type: checkboxes
id: checks
attributes:
label: Acknowledgements
options:
- label: I searched existing discussions.
required: true
+7 -1
View File
@@ -16,4 +16,10 @@ Every commit made by a contributor must include the [Developer Certificate of Or
## Name and marks
The AGPL does not grant permission to use the Fluxer name, logo or other branding. Forks must use a distinct name and branding unless Fluxer Platform AB grants permission otherwise.
Fluxer and the Fluxer logo are trademarks of Fluxer Platform AB. Neither the AGPL nor the CC BY-SA 4.0 licence on Fluxer artwork grants trademark rights. Fluxer Platform AB grants everyone the following permissions.
- You may distribute unmodified builds of Fluxer, or builds with light patches, under the Fluxer name and logo. Light patches are changes for packaging, portability, security and bug fixes, configuration defaults and translations. Linux distributions, nixpkgs, Flathub and container images are all covered.
- A self-hosted instance running such a build may show the Fluxer name and logo under the instance's own name and domain, as long as it does not imply affiliation with or endorsement by Fluxer Platform AB.
- You may refer to Fluxer by name to describe compatibility, for example "works with Fluxer".
Forks with substantive functional changes must use their own name and logo. Any other use needs permission from Fluxer Platform AB. Contact support@fluxer.com.
-83
View File
@@ -1,83 +0,0 @@
# yaml-language-server: $schema=https://www.schemastore.org/github-issue-forms.json
name: Bug report
description: Report a reproducible defect in Fluxer.
type: Bug
body:
- type: markdown
attributes:
value: |
Search [open and closed issues](https://github.com/fluxerapp/fluxer/issues?q=is%3Aissue) before filing a report.
Report vulnerabilities through the [private form](https://github.com/fluxerapp/fluxer/security/advisories/new) or <[email protected]>. Send account and billing requests to <[email protected]>.
- type: textarea
id: summary
attributes:
label: Observed behaviour
description: State what happened and what you expected.
validations:
required: true
- type: textarea
id: steps
attributes:
label: Reproduction steps
description: Give numbered steps starting from a fresh app or session.
placeholder: |
1. Go to ...
2. Select ...
3. Observe ...
validations:
required: true
- type: input
id: build
attributes:
label: Build information
description: >-
Open User Settings, scroll to the bottom of the left sidebar, and select
the build information. Fluxer copies it to the clipboard.
validations:
required: true
- type: dropdown
id: surface
attributes:
label: Affected surface
multiple: true
options:
- Desktop app
- Web app
- Voice, video, or Go Live
- Self-hosted instance
- HTTP API or Gateway
- Documentation site
validations:
required: true
- type: input
id: instance
attributes:
label: Instance
description: For a self-hosted instance, include the release tag and database backend.
placeholder: fluxer.app
validations:
required: false
- type: textarea
id: evidence
attributes:
label: Evidence
description: Attach relevant logs, screenshots or recordings. Remove tokens, keys, private messages and other personal data. Configuration files may contain secrets.
validations:
required: false
- type: checkboxes
id: checks
attributes:
label: Acknowledgements
options:
- label: I searched open and closed issues.
required: true
- label: I removed secrets and unrelated personal data from the report.
required: true
-18
View File
@@ -1,18 +0,0 @@
# yaml-language-server: $schema=https://www.schemastore.org/github-issue-config.json
blank_issues_enabled: false
contact_links:
- name: Mobile client bugs
url: https://github.com/fluxerapp/flutter_client#bug-reporting
about: Read the reporting instructions for the Fluxer mobile client.
- name: Account and billing support
url: https://fluxer.app/help
about: Find account help and support contact details.
- name: Feature proposals
url: https://github.com/orgs/fluxerapp/discussions
about: Propose a feature in a discussion.
- name: Translations
url: https://weblate.fluxer.tools
about: Improve an existing locale or start a new one.
- name: Self-hosting support
url: https://fluxer.dev
about: Read the operator documentation, then open a discussion if the problem remains.
-44
View File
@@ -1,44 +0,0 @@
# yaml-language-server: $schema=https://www.schemastore.org/github-issue-forms.json
name: Documentation
description: Report incorrect, missing or unclear documentation.
type: Task
labels:
- docs
body:
- type: markdown
attributes:
value: |
This form covers <https://fluxer.dev> and operator documentation.
- type: textarea
id: issue
attributes:
label: Documentation defect
description: State what the page says and what is correct. For missing content, state what information you needed.
validations:
required: true
- type: input
id: location
attributes:
label: Location
description: Provide the page URL or file path and heading.
placeholder: https://fluxer.dev/gateway/overview/
validations:
required: false
- type: textarea
id: suggestion
attributes:
label: Proposed wording
description: Optional.
validations:
required: false
- type: checkboxes
id: checks
attributes:
label: Acknowledgements
options:
- label: I searched open and closed issues.
required: true
+2 -2
View File
@@ -1,7 +1,7 @@
# Security policy
Do not report a vulnerability in an issue, pull request, or discussion.
Do not report a vulnerability in a pull request, on feedback.fluxer.com, in a Fluxer community, or in a direct message to staff.
Submit a report through [GitHub private vulnerability reporting](https://github.com/fluxerapp/fluxer/security/advisories/new) or email <security@fluxer.com>. Include the affected component, impact, reproduction steps, and supporting evidence. Remove unrelated personal data and secrets.
Submit a report through <https://fluxer.app/security> or email <security@fluxer.com>. Include the affected component, impact, reproduction steps, and supporting evidence. Remove unrelated personal data and secrets.
The programme scope, testing rules, safe harbour, disclosure process, and reward terms are published at <https://fluxer.app/security>. That page is authoritative.
+38
View File
@@ -0,0 +1,38 @@
# SPDX-License-Identifier: AGPL-3.0-or-later
name: App Wasm cache
description: Restore or save the shared application Wasm artifacts
inputs:
mode:
description: Cache operation (restore or save)
required: true
outputs:
cache-hit:
description: Whether the restore found the exact artifact key
value: ${{ steps.cache.outputs.cache-hit }}
runs:
using: composite
steps:
- name: Resolve artifact key
id: key
if: always()
shell: bash
run: echo "key=app-wasm-${{ runner.os }}-1.98.1-${{ hashFiles('Cargo.lock', 'tools/ci/src/app_wasm.rs', 'fluxer_app/rust/libfluxcore/Cargo.toml', 'fluxer_app/rust/libfluxcore/Cargo.lock', 'fluxer_app/rust/libfluxcore/.cargo/config.toml', 'fluxer_app/rust/libfluxcore/src/**', 'fluxer_app/rust/libfluxwebp/Cargo.toml', 'fluxer_app/rust/libfluxwebp/Cargo.lock', 'fluxer_app/rust/libfluxwebp/src/**', 'fluxer_app/rust/libfluxwebp/shim/**', 'fluxer_app/rust/libfluxwebp/simd/**', 'packages/markdown_parser/rust/Cargo.toml', 'packages/markdown_parser/rust/.cargo/config.toml', 'packages/markdown_parser/rust/src/**') }}" >> "$GITHUB_OUTPUT"
- name: Restore artifacts
id: cache
if: inputs.mode == 'restore'
uses: actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9
with:
path: |
fluxer_app/pkgs/libfluxcore
fluxer_app/pkgs/libfluxwebp
fluxer_app/src/features/messaging/utils/markdown/parser/MarkdownParserWasmBytes.ts
key: ${{ steps.key.outputs.key }}
- name: Save artifacts
if: always() && inputs.mode == 'save'
uses: actions/cache/save@55cc8345863c7cc4c66a329aec7e433d2d1c52a9
with:
path: |
fluxer_app/pkgs/libfluxcore
fluxer_app/pkgs/libfluxwebp
fluxer_app/src/features/messaging/utils/markdown/parser/MarkdownParserWasmBytes.ts
key: ${{ steps.key.outputs.key }}
+29
View File
@@ -0,0 +1,29 @@
# SPDX-License-Identifier: AGPL-3.0-or-later
name: CI helper
description: Restore or build the shared Rust CI helper
inputs:
save:
description: Save a missing helper cache on main
required: false
default: 'false'
runs:
using: composite
steps:
- name: Restore ci helper
id: cache
uses: actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9
with:
path: target/debug/fluxer-ci
key: >-
fluxer-ci-bin-${{ runner.os }}-1.98.1-${{ hashFiles('Cargo.lock', 'Cargo.toml',
'tools/ci/Cargo.toml', 'tools/ci/src/**') }}
- name: Build ci helper
if: steps.cache.outputs.cache-hit != 'true'
shell: bash
run: cargo build --locked --package fluxer-ci
- name: Save ci helper
if: inputs.save == 'true' && github.ref == 'refs/heads/main' && steps.cache.outputs.cache-hit != 'true'
uses: actions/cache/save@55cc8345863c7cc4c66a329aec7e433d2d1c52a9
with:
path: target/debug/fluxer-ci
key: ${{ steps.cache.outputs.cache-primary-key }}
-3
View File
@@ -67,9 +67,6 @@ p:geo-utils:
p:hono:
- changed-files:
- any-glob-to-any-file: packages/hono/**/*
p:hono-types:
- changed-files:
- any-glob-to-any-file: packages/hono_types/**/*
p:i18n:
- changed-files:
- any-glob-to-any-file: packages/i18n/**/*
+2 -2
View File
@@ -1,6 +1,6 @@
Closes #
Resolves https://feedback.fluxer.com/p/
<!-- Repeat this line for each resolved issue, up to 20. Remove the placeholder only if no issue is resolved and the approval gate does not apply. -->
<!-- Repeat this line for each feedback.fluxer.com post this resolves, up to 20. Remove the placeholder only if no post is resolved and the approval gate does not apply. -->
## Summary
+2 -18
View File
@@ -12,26 +12,11 @@ on:
description: "Path to the Dockerfile to build"
type: string
required: true
context:
description: "Docker build context"
type: string
required: false
default: "."
build-version:
description: "Explicit Fluxer CalVer build version (YYYY.MDD.MICRO, UTC HHMMSS without leading zeroes) to use instead of automatic UTC clock allocation"
type: string
required: false
default: ""
moving-tags:
description: "Comma-separated moving tags to repoint at this build"
type: string
required: false
default: "v1,latest"
extra-build-args:
description: "Additional Docker build args, one KEY=VALUE entry per line"
type: string
required: false
default: ""
permissions:
actions: read
contents: write
@@ -115,7 +100,7 @@ jobs:
password: ${{ github.token }}
- uses: docker/build-push-action@c3c9e263c25d99ce0380d002d59b67737d91b0dc
with:
context: ${{ inputs.context }}
context: .
file: ${{ inputs.dockerfile }}
push: true
provenance: mode=min
@@ -125,7 +110,6 @@ jobs:
BUILD_VERSION=${{ needs.meta.outputs.build_version }}
SOURCE_SHA=${{ github.sha }}
SOURCE_DATE=${{ steps.source.outputs.date }}
${{ inputs.extra-build-args }}
cache-from: type=registry,ref=ghcr.io/${{ env.GHCR_OWNER }}/${{ inputs.image }}:buildcache-${{ matrix.platform }}
cache-to: type=registry,ref=ghcr.io/${{ env.GHCR_OWNER }}/${{ inputs.image }}:buildcache-${{ matrix.platform }},mode=max,image-manifest=true,oci-mediatypes=true,ignore-error=true
env:
@@ -190,7 +174,7 @@ jobs:
- name: Advance moving image tags
env:
MOVING_TAGS: ${{ inputs.moving-tags }}
MOVING_TAGS: v1,latest
VERSION: ${{ needs.meta.outputs.build_version }}
run: >-
tools/ci/run.sh image-set
@@ -154,6 +154,7 @@ jobs:
BUILD_VERSION=${{ needs.meta.outputs.build_version }}
SOURCE_SHA=${{ github.sha }}
SOURCE_DATE=${{ steps.source.outputs.date }}
FLUXER_SELF_HOSTED=true
APP_ASSETS_REF=ghcr.io/${{ env.GHCR_OWNER }}/fluxer-app-proxy-self-hosted:${{ needs.meta.outputs.build_version }}-assets
APP_ASSETS_PLATFORM=linux/amd64
cache-from: type=registry,ref=ghcr.io/${{ env.GHCR_OWNER }}/fluxer-app-proxy-self-hosted:buildcache-${{ matrix.platform }}
+1
View File
@@ -200,6 +200,7 @@ jobs:
tags: ghcr.io/${{ env.GHCR_OWNER }}/fluxer-app-proxy:${{ needs.meta.outputs.build_version }}-arm64
build-args: |
BUILD_VERSION=${{ needs.meta.outputs.build_version }}
PUBLIC_ASSET_BASE_URL=https://fluxerstatic.com
SOURCE_SHA=${{ github.sha }}
SOURCE_DATE=${{ steps.source.outputs.date }}
APP_ASSETS_REF=ghcr.io/${{ env.GHCR_OWNER }}/fluxer-app-proxy:${{ needs.meta.outputs.build_version }}-assets
@@ -0,0 +1,355 @@
name: build desktop modules
on:
workflow_dispatch:
inputs:
channel:
description: Release channel to ship renderer modules on. The live shell on that channel stays as it is.
required: true
type: choice
options:
- canary
- stable
default: canary
build_version:
description: Explicit Fluxer CalVer build version (YYYY.MDD.MICRO, UTC HHMMSS without leading zeroes) to use instead of automatic UTC clock allocation.
required: false
default: ""
type: string
shell_contract:
description: "Did the shell contract change since the oldest shell these modules still reach? unchanged refuses if it did. compatible keeps older shells on these modules because the renderer still works on them. raise stops sending these modules to shells without the new contract: Linux and managed shells keep their current renderer, Windows and macOS update their shell first."
required: true
type: choice
options:
- unchanged
- compatible
- raise
default: unchanged
permissions:
contents: write
id-token: write
actions: read
concurrency:
group: desktop-${{ inputs.channel }}
cancel-in-progress: false
env:
CHANNEL: ${{ inputs.channel }}
BUILD_CHANNEL: ${{ inputs.channel == 'canary' && 'canary' || 'stable' }}
jobs:
meta:
name: Resolve build metadata
runs-on: ubuntu-24.04-arm
environment: desktop-releases
timeout-minutes: 25
permissions:
contents: read
outputs:
version: ${{ steps.meta.outputs.version }}
pub_date: ${{ steps.meta.outputs.pub_date }}
channel: ${{ steps.meta.outputs.channel }}
build_channel: ${{ steps.meta.outputs.build_channel }}
source_sha: ${{ steps.meta.outputs.source_sha }}
steps:
- name: Checkout source
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1
with:
ref: main
- name: Set up Rust toolchain (CI helpers)
uses: dtolnay/rust-toolchain@02cb101ec7c40f2c49e1d9714d64511d8e1b74de
with:
toolchain: "1.98.1"
- name: Create token
id: create-token
uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1
with:
client-id: ${{ vars.FLUXER_CI_APP_ID }}
private-key: ${{ secrets.FLUXER_CI_APP_KEY }}
owner: fluxerapp
repositories: fluxer
permission-contents: read
- name: Set metadata
id: meta
env:
GH_TOKEN: ${{ steps.create-token.outputs.token }}
FLUXER_BUILD_VERSION: ${{ inputs.build_version }}
run: >-
cargo run --locked --quiet --manifest-path tools/ci/Cargo.toml -- build-desktop
--step set_metadata
--channel "${{ inputs.channel }}"
shared_assets:
name: Build shared renderer assets
needs:
- meta
runs-on: ubuntu-24.04
environment: desktop-releases
timeout-minutes: 60
permissions:
contents: read
env:
CHANNEL: ${{ needs.meta.outputs.channel }}
BUILD_CHANNEL: ${{ needs.meta.outputs.build_channel }}
RELEASE_CHANNEL: ${{ needs.meta.outputs.build_channel }}
PUBLIC_RELEASE_CHANNEL: ${{ needs.meta.outputs.build_channel }}
VERSION: ${{ needs.meta.outputs.version }}
BUILD_VERSION: ${{ needs.meta.outputs.version }}
PUBLIC_BUILD_VERSION: ${{ needs.meta.outputs.version }}
SOURCE_SHA: ${{ needs.meta.outputs.source_sha }}
steps:
- name: Checkout CI helpers
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1
with:
ref: ${{ needs.meta.outputs.source_sha }}
path: _ci
- name: Checkout source
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1
with:
ref: ${{ needs.meta.outputs.source_sha }}
path: source
- name: Set up Rust toolchain (renderer wasm)
uses: dtolnay/rust-toolchain@02cb101ec7c40f2c49e1d9714d64511d8e1b74de
with:
toolchain: "1.98.1"
targets: wasm32-unknown-unknown
- name: Set workdir (Unix)
env:
SUBST_TARGET: ${{ github.workspace }}/source
run: >-
cargo run --locked --quiet --manifest-path ${{ github.workspace }}/_ci/tools/ci/Cargo.toml -- build-desktop
--step set_workdir_unix
- name: Set up Node.js
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020
with:
node-version: 26
- name: Set up pnpm
run: >-
cargo run --locked --quiet --manifest-path ${{ github.workspace }}/_ci/tools/ci/Cargo.toml -- build-desktop
--step setup_pnpm
- name: Resolve pnpm store path (Unix)
run: >-
cargo run --locked --quiet --manifest-path ${{ github.workspace }}/_ci/tools/ci/Cargo.toml -- build-desktop
--step resolve_pnpm_store_unix
- name: Cache pnpm store
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9
with:
path: ${{ env.PNPM_STORE_PATH }}
key: ${{ runner.os }}-shared-renderer-pnpm-store-${{ hashFiles('source/pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-shared-renderer-pnpm-store-
- name: Cache cargo registry
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9
with:
path: |
~/.cargo/registry
~/.cargo/git
key: ${{ runner.os }}-shared-renderer-cargo-registry-${{ hashFiles('source/Cargo.lock') }}
restore-keys: |
${{ runner.os }}-shared-renderer-cargo-registry-
- name: Install dependencies
working-directory: ${{ env.WORKDIR }}/fluxer_desktop
run: >-
cargo run --locked --quiet --manifest-path ${{ github.workspace }}/_ci/tools/ci/Cargo.toml -- build-desktop
--step install_dependencies
- name: Update version
working-directory: ${{ env.WORKDIR }}/fluxer_desktop
run: >-
cargo run --locked --quiet --manifest-path ${{ github.workspace }}/_ci/tools/ci/Cargo.toml -- build-desktop
--step update_version
- name: Set build channel
working-directory: ${{ env.WORKDIR }}/fluxer_desktop
run: >-
cargo run --locked --quiet --manifest-path ${{ github.workspace }}/_ci/tools/ci/Cargo.toml -- build-desktop
--step set_build_channel
- name: Build shared renderer assets
working-directory: ${{ env.WORKDIR }}/fluxer_desktop
run: >-
cargo run --locked --quiet --manifest-path ${{ github.workspace }}/_ci/tools/ci/Cargo.toml -- build-desktop
--step build_shared_assets
- name: Prepare shared renderer artifact
run: >-
cargo run --locked --quiet --manifest-path ${{ github.workspace }}/_ci/tools/ci/Cargo.toml -- build-desktop
--step prepare_shared_assets
- name: Upload shared renderer artifact
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02
with:
name: desktop-shared-${{ needs.meta.outputs.build_channel }}-${{ needs.meta.outputs.version }}-${{ needs.meta.outputs.source_sha }}
path: source/desktop-shared-assets
if-no-files-found: error
retention-days: 1
compression-level: 0
- name: Split renderer into desktop modules
run: >-
cargo run --locked --quiet --manifest-path ${{ github.workspace }}/_ci/tools/ci/Cargo.toml -- build-desktop
--step split_modules
- name: Pack desktop modules
run: >-
cargo run --locked --quiet --manifest-path ${{ github.workspace }}/_ci/tools/ci/Cargo.toml -- build-desktop
--step pack_modules
- name: Upload desktop module packages
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02
with:
name: desktop-modules-${{ needs.meta.outputs.build_channel }}-${{ needs.meta.outputs.version }}-${{ needs.meta.outputs.source_sha }}
path: |
source/desktop-modules/classification.json
source/desktop-modules/*/module.json
source/desktop-modules/*/package.br
source/desktop-modules/*/package.br.sha256
if-no-files-found: error
retention-days: 1
compression-level: 0
assemble:
name: Assemble the modules-only release
if: ${{ !cancelled() && needs.shared_assets.result == 'success' }}
needs:
- meta
- shared_assets
runs-on: ubuntu-24.04-arm
environment: desktop-releases
timeout-minutes: 60
permissions:
actions: read
contents: read
env:
CHANNEL: ${{ needs.meta.outputs.build_channel }}
VERSION: ${{ needs.meta.outputs.version }}
PUB_DATE: ${{ needs.meta.outputs.pub_date }}
SOURCE_SHA: ${{ needs.meta.outputs.source_sha }}
steps:
- name: Checkout source
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1
with:
ref: ${{ needs.meta.outputs.source_sha }}
fetch-depth: 0
- name: Set up Rust toolchain (CI helpers)
uses: dtolnay/rust-toolchain@02cb101ec7c40f2c49e1d9714d64511d8e1b74de
with:
toolchain: "1.98.1"
- name: Download desktop module packages
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093
with:
name: desktop-modules-${{ needs.meta.outputs.build_channel }}-${{ needs.meta.outputs.version }}-${{ needs.meta.outputs.source_sha }}
path: desktop-modules
- name: Create token
id: create-token
uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1
with:
client-id: ${{ vars.FLUXER_CI_APP_ID }}
private-key: ${{ secrets.FLUXER_CI_APP_KEY }}
owner: fluxerapp
repositories: fluxer
permission-contents: read
- name: Check which shells run these modules
env:
GH_TOKEN: ${{ steps.create-token.outputs.token }}
SHELL_CONTRACT: ${{ inputs.shell_contract }}
DESKTOP_MODULES_ONLY_RELEASE: true
run: >-
cargo run --locked --quiet --manifest-path tools/ci/Cargo.toml -- build-desktop
--step check_shell_contract
- name: Build the modules-only manifests
run: >-
cargo run --locked --quiet --manifest-path tools/ci/Cargo.toml -- build-desktop
--step build_modules_only_manifest
- name: Prepare GitHub release assets
run: >-
cargo run --locked --quiet --manifest-path tools/ci/Cargo.toml -- build-desktop
--step prepare_modules_release_assets
- name: Upload GitHub release assets
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02
with:
name: fluxer-desktop-modules-release-assets
path: release_assets
if-no-files-found: error
retention-days: 1
compression-level: 0
publish_release:
name: Publish GitHub modules-only release
if: ${{ !cancelled() && needs.assemble.result == 'success' }}
needs:
- meta
- assemble
runs-on: ubuntu-24.04-arm
environment: desktop-releases
timeout-minutes: 60
permissions:
contents: write
env:
CHANNEL: ${{ needs.meta.outputs.build_channel }}
VERSION: ${{ needs.meta.outputs.version }}
steps:
- name: Checkout source
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1
with:
ref: ${{ needs.meta.outputs.source_sha }}
- name: Set up Rust toolchain (CI helpers)
uses: dtolnay/rust-toolchain@02cb101ec7c40f2c49e1d9714d64511d8e1b74de
with:
toolchain: "1.98.1"
- name: Download GitHub release assets
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093
with:
name: fluxer-desktop-modules-release-assets
path: release_assets
- name: Create token
id: create-token
uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1
with:
client-id: ${{ vars.FLUXER_CI_APP_ID }}
private-key: ${{ secrets.FLUXER_CI_APP_KEY }}
owner: fluxerapp
repositories: fluxer
permission-contents: write
- name: Publish GitHub modules-only release
env:
GH_TOKEN: ${{ steps.create-token.outputs.token }}
CHANNEL: ${{ needs.meta.outputs.build_channel }}
VERSION: ${{ needs.meta.outputs.version }}
SOURCE_SHA: ${{ needs.meta.outputs.source_sha }}
RELEASE_BASELINE_SHA: ${{ vars.RELEASE_BASELINE_SHA }}
run: |
set -euo pipefail
release_args=(
release publish
--component "fluxer-desktop-${CHANNEL}"
--build-version "${VERSION}"
--source-sha "${SOURCE_SHA}"
--previous-sha "${RELEASE_BASELINE_SHA}"
--asset-dir release_assets
)
if [[ "${CHANNEL}" == "canary" ]]; then
release_args+=(--prerelease)
fi
cargo run --locked --quiet --manifest-path tools/ci/Cargo.toml -- "${release_args[@]}"
+274 -4
View File
@@ -21,6 +21,15 @@ on:
required: false
default: ""
type: string
shell_contract:
description: "Did the shell contract change since the oldest shell these modules still reach? unchanged refuses if it did. compatible keeps older shells on these modules because the renderer still works on them. raise stops sending these modules to shells without the new contract: Linux and managed shells keep their current renderer, Windows and macOS update their shell first."
required: true
type: choice
options:
- unchanged
- compatible
- raise
default: unchanged
permissions:
contents: write
id-token: write
@@ -28,7 +37,7 @@ permissions:
concurrency:
group: desktop-${{ inputs.channel }}
cancel-in-progress: true
cancel-in-progress: false
env:
CHANNEL: ${{ inputs.channel }}
@@ -53,6 +62,7 @@ jobs:
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1
with:
ref: main
fetch-depth: 0
- name: Set up Rust toolchain (CI helpers)
uses: dtolnay/rust-toolchain@02cb101ec7c40f2c49e1d9714d64511d8e1b74de
@@ -78,6 +88,18 @@ jobs:
--step set_metadata
--channel "${{ inputs.channel }}"
- name: Check which shells run these modules
env:
GH_TOKEN: ${{ steps.create-token.outputs.token }}
CHANNEL: ${{ steps.meta.outputs.build_channel }}
SOURCE_SHA: ${{ steps.meta.outputs.source_sha }}
VERSION: ${{ steps.meta.outputs.version }}
SHELL_CONTRACT: ${{ inputs.shell_contract }}
DESKTOP_MODULES_ONLY_RELEASE: false
run: >-
cargo run --locked --quiet --manifest-path tools/ci/Cargo.toml -- build-desktop
--step check_shell_contract
matrix:
name: Resolve build matrix
runs-on: ubuntu-24.04-arm
@@ -103,11 +125,150 @@ jobs:
--step set_matrix
--skip-targets "${{ inputs.skip_targets }}"
shared_assets:
name: Build shared renderer assets
needs:
- meta
runs-on: ubuntu-24.04
environment: desktop-releases
timeout-minutes: 60
permissions:
contents: read
env:
CHANNEL: ${{ needs.meta.outputs.channel }}
BUILD_CHANNEL: ${{ needs.meta.outputs.build_channel }}
RELEASE_CHANNEL: ${{ needs.meta.outputs.build_channel }}
PUBLIC_RELEASE_CHANNEL: ${{ needs.meta.outputs.build_channel }}
VERSION: ${{ needs.meta.outputs.version }}
BUILD_VERSION: ${{ needs.meta.outputs.version }}
PUBLIC_BUILD_VERSION: ${{ needs.meta.outputs.version }}
SOURCE_SHA: ${{ needs.meta.outputs.source_sha }}
steps:
- name: Checkout CI helpers
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1
with:
ref: ${{ needs.meta.outputs.source_sha }}
path: _ci
- name: Checkout source
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1
with:
ref: ${{ needs.meta.outputs.source_sha }}
path: source
- name: Set up Rust toolchain (renderer wasm)
uses: dtolnay/rust-toolchain@02cb101ec7c40f2c49e1d9714d64511d8e1b74de
with:
toolchain: "1.98.1"
targets: wasm32-unknown-unknown
- name: Set workdir (Unix)
env:
SUBST_TARGET: ${{ github.workspace }}/source
run: >-
cargo run --locked --quiet --manifest-path ${{ github.workspace }}/_ci/tools/ci/Cargo.toml -- build-desktop
--step set_workdir_unix
- name: Set up Node.js
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020
with:
node-version: 26
- name: Set up pnpm
run: >-
cargo run --locked --quiet --manifest-path ${{ github.workspace }}/_ci/tools/ci/Cargo.toml -- build-desktop
--step setup_pnpm
- name: Resolve pnpm store path (Unix)
run: >-
cargo run --locked --quiet --manifest-path ${{ github.workspace }}/_ci/tools/ci/Cargo.toml -- build-desktop
--step resolve_pnpm_store_unix
- name: Cache pnpm store
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9
with:
path: ${{ env.PNPM_STORE_PATH }}
key: ${{ runner.os }}-shared-renderer-pnpm-store-${{ hashFiles('source/pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-shared-renderer-pnpm-store-
- name: Cache cargo registry
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9
with:
path: |
~/.cargo/registry
~/.cargo/git
key: ${{ runner.os }}-shared-renderer-cargo-registry-${{ hashFiles('source/Cargo.lock') }}
restore-keys: |
${{ runner.os }}-shared-renderer-cargo-registry-
- name: Install dependencies
working-directory: ${{ env.WORKDIR }}/fluxer_desktop
run: >-
cargo run --locked --quiet --manifest-path ${{ github.workspace }}/_ci/tools/ci/Cargo.toml -- build-desktop
--step install_dependencies
- name: Update version
working-directory: ${{ env.WORKDIR }}/fluxer_desktop
run: >-
cargo run --locked --quiet --manifest-path ${{ github.workspace }}/_ci/tools/ci/Cargo.toml -- build-desktop
--step update_version
- name: Set build channel
working-directory: ${{ env.WORKDIR }}/fluxer_desktop
run: >-
cargo run --locked --quiet --manifest-path ${{ github.workspace }}/_ci/tools/ci/Cargo.toml -- build-desktop
--step set_build_channel
- name: Build shared renderer assets
working-directory: ${{ env.WORKDIR }}/fluxer_desktop
run: >-
cargo run --locked --quiet --manifest-path ${{ github.workspace }}/_ci/tools/ci/Cargo.toml -- build-desktop
--step build_shared_assets
- name: Prepare shared renderer artifact
run: >-
cargo run --locked --quiet --manifest-path ${{ github.workspace }}/_ci/tools/ci/Cargo.toml -- build-desktop
--step prepare_shared_assets
- name: Upload shared renderer artifact
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02
with:
name: desktop-shared-${{ needs.meta.outputs.build_channel }}-${{ needs.meta.outputs.version }}-${{ needs.meta.outputs.source_sha }}
path: source/desktop-shared-assets
if-no-files-found: error
retention-days: 1
compression-level: 0
- name: Split renderer into desktop modules
run: >-
cargo run --locked --quiet --manifest-path ${{ github.workspace }}/_ci/tools/ci/Cargo.toml -- build-desktop
--step split_modules
- name: Pack desktop modules
run: >-
cargo run --locked --quiet --manifest-path ${{ github.workspace }}/_ci/tools/ci/Cargo.toml -- build-desktop
--step pack_modules
- name: Upload desktop module packages
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02
with:
name: desktop-modules-${{ needs.meta.outputs.build_channel }}-${{ needs.meta.outputs.version }}-${{ needs.meta.outputs.source_sha }}
path: |
source/desktop-modules/classification.json
source/desktop-modules/*/module.json
source/desktop-modules/*/package.br
source/desktop-modules/*/package.br.sha256
if-no-files-found: error
retention-days: 1
compression-level: 0
build:
name: Build ${{ matrix.platform }} (${{ matrix.arch }})
needs:
- meta
- matrix
- shared_assets
runs-on: ${{ matrix.os }}
environment: desktop-releases
timeout-minutes: 180
@@ -130,6 +291,7 @@ jobs:
SOURCE_SHA: ${{ needs.meta.outputs.source_sha }}
DESKTOP_PLATFORM: ${{ matrix.platform }}
DESKTOP_ARCH: ${{ matrix.arch }}
FLUXER_MODULES: "1"
PLATFORM: ${{ matrix.platform }}
ARCH: ${{ matrix.arch }}
ELECTRON_ARCH: ${{ matrix.electron_arch }}
@@ -276,6 +438,17 @@ jobs:
cargo run --locked --quiet --manifest-path ${{ github.workspace }}/_ci/tools/ci/Cargo.toml -- build-desktop
--step set_build_channel
- name: Download shared renderer artifact
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093
with:
name: desktop-shared-${{ needs.meta.outputs.build_channel }}-${{ needs.meta.outputs.version }}-${{ needs.meta.outputs.source_sha }}
path: source/desktop-shared-assets
- name: Restore shared renderer assets
run: >-
cargo run --locked --quiet --manifest-path ${{ github.workspace }}/_ci/tools/ci/Cargo.toml -- build-desktop
--step restore_shared_assets
- name: Build Electron main process
working-directory: ${{ env.WORKDIR }}/fluxer_desktop
env:
@@ -400,6 +573,16 @@ jobs:
cargo run --locked --quiet --manifest-path ${{ github.workspace }}/_ci/tools/ci/Cargo.toml -- build-desktop
--step verify_windows_unpacked_signatures
- name: Verify the packaged shell starts and boots its bundled renderer (Windows)
if: matrix.platform == 'windows' && matrix.arch == 'x64'
working-directory: ${{ env.WORKDIR }}/fluxer_desktop
env:
BUILD_CHANNEL: ${{ env.BUILD_CHANNEL }}
BUILD_VERSION: ${{ env.VERSION }}
run: >-
cargo run --locked --quiet --manifest-path ${{ github.workspace }}/_ci/tools/ci/Cargo.toml -- build-desktop
--step verify_bundled_renderer_windows
- name: Create portable ZIP (Windows)
if: matrix.platform == 'windows'
working-directory: ${{ env.WORKDIR }}/fluxer_desktop
@@ -440,6 +623,16 @@ jobs:
cargo run --locked --quiet --manifest-path ${{ github.workspace }}/_ci/tools/ci/Cargo.toml -- build-desktop
--step build_app_linux
- name: Verify the packaged shell boots its bundled renderer (Linux)
if: matrix.platform == 'linux' && matrix.arch == 'x64'
working-directory: ${{ env.WORKDIR }}/fluxer_desktop
env:
BUILD_CHANNEL: ${{ env.BUILD_CHANNEL }}
BUILD_VERSION: ${{ env.VERSION }}
run: >-
cargo run --locked --quiet --manifest-path ${{ github.workspace }}/_ci/tools/ci/Cargo.toml -- build-desktop
--step verify_bundled_renderer_linux
- name: Verify signed Windows artifacts
if: matrix.platform == 'windows'
working-directory: ${{ env.WORKDIR }}/fluxer_desktop
@@ -500,16 +693,64 @@ jobs:
retention-days: 1
compression-level: 0
upload:
name: Assemble desktop release assets
verify_windows_arm64:
name: Verify windows (arm64)
if: ${{ !cancelled() && needs.build.result == 'success' }}
needs:
- meta
- build
runs-on: windows-11-arm
timeout-minutes: 30
permissions:
actions: read
contents: read
env:
CHANNEL: ${{ needs.meta.outputs.build_channel }}
VERSION: ${{ needs.meta.outputs.version }}
steps:
- name: Checkout source
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1
with:
ref: ${{ needs.meta.outputs.source_sha }}
sparse-checkout: fluxer_desktop/scripts
- name: Set up Node.js
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020
with:
node-version: 24
- name: Download windows arm64 build artifacts
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093
with:
path: artifacts
pattern: fluxer-desktop-${{ needs.meta.outputs.build_channel }}-windows-arm64*
- name: Verify the packaged shell starts and boots its bundled renderer
shell: pwsh
run: |
$ErrorActionPreference = 'Stop'
$zip = Get-ChildItem -Path artifacts -Recurse -Filter "*-$env:VERSION-portable-win-arm64.zip" | Select-Object -First 1
if ($null -eq $zip) { Write-Host 'This build has no windows arm64 portable zip'; exit 0 }
$app = Join-Path $env:RUNNER_TEMP 'app'
Expand-Archive -Path $zip.FullName -DestinationPath $app
$exe = Get-ChildItem -Path $app -Recurse -Filter 'Fluxer*.exe' | Where-Object { $_.Name -notlike '*_ExecutionStub.exe' } | Select-Object -First 1
if ($null -eq $exe) { throw "$($zip.Name) holds no Fluxer executable" }
node fluxer_desktop/scripts/release-check.mjs --app $exe.DirectoryName --channel $env:CHANNEL --expect-renderer $env:VERSION --expect-source bundled --package-origin http://127.0.0.1:9 --app-arg=--disable-gpu --workdir (Join-Path $env:RUNNER_TEMP 'runs') --timeout-seconds 240
if ($LASTEXITCODE -ne 0) { throw "The windows arm64 build fails its release check" }
upload:
name: Assemble desktop release assets
if: ${{ !cancelled() && needs.build.result == 'success' && needs.verify_windows_arm64.result == 'success' }}
needs:
- meta
- shared_assets
- build
- verify_windows_arm64
runs-on: ubuntu-24.04-arm
environment: desktop-releases
timeout-minutes: 180
permissions:
actions: read
contents: read
env:
CHANNEL: ${{ needs.meta.outputs.build_channel }}
@@ -521,12 +762,12 @@ jobs:
PUBLIC_BUILD_VERSION: ${{ needs.meta.outputs.version }}
PUB_DATE: ${{ needs.meta.outputs.pub_date }}
SOURCE_SHA: ${{ needs.meta.outputs.source_sha }}
PUBLIC_DL_BASE: https://api.fluxer.app/dl
steps:
- name: Checkout source
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1
with:
ref: ${{ needs.meta.outputs.source_sha }}
fetch-depth: 0
- name: Set up Rust toolchain (CI helpers)
uses: dtolnay/rust-toolchain@02cb101ec7c40f2c49e1d9714d64511d8e1b74de
@@ -547,6 +788,35 @@ jobs:
cargo run --locked --quiet --manifest-path tools/ci/Cargo.toml -- build-desktop
--step build_payload
- name: Download desktop module packages
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093
with:
name: desktop-modules-${{ needs.meta.outputs.build_channel }}-${{ needs.meta.outputs.version }}-${{ needs.meta.outputs.source_sha }}
path: desktop-modules
- name: Create token
id: create-token
uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1
with:
client-id: ${{ vars.FLUXER_CI_APP_ID }}
private-key: ${{ secrets.FLUXER_CI_APP_KEY }}
owner: fluxerapp
repositories: fluxer
permission-contents: read
- name: Check which shells run these modules
env:
GH_TOKEN: ${{ steps.create-token.outputs.token }}
SHELL_CONTRACT: ${{ inputs.shell_contract }}
DESKTOP_MODULES_ONLY_RELEASE: false
run: >-
cargo run --locked --quiet --manifest-path tools/ci/Cargo.toml -- build-desktop
--step check_shell_contract
- name: Build desktop module manifest
run: >-
cargo run --locked --quiet --manifest-path tools/ci/Cargo.toml -- build-desktop
--step build_module_manifest
- name: Prepare GitHub release assets
run: >-
cargo run --locked --quiet --manifest-path tools/ci/Cargo.toml -- build-desktop
@@ -0,0 +1,152 @@
name: desktop windows release check
on:
workflow_dispatch:
inputs:
channel:
description: Release channel
type: choice
options:
- canary
- stable
default: canary
version:
description: Published version to check
required: true
type: string
expect_failure:
description: Pass only when the published build fails to start
type: boolean
default: false
update_from:
description: Comma-separated published versions to install with Setup and update to the version above from the live feed
required: false
type: string
default: ""
stale_apply_record:
description: Before each update, leave behind an hour-old record of an update that never installed (passes only when the installed versions retry it)
type: boolean
default: false
pull_request:
paths:
- .github/workflows/desktop-windows-release-check.yaml
- fluxer_desktop/scripts/release-check.mjs
permissions:
contents: read
concurrency:
group: desktop-windows-release-check-${{ github.ref }}-${{ inputs.version || 'pr' }}
cancel-in-progress: false
jobs:
check:
name: Check windows (${{ matrix.arch }})
runs-on: ${{ matrix.os }}
timeout-minutes: 45
strategy:
fail-fast: false
matrix:
include:
- arch: x64
os: windows-2025
- arch: arm64
os: windows-11-arm
env:
CHANNEL: ${{ inputs.channel || 'canary' }}
VERSION: ${{ inputs.version || '2026.1009.20411' }}
EXPECT_FAILURE: ${{ github.event_name == 'pull_request' && 'true' || inputs.expect_failure }}
UPDATE_FROM: ${{ inputs.update_from || '' }}
STALE_APPLY_RECORD: ${{ inputs.stale_apply_record && 'true' || 'false' }}
ARCH: ${{ matrix.arch }}
steps:
- name: Checkout source
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1
with:
sparse-checkout: fluxer_desktop/scripts
- name: Set up Node.js
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020
with:
node-version: 24
- name: Check the portable build
shell: pwsh
run: |
$ErrorActionPreference = 'Stop'
$work = Join-Path $env:RUNNER_TEMP 'portable'
New-Item -ItemType Directory -Force -Path $work | Out-Null
$zip = Join-Path $work 'portable.zip'
Invoke-WebRequest -Uri "https://pkgs.fluxer.com/desktop/$env:CHANNEL/win32/$env:ARCH/$env:VERSION/portable" -OutFile $zip
Expand-Archive -Path $zip -DestinationPath (Join-Path $work 'app')
$exe = Get-ChildItem -Path (Join-Path $work 'app') -Recurse -Filter 'Fluxer*.exe' | Where-Object { $_.Name -notlike '*_ExecutionStub.exe' } | Select-Object -First 1
if ($null -eq $exe) { throw "The portable build holds no Fluxer executable" }
$output = & node fluxer_desktop/scripts/release-check.mjs --app $exe.DirectoryName --channel $env:CHANNEL --expect-renderer $env:VERSION --expect-source bundled --app-arg=--disable-gpu --workdir (Join-Path $work 'runs') --timeout-seconds 240 2>&1
$code = $LASTEXITCODE
$output | ForEach-Object { Write-Host $_ }
if ($env:EXPECT_FAILURE -eq 'true') {
if ($code -eq 0) { throw "Expected $env:VERSION to fail its release check, it passed" }
Write-Host "$env:VERSION fails its release check on windows $env:ARCH as expected"
exit 0
}
if ($code -ne 0) { throw "The release check failed for $env:VERSION on windows $env:ARCH" }
- name: Update installed builds from the live feed
if: env.UPDATE_FROM != ''
shell: pwsh
run: |
$ErrorActionPreference = 'Stop'
$channelDirs = @{ canary = 'fluxer_desktop_canary'; stable = 'fluxer_desktop' }
$dataDirs = @{ canary = 'fluxercanary'; stable = 'fluxer' }
$installRoot = Join-Path $env:LOCALAPPDATA $channelDirs[$env:CHANNEL]
$logPath = Join-Path (Join-Path $env:APPDATA $dataDirs[$env:CHANNEL]) 'logs\main.log'
$failures = @()
foreach ($from in ($env:UPDATE_FROM -split ',' | ForEach-Object { $_.Trim() } | Where-Object { $_ })) {
Write-Host "== install $from, then update to $env:VERSION"
Get-Process | Where-Object { $_.Path -like "$installRoot*" } | Stop-Process -Force -ErrorAction SilentlyContinue
if (Test-Path $installRoot) { Remove-Item -Recurse -Force $installRoot }
Remove-Item -Recurse -Force (Split-Path (Split-Path $logPath -Parent) -Parent) -ErrorAction SilentlyContinue
$setup = Join-Path $env:RUNNER_TEMP "setup-$from.exe"
Invoke-WebRequest -Uri "https://pkgs.fluxer.com/desktop/$env:CHANNEL/win32/$env:ARCH/$from/setup" -OutFile $setup
Start-Process -FilePath $setup -ArgumentList '--silent' -Wait
Start-Sleep -Seconds 5
Get-Process | Where-Object { $_.Path -like "$installRoot*" } | Stop-Process -Force -ErrorAction SilentlyContinue
$exe = Get-ChildItem -Path (Join-Path $installRoot 'current') -Filter 'Fluxer*.exe' | Where-Object { $_.Name -notlike '*_ExecutionStub.exe' } | Select-Object -First 1
if ($null -eq $exe) { $failures += "$from did not install"; continue }
if ($env:STALE_APPLY_RECORD -eq 'true') {
$dataRoot = Split-Path (Split-Path $logPath -Parent) -Parent
New-Item -ItemType Directory -Force -Path $dataRoot | Out-Null
$attemptedAt = [DateTimeOffset]::UtcNow.AddHours(-1).ToUnixTimeMilliseconds()
Set-Content -Path (Join-Path $dataRoot 'update-apply-state.json') -Value "{`"version`":`"$env:VERSION`",`"attemptedAt`":$attemptedAt}" -NoNewline
Write-Host "left a stale apply record for $env:VERSION"
}
Start-Process -FilePath $exe.FullName -ArgumentList '--disable-gpu'
$deadline = (Get-Date).AddMinutes(8)
$reported = $null
while ((Get-Date) -lt $deadline) {
Start-Sleep -Seconds 10
if (Test-Path $logPath) {
$line = Select-String -Path $logPath -Pattern "The renderer reported its build: .*Desktop $([regex]::Escape($env:VERSION)), Web $([regex]::Escape($env:VERSION))" | Select-Object -Last 1
if ($null -ne $line) { $reported = $line.Line; break }
}
}
$installed = Get-Content (Join-Path $installRoot 'current\sq.version') -Raw -ErrorAction SilentlyContinue
Write-Host "installed package after update: $installed"
$logs = Join-Path $env:RUNNER_TEMP "logs\$from"
New-Item -ItemType Directory -Force -Path $logs | Out-Null
if (Test-Path $logPath) { Copy-Item $logPath $logs }
Get-ChildItem -Path $installRoot -Filter '*.log' -ErrorAction SilentlyContinue | Copy-Item -Destination $logs
if (Test-Path $logPath) { Get-Content $logPath | Select-String -Pattern 'Bootstrap|ShellSelfUpdate|Velopack|velopack|NativeModulePreflight|reported its build' | Select-Object -Last 40 | ForEach-Object { Write-Host $_.Line } }
if ($null -eq $reported) { $failures += "$from did not reach Desktop and Web $env:VERSION" } else { Write-Host "$from updated: $reported" }
Get-Process | Where-Object { $_.Path -like "$installRoot*" } | Stop-Process -Force -ErrorAction SilentlyContinue
}
if ($failures.Count -gt 0) { throw ($failures -join "`n") }
- name: Upload update logs
if: always() && env.UPDATE_FROM != ''
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02
with:
name: update-logs-${{ matrix.arch }}
path: ${{ runner.temp }}/logs
if-no-files-found: ignore
retention-days: 7
+183
View File
@@ -0,0 +1,183 @@
# SPDX-License-Identifier: AGPL-3.0-or-later
name: Helm chart
on:
pull_request:
paths:
- deploy/helm/**
- deploy/self-hosting/Caddyfile
- .github/workflows/helm.yaml
push:
branches: [main]
paths:
- deploy/helm/**
- deploy/self-hosting/Caddyfile
- .github/workflows/helm.yaml
permissions:
contents: read
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
jobs:
render:
name: Lint and render with Helm ${{ matrix.helm }}
runs-on: ubuntu-24.04
timeout-minutes: 15
strategy:
fail-fast: false
matrix:
include:
- helm: v3.19.0
sha256: a7f81ce08007091b86d8bd696eb4d86b8d0f2e1b9f6c714be62f82f96a594496
- helm: v4.2.4
sha256: c306b46f719b0a4da32d0f78ee21bf90ce8d602f15b22ab753f0674d1670a7f3
env:
CHART: deploy/helm/fluxer
KUBECONFORM_VERSION: v0.7.0
KUBECONFORM_SHA256: c31518ddd122663b3f3aa874cfe8178cb0988de944f29c74a0b9260920d115d3
CRD_CATALOG: https://raw.githubusercontent.com/datreeio/CRDs-catalog/63669a570e231d4f1f8396d229a1de512bcf0a34/{{.Group}}/{{.ResourceKind}}_{{.ResourceAPIVersion}}.json
steps:
- name: Checkout fluxer
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1
with:
persist-credentials: false
- name: Install Helm and kubeconform
env:
HELM_VERSION: ${{ matrix.helm }}
HELM_SHA256: ${{ matrix.sha256 }}
run: |
set -euo pipefail
mkdir -p "$RUNNER_TEMP/bin"
cd "$RUNNER_TEMP"
curl -fsSLo helm.tgz "https://get.helm.sh/helm-${HELM_VERSION}-linux-amd64.tar.gz"
echo "${HELM_SHA256} helm.tgz" | sha256sum -c -
tar -xzf helm.tgz linux-amd64/helm
mv linux-amd64/helm bin/helm
curl -fsSLo kubeconform.tgz "https://github.com/yannh/kubeconform/releases/download/${KUBECONFORM_VERSION}/kubeconform-linux-amd64.tar.gz"
echo "${KUBECONFORM_SHA256} kubeconform.tgz" | sha256sum -c -
tar -xzf kubeconform.tgz kubeconform
mv kubeconform bin/kubeconform
echo "$RUNNER_TEMP/bin" >> "$GITHUB_PATH"
- name: Lint, render and validate every CI values file
run: |
set -euo pipefail
command -v helm kubeconform python3
python3 -c 'import yaml'
helm version --short
shopt -s nullglob
files=("$CHART"/ci/*.yaml)
if [ "${#files[@]}" -eq 0 ]; then
echo "::error::no values files under $CHART/ci"
exit 1
fi
for values in "${files[@]}"; do
echo "::group::$values"
helm lint --strict "$CHART" -f "$values"
helm template fluxer "$CHART" -n fluxer -f "$values" \
--api-versions cert-manager.io/v1 --api-versions gateway.networking.k8s.io/v1 > rendered.yaml
kubeconform -strict -summary -kubernetes-version 1.29.0 \
-schema-location default -schema-location "$CRD_CATALOG" rendered.yaml
if grep -qi 'example\.com' rendered.yaml; then
echo "::error::$values renders example.com"
exit 1
fi
if grep -Eqi 'change_?me' rendered.yaml; then
echo "::error::$values renders a CHANGE_ME placeholder"
exit 1
fi
echo "::endgroup::"
done
- name: Check the guards
run: |
set -euo pipefail
expect_failure() {
local message=$1
shift
if output=$(helm template fluxer "$CHART" "$@" 2>&1); then
echo "::error::rendered with $*"
exit 1
fi
if ! grep -qF "$message" <<<"$output"; then
echo "::error::expected \"$message\" for $*, got: $output"
exit 1
fi
}
d=domain=chat.example.test
expect_failure "domain must be set"
expect_failure "is a placeholder" --set $d --set secrets.values.FLUXER_SUDO_MODE_SECRET=CHANGE_ME
expect_failure "is a placeholder" --set $d --set-string 'secrets.extra.FLUXER_STRIPE_SECRET_KEY=<change-me>'
expect_failure "at least 32 characters" --set $d --set secrets.values.FLUXER_GATEWAY_RPC_AUTH_TOKEN=short
expect_failure "letters and digits only" --set $d --set-string 'secrets.values.FLUXER_VALKEY_PASSWORD=0123456789abcdef0123456789abcd:@'
expect_failure "worker.replicas must be 1" --set $d --set worker.replicas=2
expect_failure "at least 32 bytes" --set $d --set secrets.values.FLUXER_MEDIA_PROXY_UPLOAD_RELAY_SECRET_BASE64=c2hvcnQ=
expect_failure "set postgres.external.host" --set $d --set postgres.enabled=false
expect_failure "set together" --set $d --set secrets.values.FLUXER_VAPID_PUBLIC_KEY=abc
expect_failure "is a secret and would be stored in plain text" --set $d --set env.FLUXER_EMAIL_SMTP_PASSWORD=not-a-real-password
expect_failure "is a secret and would be stored in plain text" --set $d --set api.env.FLUXER_STRIPE_SECRET_KEY=not-a-real-key
expect_failure "holds a password" --set $d --set-string 'env.FLUXER_SEARCH_URL=https://user:[email protected]'
expect_failure "Set livekit.nodeCIDRs" --set $d --set networkPolicy.ingressNamespaceSelector.matchLabels.name=ingress
expect_failure "set livekit.external.publicUrl" --set $d --set livekit.enabled=false --set livekit.external.url=http://livekit.voice.svc:7880
expect_failure "reserve a public address" --set $d --set livekit.expose=loadBalancer
- name: Check that a chart version bump restarts nothing
run: |
set -euo pipefail
cp -R "$CHART" "$RUNNER_TEMP/bumped"
sed -e 's/^version: .*/version: 99.0.0/' -e 's/^appVersion: .*/appVersion: "bumped"/' "$CHART/Chart.yaml" > "$RUNNER_TEMP/bumped/Chart.yaml"
helm template fluxer "$CHART" -n fluxer -f "$CHART/ci/existing-secret-values.yaml" > before.yaml
helm template fluxer "$RUNNER_TEMP/bumped" -n fluxer -f "$CHART/ci/existing-secret-values.yaml" > after.yaml
python3 - before.yaml after.yaml <<'PY'
import sys, yaml
def pods(path):
out = {}
for doc in yaml.safe_load_all(open(path)):
if doc and doc.get('kind') in ('Deployment', 'StatefulSet'):
spec = doc['spec']
out[f"{doc['kind']}/{doc['metadata']['name']}"] = (spec['template'], spec['selector'], spec.get('volumeClaimTemplates'))
return out
before, after = pods(sys.argv[1]), pods(sys.argv[2])
if len(before) < 20:
sys.exit(f'only {len(before)} workloads rendered')
changed = sorted(name for name in before.keys() | after.keys() if before.get(name) != after.get(name))
if changed:
sys.exit('a chart version bump changes the pod template of: ' + ', '.join(changed))
print(f'{len(before)} workloads keep their pod template across a chart version bump')
PY
- name: Check the default resource requests
run: |
set -euo pipefail
helm template fluxer "$CHART" -n fluxer -f "$CHART/ci/default-values.yaml" > rendered.yaml
python3 - rendered.yaml <<'PY'
import sys, yaml
def cpu(v):
v = str(v)
return float(v[:-1]) if v.endswith('m') else float(v) * 1000
units = {'Ki': 2**10, 'Mi': 2**20, 'Gi': 2**30}
def mem(v):
v = str(v)
for unit, factor in units.items():
if v.endswith(unit):
return float(v[:-len(unit)]) * factor
return float(v)
total_cpu = total_mem = 0
for doc in yaml.safe_load_all(open(sys.argv[1])):
if not doc or doc.get('kind') not in ('Deployment', 'StatefulSet'):
continue
replicas = doc['spec'].get('replicas', 1)
for container in doc['spec']['template']['spec']['containers']:
requests = (container.get('resources') or {}).get('requests') or {}
if 'cpu' not in requests or 'memory' not in requests:
sys.exit(f"{doc['kind']}/{doc['metadata']['name']} has a container without cpu and memory requests")
total_cpu += replicas * cpu(requests['cpu'])
total_mem += replicas * mem(requests['memory'])
print(f"requests: {total_cpu:.0f}m cpu, {total_mem / 2**30:.2f}Gi memory")
if total_cpu > 1200 or total_mem > 4.5 * 2**30:
sys.exit('the default install requests more than 1200m cpu or 4.5Gi memory')
PY
+2
View File
@@ -61,6 +61,8 @@ jobs:
--step install_dependencies
- name: Refresh source catalogs
env:
OPENROUTER_API_KEY: ${{ secrets.OPENROUTER_API_KEY }}
run: pnpm i18n:source-sync
- name: Format generated catalogs
+1 -1
View File
@@ -67,7 +67,7 @@ jobs:
--step install_dependencies
- name: Compile translated catalogs
run: pnpm i18n:weblate-compile
run: pnpm i18n:compile
- name: Format generated catalogs
run: pnpm biome format --write package.json packages/i18n/package.json packages/i18n/scripts/SyncStaticI18nCatalogs.ts packages/errors/src/i18n fluxer_api/pkgs/email/src/email_i18n fluxer_api/src/api/content_i18n
+30 -124
View File
@@ -15,7 +15,6 @@ concurrency:
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
env:
GHCR_REGISTRY: ghcr.io/${{ github.repository_owner }}
CARGO_PROFILE_DEV_DEBUG: none
CARGO_PROFILE_TEST_DEBUG: none
CARGO_INCREMENTAL: '0'
@@ -36,27 +35,10 @@ jobs:
toolchain: "1.98.1"
targets: wasm32-unknown-unknown
- name: Restore ci helper
id: ci-helper
uses: actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9
- name: Set up ci helper
uses: ./.github/actions/ci-helper
with:
path: target/debug/fluxer-ci
key: >-
fluxer-ci-bin-${{ runner.os }}-1.98.1-${{ hashFiles('Cargo.lock', 'Cargo.toml',
'tools/ci/Cargo.toml', 'tools/ci/src/**', 'tools/ci/templates/**') }}
- name: Build ci helper
if: steps.ci-helper.outputs.cache-hit != 'true'
run: cargo build --locked --package fluxer-ci
- name: Save ci helper
if: github.ref == 'refs/heads/main' && steps.ci-helper.outputs.cache-hit != 'true'
uses: actions/cache/save@55cc8345863c7cc4c66a329aec7e433d2d1c52a9
with:
path: target/debug/fluxer-ci
key: >-
fluxer-ci-bin-${{ runner.os }}-1.98.1-${{ hashFiles('Cargo.lock', 'Cargo.toml',
'tools/ci/Cargo.toml', 'tools/ci/src/**', 'tools/ci/templates/**') }}
save: 'true'
- name: Install pnpm
uses: pnpm/action-setup@ea17c68df8912ef543352723c149a84f56e3d413
@@ -91,18 +73,8 @@ jobs:
toolchain: "1.98.1"
targets: wasm32-unknown-unknown
- name: Restore ci helper
id: ci-helper
uses: actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9
with:
path: target/debug/fluxer-ci
key: >-
fluxer-ci-bin-${{ runner.os }}-1.98.1-${{ hashFiles('Cargo.lock', 'Cargo.toml',
'tools/ci/Cargo.toml', 'tools/ci/src/**', 'tools/ci/templates/**') }}
- name: Build ci helper
if: steps.ci-helper.outputs.cache-hit != 'true'
run: cargo build --locked --package fluxer-ci
- name: Set up ci helper
uses: ./.github/actions/ci-helper
- name: Install pnpm
uses: pnpm/action-setup@ea17c68df8912ef543352723c149a84f56e3d413
@@ -119,22 +91,9 @@ jobs:
- name: Restore fluxer_app wasm artifacts
id: app-wasm
uses: actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9
uses: ./.github/actions/app-wasm-cache
with:
path: |
fluxer_app/pkgs/libfluxcore
fluxer_app/pkgs/libfluxwebp
fluxer_app/src/features/messaging/utils/markdown/parser/MarkdownParserWasmBytes.ts
key: >-
app-wasm-${{ runner.os }}-1.98.1-${{ hashFiles('Cargo.lock', 'tools/ci/src/app_wasm.rs',
'tools/ci/templates/libfluxcore_wrapper.js', 'tools/ci/templates/libfluxcore_wrapper.d.ts',
'fluxer_app/rust/libfluxcore/Cargo.toml', 'fluxer_app/rust/libfluxcore/Cargo.lock',
'fluxer_app/rust/libfluxcore/.cargo/config.toml', 'fluxer_app/rust/libfluxcore/src/**',
'fluxer_app/rust/libfluxwebp/Cargo.toml', 'fluxer_app/rust/libfluxwebp/Cargo.lock',
'fluxer_app/rust/libfluxwebp/src/**', 'fluxer_app/rust/libfluxwebp/shim/**',
'fluxer_app/rust/libfluxwebp/simd/**',
'packages/markdown_parser/rust/Cargo.toml', 'packages/markdown_parser/rust/.cargo/config.toml',
'packages/markdown_parser/rust/src/**') }}
mode: restore
- name: Run tests
run: |
@@ -142,22 +101,9 @@ jobs:
- name: Save fluxer_app wasm artifacts
if: always() && github.ref == 'refs/heads/main' && steps.app-wasm.outputs.cache-hit != 'true'
uses: actions/cache/save@55cc8345863c7cc4c66a329aec7e433d2d1c52a9
uses: ./.github/actions/app-wasm-cache
with:
path: |
fluxer_app/pkgs/libfluxcore
fluxer_app/pkgs/libfluxwebp
fluxer_app/src/features/messaging/utils/markdown/parser/MarkdownParserWasmBytes.ts
key: >-
app-wasm-${{ runner.os }}-1.98.1-${{ hashFiles('Cargo.lock', 'tools/ci/src/app_wasm.rs',
'tools/ci/templates/libfluxcore_wrapper.js', 'tools/ci/templates/libfluxcore_wrapper.d.ts',
'fluxer_app/rust/libfluxcore/Cargo.toml', 'fluxer_app/rust/libfluxcore/Cargo.lock',
'fluxer_app/rust/libfluxcore/.cargo/config.toml', 'fluxer_app/rust/libfluxcore/src/**',
'fluxer_app/rust/libfluxwebp/Cargo.toml', 'fluxer_app/rust/libfluxwebp/Cargo.lock',
'fluxer_app/rust/libfluxwebp/src/**', 'fluxer_app/rust/libfluxwebp/shim/**',
'fluxer_app/rust/libfluxwebp/simd/**',
'packages/markdown_parser/rust/Cargo.toml', 'packages/markdown_parser/rust/.cargo/config.toml',
'packages/markdown_parser/rust/src/**') }}
mode: save
rust:
runs-on: blacksmith-4vcpu-ubuntu-2404
@@ -171,6 +117,7 @@ jobs:
with:
toolchain: "1.98.1"
components: clippy, rustfmt
targets: x86_64-pc-windows-msvc
- name: Install pnpm
uses: pnpm/action-setup@ea17c68df8912ef543352723c149a84f56e3d413
@@ -262,7 +209,7 @@ jobs:
- name: Clippy (warnings as errors)
run: cargo clippy --workspace --all-targets --all-features --locked -- -D warnings
- name: Clippy (desktop native workspaces on Linux, warnings as errors)
- name: Clippy (desktop native workspaces on Linux and Windows backends, warnings as errors)
run: tools/ci/check-desktop-native-workspaces.sh clippy
- name: Verify the source-built ffmpeg CLI is on PATH
@@ -277,6 +224,9 @@ jobs:
FLUXER_REQUIRE_MEDIA_FIXTURES: "1"
run: cargo test --workspace --all-features --locked
- name: Run tests (libfluxcore)
run: cargo test --locked --manifest-path fluxer_app/rust/libfluxcore/Cargo.toml
- name: Run desktop native workspace tests on Linux
run: tools/ci/check-desktop-native-workspaces.sh test
@@ -302,18 +252,8 @@ jobs:
fluxer_gateway/native/push_markdown_plaintext_nif -> target
save-if: ${{ github.ref == 'refs/heads/main' }}
- name: Restore ci helper
id: ci-helper
uses: actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9
with:
path: target/debug/fluxer-ci
key: >-
fluxer-ci-bin-${{ runner.os }}-1.98.1-${{ hashFiles('Cargo.lock', 'Cargo.toml',
'tools/ci/Cargo.toml', 'tools/ci/src/**', 'tools/ci/templates/**') }}
- name: Build ci helper
if: steps.ci-helper.outputs.cache-hit != 'true'
run: cargo build --locked --package fluxer-ci
- name: Set up ci helper
uses: ./.github/actions/ci-helper
- name: Set up Erlang
uses: erlef/setup-beam@54075bcc5e249e4758d363f27d099f55d843f124
@@ -383,18 +323,8 @@ jobs:
toolchain: "1.98.1"
targets: wasm32-unknown-unknown
- name: Restore ci helper
id: ci-helper
uses: actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9
with:
path: target/debug/fluxer-ci
key: >-
fluxer-ci-bin-${{ runner.os }}-1.98.1-${{ hashFiles('Cargo.lock', 'Cargo.toml',
'tools/ci/Cargo.toml', 'tools/ci/src/**', 'tools/ci/templates/**') }}
- name: Build ci helper
if: steps.ci-helper.outputs.cache-hit != 'true'
run: cargo build --locked --package fluxer-ci
- name: Set up ci helper
uses: ./.github/actions/ci-helper
- name: Install pnpm
uses: pnpm/action-setup@ea17c68df8912ef543352723c149a84f56e3d413
@@ -411,22 +341,9 @@ jobs:
- name: Restore fluxer_app wasm artifacts
id: app-wasm
uses: actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9
uses: ./.github/actions/app-wasm-cache
with:
path: |
fluxer_app/pkgs/libfluxcore
fluxer_app/pkgs/libfluxwebp
fluxer_app/src/features/messaging/utils/markdown/parser/MarkdownParserWasmBytes.ts
key: >-
app-wasm-${{ runner.os }}-1.98.1-${{ hashFiles('Cargo.lock', 'tools/ci/src/app_wasm.rs',
'tools/ci/templates/libfluxcore_wrapper.js', 'tools/ci/templates/libfluxcore_wrapper.d.ts',
'fluxer_app/rust/libfluxcore/Cargo.toml', 'fluxer_app/rust/libfluxcore/Cargo.lock',
'fluxer_app/rust/libfluxcore/.cargo/config.toml', 'fluxer_app/rust/libfluxcore/src/**',
'fluxer_app/rust/libfluxwebp/Cargo.toml', 'fluxer_app/rust/libfluxwebp/Cargo.lock',
'fluxer_app/rust/libfluxwebp/src/**', 'fluxer_app/rust/libfluxwebp/shim/**',
'fluxer_app/rust/libfluxwebp/simd/**',
'packages/markdown_parser/rust/Cargo.toml', 'packages/markdown_parser/rust/.cargo/config.toml',
'packages/markdown_parser/rust/src/**') }}
mode: restore
- name: Run knip
run: |
@@ -434,22 +351,9 @@ jobs:
- name: Save fluxer_app wasm artifacts
if: always() && github.ref == 'refs/heads/main' && steps.app-wasm.outputs.cache-hit != 'true'
uses: actions/cache/save@55cc8345863c7cc4c66a329aec7e433d2d1c52a9
uses: ./.github/actions/app-wasm-cache
with:
path: |
fluxer_app/pkgs/libfluxcore
fluxer_app/pkgs/libfluxwebp
fluxer_app/src/features/messaging/utils/markdown/parser/MarkdownParserWasmBytes.ts
key: >-
app-wasm-${{ runner.os }}-1.98.1-${{ hashFiles('Cargo.lock', 'tools/ci/src/app_wasm.rs',
'tools/ci/templates/libfluxcore_wrapper.js', 'tools/ci/templates/libfluxcore_wrapper.d.ts',
'fluxer_app/rust/libfluxcore/Cargo.toml', 'fluxer_app/rust/libfluxcore/Cargo.lock',
'fluxer_app/rust/libfluxcore/.cargo/config.toml', 'fluxer_app/rust/libfluxcore/src/**',
'fluxer_app/rust/libfluxwebp/Cargo.toml', 'fluxer_app/rust/libfluxwebp/Cargo.lock',
'fluxer_app/rust/libfluxwebp/src/**', 'fluxer_app/rust/libfluxwebp/shim/**',
'fluxer_app/rust/libfluxwebp/simd/**',
'packages/markdown_parser/rust/Cargo.toml', 'packages/markdown_parser/rust/.cargo/config.toml',
'packages/markdown_parser/rust/src/**') }}
mode: save
lint:
runs-on: ubuntu-24.04
@@ -476,6 +380,9 @@ jobs:
- name: Lint JSX for browser-translation safety
run: pnpm exec eslint . --max-warnings 0
- name: Check data-flx attributes
run: pnpm --filter fluxer_app theming:data-flx:check
i18n:
runs-on: ubuntu-24.04
timeout-minutes: 25
@@ -498,15 +405,14 @@ jobs:
- name: Compile locale catalogs
run: pnpm i18n:compile
- name: Check drift of compiled locale modules
- name: Check drift of synced catalogs
run: |
if ! git diff --exit-code -- \
packages/errors/src/i18n/locales \
packages/errors/src/i18n/ErrorI18nTypes.generated.ts \
fluxer_api/pkgs/email/src/email_i18n/locales \
packages/errors/src/i18n/weblate \
fluxer_api/pkgs/email/src/email_i18n/weblate \
fluxer_api/pkgs/email/src/email_i18n/EmailI18nTypes.generated.ts \
fluxer_api/src/api/content_i18n/locales; then
echo "::error::Compiled locale modules are outdated. Run 'pnpm i18n:compile' and commit the result. Translations belong in the weblate/ catalogs, not in the generated locales/ modules."
fluxer_api/src/api/content_i18n/weblate; then
echo "::error::Static catalogs are out of sync with their sources. Run 'pnpm i18n:compile' and commit the result."
exit 1
fi
+7 -5
View File
@@ -28,22 +28,24 @@
/fluxer_app/pkgs/libfluxcore/
/fluxer_app/pkgs/libfluxwebp/
/fluxer_app/src/features/i18n/locales/*/messages.mjs
**/auto-i18n-reviewed-unchanged.json.lock
**/weblate/locales/auto-i18n-reviewed-unchanged.json
/fluxer_app/src/features/messaging/utils/markdown/parser/MarkdownParserWasmBytes.ts
/fluxer_app/src/features/theme/styles/generated/
/fluxer_app/src/features/ui/components/SVGMasks.tsx
/fluxer_app/src/features/ui/constants/AvatarStatusGeometry.ts
/fluxer_gateway/priv/
/fluxer_static/desktop/spellcheck/dictionaries/*/*
!/fluxer_static/desktop/spellcheck/dictionaries/[email protected]/LICENSE
!/fluxer_static/desktop/spellcheck/dictionaries/[email protected]/LICENSE
/fluxer_desktop/native/rust/fuzz/artifacts/
/fluxer_desktop/native/rust/fuzz/corpus/
/packages/markdown_parser/rust/fuzz/artifacts/
/packages/markdown_parser/rust/fuzz/corpus/
/fluxer_media_proxy/.benchmark-cache/
/fluxer_media_proxy/bench-results/
/app-dist-output/
/artifacts/
/desktop-shared-assets/
/desktop-modules/
/s3_payload/
/upload_staging/
Generated
+60 -253
View File
@@ -49,9 +49,9 @@ checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923"
[[package]]
name = "ammonia"
version = "4.2.0"
version = "4.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f7ed3ac4252be4d3e36fb589ed5659356842587b21a31596a9dc89d3b2f5e2a"
checksum = "061e83b03c2681c18a6787d956e355c74e0b98ba7ba3d69b0822ade1e6f1d716"
dependencies = [
"cssparser 0.38.0",
"html5ever 0.40.1",
@@ -863,24 +863,12 @@ version = "3.20.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649"
[[package]]
name = "bytemuck"
version = "1.25.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "95832e849adfb21180ccb6826a99da14e5d266ae5c2e668e1602cf234f153797"
[[package]]
name = "byteorder"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
[[package]]
name = "byteorder-lite"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495"
[[package]]
name = "bytes"
version = "1.12.1"
@@ -1048,12 +1036,6 @@ version = "0.5.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0c9ea0ac24bc397ab3c98583a3c9ba74fa56b09a4449bbe172b9b1ddb016027a"
[[package]]
name = "color_quant"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b"
[[package]]
name = "colorchoice"
version = "1.0.5"
@@ -1100,16 +1082,6 @@ version = "0.10.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a6ef517f0926dd24a1582492c791b6a4818a4d94e789a334894aa15b0d12f55c"
[[package]]
name = "cookie"
version = "0.18.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1a373e3602691c3cdea496d2f0ee5935151e6168fe87739483c463db1b2f2f87"
dependencies = [
"time",
"version_check",
]
[[package]]
name = "core-foundation"
version = "0.10.1"
@@ -1185,8 +1157,6 @@ dependencies = [
"num-traits",
"oorandom",
"page_size",
"plotters",
"rayon",
"regex",
"serde",
"serde_json",
@@ -1213,16 +1183,6 @@ dependencies = [
"crossbeam-utils",
]
[[package]]
name = "crossbeam-deque"
version = "0.8.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "622f3fc73690be383c7214310406f28a90e6edeadc3cea882f9d71e495b9711a"
dependencies = [
"crossbeam-epoch",
"crossbeam-utils",
]
[[package]]
name = "crossbeam-epoch"
version = "0.9.21"
@@ -1685,15 +1645,6 @@ version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "da7c62ceae207dd37ea5b845da6a0696c799f85e97da1ab5b7910be3c1c80223"
[[package]]
name = "fdeflate"
version = "0.3.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e6853b52649d4ac5c0bd02320cddc5ba956bdb407c4b75a2c6b75bf51500f8c"
dependencies = [
"simd-adler32",
]
[[package]]
name = "ff"
version = "0.13.1"
@@ -1710,6 +1661,16 @@ version = "0.2.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d"
[[package]]
name = "filetime"
version = "0.2.29"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c287a33c7f0a620c38e641e7f60827713987b3c0f26e8ddc9462cc69cf75759"
dependencies = [
"cfg-if",
"libc",
]
[[package]]
name = "find-msvc-tools"
version = "0.1.12"
@@ -1729,7 +1690,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6e634e2e0ebac1ee034020da1ca582e17ffe4e0f5e985823721e168928136dcb"
dependencies = [
"crc32fast",
"miniz_oxide 0.9.1",
"miniz_oxide",
"zlib-rs",
]
@@ -1741,7 +1702,7 @@ dependencies = [
"aws-config",
"aws-sdk-s3",
"base64 0.23.1",
"bytes",
"brotli",
"chrono",
"clap",
"hex",
@@ -1750,6 +1711,7 @@ dependencies = [
"serde",
"serde_json",
"sha2 0.11.0",
"tar",
"tempfile",
"tokio",
"walkdir",
@@ -1762,12 +1724,11 @@ version = "0.1.0"
dependencies = [
"anyhow",
"axum",
"chrono",
"clap",
"fluxer_common",
"hmac 0.13.0",
"hex",
"hyper",
"hyper-util",
"image",
"libc",
"regex",
"reqwest",
@@ -1789,12 +1750,10 @@ dependencies = [
"fluxer-svc",
"fluxer_common",
"futures",
"hmac 0.13.0",
"moka",
"reqwest",
"serde",
"serde_json",
"sha2 0.11.0",
"tokio",
"tracing",
"url",
@@ -1811,7 +1770,6 @@ dependencies = [
"reqwest",
"serde",
"serde_json",
"tempfile",
]
[[package]]
@@ -1824,7 +1782,6 @@ dependencies = [
"bytes",
"cc",
"clap",
"criterion",
"flate2",
"fluxer_common",
"futures-util",
@@ -1835,6 +1792,7 @@ dependencies = [
"http-body-util",
"hyper",
"libc",
"memmap2",
"moka",
"parking_lot",
"percent-encoding",
@@ -1851,7 +1809,6 @@ dependencies = [
"tempfile",
"thiserror",
"tokio",
"tokio-util",
"tower",
"tower-http 0.7.1",
"tracing",
@@ -1867,20 +1824,16 @@ dependencies = [
"anyhow",
"base64 0.23.1",
"chrono",
"criterion",
"fluxer-svc",
"fluxer_common",
"fluxer_markdown_parser",
"futures",
"hmac 0.13.0",
"linkify",
"mime_guess",
"moka",
"rmp-serde",
"scylla",
"serde",
"serde_json",
"sha2 0.11.0",
"tokio",
"tracing",
"url",
@@ -1895,6 +1848,7 @@ dependencies = [
"base64 0.23.1",
"clap",
"fluxer-svc",
"fluxer_common",
"futures",
"hmac 0.13.0",
"p256",
@@ -1935,15 +1889,14 @@ dependencies = [
"bytes",
"chrono",
"deadpool-postgres",
"fluxer_common",
"futures",
"libc",
"moka",
"rmp-serde",
"rustls",
"scylla",
"serde",
"serde_json",
"thiserror",
"tokio",
"tokio-postgres",
"tokio-postgres-rustls",
@@ -1962,7 +1915,6 @@ dependencies = [
"entities",
"fluxer-svc",
"fluxer_common",
"hmac 0.13.0",
"infer",
"moka",
"regex",
@@ -1970,7 +1922,6 @@ dependencies = [
"scraper",
"serde",
"serde_json",
"sha2 0.11.0",
"tokio",
"tracing",
"url",
@@ -1986,11 +1937,12 @@ dependencies = [
"fluxer-svc",
"fluxer_common",
"futures",
"hmac 0.13.0",
"moka",
"rmp-serde",
"scylla",
"serde",
"serde_json",
"sha2 0.11.0",
"tokio",
"tracing",
]
@@ -2003,7 +1955,6 @@ dependencies = [
"axum",
"base64 0.23.1",
"chrono",
"cookie",
"fluxer_common",
"hmac 0.13.0",
"maud",
@@ -2040,7 +1991,6 @@ dependencies = [
"hex",
"rand 0.10.2",
"reqwest",
"serde",
"serde_json",
"sha2 0.11.0",
"tokio",
@@ -2056,24 +2006,21 @@ name = "fluxer_common"
version = "0.1.0"
dependencies = [
"anyhow",
"aws-credential-types",
"aws-sigv4",
"axum",
"base64 0.23.1",
"hex",
"hmac 0.13.0",
"maxminddb",
"moka",
"http 1.5.0",
"libc",
"reqwest",
"serde",
"serde_json",
"sha2 0.11.0",
"tempfile",
"thiserror",
"time",
"tokio",
"tracing",
"tracing-subscriber",
"url",
"urlencoding",
]
[[package]]
@@ -2260,16 +2207,6 @@ dependencies = [
"wasm-bindgen",
]
[[package]]
name = "gif"
version = "0.14.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ee8cfcc411d9adbbaba82fb72661cc1bcca13e8bba98b364e62b2dba8f960159"
dependencies = [
"color_quant",
"weezl",
]
[[package]]
name = "group"
version = "0.13.0"
@@ -2659,34 +2596,6 @@ dependencies = [
"icu_properties",
]
[[package]]
name = "image"
version = "0.25.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "85ab80394333c02fe689eaf900ab500fbd0c2213da414687ebf995a65d5a6104"
dependencies = [
"bytemuck",
"byteorder-lite",
"color_quant",
"gif",
"image-webp",
"moxcms",
"num-traits",
"png",
"zune-core",
"zune-jpeg",
]
[[package]]
name = "image-webp"
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "525e9ff3e1a4be2fbea1fdf0e98686a6d98b4d8f937e1bf7402245af1909e8c3"
dependencies = [
"byteorder-lite",
"quick-error 2.0.1",
]
[[package]]
name = "indexmap"
version = "2.14.2"
@@ -2714,12 +2623,6 @@ version = "2.12.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "791930b43c0d5973160d90a8f3894509f2b273430f5c5c73b668636d0287c5c0"
[[package]]
name = "ipnetwork"
version = "0.21.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf370abdafd54d13e54a620e8c3e1145f28e46cc9d704bc6d94414559df41763"
[[package]]
name = "is_terminal_polyfill"
version = "1.70.2"
@@ -2968,18 +2871,6 @@ dependencies = [
"syn 2.0.119",
]
[[package]]
name = "maxminddb"
version = "0.32.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b43d2458d977b610b04a3858e6504f06133663d303a1a0606601b530bfc9bc6"
dependencies = [
"ipnetwork",
"memchr",
"serde",
"thiserror",
]
[[package]]
name = "md-5"
version = "0.11.0"
@@ -2996,6 +2887,15 @@ version = "2.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf8baf1c55e62ffcace7a9f06f4bd9cd3f0c4beb022d3b367256b91b87513d98"
[[package]]
name = "memmap2"
version = "0.9.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d1219ed1b7f229ee7104d281dd01d6802fe28bb6e95d292942c4daacdeb798c0"
dependencies = [
"libc",
]
[[package]]
name = "mime"
version = "0.3.17"
@@ -3012,16 +2912,6 @@ dependencies = [
"unicase",
]
[[package]]
name = "miniz_oxide"
version = "0.8.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316"
dependencies = [
"adler2",
"simd-adler32",
]
[[package]]
name = "miniz_oxide"
version = "0.9.1"
@@ -3063,16 +2953,6 @@ dependencies = [
"uuid",
]
[[package]]
name = "moxcms"
version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bb85c154ba489f01b25c0d36ae69a87e4a1c73a72631fc6c0eb6dde34a73e44b"
dependencies = [
"num-traits",
"pxfm",
]
[[package]]
name = "multiversion"
version = "0.9.0"
@@ -3428,47 +3308,6 @@ version = "0.3.34"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f6b464fbc74e149a392436b17d523f769e057cb6877f6a5c4618bc6f11800548"
[[package]]
name = "plotters"
version = "0.3.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5aeb6f403d7a4911efb1e33402027fc44f29b5bf6def3effcc22d7bb75f2b747"
dependencies = [
"num-traits",
"plotters-backend",
"plotters-svg",
"wasm-bindgen",
"web-sys",
]
[[package]]
name = "plotters-backend"
version = "0.3.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "df42e13c12958a16b3f7f4386b9ab1f3e7933914ecea48da7139435263a4172a"
[[package]]
name = "plotters-svg"
version = "0.3.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "51bae2ac328883f7acdfea3d66a7c35751187f870bc81f94563733a154d7a670"
dependencies = [
"plotters-backend",
]
[[package]]
name = "png"
version = "0.18.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "60769b8b31b2a9f263dae2776c37b1b28ae246943cf719eb6946a1db05128a61"
dependencies = [
"bitflags",
"crc32fast",
"fdeflate",
"flate2",
"miniz_oxide 0.8.9",
]
[[package]]
name = "portable-atomic"
version = "1.15.0"
@@ -3641,24 +3480,12 @@ dependencies = [
"unarray",
]
[[package]]
name = "pxfm"
version = "0.1.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d55d956fa96f5ec02be2e13af0e20391a5aa83d6a074e3ad368959d0fab299ea"
[[package]]
name = "quick-error"
version = "1.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0"
[[package]]
name = "quick-error"
version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3"
[[package]]
name = "quinn"
version = "0.11.12"
@@ -3850,26 +3677,6 @@ dependencies = [
"rand_core 0.9.5",
]
[[package]]
name = "rayon"
version = "1.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fb39b166781f92d482534ef4b4b1b2568f42613b53e5b6c160e24cfbfa30926d"
dependencies = [
"either",
"rayon-core",
]
[[package]]
name = "rayon-core"
version = "1.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "22e18b0f0062d30d4230b2e85ff77fdfe4326feb054b9783a3460d8435c8ab91"
dependencies = [
"crossbeam-deque",
"crossbeam-utils",
]
[[package]]
name = "redox_syscall"
version = "0.5.18"
@@ -4083,9 +3890,9 @@ dependencies = [
[[package]]
name = "rustls"
version = "0.23.45"
version = "0.23.46"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0d41d731c7d2f962d1ccc364cec258de3c0e93b38c2fb3ba97ac74513048d634"
checksum = "48e13bd8c0e9365c43cfa5c9e8f9ad49d3c8444926c9aac819e0e4dc503c8fdf"
dependencies = [
"aws-lc-rs",
"log",
@@ -4171,7 +3978,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cc6bf79ff24e648f6da1f8d1f011e9cac26491b619e6b9280f2b47f1774e6ee2"
dependencies = [
"fnv",
"quick-error 1.2.3",
"quick-error",
"tempfile",
"wait-timeout",
]
@@ -4788,6 +4595,17 @@ version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7b2093cf4c8eb1e67749a6762251bc9cd836b6fc171623bd0a9d324d37af2417"
[[package]]
name = "tar"
version = "0.4.46"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f6221d9a6003c78398e3b239969f352578258df48c8eb051caadae0015bc840"
dependencies = [
"filetime",
"libc",
"xattr",
]
[[package]]
name = "tempfile"
version = "3.27.0"
@@ -4795,7 +4613,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd"
dependencies = [
"fastrand",
"getrandom 0.4.3",
"getrandom 0.3.4",
"once_cell",
"rustix",
"windows-sys 0.61.2",
@@ -5592,12 +5410,6 @@ dependencies = [
"rustls-pki-types",
]
[[package]]
name = "weezl"
version = "0.1.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a28ac98ddc8b9274cb41bb4d9d4d5c425b6020c50c46f25559911905610b4a88"
[[package]]
name = "whoami"
version = "2.1.3"
@@ -5816,6 +5628,16 @@ dependencies = [
"tls_codec",
]
[[package]]
name = "xattr"
version = "1.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32e45ad4206f6d2479085147f02bc2ef834ac85886624a23575ae137c8aa8156"
dependencies = [
"libc",
"rustix",
]
[[package]]
name = "xmlparser"
version = "0.13.6"
@@ -5976,18 +5798,3 @@ dependencies = [
"log",
"simd-adler32",
]
[[package]]
name = "zune-core"
version = "0.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d56377fd46368984a170bc5aac5567e52ca5da874caa60bea39fcbca78fb658b"
[[package]]
name = "zune-jpeg"
version = "0.5.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "27bc9d5b815bc103f142aa054f561d9187d191692ec7c2d1e2b4737f8dbd7296"
dependencies = [
"zune-core",
]
+9 -7
View File
@@ -23,10 +23,13 @@
# Fluxer
> [!IMPORTANT]
> Bug reports and feature requests have moved to [feedback.fluxer.com](https://feedback.fluxer.com). Sign in with your Fluxer account to post, vote and follow updates. GitHub Issues and Discussions are closed. Report security vulnerabilities privately through [fluxer.app/security](https://fluxer.app/security).
Fluxer is a free and open source instant messaging and VoIP chat app built for friends, groups, and communities.
<p align="center">
<img src="./fluxer_static/marketing/screenshots/desktop-readme-1920w.png" alt="Fluxer running side by side on a desktop monitor and a phone" width="640">
<img src="https://fluxer.app/static/img/screenshots-desktop-readme-1920w.70cb6ce340007e0a.png" alt="Fluxer running side by side on a desktop monitor and a phone" width="640">
</p>
## Download
@@ -143,14 +146,13 @@ Full setup notes, including canary, are in the [Linux repositories documentation
The source is licensed under the [AGPL-3.0-or-later](./LICENSE) license.
Fluxer branding, icons, default avatars, badge artwork, screenshots and marketing
imagery are copyright Fluxer, all rights reserved, as set out in
[fluxer_static/LICENSE](./fluxer_static/LICENSE). Third-party material keeps its own
terms, listed in
Fluxer artwork, such as the logo, icons, badges and default avatars, is
licensed under [CC BY-SA 4.0](./fluxer_static/LICENSE). Third-party material
keeps its own terms, listed in
[fluxer_static/THIRD_PARTY_LICENSES.md](./fluxer_static/THIRD_PARTY_LICENSES.md).
Public availability of this repository does not grant trademark, brand, or
endorsement rights.
Use of the Fluxer name and logo is covered by the
[name and marks policy](./.github/GOVERNANCE.md#name-and-marks).
[win-setup-x64]: https://pkgs.fluxer.com/desktop/stable/win32/x64/latest/setup
[win-setup-arm64]: https://pkgs.fluxer.com/desktop/stable/win32/arm64/latest/setup
-4
View File
@@ -163,7 +163,6 @@
"**",
"!**/.git",
"!**/app.css",
"!fluxer_admin/public/static/app.css",
"!**/build",
"fluxer_app/scripts/build",
"!**/dist",
@@ -184,11 +183,8 @@
"!packages/fonts",
"!fluxer_admin/static/htmx.min.js",
"!fluxer_api/src/api/openapi/openapi.json",
"!fluxer_api/pkgs/email/src/email_i18n/locales",
"!fluxer_api/pkgs/email/src/email_i18n/weblate",
"!fluxer_api/src/api/content_i18n/locales",
"!fluxer_api/src/api/content_i18n/weblate",
"!packages/errors/src/i18n/locales",
"!packages/errors/src/i18n/weblate",
"!**/auto-i18n-reviewed-unchanged.json"
],
+2 -4
View File
@@ -89,6 +89,7 @@ FLUXER_ADMIN_OAUTH_REDIRECT_URI=http://localhost:8088/admin/oauth2_callback
FLUXER_SUDO_MODE_SECRET=dev-sudo-secret
FLUXER_CONNECTION_INITIATION_SECRET=dev-connection-initiation-secret
FLUXER_PROFILE_PSEUDONYM_SECRET=fluxer-dev-profile-pseudonym-secret
FLUXER_VAPID_PUBLIC_KEY=BHIbdKs24FdPkOQS7hbeg3adceLS0IqlKsn71ywEe6kbeopeFFiG3lkvJac7BVqkuk7mxwEa555O2FXV3HLt56w
FLUXER_VAPID_PRIVATE_KEY=cs24JvXSxHiqJQgkJNocJFAdzJpPmpfU9xD-fDpn3tw
FLUXER_VAPID_EMAIL=dev@localhost
@@ -103,7 +104,7 @@ FLUXER_EMAIL_SMTP_HOST=mailpit
FLUXER_EMAIL_SMTP_PORT=1025
FLUXER_EMAIL_SMTP_USERNAME=dev
FLUXER_EMAIL_SMTP_PASSWORD=dev
FLUXER_EMAIL_SMTP_SECURE=false
FLUXER_EMAIL_SMTP_TLS_MODE=none
FLUXER_SEARCH_ENGINE=meilisearch
FLUXER_SEARCH_URL=http://meilisearch:7700
FLUXER_SEARCH_API_KEY=fluxer-dev-meilisearch
@@ -111,7 +112,6 @@ FLUXER_SEARCH_USERNAME=
FLUXER_SEARCH_PASSWORD=
FLUXER_SEARCH_TLS_REJECT_UNAUTHORIZED=false
FLUXER_STRIPE_ENABLED=false
FLUXER_NCMEC_ENABLED=false
FLUXER_CLAMAV_ENABLED=false
FLUXER_DISCOVERY_ENABLED=true
FLUXER_SELF_HOSTED=true
@@ -120,8 +120,6 @@ FLUXER_TEST_MODE_ENABLED=false
PUBLIC_BUILD_VERSION=dev
PUBLIC_RELEASE_CHANNEL=canary
PUBLIC_BOOTSTRAP_API_ENDPOINT=/api
PUBLIC_BOOTSTRAP_API_PUBLIC_ENDPOINT=http://localhost:8088/api
FLUXER_MEDIA_PROXY_UPLOAD_RELAY_SECRET_BASE64=Zmx1eGVyLWRldi11cGxvYWQtcmVsYXktc2VjcmV0LTAwMDA=
FLUXER_MEDIA_PROXY_ATTACHMENT_URL_SECRETS_BASE64=Zmx1eGVyLWRldi1hdHRhY2htZW50LXVybC1zZWNyZXQ=
AWS_EC2_METADATA_DISABLED=true
-1
View File
@@ -94,7 +94,6 @@ skip = [
{ crate = "[email protected]", reason = "transitive dependency requires the older HTTP API" },
{ crate = "[email protected]", reason = "transitive dependency requires the older HTTP body API" },
{ crate = "[email protected]", reason = "transitive dependency requires the older release line" },
{ crate = "[email protected]", reason = "transitive dependency requires the older release line" },
{ crate = "[email protected]", reason = "transitive dependency requires the older release line" },
{ crate = "[email protected]", reason = "transitive dependency requires the older release line" },
{ crate = "[email protected]", reason = "transitive dependency requires the older release line" },
-6
View File
@@ -1,6 +0,0 @@
apiVersion: v2
name: fluxer-api
description: Fluxer HTTP API and background job workers
type: application
version: 0.1.0
appVersion: "v1"
@@ -1,244 +0,0 @@
{{- define "fluxer-api.chart" -}}
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}}
{{- end }}
{{- define "fluxer-api.selectorLabels" -}}
app.kubernetes.io/name: {{ .name }}
app.kubernetes.io/instance: {{ .root.Release.Name }}
{{- end }}
{{- define "fluxer-api.labels" -}}
{{ include "fluxer-api.selectorLabels" . }}
app.kubernetes.io/component: {{ .component }}
app.kubernetes.io/part-of: fluxer
app.kubernetes.io/managed-by: {{ .root.Release.Service }}
helm.sh/chart: {{ include "fluxer-api.chart" .root }}
{{- end }}
{{- define "fluxer-api.image" -}}
{{- $g := .root.Values.image | default dict -}}
{{- $i := .w.image | default dict -}}
{{- $repo := $i.repository -}}
{{- if not $repo -}}
{{- $repo = printf "%s/%s" (required "image.registry is required" $g.registry) ($i.name | default "fluxer-api") -}}
{{- end -}}
{{- $tag := required "image.tag is required" ($i.tag | default $g.tag) -}}
{{- if $i.digest -}}
{{- printf "%s:%s@%s" $repo $tag $i.digest | quote -}}
{{- else -}}
{{- printf "%s:%s" $repo $tag | quote -}}
{{- end -}}
{{- end }}
{{- define "fluxer-api.pick" -}}
{{- $v := ternary (get .w .key) (get .root.Values .key) (hasKey .w .key) -}}
{{- if $v }}
{{- toYaml $v }}
{{- end }}
{{- end }}
{{- define "fluxer-api.str" -}}
{{- if and (kindIs "float64" .) (eq . (floor .)) -}}
{{- int64 . | toString | quote -}}
{{- else -}}
{{- toString . | quote -}}
{{- end -}}
{{- end }}
{{- define "fluxer-api.env" -}}
{{- $env := dict -}}
{{- range $k, $val := .root.Values.env | default dict }}
{{- $_ := set $env $k $val }}
{{- end }}
{{- range $k, $val := .w.env | default dict }}
{{- $_ := set $env $k $val }}
{{- end }}
{{- range $k, $val := $env }}
{{- if not (kindIs "invalid" $val) }}
- name: {{ $k }}
value: {{ include "fluxer-api.str" $val }}
{{- end }}
{{- end }}
{{- with .w.buildVersion }}
- name: BUILD_VERSION
value: {{ include "fluxer-api.str" . }}
{{- end }}
{{- with concat (.root.Values.extraEnv | default list) (.w.extraEnv | default list) }}
{{ toYaml . }}
{{- end }}
{{- end }}
{{- define "fluxer-api.topologySpread" -}}
{{- $tscs := ternary .w.topologySpreadConstraints .root.Values.topologySpreadConstraints (hasKey .w "topologySpreadConstraints") -}}
{{- range $tscs }}
{{- $c := deepCopy . }}
{{- if not $c.labelSelector }}
{{- $_ := set $c "labelSelector" (dict "matchLabels" (include "fluxer-api.selectorLabels" $ | fromYaml)) }}
{{- end }}
- {{- toYaml $c | nindent 2 }}
{{- end }}
{{- end }}
{{- define "fluxer-api.pdb" -}}
{{- with .w.pdb }}
---
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: {{ $.name }}-pdb
namespace: {{ $.root.Release.Namespace }}
labels:
{{- include "fluxer-api.labels" $ | nindent 4 }}
spec:
{{- toYaml . | nindent 2 }}
selector:
matchLabels:
{{- include "fluxer-api.selectorLabels" $ | nindent 6 }}
{{- end }}
{{- end }}
{{- define "fluxer-api.hpa" -}}
{{- with .w.hpa }}
---
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: {{ $.name }}
namespace: {{ $.root.Release.Namespace }}
labels:
{{- include "fluxer-api.labels" $ | nindent 4 }}
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: {{ $.name }}
minReplicas: {{ required (printf "%s.hpa.minReplicas is required" $.name) .minReplicas }}
maxReplicas: {{ required (printf "%s.hpa.maxReplicas is required" $.name) .maxReplicas }}
{{- with .targetCPUUtilizationPercentage }}
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: {{ . }}
{{- end }}
{{- with .behavior }}
behavior:
{{- toYaml . | nindent 4 }}
{{- end }}
{{- end }}
{{- end }}
{{- define "fluxer-api.deployment" -}}
{{- $root := .root -}}
{{- $v := $root.Values -}}
{{- $w := .w -}}
{{- $envFrom := concat ($v.envFrom | default list) ($w.envFrom | default list) -}}
{{- $podAnnotations := merge (dict) ($w.podAnnotations | default dict) ($v.podAnnotations | default dict) -}}
{{- $wProbes := $w.probes | default dict -}}
{{- $gProbes := .probes | default dict -}}
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ .name }}
namespace: {{ $root.Release.Namespace }}
labels:
{{- include "fluxer-api.labels" . | nindent 4 }}
spec:
{{- if not $w.hpa }}
replicas: {{ if kindIs "invalid" $w.replicas }}1{{ else }}{{ int $w.replicas }}{{ end }}
{{- end }}
{{- if not (kindIs "invalid" $w.minReadySeconds) }}
minReadySeconds: {{ int $w.minReadySeconds }}
{{- end }}
selector:
matchLabels:
{{- include "fluxer-api.selectorLabels" . | nindent 6 }}
{{- with include "fluxer-api.pick" (dict "root" $root "w" $w "key" "strategy") }}
strategy:
{{- . | nindent 4 }}
{{- end }}
template:
metadata:
labels:
{{- include "fluxer-api.labels" . | nindent 8 }}
{{- with $podAnnotations }}
annotations:
{{- toYaml . | nindent 8 }}
{{- end }}
spec:
{{- with include "fluxer-api.pick" (dict "root" $root "w" $w "key" "imagePullSecrets") }}
imagePullSecrets:
{{- . | nindent 8 }}
{{- end }}
{{- with include "fluxer-api.pick" (dict "root" $root "w" $w "key" "podSecurityContext") }}
securityContext:
{{- . | nindent 8 }}
{{- end }}
{{- if not (kindIs "invalid" $w.terminationGracePeriodSeconds) }}
terminationGracePeriodSeconds: {{ int $w.terminationGracePeriodSeconds }}
{{- end }}
{{- with include "fluxer-api.pick" (dict "root" $root "w" $w "key" "nodeSelector") }}
nodeSelector:
{{- . | nindent 8 }}
{{- end }}
{{- with include "fluxer-api.pick" (dict "root" $root "w" $w "key" "affinity") }}
affinity:
{{- . | nindent 8 }}
{{- end }}
{{- with include "fluxer-api.pick" (dict "root" $root "w" $w "key" "tolerations") }}
tolerations:
{{- . | nindent 8 }}
{{- end }}
{{- with include "fluxer-api.topologySpread" . | trim }}
topologySpreadConstraints:
{{- . | nindent 8 }}
{{- end }}
containers:
- name: {{ .name }}
image: {{ include "fluxer-api.image" . }}
imagePullPolicy: {{ ($w.image | default dict).pullPolicy | default ($v.image | default dict).pullPolicy | default "IfNotPresent" }}
{{- with .command }}
command:
{{- toYaml . | nindent 12 }}
{{- end }}
{{- with include "fluxer-api.env" . | trim }}
env:
{{- . | nindent 12 }}
{{- end }}
{{- with $envFrom }}
envFrom:
{{- toYaml . | nindent 12 }}
{{- end }}
ports:
- name: http
containerPort: 8080
{{- with $w.lifecycle }}
lifecycle:
{{- toYaml . | nindent 12 }}
{{- end }}
{{- range $probe := list "startup" "liveness" "readiness" }}
{{- with hasKey $wProbes $probe | ternary (get $wProbes $probe) (get $gProbes $probe) }}
{{ $probe }}Probe:
{{- toYaml . | nindent 12 }}
{{- end }}
{{- end }}
{{- with $w.resources }}
resources:
{{- toYaml . | nindent 12 }}
{{- end }}
{{- with include "fluxer-api.pick" (dict "root" $root "w" $w "key" "securityContext") }}
securityContext:
{{- . | nindent 12 }}
{{- end }}
{{- with $w.extraVolumeMounts }}
volumeMounts:
{{- toYaml . | nindent 12 }}
{{- end }}
{{- with $w.extraVolumes }}
volumes:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- end }}
-24
View File
@@ -1,24 +0,0 @@
{{- range $name, $w := .Values.api }}
{{- if not (kindIs "invalid" $w) }}
{{- $ctx := dict "root" $ "name" $name "w" $w "component" "api" "probes" ($.Values.probes | default dict) }}
{{ include "fluxer-api.deployment" $ctx }}
{{ include "fluxer-api.hpa" $ctx }}
{{ include "fluxer-api.pdb" $ctx }}
---
apiVersion: v1
kind: Service
metadata:
name: {{ $name }}
namespace: {{ $.Release.Namespace }}
labels:
{{- include "fluxer-api.labels" $ctx | nindent 4 }}
spec:
type: ClusterIP
selector:
{{- include "fluxer-api.selectorLabels" $ctx | nindent 4 }}
ports:
- name: http
port: 8080
targetPort: http
{{- end }}
{{- end }}
@@ -1,8 +0,0 @@
{{- range $name, $w := .Values.workers }}
{{- if not (kindIs "invalid" $w) }}
{{- $ctx := dict "root" $ "name" $name "w" $w "component" "worker" "command" (list "node" "dist/WorkerEntrypoint.js") "probes" (dict) }}
{{ include "fluxer-api.deployment" $ctx }}
{{ include "fluxer-api.hpa" $ctx }}
{{ include "fluxer-api.pdb" $ctx }}
{{- end }}
{{- end }}
-86
View File
@@ -1,86 +0,0 @@
image:
registry: ghcr.io/fluxerapp
tag: v1
pullPolicy: IfNotPresent
imagePullSecrets: []
env:
NODE_ENV: production
FLUXER_ENV: production
FLUXER_PUBLIC_ORIGIN: https://web.example.com
FLUXER_API_ENDPOINT: https://api.example.com
FLUXER_GATEWAY_ENDPOINT: wss://gateway.example.com
FLUXER_MEDIA_ENDPOINT: https://media.example.com
FLUXER_ADMIN_ENDPOINT: https://admin.example.com
FLUXER_MEDIA_PROXY_UPLOAD_RELAY_ENDPOINT: https://uploads.example.com
FLUXER_INTERNAL_MEDIA_PROXY_ENDPOINT: http://media-proxy:8080
FLUXER_KV_URL: redis://valkey:6379/0
FLUXER_NATS_URL: nats://nats:4222
FLUXER_NATS_JETSTREAM_URL: nats://nats:4222
extraEnv: []
envFrom:
- secretRef:
name: fluxer-env
podAnnotations: {}
podSecurityContext:
runAsNonRoot: true
seccompProfile:
type: RuntimeDefault
securityContext:
allowPrivilegeEscalation: false
probes:
startup:
httpGet:
path: /_health
port: http
periodSeconds: 10
failureThreshold: 30
liveness:
httpGet:
path: /_health
port: http
readiness:
httpGet:
path: /_health
port: http
strategy:
type: RollingUpdate
topologySpreadConstraints: []
nodeSelector: {}
tolerations: []
affinity: {}
api:
api:
replicas: 1
resources:
requests:
cpu: 250m
memory: 1Gi
limits:
memory: 2560Mi
workers:
worker:
replicas: 1
env:
FLUXER_API_WORKER_MODE: all_lanes
FLUXER_API_WORKER_ENABLE_CRON_SCHEDULER: "true"
resources:
requests:
cpu: 250m
memory: 1Gi
limits:
memory: 2560Mi
-6
View File
@@ -1,6 +0,0 @@
apiVersion: v2
name: fluxer-gateway
description: A Helm chart for the Fluxer realtime gateway.
type: application
version: 0.1.0
appVersion: "v1"
@@ -1,280 +0,0 @@
{{- define "gateway.selectorLabels" -}}
app.kubernetes.io/name: {{ .name }}
app.kubernetes.io/instance: {{ .root.Release.Name }}
{{- end }}
{{- define "gateway.labels" -}}
{{ include "gateway.selectorLabels" . }}
{{- with .component }}
app.kubernetes.io/component: {{ . }}
{{- end }}
app.kubernetes.io/part-of: fluxer
app.kubernetes.io/managed-by: {{ .root.Release.Service }}
helm.sh/chart: {{ printf "%s-%s" .root.Chart.Name .root.Chart.Version | replace "+" "_" }}
{{- end }}
{{- define "gateway.headlessName" -}}
{{ printf "%s-headless" .Release.Name }}
{{- end }}
{{- define "gateway.pick" -}}
{{- $v := get .root.Values .key }}
{{- if hasKey .w .key }}
{{- $v = get .w .key }}
{{- end }}
{{- with $v }}
{{- toYaml . }}
{{- end }}
{{- end }}
{{- define "gateway.string" -}}
{{- if and (kindIs "float64" .) (eq . (float64 (int64 .))) }}
{{- int64 . | toString }}
{{- else }}
{{- toString . }}
{{- end }}
{{- end }}
{{- define "gateway.envList" -}}
{{- $env := deepCopy (.root.Values.env | default dict) }}
{{- range $k, $v := .w.env | default dict }}
{{- if kindIs "invalid" $v }}
{{- $_ := unset $env $k }}
{{- else }}
{{- $_ := set $env $k $v }}
{{- end }}
{{- end }}
{{- range $k, $v := $env }}
{{- if not (kindIs "invalid" $v) }}
- name: {{ $k }}
value: {{ include "gateway.string" $v | quote }}
{{- end }}
{{- end }}
{{- with concat (.root.Values.extraEnv | default list) (.w.extraEnv | default list) }}
{{ toYaml . }}
{{- end }}
{{- end }}
{{- define "gateway.envFrom" -}}
{{- with concat (.root.Values.envFrom | default list) (.w.envFrom | default list) }}
{{- toYaml . }}
{{- end }}
{{- end }}
{{- define "gateway.podAnnotations" -}}
{{- with merge (deepCopy (.w.podAnnotations | default dict)) (deepCopy (.root.Values.podAnnotations | default dict)) }}
{{- toYaml . }}
{{- end }}
{{- end }}
{{- define "gateway.probes" -}}
{{- $global := .root.Values.probes | default dict }}
{{- $own := .w.probes | default dict }}
{{- range $probe := list "startup" "liveness" "readiness" }}
{{- $p := get $global $probe }}
{{- if hasKey $own $probe }}
{{- $p = get $own $probe }}
{{- end }}
{{- with $p }}
{{ $probe }}Probe:
{{- toYaml . | nindent 2 }}
{{- end }}
{{- end }}
{{- end }}
{{- define "gateway.topologySpreadConstraints" -}}
{{- $out := list }}
{{- range include "gateway.pick" (dict "root" .root "w" .w "key" "topologySpreadConstraints") | fromYamlArray }}
{{- $c := deepCopy . }}
{{- if not (hasKey $c "labelSelector") }}
{{- $_ := set $c "labelSelector" (dict "matchLabels" (include "gateway.selectorLabels" $ | fromYaml)) }}
{{- end }}
{{- $out = append $out $c }}
{{- end }}
{{- with $out }}
{{- toYaml . }}
{{- end }}
{{- end }}
{{- define "gateway.image" -}}
{{- $img := .w.image | default dict }}
{{- $v := .root.Values.image }}
{{- $repo := $img.repository | default (printf "%s/%s" $v.registry ($img.name | default "fluxer-gateway")) }}
{{- $ref := printf "%s:%s" $repo ($img.tag | default $v.tag) }}
{{- with $img.digest }}
{{- $ref = printf "%s@%s" $ref . }}
{{- end }}
{{- $ref | quote }}
{{- end }}
{{- define "gateway.replicas" -}}
{{- if kindIs "invalid" .w.replicas }}1{{ else }}{{ .w.replicas }}{{ end }}
{{- end }}
{{- define "gateway.env" -}}
{{- $root := .root }}
{{- $w := .w -}}
{{- with $w.role }}
- name: FLUXER_GATEWAY_ROLE
value: {{ . | quote }}
{{- end }}
{{- if not (kindIs "invalid" $w.buildVersion) }}
- name: BUILD_VERSION
value: {{ include "gateway.string" $w.buildVersion | quote }}
{{- end }}
- name: POD_IP
valueFrom:
fieldRef:
apiVersion: v1
fieldPath: status.podIP
- name: FLUXER_ERLANG_NODE_NAME
value: fluxer_gateway@$(POD_IP)
- name: FLUXER_ERLANG_DIST_PORT
value: "8081"
- name: FLUXER_GATEWAY_CLUSTER_ENABLED
value: "true"
- name: FLUXER_GATEWAY_CLUSTER_DISCOVERY_DNS_NAME
value: {{ printf "%s.%s.svc.%s" (include "gateway.headlessName" $root) $root.Release.Namespace $root.Values.clusterDomain | quote }}
- name: FLUXER_GATEWAY_CLUSTER_DISCOVERY_NODE_BASENAME
value: fluxer_gateway
{{- include "gateway.envList" . }}
{{- end }}
{{- define "gateway.pod" -}}
{{- $root := .root }}
{{- $w := .w -}}
metadata:
labels:
{{- include "gateway.labels" . | nindent 4 }}
{{- with include "gateway.podAnnotations" . }}
annotations:
{{- . | nindent 4 }}
{{- end }}
spec:
{{- with include "gateway.pick" (dict "root" $root "w" $w "key" "affinity") }}
affinity:
{{- . | nindent 4 }}
{{- end }}
{{- with include "gateway.pick" (dict "root" $root "w" $w "key" "imagePullSecrets") }}
imagePullSecrets:
{{- . | nindent 4 }}
{{- end }}
{{- with include "gateway.pick" (dict "root" $root "w" $w "key" "nodeSelector") }}
nodeSelector:
{{- . | nindent 4 }}
{{- end }}
{{- with include "gateway.pick" (dict "root" $root "w" $w "key" "tolerations") }}
tolerations:
{{- . | nindent 4 }}
{{- end }}
{{- with include "gateway.topologySpreadConstraints" . }}
topologySpreadConstraints:
{{- . | nindent 4 }}
{{- end }}
{{- with include "gateway.pick" (dict "root" $root "w" $w "key" "podSecurityContext") }}
securityContext:
{{- . | nindent 4 }}
{{- end }}
{{- if not (kindIs "invalid" $w.terminationGracePeriodSeconds) }}
terminationGracePeriodSeconds: {{ $w.terminationGracePeriodSeconds }}
{{- end }}
containers:
- name: gateway
image: {{ include "gateway.image" . }}
imagePullPolicy: {{ ($w.image | default dict).pullPolicy | default $root.Values.image.pullPolicy }}
env:
{{- include "gateway.env" . | trim | nindent 6 }}
{{- with include "gateway.envFrom" . }}
envFrom:
{{- . | nindent 6 }}
{{- end }}
{{- with $w.lifecycle }}
lifecycle:
{{- toYaml . | nindent 6 }}
{{- end }}
ports:
- name: http
containerPort: 8080
protocol: TCP
- name: epmd
containerPort: 4369
protocol: TCP
- name: erl-dist
containerPort: 8081
protocol: TCP
{{- with include "gateway.probes" . | trim }}
{{- . | nindent 4 }}
{{- end }}
{{- with $w.resources }}
resources:
{{- toYaml . | nindent 6 }}
{{- end }}
{{- with include "gateway.pick" (dict "root" $root "w" $w "key" "securityContext") }}
securityContext:
{{- . | nindent 6 }}
{{- end }}
{{- with $w.extraVolumeMounts }}
volumeMounts:
{{- toYaml . | nindent 6 }}
{{- end }}
{{- with $w.extraVolumes }}
volumes:
{{- toYaml . | nindent 4 }}
{{- end }}
{{- end }}
{{- define "gateway.pdb" -}}
{{- with .w.pdb }}
---
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: {{ $.name }}-pdb
namespace: {{ $.root.Release.Namespace }}
labels:
{{- include "gateway.labels" $ | nindent 4 }}
spec:
{{- if not (kindIs "invalid" .minAvailable) }}
minAvailable: {{ .minAvailable }}
{{- end }}
{{- if not (kindIs "invalid" .maxUnavailable) }}
maxUnavailable: {{ .maxUnavailable }}
{{- end }}
selector:
matchLabels:
{{- include "gateway.selectorLabels" $ | nindent 6 }}
{{- end }}
{{- end }}
{{- define "gateway.hpa" -}}
{{- with .w.hpa }}
---
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: {{ $.name }}
namespace: {{ $.root.Release.Namespace }}
labels:
{{- include "gateway.labels" $ | nindent 4 }}
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: {{ $.name }}
minReplicas: {{ required (printf "%s.hpa.minReplicas is required" $.name) .minReplicas }}
maxReplicas: {{ required (printf "%s.hpa.maxReplicas is required" $.name) .maxReplicas }}
{{- if not (kindIs "invalid" .targetCPUUtilizationPercentage) }}
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: {{ .targetCPUUtilizationPercentage }}
{{- end }}
{{- with .behavior }}
behavior:
{{- toYaml . | nindent 4 }}
{{- end }}
{{- end }}
{{- end }}
@@ -1,48 +0,0 @@
{{- range $name, $w := .Values.deployments }}
{{- if not (kindIs "invalid" $w) }}
{{- $ctx := dict "root" $ "name" $name "component" $w.role "w" $w }}
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ $name }}
namespace: {{ $.Release.Namespace }}
labels:
{{- include "gateway.labels" $ctx | nindent 4 }}
spec:
{{- if not $w.hpa }}
replicas: {{ include "gateway.replicas" $ctx }}
{{- end }}
{{- if not (kindIs "invalid" $w.minReadySeconds) }}
minReadySeconds: {{ $w.minReadySeconds }}
{{- end }}
selector:
matchLabels:
{{- include "gateway.selectorLabels" $ctx | nindent 6 }}
{{- with include "gateway.pick" (dict "root" $ "w" $w "key" "strategy") }}
strategy:
{{- . | nindent 4 }}
{{- end }}
template:
{{- include "gateway.pod" $ctx | nindent 4 }}
---
apiVersion: v1
kind: Service
metadata:
name: {{ $name }}
namespace: {{ $.Release.Namespace }}
labels:
{{- include "gateway.labels" $ctx | nindent 4 }}
spec:
type: ClusterIP
ports:
- name: http
port: 8080
protocol: TCP
targetPort: http
selector:
{{- include "gateway.selectorLabels" $ctx | nindent 4 }}
{{- include "gateway.hpa" $ctx }}
{{- include "gateway.pdb" $ctx }}
{{- end }}
{{- end }}
@@ -1,26 +0,0 @@
apiVersion: v1
kind: Service
metadata:
name: {{ include "gateway.headlessName" . }}
namespace: {{ .Release.Namespace }}
labels:
{{- include "gateway.labels" (dict "root" . "name" "gateway" "component" "discovery") | nindent 4 }}
spec:
type: ClusterIP
clusterIP: None
ports:
- name: http
port: 8080
protocol: TCP
targetPort: http
- name: epmd
port: 4369
protocol: TCP
targetPort: epmd
- name: erl-dist
port: 8081
protocol: TCP
targetPort: erl-dist
selector:
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/part-of: fluxer
@@ -1,53 +0,0 @@
{{- $np := .Values.networkPolicy | default dict }}
{{- if $np.enabled }}
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: gateway
namespace: {{ .Release.Namespace }}
labels:
{{- include "gateway.labels" (dict "root" . "name" "gateway") | nindent 4 }}
spec:
podSelector:
matchLabels:
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/part-of: fluxer
policyTypes:
- Ingress
- Egress
egress:
- {}
ingress:
{{- with $np.ingressNamespace }}
- from:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: {{ . }}
ports:
- port: 8080
protocol: TCP
{{- end }}
{{- with $np.clients }}
- from:
{{- range . }}
- podSelector:
matchLabels:
{{- toYaml . | nindent 10 }}
{{- end }}
ports:
- port: 8080
protocol: TCP
{{- end }}
- from:
- podSelector:
matchLabels:
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/part-of: fluxer
ports:
- port: 8080
protocol: TCP
- port: 4369
protocol: TCP
- port: 8081
protocol: TCP
{{- end }}
@@ -1,29 +0,0 @@
{{- range $name, $w := .Values.statefulsets }}
{{- if not (kindIs "invalid" $w) }}
{{- $ctx := dict "root" $ "name" $name "component" $w.role "w" $w }}
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: {{ $name }}
namespace: {{ $.Release.Namespace }}
labels:
{{- include "gateway.labels" $ctx | nindent 4 }}
spec:
replicas: {{ include "gateway.replicas" $ctx }}
{{- if not (kindIs "invalid" $w.minReadySeconds) }}
minReadySeconds: {{ $w.minReadySeconds }}
{{- end }}
serviceName: {{ include "gateway.headlessName" $ }}
selector:
matchLabels:
{{- include "gateway.selectorLabels" $ctx | nindent 6 }}
{{- with include "gateway.pick" (dict "root" $ "w" $w "key" "updateStrategy") }}
updateStrategy:
{{- . | nindent 4 }}
{{- end }}
template:
{{- include "gateway.pod" $ctx | nindent 4 }}
{{- include "gateway.pdb" $ctx }}
{{- end }}
{{- end }}
-86
View File
@@ -1,86 +0,0 @@
image:
registry: ghcr.io/fluxerapp
tag: v1
pullPolicy: IfNotPresent
imagePullSecrets: []
clusterDomain: cluster.local
env:
FLUXER_ENV: production
FLUXER_GATEWAY_PORT: "8080"
FLUXER_GATEWAY_MEDIA_PROXY_ENDPOINT: https://media.example.com
FLUXER_INTERNAL_API_ENDPOINT: http://api:8080
extraEnv: []
envFrom:
- secretRef:
name: fluxer-env
podAnnotations: {}
podSecurityContext:
runAsNonRoot: true
seccompProfile:
type: RuntimeDefault
securityContext:
allowPrivilegeEscalation: false
probes:
startup:
httpGet:
path: /_health
port: http
failureThreshold: 30
liveness:
httpGet:
path: /_health
port: http
readiness:
exec:
command:
- curl
- -fsS
- -o
- /dev/null
- --max-time
- "2"
- http://127.0.0.1:8080/_health/ready
timeoutSeconds: 3
strategy: {}
updateStrategy: {}
topologySpreadConstraints: []
nodeSelector: {}
tolerations: []
affinity: {}
networkPolicy:
enabled: false
ingressNamespace: ingress-nginx
clients:
- app.kubernetes.io/part-of: fluxer
deployments:
gateway:
role: all
replicas: 1
lifecycle:
preStop:
exec:
command:
- /bin/sh
- -c
- curl -fsS -o /dev/null --max-time 2 http://127.0.0.1:8080/_health/drain; sleep 5
resources:
requests:
cpu: 100m
memory: 384Mi
limits:
memory: 1Gi
statefulsets: {}
-6
View File
@@ -1,6 +0,0 @@
apiVersion: v2
name: fluxer-infra
description: NATS and Valkey for a Fluxer installation.
type: application
version: 0.1.0
appVersion: "v1"
@@ -1,282 +0,0 @@
{{- define "fluxer-infra.chart" -}}
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}}
{{- end }}
{{- define "fluxer-infra.selectorLabels" -}}
app.kubernetes.io/name: {{ .name }}
app.kubernetes.io/instance: {{ .root.Release.Name }}
{{- end }}
{{- define "fluxer-infra.labels" -}}
{{ include "fluxer-infra.selectorLabels" . }}
app.kubernetes.io/component: {{ .component }}
app.kubernetes.io/part-of: fluxer
app.kubernetes.io/managed-by: {{ .root.Release.Service }}
helm.sh/chart: {{ include "fluxer-infra.chart" .root }}
{{- end }}
{{- define "fluxer-infra.pick" -}}
{{- $v := get .root.Values .key }}
{{- if hasKey .w .key }}
{{- $v = get .w .key }}
{{- end }}
{{- with $v }}
{{- toYaml . }}
{{- end }}
{{- end }}
{{- define "fluxer-infra.string" -}}
{{- if and (kindIs "float64" .) (eq . (float64 (int64 .))) }}
{{- int64 . | toString }}
{{- else }}
{{- toString . }}
{{- end }}
{{- end }}
{{- define "fluxer-infra.envList" -}}
{{- $env := deepCopy (.root.Values.env | default dict) }}
{{- range $k, $v := .w.env | default dict }}
{{- if kindIs "invalid" $v }}
{{- $_ := unset $env $k }}
{{- else }}
{{- $_ := set $env $k $v }}
{{- end }}
{{- end }}
{{- range $k, $v := $env }}
{{- if not (kindIs "invalid" $v) }}
- name: {{ $k }}
value: {{ include "fluxer-infra.string" $v | quote }}
{{- end }}
{{- end }}
{{- with concat (.root.Values.extraEnv | default list) (.w.extraEnv | default list) }}
{{ toYaml . }}
{{- end }}
{{- end }}
{{- define "fluxer-infra.envFrom" -}}
{{- with concat (.root.Values.envFrom | default list) (.w.envFrom | default list) }}
{{- toYaml . }}
{{- end }}
{{- end }}
{{- define "fluxer-infra.probes" -}}
{{- $global := .root.Values.probes | default dict }}
{{- $own := .w.probes | default dict }}
{{- range $probe := list "startup" "liveness" "readiness" }}
{{- $p := get $global $probe }}
{{- if hasKey $own $probe }}
{{- $p = get $own $probe }}
{{- end }}
{{- with $p }}
{{ $probe }}Probe:
{{- toYaml . | nindent 2 }}
{{- end }}
{{- end }}
{{- end }}
{{- define "fluxer-infra.topologySpreadConstraints" -}}
{{- $out := list }}
{{- range include "fluxer-infra.pick" (dict "root" .root "w" .w "key" "topologySpreadConstraints") | fromYamlArray }}
{{- $c := deepCopy . }}
{{- if not (hasKey $c "labelSelector") }}
{{- $_ := set $c "labelSelector" (dict "matchLabels" (include "fluxer-infra.selectorLabels" $ | fromYaml)) }}
{{- end }}
{{- $out = append $out $c }}
{{- end }}
{{- with $out }}
{{- toYaml . }}
{{- end }}
{{- end }}
{{- define "fluxer-infra.replicas" -}}
{{- if kindIs "invalid" .w.replicas }}1{{ else }}{{ .w.replicas }}{{ end }}
{{- end }}
{{- define "fluxer-infra.image" -}}
{{- $ref := printf "%s:%s" .repository .tag }}
{{- with .digest }}
{{- $ref = printf "%s@%s" $ref . }}
{{- end }}
{{- $ref | quote }}
{{- end }}
{{- define "fluxer-infra.podAnnotations" -}}
{{- with merge (deepCopy (.extra | default dict)) (deepCopy (.w.podAnnotations | default dict)) (deepCopy (.root.Values.podAnnotations | default dict)) }}
annotations:
{{- toYaml . | nindent 2 }}
{{- end }}
{{- end }}
{{- define "fluxer-infra.podSpec" -}}
{{- $root := .root }}
{{- $w := .w }}
{{- with include "fluxer-infra.pick" (dict "root" $root "w" $w "key" "affinity") }}
affinity:
{{- . | nindent 2 }}
{{- end }}
{{- with include "fluxer-infra.pick" (dict "root" $root "w" $w "key" "imagePullSecrets") }}
imagePullSecrets:
{{- . | nindent 2 }}
{{- end }}
{{- with include "fluxer-infra.pick" (dict "root" $root "w" $w "key" "nodeSelector") }}
nodeSelector:
{{- . | nindent 2 }}
{{- end }}
{{- with include "fluxer-infra.pick" (dict "root" $root "w" $w "key" "tolerations") }}
tolerations:
{{- . | nindent 2 }}
{{- end }}
{{- with include "fluxer-infra.topologySpreadConstraints" . }}
topologySpreadConstraints:
{{- . | nindent 2 }}
{{- end }}
{{- with include "fluxer-infra.pick" (dict "root" $root "w" $w "key" "podSecurityContext") }}
securityContext:
{{- . | nindent 2 }}
{{- end }}
{{- if not (kindIs "invalid" $w.terminationGracePeriodSeconds) }}
terminationGracePeriodSeconds: {{ $w.terminationGracePeriodSeconds }}
{{- end }}
{{- end }}
{{- define "fluxer-infra.containerCommon" -}}
{{- $root := .root }}
{{- $w := .w }}
{{- $img := $w.image | default dict }}
image: {{ include "fluxer-infra.image" $img }}
imagePullPolicy: {{ $img.pullPolicy }}
{{- $env := include "fluxer-infra.envList" . | trim }}
{{- if or .env $env }}
env:
{{- with .env }}
{{- toYaml . | nindent 2 }}
{{- end }}
{{- with $env }}
{{- . | nindent 2 }}
{{- end }}
{{- end }}
{{- with include "fluxer-infra.envFrom" . }}
envFrom:
{{- . | nindent 2 }}
{{- end }}
{{- with $w.lifecycle }}
lifecycle:
{{- toYaml . | nindent 2 }}
{{- end }}
{{- include "fluxer-infra.probes" . }}
{{- with $w.resources }}
resources:
{{- toYaml . | nindent 2 }}
{{- end }}
{{- with include "fluxer-infra.pick" (dict "root" $root "w" $w "key" "securityContext") }}
securityContext:
{{- . | nindent 2 }}
{{- end }}
{{- with concat .mounts ($w.extraVolumeMounts | default list) }}
volumeMounts:
{{- toYaml . | nindent 2 }}
{{- end }}
{{- end }}
{{- define "fluxer-infra.statefulSetSpec" -}}
{{- $w := .w }}
{{- with include "fluxer-infra.pick" (dict "root" .root "w" $w "key" "updateStrategy") }}
updateStrategy:
{{- . | nindent 2 }}
{{- end }}
{{- if not (kindIs "invalid" $w.minReadySeconds) }}
minReadySeconds: {{ $w.minReadySeconds }}
{{- end }}
{{- end }}
{{- define "fluxer-infra.volumeClaim" -}}
- metadata:
name: data
spec:
accessModes:
- ReadWriteOnce
{{- with .storageClassName }}
storageClassName: {{ . | quote }}
{{- end }}
resources:
requests:
storage: {{ .size }}
{{- end }}
{{- define "fluxer-infra.pdb" -}}
{{- with .w.pdb }}
---
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: {{ $.name }}-pdb
namespace: {{ $.root.Release.Namespace }}
labels:
{{- include "fluxer-infra.labels" $ | nindent 4 }}
spec:
{{- if not (kindIs "invalid" .minAvailable) }}
minAvailable: {{ .minAvailable }}
{{- end }}
{{- if not (kindIs "invalid" .maxUnavailable) }}
maxUnavailable: {{ .maxUnavailable }}
{{- end }}
selector:
matchLabels:
{{- include "fluxer-infra.selectorLabels" $ | nindent 6 }}
{{- end }}
{{- end }}
{{- define "fluxer-infra.service" }}
---
apiVersion: v1
kind: Service
metadata:
name: {{ .svcName }}
namespace: {{ .root.Release.Namespace }}
labels:
{{- include "fluxer-infra.labels" . | nindent 4 }}
spec:
{{- if .headless }}
clusterIP: None
{{- end }}
{{- if .publishNotReady }}
publishNotReadyAddresses: true
{{- end }}
selector:
{{- include "fluxer-infra.selectorLabels" . | nindent 4 }}
ports:
{{- range .ports }}
- name: {{ index . 0 }}
port: {{ index . 1 }}
targetPort: {{ index . 0 }}
{{- end }}
{{- end }}
{{- define "fluxer-infra.natsConf" -}}
{{- $w := .Values.nats -}}
{{- with $w.config -}}
listen: 0.0.0.0:4222
http: 0.0.0.0:8222
max_payload: {{ .maxPayload }}
max_pending: {{ .maxPending }}
max_connections: {{ .maxConnections }}
{{- if $w.jetstream.enabled }}
server_name: $POD_NAME
jetstream {
store_dir: /data
}
{{- end }}
cluster {
name: {{ .clusterName }}
listen: 0.0.0.0:6222
routes = [
{{- range $i := until (int (include "fluxer-infra.replicas" (dict "w" $w))) }}
nats-route://nats-{{ $i }}.nats-headless.{{ $.Release.Namespace }}.svc.{{ $.Values.clusterDomain }}:6222
{{- end }}
]
}
{{ end }}
{{- end }}
@@ -1,71 +0,0 @@
{{- with .Values.nats }}
{{- $ctx := dict "root" $ "w" . "name" "nats" "component" "messaging" }}
apiVersion: v1
kind: ConfigMap
metadata:
name: nats-config
namespace: {{ $.Release.Namespace }}
labels:
{{- include "fluxer-infra.labels" $ctx | nindent 4 }}
data:
nats.conf: {{ include "fluxer-infra.natsConf" $ | toJson }}
{{- include "fluxer-infra.pdb" $ctx }}
{{- include "fluxer-infra.service" (merge (dict "svcName" "nats" "ports" (list (list "client" 4222))) $ctx) }}
{{- include "fluxer-infra.service" (merge (dict "svcName" "nats-headless" "headless" true "ports" (list (list "client" 4222) (list "cluster" 6222) (list "monitor" 8222))) $ctx) }}
{{- $mounts := list (dict "name" "config" "mountPath" "/etc/nats") }}
{{- $env := list }}
{{- if .jetstream.enabled }}
{{- $mounts = append $mounts (dict "name" "data" "mountPath" "/data") }}
{{- $env = append $env (dict "name" "POD_NAME" "valueFrom" (dict "fieldRef" (dict "fieldPath" "metadata.name"))) }}
{{- end }}
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: nats
namespace: {{ $.Release.Namespace }}
labels:
{{- include "fluxer-infra.labels" $ctx | nindent 4 }}
spec:
replicas: {{ include "fluxer-infra.replicas" $ctx }}
serviceName: nats-headless
{{- with include "fluxer-infra.statefulSetSpec" $ctx | trim }}
{{- . | nindent 2 }}
{{- end }}
selector:
matchLabels:
{{- include "fluxer-infra.selectorLabels" $ctx | nindent 6 }}
template:
metadata:
labels:
{{- include "fluxer-infra.labels" $ctx | nindent 8 }}
{{- with include "fluxer-infra.podAnnotations" (merge (dict "extra" (dict "checksum/config" (include "fluxer-infra.natsConf" $ | sha256sum))) $ctx) | trim }}
{{- . | nindent 6 }}
{{- end }}
spec:
{{- include "fluxer-infra.podSpec" $ctx | trim | nindent 6 }}
containers:
- name: nats
{{- include "fluxer-infra.containerCommon" (merge (dict "env" $env "mounts" $mounts) $ctx) | trim | nindent 10 }}
args:
- -c
- /etc/nats/nats.conf
ports:
- name: client
containerPort: 4222
- name: cluster
containerPort: 6222
- name: monitor
containerPort: 8222
volumes:
- name: config
configMap:
name: nats-config
{{- with .extraVolumes }}
{{- toYaml . | nindent 8 }}
{{- end }}
{{- if .jetstream.enabled }}
volumeClaimTemplates:
{{- include "fluxer-infra.volumeClaim" .jetstream.storage | nindent 4 }}
{{- end }}
{{- end }}
@@ -1,67 +0,0 @@
{{- with .Values.valkey }}
{{- $ctx := dict "root" $ "w" . "name" "valkey" "component" "cache" }}
{{- include "fluxer-infra.pdb" $ctx }}
{{- include "fluxer-infra.service" (merge (dict "svcName" "valkey" "ports" (list (list "valkey" 6379))) $ctx) }}
{{- include "fluxer-infra.service" (merge (dict "svcName" "valkey-headless" "headless" true "publishNotReady" true "ports" (list (list "valkey" 6379))) $ctx) }}
{{- $mounts := list }}
{{- if .persistence.enabled }}
{{- $mounts = append $mounts (dict "name" "data" "mountPath" "/data") }}
{{- end }}
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: valkey
namespace: {{ $.Release.Namespace }}
labels:
{{- include "fluxer-infra.labels" $ctx | nindent 4 }}
spec:
replicas: 1
serviceName: valkey-headless
{{- with include "fluxer-infra.statefulSetSpec" $ctx | trim }}
{{- . | nindent 2 }}
{{- end }}
selector:
matchLabels:
{{- include "fluxer-infra.selectorLabels" $ctx | nindent 6 }}
template:
metadata:
labels:
{{- include "fluxer-infra.labels" $ctx | nindent 8 }}
{{- with include "fluxer-infra.podAnnotations" $ctx | trim }}
{{- . | nindent 6 }}
{{- end }}
spec:
{{- include "fluxer-infra.podSpec" $ctx | trim | nindent 6 }}
containers:
- name: valkey
{{- include "fluxer-infra.containerCommon" (merge (dict "env" list "mounts" $mounts) $ctx) | trim | nindent 10 }}
command:
- valkey-server
{{- if .persistence.enabled }}
- --appendonly
- "yes"
- --dir
- /data
{{- else }}
- --save
- ""
- --appendonly
- "no"
{{- end }}
- --maxmemory
- {{ .maxmemory | quote }}
- --maxmemory-policy
- {{ .maxmemoryPolicy | quote }}
ports:
- name: valkey
containerPort: 6379
{{- with .extraVolumes }}
volumes:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- if .persistence.enabled }}
volumeClaimTemplates:
{{- include "fluxer-infra.volumeClaim" .persistence | nindent 4 }}
{{- end }}
{{- end }}
-108
View File
@@ -1,108 +0,0 @@
imagePullSecrets: []
clusterDomain: cluster.local
env: {}
extraEnv: []
envFrom: []
podAnnotations: {}
podSecurityContext:
runAsNonRoot: true
seccompProfile:
type: RuntimeDefault
securityContext:
allowPrivilegeEscalation: false
probes: {}
updateStrategy: {}
topologySpreadConstraints: []
nodeSelector: {}
tolerations: []
affinity: {}
nats:
image:
repository: nats
tag: 2.14-alpine
pullPolicy: IfNotPresent
replicas: 3
config:
clusterName: nats
maxPayload: 1MB
maxPending: 64MB
maxConnections: 65536
jetstream:
enabled: true
storage:
size: 10Gi
storageClassName: ""
podSecurityContext:
fsGroup: 65534
runAsGroup: 65534
runAsNonRoot: true
runAsUser: 65534
seccompProfile:
type: RuntimeDefault
probes:
liveness:
httpGet:
path: /healthz
port: monitor
initialDelaySeconds: 10
readiness:
httpGet:
path: /healthz?js-enabled-only=true
port: monitor
resources:
requests:
cpu: 50m
memory: 128Mi
limits:
memory: 512Mi
valkey:
image:
repository: valkey/valkey
tag: 9.1-alpine
pullPolicy: IfNotPresent
maxmemory: 192mb
maxmemoryPolicy: noeviction
persistence:
enabled: true
size: 1Gi
storageClassName: ""
podSecurityContext:
fsGroup: 999
runAsGroup: 999
runAsNonRoot: true
runAsUser: 999
seccompProfile:
type: RuntimeDefault
probes:
liveness:
exec:
command:
- valkey-cli
- ping
initialDelaySeconds: 10
readiness:
exec:
command:
- valkey-cli
- ping
resources:
requests:
cpu: 50m
memory: 64Mi
limits:
memory: 256Mi
-6
View File
@@ -1,6 +0,0 @@
apiVersion: v2
name: fluxer-ingress
description: Ingress routing for the public Fluxer endpoints.
type: application
version: 0.1.0
appVersion: "v1"
@@ -1,27 +0,0 @@
{{- define "fluxer-ingress.chart" -}}
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}}
{{- end }}
{{- define "fluxer-ingress.labels" -}}
app.kubernetes.io/name: {{ .Chart.Name }}
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/part-of: fluxer
app.kubernetes.io/managed-by: {{ .Release.Service }}
helm.sh/chart: {{ include "fluxer-ingress.chart" . }}
{{- end }}
{{- define "fluxer-ingress.annotationKey" -}}
{{- if or (contains "/" .key) (not .prefix) -}}
{{- .key -}}
{{- else -}}
{{- printf "%s/%s" .prefix .key -}}
{{- end -}}
{{- end }}
{{- define "fluxer-ingress.string" -}}
{{- if and (kindIs "float64" .) (eq . (floor .)) -}}
{{- . | int64 | toString -}}
{{- else -}}
{{- . | toString -}}
{{- end -}}
{{- end }}
@@ -1,20 +0,0 @@
{{- with .Values.clusterIssuer }}
{{- if .enabled }}
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: {{ required "clusterIssuer.name is required" .name }}
labels:
{{- include "fluxer-ingress.labels" $ | nindent 4 }}
spec:
acme:
email: {{ required "clusterIssuer.email is required" .email | quote }}
privateKeySecretRef:
name: {{ required "clusterIssuer.privateKeySecretName is required" .privateKeySecretName }}
server: {{ required "clusterIssuer.server is required" .server }}
solvers:
- http01:
ingress:
class: {{ required "clusterIssuer.solverIngressClass is required" .solverIngressClass }}
{{- end }}
{{- end }}
@@ -1,58 +0,0 @@
{{- $v := .Values }}
{{- $presets := $v.annotationPresets | default dict }}
{{- $issuer := $v.clusterIssuer | default dict }}
{{- range $name, $spec := ($v.ingresses | default dict) }}
{{- if not (kindIs "invalid" $spec) }}
{{- $ann := deepCopy ($v.commonAnnotations | default dict) }}
{{- range ($spec.presets | default list) }}
{{- $ann = mergeOverwrite $ann (deepCopy (required (printf "unknown annotation preset %s" .) (index $presets .))) }}
{{- end }}
{{- if and $spec.tls $issuer.enabled }}
{{- $_ := set $ann "cert-manager.io/cluster-issuer" (required "clusterIssuer.name is required" $issuer.name) }}
{{- end }}
{{- $ann = mergeOverwrite $ann (deepCopy ($spec.annotations | default dict)) }}
{{- range $k, $val := $ann }}
{{- if kindIs "invalid" $val }}
{{- $_ := unset $ann $k }}
{{- end }}
{{- end }}
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: {{ $name }}
namespace: {{ $.Release.Namespace }}
labels:
{{- include "fluxer-ingress.labels" $ | nindent 4 }}
{{- with $ann }}
annotations:
{{- range $k, $val := . }}
{{ include "fluxer-ingress.annotationKey" (dict "key" $k "prefix" $v.annotationPrefix) }}: {{ include "fluxer-ingress.string" $val | quote }}
{{- end }}
{{- end }}
spec:
{{- with $spec.ingressClassName | default $v.ingressClassName }}
ingressClassName: {{ . }}
{{- end }}
{{- with $spec.tls }}
tls:
{{- toYaml . | nindent 4 }}
{{- end }}
rules:
{{- range $rule := required (printf "ingress %s needs rules" $name) $spec.rules }}
- host: {{ required (printf "ingress %s has a rule without a host" $name) $rule.host | quote }}
http:
paths:
{{- range $p := $rule.paths | default (list dict) }}
{{- $p = $p | default dict }}
- path: {{ $p.path | default "/" | quote }}
pathType: {{ $p.pathType | default "Prefix" }}
backend:
service:
name: {{ required (printf "ingress %s host %s needs a service" $name $rule.host) ($p.service | default $rule.service) }}
port:
number: {{ required (printf "ingress %s host %s needs a port or servicePort" $name $rule.host) ($p.port | default $rule.port | default $v.servicePort) | int64 }}
{{- end }}
{{- end }}
{{- end }}
{{- end }}
-53
View File
@@ -1,53 +0,0 @@
ingressClassName: nginx
annotationPrefix: nginx.ingress.kubernetes.io
servicePort: 8080
commonAnnotations: {}
annotationPresets:
websocket:
proxy-read-timeout: "3600"
proxy-send-timeout: "3600"
stripPrefix:
use-regex: "true"
rewrite-target: /$2
ingresses:
fluxer:
rules:
- host: web.example.com
service: app-proxy
- host: api.example.com
service: api
- host: admin.example.com
service: admin
- host: media.example.com
service: media-proxy
fluxer-web-api:
presets: [stripPrefix]
rules:
- host: web.example.com
service: api
paths:
- path: /api(/(.*))?$
pathType: ImplementationSpecific
fluxer-gateway:
presets: [websocket]
rules:
- host: gateway.example.com
service: gateway
fluxer-uploads:
annotations:
proxy-body-size: 100m
proxy-request-buffering: "off"
rules:
- host: uploads.example.com
service: uploads
clusterIssuer:
enabled: false
name: letsencrypt
email: ""
server: https://acme-v02.api.letsencrypt.org/directory
privateKeySecretName: letsencrypt-account-key
solverIngressClass: nginx
@@ -1,6 +0,0 @@
apiVersion: v2
name: fluxer-media-proxy
description: Fluxer media proxy and upload relay workloads.
type: application
version: 0.1.0
appVersion: "v1"
@@ -1,87 +0,0 @@
{{- define "fluxer-media-proxy.chart" -}}
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}}
{{- end }}
{{- define "fluxer-media-proxy.selectorLabels" -}}
app.kubernetes.io/name: {{ .name }}
app.kubernetes.io/instance: {{ .root.Release.Name }}
{{- end }}
{{- define "fluxer-media-proxy.labels" -}}
{{ include "fluxer-media-proxy.selectorLabels" . }}
app.kubernetes.io/component: {{ include "fluxer-media-proxy.mode" . }}
app.kubernetes.io/part-of: fluxer
app.kubernetes.io/managed-by: {{ .root.Release.Service }}
helm.sh/chart: {{ include "fluxer-media-proxy.chart" .root }}
{{- end }}
{{- define "fluxer-media-proxy.image" -}}
{{- $g := .root.Values.image -}}
{{- $i := .w.image | default dict -}}
{{- $repo := $i.repository | default (printf "%s/%s" $g.registry ($i.name | default "fluxer-media-proxy")) -}}
{{- $tag := $i.tag | default $g.tag -}}
{{- if $i.digest -}}
{{- printf "%s:%s@%s" $repo $tag $i.digest | quote -}}
{{- else -}}
{{- printf "%s:%s" $repo $tag | quote -}}
{{- end -}}
{{- end }}
{{- define "fluxer-media-proxy.pick" -}}
{{- $v := ternary (get .w .key) (get .root.Values .key) (hasKey .w .key) -}}
{{- if $v }}
{{- toYaml $v }}
{{- end }}
{{- end }}
{{- define "fluxer-media-proxy.mode" -}}
{{- $mode := required (printf "workloads.%s.mode is required" .name) .w.mode -}}
{{- if not (has $mode (list "mp" "static" "upload" "relay")) -}}
{{- fail (printf "workloads.%s.mode must be mp, static, upload or relay" .name) -}}
{{- end -}}
{{- $mode -}}
{{- end }}
{{- define "fluxer-media-proxy.envValue" -}}
{{- if and (kindIs "float64" .) (eq . (float64 (int64 .))) -}}
{{- int64 . | toString -}}
{{- else -}}
{{- toString . -}}
{{- end -}}
{{- end }}
{{- define "fluxer-media-proxy.mergeEnv" -}}
{{- $out := dict -}}
{{- range $layer := . -}}
{{- range $k, $v := ($layer | default dict) -}}
{{- if kindIs "invalid" $v -}}
{{- $_ := unset $out $k -}}
{{- else -}}
{{- $_ := set $out $k $v -}}
{{- end -}}
{{- end -}}
{{- end -}}
{{- toYaml $out -}}
{{- end }}
{{- define "fluxer-media-proxy.topologySpreadConstraints" -}}
{{- $out := list -}}
{{- range .constraints -}}
{{- if .labelSelector -}}
{{- $out = append $out . -}}
{{- else -}}
{{- $out = append $out (merge (dict "labelSelector" (dict "matchLabels" $.selector)) .) -}}
{{- end -}}
{{- end -}}
{{- toYaml $out -}}
{{- end }}
{{- define "fluxer-media-proxy.pdb" -}}
{{- $out := dict -}}
{{- range $k := list "minAvailable" "maxUnavailable" -}}
{{- if and (hasKey $ $k) (not (kindIs "invalid" (index $ $k))) -}}
{{- $_ := set $out $k (index $ $k) -}}
{{- end -}}
{{- end -}}
{{- toYaml $out -}}
{{- end }}
@@ -1,191 +0,0 @@
{{- range $name, $w := .Values.workloads }}
{{- if not (kindIs "invalid" $w) }}
{{- $ctx := dict "root" $ "name" $name "w" $w }}
{{- $mode := include "fluxer-media-proxy.mode" $ctx }}
{{- $sel := include "fluxer-media-proxy.selectorLabels" $ctx | fromYaml }}
{{- $env := include "fluxer-media-proxy.mergeEnv" (list $.Values.env $w.env) | fromYaml }}
{{- $extraEnv := concat ($.Values.extraEnv | default list) ($w.extraEnv | default list) }}
{{- $envFrom := concat ($.Values.envFrom | default list) ($w.envFrom | default list) }}
{{- $podAnnotations := merge (dict) ($w.podAnnotations | default dict) ($.Values.podAnnotations | default dict) }}
{{- $probes := dict }}
{{- range $k, $v := ($.Values.probes | default dict) }}
{{- $_ := set $probes $k $v }}
{{- end }}
{{- range $k, $v := ($w.probes | default dict) }}
{{- $_ := set $probes $k $v }}
{{- end }}
{{- $pick := dict "root" $ "w" $w }}
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ $name }}
namespace: {{ $.Release.Namespace }}
labels:
{{- include "fluxer-media-proxy.labels" $ctx | nindent 4 }}
spec:
{{- if not $w.hpa }}
replicas: {{ ternary $w.replicas 1 (hasKey $w "replicas") | int64 }}
{{- end }}
{{- if not (kindIs "invalid" $w.minReadySeconds) }}
minReadySeconds: {{ $w.minReadySeconds | int64 }}
{{- end }}
selector:
matchLabels:
{{- toYaml $sel | nindent 6 }}
{{- with include "fluxer-media-proxy.pick" (set (deepCopy $pick) "key" "strategy") }}
strategy:
{{- . | nindent 4 }}
{{- end }}
template:
metadata:
{{- with $podAnnotations }}
annotations:
{{- toYaml . | nindent 8 }}
{{- end }}
labels:
{{- include "fluxer-media-proxy.labels" $ctx | nindent 8 }}
spec:
{{- with include "fluxer-media-proxy.pick" (set (deepCopy $pick) "key" "imagePullSecrets") }}
imagePullSecrets:
{{- . | nindent 8 }}
{{- end }}
{{- with include "fluxer-media-proxy.pick" (set (deepCopy $pick) "key" "podSecurityContext") }}
securityContext:
{{- . | nindent 8 }}
{{- end }}
{{- if not (kindIs "invalid" $w.terminationGracePeriodSeconds) }}
terminationGracePeriodSeconds: {{ $w.terminationGracePeriodSeconds | int64 }}
{{- end }}
{{- with include "fluxer-media-proxy.pick" (set (deepCopy $pick) "key" "nodeSelector") }}
nodeSelector:
{{- . | nindent 8 }}
{{- end }}
{{- with include "fluxer-media-proxy.pick" (set (deepCopy $pick) "key" "tolerations") }}
tolerations:
{{- . | nindent 8 }}
{{- end }}
{{- with include "fluxer-media-proxy.pick" (set (deepCopy $pick) "key" "affinity") }}
affinity:
{{- . | nindent 8 }}
{{- end }}
{{- with include "fluxer-media-proxy.pick" (set (deepCopy $pick) "key" "topologySpreadConstraints") | fromYamlArray }}
topologySpreadConstraints:
{{- include "fluxer-media-proxy.topologySpreadConstraints" (dict "constraints" . "selector" $sel) | nindent 8 }}
{{- end }}
containers:
- name: {{ $name }}
image: {{ include "fluxer-media-proxy.image" $ctx }}
imagePullPolicy: {{ ($w.image | default dict).pullPolicy | default $.Values.image.pullPolicy }}
env:
{{- if not (kindIs "invalid" $w.buildVersion) }}
- name: BUILD_VERSION
value: {{ include "fluxer-media-proxy.envValue" $w.buildVersion | quote }}
{{- end }}
- name: FLUXER_MEDIA_PROXY_MODE
value: {{ $mode | quote }}
{{- range $k, $v := $env }}
- name: {{ $k }}
value: {{ include "fluxer-media-proxy.envValue" $v | quote }}
{{- end }}
{{- with $extraEnv }}
{{- toYaml . | nindent 12 }}
{{- end }}
{{- with $envFrom }}
envFrom:
{{- toYaml . | nindent 12 }}
{{- end }}
ports:
- name: http
containerPort: 8080
protocol: TCP
{{- with $w.lifecycle }}
lifecycle:
{{- toYaml . | nindent 12 }}
{{- end }}
{{- range $k := list "startup" "liveness" "readiness" }}
{{- with get $probes $k }}
{{ $k }}Probe:
{{- toYaml . | nindent 12 }}
{{- end }}
{{- end }}
{{- with $w.resources }}
resources:
{{- toYaml . | nindent 12 }}
{{- end }}
{{- with include "fluxer-media-proxy.pick" (set (deepCopy $pick) "key" "securityContext") }}
securityContext:
{{- . | nindent 12 }}
{{- end }}
{{- with $w.extraVolumeMounts }}
volumeMounts:
{{- toYaml . | nindent 12 }}
{{- end }}
{{- with $w.extraVolumes }}
volumes:
{{- toYaml . | nindent 8 }}
{{- end }}
---
apiVersion: v1
kind: Service
metadata:
name: {{ $name }}
namespace: {{ $.Release.Namespace }}
labels:
{{- include "fluxer-media-proxy.labels" $ctx | nindent 4 }}
spec:
type: ClusterIP
selector:
{{- toYaml $sel | nindent 4 }}
ports:
- name: http
port: 8080
targetPort: http
protocol: TCP
{{- with include "fluxer-media-proxy.pdb" ($w.pdb | default dict) | fromYaml }}
---
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: {{ $name }}-pdb
namespace: {{ $.Release.Namespace }}
labels:
{{- include "fluxer-media-proxy.labels" $ctx | nindent 4 }}
spec:
{{- toYaml . | nindent 2 }}
selector:
matchLabels:
{{- toYaml $sel | nindent 6 }}
{{- end }}
{{- with $w.hpa }}
---
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: {{ $name }}
namespace: {{ $.Release.Namespace }}
labels:
{{- include "fluxer-media-proxy.labels" $ctx | nindent 4 }}
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: {{ $name }}
minReplicas: {{ required (printf "workloads.%s.hpa.minReplicas is required" $name) .minReplicas | int64 }}
maxReplicas: {{ required (printf "workloads.%s.hpa.maxReplicas is required" $name) .maxReplicas | int64 }}
{{- if not (kindIs "invalid" .targetCPUUtilizationPercentage) }}
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: {{ .targetCPUUtilizationPercentage | int64 }}
{{- end }}
{{- with .behavior }}
behavior:
{{- toYaml . | nindent 4 }}
{{- end }}
{{- end }}
{{- end }}
{{- end }}
@@ -1,72 +0,0 @@
image:
registry: ghcr.io/fluxerapp
tag: v1
pullPolicy: IfNotPresent
imagePullSecrets: []
env: {}
extraEnv: []
envFrom:
- secretRef:
name: fluxer-env
podAnnotations: {}
podSecurityContext:
runAsNonRoot: true
seccompProfile:
type: RuntimeDefault
securityContext:
allowPrivilegeEscalation: false
capabilities:
drop:
- ALL
probes:
liveness:
httpGet:
path: /_health
port: http
readiness:
httpGet:
path: /_health
port: http
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 25%
maxUnavailable: 25%
topologySpreadConstraints: []
nodeSelector: {}
tolerations: []
affinity: {}
workloads:
media-proxy:
mode: mp
replicas: 1
resources:
requests:
cpu: 100m
memory: 256Mi
limits:
memory: 1Gi
uploads:
mode: relay
replicas: 1
resources:
requests:
cpu: 50m
memory: 64Mi
limits:
memory: 512Mi
-6
View File
@@ -1,6 +0,0 @@
apiVersion: v2
name: fluxer-push
description: Fluxer push notification delivery service
type: application
version: 0.1.0
appVersion: "v1"
@@ -1,71 +0,0 @@
{{- define "fluxer-push.selectorLabels" -}}
app.kubernetes.io/name: {{ .name }}
app.kubernetes.io/instance: {{ .root.Release.Name }}
{{- end }}
{{- define "fluxer-push.labels" -}}
{{ include "fluxer-push.selectorLabels" . }}
app.kubernetes.io/component: {{ include "fluxer-push.mode" . }}
app.kubernetes.io/part-of: fluxer
app.kubernetes.io/managed-by: {{ .root.Release.Service }}
helm.sh/chart: {{ printf "%s-%s" .root.Chart.Name .root.Chart.Version | replace "+" "_" }}
{{- end }}
{{- define "fluxer-push.mode" -}}
{{- $mode := .w.mode | default "delivery" -}}
{{- if not (has $mode (list "delivery" "relay")) -}}
{{- fail (printf "workloads.%s.mode must be delivery or relay" .name) -}}
{{- end -}}
{{- $mode -}}
{{- end }}
{{- define "fluxer-push.port" -}}
{{- .w.port | default (ternary 8127 8126 (eq (include "fluxer-push.mode" .) "relay")) -}}
{{- end }}
{{- define "fluxer-push.image" -}}
{{- $global := .root.Values.image | default dict -}}
{{- $img := .w.image | default dict -}}
{{- $repo := $img.repository -}}
{{- if not $repo -}}
{{- $repo = printf "%s/%s" (required "image.registry is required" $global.registry) ($img.name | default "fluxer-push") -}}
{{- end -}}
{{- $ref := printf "%s:%s" $repo (include "fluxer-push.string" (required "image.tag is required" ($img.tag | default $global.tag))) -}}
{{- with $img.digest }}{{ $ref = printf "%s@%s" $ref . }}{{ end -}}
{{- $ref -}}
{{- end }}
{{- define "fluxer-push.string" -}}
{{- if and (kindIs "float64" .) (eq . (floor .)) -}}
{{- . | int64 | toString -}}
{{- else -}}
{{- . | toString -}}
{{- end -}}
{{- end }}
{{- define "fluxer-push.env" -}}
{{- $env := deepCopy (.root.Values.env | default dict) -}}
{{- range $k, $v := (.w.env | default dict) -}}
{{- if kindIs "invalid" $v -}}
{{- $_ := unset $env $k -}}
{{- else -}}
{{- $_ := set $env $k $v -}}
{{- end -}}
{{- end -}}
{{- if not (kindIs "invalid" .w.port) -}}
{{- $_ := set $env "FLUXER_PUSH_SERVICE_PORT" .w.port -}}
{{- end -}}
{{- if not (kindIs "invalid" .w.buildVersion) }}
- name: BUILD_VERSION
value: {{ include "fluxer-push.string" .w.buildVersion | quote }}
{{- end }}
{{- range $k, $v := $env }}
{{- if not (kindIs "invalid" $v) }}
- name: {{ $k }}
value: {{ include "fluxer-push.string" $v | quote }}
{{- end }}
{{- end }}
{{- with concat (.root.Values.extraEnv | default list) (.w.extraEnv | default list) }}
{{ toYaml . }}
{{- end }}
{{- end }}
@@ -1,205 +0,0 @@
{{- range $name, $w := .Values.workloads }}
{{- if not (kindIs "invalid" $w) }}
{{- $ctx := dict "root" $ "name" $name "w" $w }}
{{- $mode := include "fluxer-push.mode" $ctx }}
{{- $port := include "fluxer-push.port" $ctx | int }}
{{- $globalProbes := $.Values.probes | default dict }}
{{- $workloadProbes := $w.probes | default dict }}
{{- $probes := dict }}
{{- range $probe := list "startup" "liveness" "readiness" }}
{{- $_ := set $probes $probe (ternary (index $workloadProbes $probe) (index $globalProbes $probe) (hasKey $workloadProbes $probe)) }}
{{- end }}
{{- $annotations := mergeOverwrite (deepCopy ($.Values.podAnnotations | default dict)) (deepCopy ($w.podAnnotations | default dict)) }}
{{- $pullSecrets := ternary $w.imagePullSecrets $.Values.imagePullSecrets (hasKey $w "imagePullSecrets") }}
{{- $podSecurityContext := ternary $w.podSecurityContext $.Values.podSecurityContext (hasKey $w "podSecurityContext") }}
{{- $securityContext := ternary $w.securityContext $.Values.securityContext (hasKey $w "securityContext") }}
{{- $strategy := ternary $w.strategy $.Values.strategy (hasKey $w "strategy") }}
{{- $tsc := ternary $w.topologySpreadConstraints $.Values.topologySpreadConstraints (hasKey $w "topologySpreadConstraints") }}
{{- $nodeSelector := ternary $w.nodeSelector $.Values.nodeSelector (hasKey $w "nodeSelector") }}
{{- $tolerations := ternary $w.tolerations $.Values.tolerations (hasKey $w "tolerations") }}
{{- $affinity := ternary $w.affinity $.Values.affinity (hasKey $w "affinity") }}
{{- $envFrom := concat ($.Values.envFrom | default list) ($w.envFrom | default list) }}
{{- $env := include "fluxer-push.env" $ctx }}
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ $name }}
namespace: {{ $.Release.Namespace }}
labels:
{{- include "fluxer-push.labels" $ctx | nindent 4 }}
spec:
{{- if not $w.hpa }}
replicas: {{ ternary $w.replicas 1 (hasKey $w "replicas") | int }}
{{- end }}
{{- if hasKey $w "minReadySeconds" }}
minReadySeconds: {{ $w.minReadySeconds | int }}
{{- end }}
selector:
matchLabels:
{{- include "fluxer-push.selectorLabels" $ctx | nindent 6 }}
{{- with $strategy }}
strategy:
{{- toYaml . | nindent 4 }}
{{- end }}
template:
metadata:
{{- with $annotations }}
annotations:
{{- toYaml . | nindent 8 }}
{{- end }}
labels:
{{- include "fluxer-push.labels" $ctx | nindent 8 }}
spec:
{{- with $pullSecrets }}
imagePullSecrets:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with $podSecurityContext }}
securityContext:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- if hasKey $w "terminationGracePeriodSeconds" }}
terminationGracePeriodSeconds: {{ $w.terminationGracePeriodSeconds | int }}
{{- end }}
{{- with $nodeSelector }}
nodeSelector:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with $tolerations }}
tolerations:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with $affinity }}
affinity:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with $tsc }}
topologySpreadConstraints:
{{- range . }}
{{- $c := deepCopy . }}
{{- if not $c.labelSelector }}
{{- $_ := set $c "labelSelector" (dict "matchLabels" (include "fluxer-push.selectorLabels" $ctx | fromYaml)) }}
{{- end }}
{{- toYaml (list $c) | nindent 8 }}
{{- end }}
{{- end }}
containers:
- name: {{ $name }}
image: {{ include "fluxer-push.image" $ctx | quote }}
imagePullPolicy: {{ ($w.image | default dict).pullPolicy | default ($.Values.image | default dict).pullPolicy | default "IfNotPresent" }}
command:
- /usr/local/bin/fluxer-push
{{- if eq $mode "relay" }}
args:
- --mode
- relay
{{- end }}
{{- with trim $env }}
env:
{{- . | nindent 12 }}
{{- end }}
{{- with $envFrom }}
envFrom:
{{- toYaml . | nindent 12 }}
{{- end }}
ports:
- name: http
containerPort: {{ $port }}
protocol: TCP
{{- with $probes.startup }}
startupProbe:
{{- toYaml . | nindent 12 }}
{{- end }}
{{- with $probes.liveness }}
livenessProbe:
{{- toYaml . | nindent 12 }}
{{- end }}
{{- with $probes.readiness }}
readinessProbe:
{{- toYaml . | nindent 12 }}
{{- end }}
{{- with $w.resources }}
resources:
{{- toYaml . | nindent 12 }}
{{- end }}
{{- with $securityContext }}
securityContext:
{{- toYaml . | nindent 12 }}
{{- end }}
{{- with $w.lifecycle }}
lifecycle:
{{- toYaml . | nindent 12 }}
{{- end }}
{{- with $w.extraVolumeMounts }}
volumeMounts:
{{- toYaml . | nindent 12 }}
{{- end }}
{{- with $w.extraVolumes }}
volumes:
{{- toYaml . | nindent 8 }}
{{- end }}
---
apiVersion: v1
kind: Service
metadata:
name: {{ $name }}
namespace: {{ $.Release.Namespace }}
labels:
{{- include "fluxer-push.labels" $ctx | nindent 4 }}
spec:
type: ClusterIP
selector:
{{- include "fluxer-push.selectorLabels" $ctx | nindent 4 }}
ports:
- name: http
port: {{ $port }}
protocol: TCP
targetPort: http
{{- with $w.pdb }}
---
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: {{ $name }}-pdb
namespace: {{ $.Release.Namespace }}
labels:
{{- include "fluxer-push.labels" $ctx | nindent 4 }}
spec:
{{- toYaml . | nindent 2 }}
selector:
matchLabels:
{{- include "fluxer-push.selectorLabels" $ctx | nindent 6 }}
{{- end }}
{{- with $w.hpa }}
---
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: {{ $name }}
namespace: {{ $.Release.Namespace }}
labels:
{{- include "fluxer-push.labels" $ctx | nindent 4 }}
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: {{ $name }}
minReplicas: {{ required (printf "workloads.%s.hpa.minReplicas is required" $name) .minReplicas | int }}
maxReplicas: {{ required (printf "workloads.%s.hpa.maxReplicas is required" $name) .maxReplicas | int }}
{{- if not (kindIs "invalid" .targetCPUUtilizationPercentage) }}
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: {{ .targetCPUUtilizationPercentage | int }}
{{- end }}
{{- with .behavior }}
behavior:
{{- toYaml . | nindent 4 }}
{{- end }}
{{- end }}
{{- end }}
{{- end }}
-65
View File
@@ -1,65 +0,0 @@
image:
registry: ghcr.io/fluxerapp
tag: v1
pullPolicy: IfNotPresent
imagePullSecrets: []
env: {}
extraEnv: []
envFrom:
- secretRef:
name: fluxer-env
podAnnotations: {}
podSecurityContext:
runAsNonRoot: true
seccompProfile:
type: RuntimeDefault
securityContext:
allowPrivilegeEscalation: false
capabilities:
drop:
- ALL
probes:
liveness:
httpGet:
path: /_healthz
port: http
readiness:
httpGet:
path: /_healthz
port: http
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 25%
maxUnavailable: 25%
topologySpreadConstraints: []
nodeSelector: {}
tolerations: []
affinity: {}
workloads:
push:
mode: delivery
replicas: 1
env:
FLUXER_INTERNAL_API_ENDPOINT: http://api:8080
FLUXER_SVC_NATS_URL: nats://nats:4222
resources:
requests:
cpu: 50m
memory: 64Mi
limits:
memory: 256Mi
-6
View File
@@ -1,6 +0,0 @@
apiVersion: v2
name: fluxer-svc
description: Fluxer internal services, each a router Deployment and a shard StatefulSet
type: application
version: 0.1.0
appVersion: v1
@@ -1,203 +0,0 @@
{{- define "fluxer-svc.chart" -}}
{{ printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" }}
{{- end }}
{{- define "fluxer-svc.selectorLabels" -}}
app.kubernetes.io/name: {{ .name }}
app.kubernetes.io/instance: {{ .root.Release.Name }}
{{- end }}
{{- define "fluxer-svc.labels" -}}
{{ include "fluxer-svc.selectorLabels" . }}
app.kubernetes.io/component: {{ .mode }}
app.kubernetes.io/part-of: fluxer
app.kubernetes.io/managed-by: {{ .root.Release.Service }}
helm.sh/chart: {{ include "fluxer-svc.chart" .root }}
{{- end }}
{{- define "fluxer-svc.envValue" -}}
{{- if and (kindIs "float64" .) (eq . (float64 (int64 .))) -}}
{{- int64 . | toString -}}
{{- else -}}
{{- toString . -}}
{{- end -}}
{{- end }}
{{- define "fluxer-svc.mergeEnv" -}}
{{- $out := dict -}}
{{- range $layer := . -}}
{{- range $k, $v := ($layer | default dict) -}}
{{- if kindIs "invalid" $v -}}
{{- $_ := unset $out $k -}}
{{- else -}}
{{- $_ := set $out $k $v -}}
{{- end -}}
{{- end -}}
{{- end -}}
{{- toYaml $out -}}
{{- end }}
{{- define "fluxer-svc.topologySpreadConstraints" -}}
{{- $out := list -}}
{{- range .constraints -}}
{{- if .labelSelector -}}
{{- $out = append $out . -}}
{{- else -}}
{{- $out = append $out (merge (dict "labelSelector" (dict "matchLabels" $.selector)) .) -}}
{{- end -}}
{{- end -}}
{{- toYaml $out -}}
{{- end }}
{{- define "fluxer-svc.pdb" -}}
{{- $out := dict -}}
{{- range $k := list "minAvailable" "maxUnavailable" -}}
{{- if and (hasKey $ $k) (not (kindIs "invalid" (index $ $k))) -}}
{{- $_ := set $out $k (index $ $k) -}}
{{- end -}}
{{- end -}}
{{- toYaml $out -}}
{{- end }}
{{- define "fluxer-svc.config" -}}
{{- $v := .root.Values -}}
{{- $levels := list (index $v .mode) (index .svc .mode) -}}
{{- $c := dict "extraEnv" ($v.extraEnv | default list) "envFrom" ($v.envFrom | default list) "podAnnotations" (deepCopy ($v.podAnnotations | default dict)) "probes" (deepCopy ($v.probes | default dict)) "image" (deepCopy (.svc.image | default dict)) -}}
{{- range $k := list "imagePullSecrets" "podSecurityContext" "securityContext" "topologySpreadConstraints" "nodeSelector" "tolerations" "affinity" (ternary "updateStrategy" "strategy" (eq .mode "shard")) -}}
{{- $_ := set $c $k (index $v $k) -}}
{{- end -}}
{{- $envLayers := list $v.env -}}
{{- range $level := $levels -}}
{{- range $k, $x := ($level | default dict) -}}
{{- if eq $k "env" -}}
{{- $envLayers = append $envLayers $x -}}
{{- else if has $k (list "podAnnotations" "image") -}}
{{- $_ := set $c $k (mergeOverwrite (index $c $k) (deepCopy ($x | default dict))) -}}
{{- else if has $k (list "extraEnv" "envFrom") -}}
{{- $_ := set $c $k (concat (index $c $k) ($x | default list)) -}}
{{- else if eq $k "probes" -}}
{{- range $name, $p := ($x | default dict) -}}
{{- $_ := set $c.probes $name $p -}}
{{- end -}}
{{- else -}}
{{- $_ := set $c $k $x -}}
{{- end -}}
{{- end -}}
{{- end -}}
{{- $_ := set $c "env" (include "fluxer-svc.mergeEnv" $envLayers | fromYaml) -}}
{{- toYaml $c }}
{{- end }}
{{- define "fluxer-svc.image" -}}
{{- $g := .root.Values.image -}}
{{- $i := .c.image -}}
{{- $repo := $i.repository | default (printf "%s/%s" $g.registry ($i.name | default (printf "fluxer-%s" .service))) -}}
{{- $ref := printf "%s:%s" $repo ($i.tag | default $g.tag) -}}
{{- with $i.digest }}{{ $ref = printf "%s@%s" $ref . }}{{ end -}}
{{- $ref -}}
{{- end }}
{{- define "fluxer-svc.pod" -}}
{{- $v := .root.Values -}}
{{- $c := .c -}}
metadata:
{{- with $c.podAnnotations }}
annotations:
{{- toYaml . | nindent 4 }}
{{- end }}
labels:
{{- include "fluxer-svc.labels" . | nindent 4 }}
spec:
{{- with $c.imagePullSecrets }}
imagePullSecrets:
{{- toYaml . | nindent 4 }}
{{- end }}
{{- with $c.podSecurityContext }}
securityContext:
{{- toYaml . | nindent 4 }}
{{- end }}
{{- if not (kindIs "invalid" $c.terminationGracePeriodSeconds) }}
terminationGracePeriodSeconds: {{ $c.terminationGracePeriodSeconds | int64 }}
{{- end }}
{{- with $c.nodeSelector }}
nodeSelector:
{{- toYaml . | nindent 4 }}
{{- end }}
{{- with $c.tolerations }}
tolerations:
{{- toYaml . | nindent 4 }}
{{- end }}
{{- with $c.affinity }}
affinity:
{{- toYaml . | nindent 4 }}
{{- end }}
{{- with $c.topologySpreadConstraints }}
topologySpreadConstraints:
{{- include "fluxer-svc.topologySpreadConstraints" (dict "constraints" . "selector" (include "fluxer-svc.selectorLabels" $ | fromYaml)) | nindent 4 }}
{{- end }}
containers:
- name: {{ .mode }}
image: {{ include "fluxer-svc.image" . | quote }}
imagePullPolicy: {{ $c.image.pullPolicy | default $v.image.pullPolicy }}
env:
- name: FLUXER_SVC_MODE
value: {{ .mode | quote }}
- name: FLUXER_SVC_NAME
value: {{ .service | quote }}
- name: FLUXER_SVC_SHARD_COUNT
value: {{ .shardCount | quote }}
- name: FLUXER_SVC_PORT
value: {{ include "fluxer-svc.envValue" $v.port | quote }}
{{- if not (kindIs "invalid" $c.buildVersion) }}
- name: BUILD_VERSION
value: {{ include "fluxer-svc.envValue" $c.buildVersion | quote }}
{{- end }}
{{- if eq .mode "shard" }}
- name: POD_NAME
valueFrom:
fieldRef:
apiVersion: v1
fieldPath: metadata.name
{{- end }}
{{- range $name, $value := $c.env }}
- name: {{ $name }}
value: {{ include "fluxer-svc.envValue" $value | quote }}
{{- end }}
{{- with $c.extraEnv }}
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with $c.envFrom }}
envFrom:
{{- toYaml . | nindent 8 }}
{{- end }}
ports:
- name: http
containerPort: {{ $v.port }}
protocol: TCP
{{- with $c.lifecycle }}
lifecycle:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- range $name := list "startup" "liveness" "readiness" }}
{{- with index $c.probes $name }}
{{ $name }}Probe:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- end }}
{{- with $c.resources }}
resources:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with $c.securityContext }}
securityContext:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with $c.extraVolumeMounts }}
volumeMounts:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with $c.extraVolumes }}
volumes:
{{- toYaml . | nindent 4 }}
{{- end }}
{{- end }}
@@ -1,145 +0,0 @@
{{- range $service, $svc := .Values.services }}
{{- if not (kindIs "invalid" $svc) }}
{{- $svc = $svc | default dict }}
{{- $rc := fromYaml (include "fluxer-svc.config" (dict "root" $ "svc" $svc "mode" "router")) }}
{{- $sc := fromYaml (include "fluxer-svc.config" (dict "root" $ "svc" $svc "mode" "shard")) }}
{{- $routerReplicas := ternary $rc.replicas 1 (hasKey $rc "replicas") | int64 }}
{{- $shardCount := ternary $sc.replicas 1 (hasKey $sc "replicas") | int64 }}
{{- if lt $shardCount 1 }}
{{- fail (printf "services.%s shard replicas must be at least 1" $service) }}
{{- end }}
{{- $router := dict "root" $ "service" $service "svc" $svc "mode" "router" "name" $service "c" $rc "shardCount" (toString $shardCount) }}
{{- $shard := dict "root" $ "service" $service "svc" $svc "mode" "shard" "name" (printf "%s-shard" $service) "c" $sc "shardCount" (toString $shardCount) }}
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ $service }}
namespace: {{ $.Release.Namespace }}
labels:
{{- include "fluxer-svc.labels" $router | nindent 4 }}
spec:
{{- if not $rc.hpa }}
replicas: {{ $routerReplicas }}
{{- end }}
{{- if not (kindIs "invalid" $rc.minReadySeconds) }}
minReadySeconds: {{ $rc.minReadySeconds | int64 }}
{{- end }}
selector:
matchLabels:
{{- include "fluxer-svc.selectorLabels" $router | nindent 6 }}
{{- with $rc.strategy }}
strategy:
{{- toYaml . | nindent 4 }}
{{- end }}
template:
{{- include "fluxer-svc.pod" $router | nindent 4 }}
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: {{ $service }}-shard
namespace: {{ $.Release.Namespace }}
labels:
{{- include "fluxer-svc.labels" $shard | nindent 4 }}
spec:
replicas: {{ $shardCount }}
{{- if not (kindIs "invalid" $sc.minReadySeconds) }}
minReadySeconds: {{ $sc.minReadySeconds | int64 }}
{{- end }}
podManagementPolicy: Parallel
serviceName: {{ $service }}-shard-headless
selector:
matchLabels:
{{- include "fluxer-svc.selectorLabels" $shard | nindent 6 }}
{{- with $sc.updateStrategy }}
updateStrategy:
{{- toYaml . | nindent 4 }}
{{- end }}
template:
{{- include "fluxer-svc.pod" $shard | nindent 4 }}
---
apiVersion: v1
kind: Service
metadata:
name: {{ $service }}
namespace: {{ $.Release.Namespace }}
labels:
{{- include "fluxer-svc.labels" $router | nindent 4 }}
spec:
type: ClusterIP
selector:
{{- include "fluxer-svc.selectorLabels" $router | nindent 4 }}
ports:
- name: http
port: {{ $.Values.port }}
targetPort: {{ $.Values.port }}
protocol: TCP
---
apiVersion: v1
kind: Service
metadata:
name: {{ $service }}-shard-headless
namespace: {{ $.Release.Namespace }}
labels:
{{- include "fluxer-svc.labels" $shard | nindent 4 }}
spec:
type: ClusterIP
clusterIP: None
publishNotReadyAddresses: true
selector:
{{- include "fluxer-svc.selectorLabels" $shard | nindent 4 }}
ports:
- name: http
port: {{ $.Values.port }}
targetPort: {{ $.Values.port }}
protocol: TCP
{{- with $rc.hpa }}
---
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: {{ $service }}
namespace: {{ $.Release.Namespace }}
labels:
{{- include "fluxer-svc.labels" $router | nindent 4 }}
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: {{ $service }}
minReplicas: {{ required (printf "services.%s router hpa.minReplicas is required" $service) .minReplicas | int64 }}
maxReplicas: {{ required (printf "services.%s router hpa.maxReplicas is required" $service) .maxReplicas | int64 }}
{{- if not (kindIs "invalid" .targetCPUUtilizationPercentage) }}
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: {{ .targetCPUUtilizationPercentage | int64 }}
{{- end }}
{{- with .behavior }}
behavior:
{{- toYaml . | nindent 4 }}
{{- end }}
{{- end }}
{{- range $ctx := list $router $shard }}
{{- with include "fluxer-svc.pdb" ($ctx.c.pdb | default dict) | fromYaml }}
---
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: {{ $ctx.name }}-pdb
namespace: {{ $.Release.Namespace }}
labels:
{{- include "fluxer-svc.labels" $ctx | nindent 4 }}
spec:
{{- toYaml . | nindent 2 }}
selector:
matchLabels:
{{- include "fluxer-svc.selectorLabels" $ctx | nindent 6 }}
{{- end }}
{{- end }}
{{- end }}
{{- end }}
-89
View File
@@ -1,89 +0,0 @@
image:
registry: ghcr.io/fluxerapp
tag: v1
pullPolicy: IfNotPresent
imagePullSecrets: []
env:
FLUXER_SVC_NATS_URL: nats://nats:4222
extraEnv: []
envFrom:
- secretRef:
name: fluxer-env
podAnnotations: {}
podSecurityContext:
runAsNonRoot: true
seccompProfile:
type: RuntimeDefault
securityContext:
allowPrivilegeEscalation: false
probes:
liveness:
httpGet:
path: /_healthz
port: http
readiness:
httpGet:
path: /_health
port: http
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 25%
maxUnavailable: 25%
updateStrategy:
type: RollingUpdate
topologySpreadConstraints: []
nodeSelector: {}
tolerations: []
affinity: {}
port: 8090
router:
replicas: 1
resources:
requests:
cpu: 50m
memory: 64Mi
limits:
memory: 192Mi
shard:
replicas: 2
probes:
startup:
httpGet:
path: /_healthz
port: http
periodSeconds: 10
failureThreshold: 30
resources:
requests:
cpu: 50m
memory: 96Mi
limits:
memory: 384Mi
services:
gifs:
shard:
env:
FLUXER_MEDIA_PROXY_PUBLIC_ENDPOINT: https://media.example.com
messages: {}
snowflakes: {}
unfurl:
shard:
env:
FLUXER_MEDIA_PROXY_ENDPOINT: http://media-proxy:8080
users: {}
-6
View File
@@ -1,6 +0,0 @@
apiVersion: v2
name: fluxer-web
description: Fluxer web app proxy and admin dashboard.
type: application
version: 0.1.0
appVersion: "v1"
@@ -1,80 +0,0 @@
{{- define "fluxer-web.chart" -}}
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}}
{{- end }}
{{- define "fluxer-web.selectorLabels" -}}
app.kubernetes.io/name: {{ .name }}
app.kubernetes.io/instance: {{ .root.Release.Name }}
{{- end }}
{{- define "fluxer-web.labels" -}}
{{ include "fluxer-web.selectorLabels" . }}
app.kubernetes.io/component: web
app.kubernetes.io/part-of: fluxer
app.kubernetes.io/managed-by: {{ .root.Release.Service }}
helm.sh/chart: {{ include "fluxer-web.chart" .root }}
{{- end }}
{{- define "fluxer-web.image" -}}
{{- $g := .root.Values.image | default dict -}}
{{- $i := .w.image | default dict -}}
{{- $repo := $i.repository -}}
{{- if not $repo -}}
{{- $repo = printf "%s/%s" (required "image.registry is required" $g.registry) ($i.name | default (printf "fluxer-%s" .name)) -}}
{{- end -}}
{{- $tag := required "image.tag is required" ($i.tag | default $g.tag) -}}
{{- if $i.digest -}}
{{- printf "%s:%s@%s" $repo $tag $i.digest | quote -}}
{{- else -}}
{{- printf "%s:%s" $repo $tag | quote -}}
{{- end -}}
{{- end }}
{{- define "fluxer-web.pick" -}}
{{- $v := ternary (get .w .key) (get .root.Values .key) (hasKey .w .key) -}}
{{- if $v }}
{{- toYaml $v }}
{{- end }}
{{- end }}
{{- define "fluxer-web.str" -}}
{{- if and (kindIs "float64" .) (eq . (floor .)) -}}
{{- int64 . | toString | quote -}}
{{- else -}}
{{- toString . | quote -}}
{{- end -}}
{{- end }}
{{- define "fluxer-web.env" -}}
{{- $env := dict -}}
{{- range $k, $val := .root.Values.env | default dict }}
{{- $_ := set $env $k $val }}
{{- end }}
{{- range $k, $val := .w.env | default dict }}
{{- $_ := set $env $k $val }}
{{- end }}
{{- range $k, $val := $env }}
{{- if not (kindIs "invalid" $val) }}
- name: {{ $k }}
value: {{ include "fluxer-web.str" $val }}
{{- end }}
{{- end }}
{{- with .w.buildVersion }}
- name: BUILD_VERSION
value: {{ include "fluxer-web.str" . }}
{{- end }}
{{- with concat (.root.Values.extraEnv | default list) (.w.extraEnv | default list) }}
{{ toYaml . }}
{{- end }}
{{- end }}
{{- define "fluxer-web.topologySpread" -}}
{{- $tscs := ternary .w.topologySpreadConstraints .root.Values.topologySpreadConstraints (hasKey .w "topologySpreadConstraints") -}}
{{- range $tscs }}
{{- $c := deepCopy . }}
{{- if not $c.labelSelector }}
{{- $_ := set $c "labelSelector" (dict "matchLabels" (include "fluxer-web.selectorLabels" $ | fromYaml)) }}
{{- end }}
- {{- toYaml $c | nindent 2 }}
{{- end }}
{{- end }}
@@ -1,172 +0,0 @@
{{- $v := .Values }}
{{- range $name, $w := .Values.workloads }}
{{- if not (kindIs "invalid" $w) }}
{{- $ctx := dict "root" $ "name" $name "w" $w }}
{{- $envFrom := concat ($v.envFrom | default list) ($w.envFrom | default list) }}
{{- $podAnnotations := merge (dict) ($w.podAnnotations | default dict) ($v.podAnnotations | default dict) }}
{{- $wProbes := $w.probes | default dict }}
{{- $gProbes := $v.probes | default dict }}
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ $name }}
namespace: {{ $.Release.Namespace }}
labels:
{{- include "fluxer-web.labels" $ctx | nindent 4 }}
spec:
{{- if not $w.hpa }}
replicas: {{ if kindIs "invalid" $w.replicas }}1{{ else }}{{ int $w.replicas }}{{ end }}
{{- end }}
{{- if not (kindIs "invalid" $w.minReadySeconds) }}
minReadySeconds: {{ int $w.minReadySeconds }}
{{- end }}
selector:
matchLabels:
{{- include "fluxer-web.selectorLabels" $ctx | nindent 6 }}
{{- with include "fluxer-web.pick" (dict "root" $ "w" $w "key" "strategy") }}
strategy:
{{- . | nindent 4 }}
{{- end }}
template:
metadata:
labels:
{{- include "fluxer-web.labels" $ctx | nindent 8 }}
{{- with $podAnnotations }}
annotations:
{{- toYaml . | nindent 8 }}
{{- end }}
spec:
{{- with include "fluxer-web.pick" (dict "root" $ "w" $w "key" "imagePullSecrets") }}
imagePullSecrets:
{{- . | nindent 8 }}
{{- end }}
{{- with include "fluxer-web.pick" (dict "root" $ "w" $w "key" "podSecurityContext") }}
securityContext:
{{- . | nindent 8 }}
{{- end }}
{{- if not (kindIs "invalid" $w.terminationGracePeriodSeconds) }}
terminationGracePeriodSeconds: {{ int $w.terminationGracePeriodSeconds }}
{{- end }}
{{- with include "fluxer-web.pick" (dict "root" $ "w" $w "key" "nodeSelector") }}
nodeSelector:
{{- . | nindent 8 }}
{{- end }}
{{- with include "fluxer-web.pick" (dict "root" $ "w" $w "key" "affinity") }}
affinity:
{{- . | nindent 8 }}
{{- end }}
{{- with include "fluxer-web.pick" (dict "root" $ "w" $w "key" "tolerations") }}
tolerations:
{{- . | nindent 8 }}
{{- end }}
{{- with include "fluxer-web.topologySpread" $ctx | trim }}
topologySpreadConstraints:
{{- . | nindent 8 }}
{{- end }}
containers:
- name: {{ $name }}
image: {{ include "fluxer-web.image" $ctx }}
imagePullPolicy: {{ ($w.image | default dict).pullPolicy | default ($v.image | default dict).pullPolicy | default "IfNotPresent" }}
{{- with include "fluxer-web.env" $ctx | trim }}
env:
{{- . | nindent 12 }}
{{- end }}
{{- with $envFrom }}
envFrom:
{{- toYaml . | nindent 12 }}
{{- end }}
ports:
- name: http
containerPort: 8080
protocol: TCP
{{- with $w.lifecycle }}
lifecycle:
{{- toYaml . | nindent 12 }}
{{- end }}
{{- range $probe := list "startup" "liveness" "readiness" }}
{{- with hasKey $wProbes $probe | ternary (get $wProbes $probe) (get $gProbes $probe) }}
{{ $probe }}Probe:
{{- toYaml . | nindent 12 }}
{{- end }}
{{- end }}
{{- with $w.resources }}
resources:
{{- toYaml . | nindent 12 }}
{{- end }}
{{- with include "fluxer-web.pick" (dict "root" $ "w" $w "key" "securityContext") }}
securityContext:
{{- . | nindent 12 }}
{{- end }}
{{- with $w.extraVolumeMounts }}
volumeMounts:
{{- toYaml . | nindent 12 }}
{{- end }}
{{- with $w.extraVolumes }}
volumes:
{{- toYaml . | nindent 8 }}
{{- end }}
---
apiVersion: v1
kind: Service
metadata:
name: {{ $name }}
namespace: {{ $.Release.Namespace }}
labels:
{{- include "fluxer-web.labels" $ctx | nindent 4 }}
spec:
type: ClusterIP
selector:
{{- include "fluxer-web.selectorLabels" $ctx | nindent 4 }}
ports:
- name: http
port: 8080
targetPort: http
protocol: TCP
{{- with $w.hpa }}
---
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: {{ $name }}
namespace: {{ $.Release.Namespace }}
labels:
{{- include "fluxer-web.labels" $ctx | nindent 4 }}
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: {{ $name }}
minReplicas: {{ required (printf "%s.hpa.minReplicas is required" $name) .minReplicas }}
maxReplicas: {{ required (printf "%s.hpa.maxReplicas is required" $name) .maxReplicas }}
{{- with .targetCPUUtilizationPercentage }}
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: {{ . }}
{{- end }}
{{- with .behavior }}
behavior:
{{- toYaml . | nindent 4 }}
{{- end }}
{{- end }}
{{- with $w.pdb }}
---
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: {{ $name }}-pdb
namespace: {{ $.Release.Namespace }}
labels:
{{- include "fluxer-web.labels" $ctx | nindent 4 }}
spec:
{{- toYaml . | nindent 2 }}
selector:
matchLabels:
{{- include "fluxer-web.selectorLabels" $ctx | nindent 6 }}
{{- end }}
{{- end }}
{{- end }}
-83
View File
@@ -1,83 +0,0 @@
image:
registry: ghcr.io/fluxerapp
tag: v1
pullPolicy: IfNotPresent
imagePullSecrets: []
env: {}
extraEnv: []
envFrom:
- secretRef:
name: fluxer-env
podAnnotations: {}
podSecurityContext:
runAsNonRoot: true
seccompProfile:
type: RuntimeDefault
securityContext:
allowPrivilegeEscalation: false
probes:
startup:
httpGet:
path: /_health
port: http
periodSeconds: 10
failureThreshold: 30
liveness:
httpGet:
path: /_health
port: http
readiness:
httpGet:
path: /_health
port: http
strategy:
type: RollingUpdate
topologySpreadConstraints: []
nodeSelector: {}
tolerations: []
affinity: {}
workloads:
admin:
image:
name: fluxer-admin
replicas: 1
env:
FLUXER_ENV: production
FLUXER_API_ENDPOINT: https://api.example.com
FLUXER_ADMIN_ENDPOINT: https://admin.example.com
FLUXER_MEDIA_ENDPOINT: https://media.example.com
FLUXER_APP_ENDPOINT: https://web.example.com
resources:
requests:
cpu: 50m
memory: 96Mi
limits:
memory: 384Mi
app-proxy:
image:
name: fluxer-app-proxy-self-hosted
replicas: 1
env:
RELEASE_CHANNEL: stable
PUBLIC_BOOTSTRAP_API_ENDPOINT: /api
PUBLIC_BOOTSTRAP_API_PUBLIC_ENDPOINT: https://web.example.com/api
resources:
requests:
cpu: 50m
memory: 96Mi
limits:
memory: 384Mi
+11
View File
@@ -0,0 +1,11 @@
.DS_Store
.git/
.gitignore
*.swp
*.bak
*.tmp
*.orig
*~
.idea/
.vscode/
ci/
+13
View File
@@ -0,0 +1,13 @@
apiVersion: v2
name: fluxer
description: A complete Fluxer instance on Kubernetes, at parity with the self-hosted Docker Compose stack.
type: application
version: 0.1.0
appVersion: "v1"
kubeVersion: ">=1.29.0-0"
home: https://docs.fluxer.app/operator/kubernetes/
sources:
- https://github.com/fluxerapp/fluxer/tree/main/deploy/helm/fluxer
keywords:
- fluxer
- chat
@@ -0,0 +1 @@
domain: chat.example.test
@@ -0,0 +1,3 @@
domain: chat.example.test
secrets:
existingSecret: fluxer-ci-secret
@@ -0,0 +1,38 @@
domain: chat.example.test
postgres:
enabled: false
external:
host: db.example.test
ssl: "true"
valkey:
enabled: false
nats:
enabled: false
external:
url: nats://mq.example.test:4222
meilisearch:
enabled: false
external:
url: https://search.example.test
seaweedfs:
enabled: false
s3:
endpoint: https://s3.example.test
publicEndpoint: https://cdn.example.test
region: eu-central-1
forcePathStyle: false
livekit:
enabled: false
external:
url: http://livekit.voice.svc:7880
publicUrl: wss://livekit.example.test
secrets:
values:
FLUXER_POSTGRES_PASSWORD: ci-postgres-password
FLUXER_SEARCH_API_KEY: ci-search-key
FLUXER_S3_SECRET_ACCESS_KEY: ci-s3-secret
FLUXER_NATS_AUTH_TOKEN: ci-nats-token
FLUXER_LIVEKIT_API_KEY: ci-livekit-key
FLUXER_LIVEKIT_API_SECRET: ci-livekit-secret-of-at-least-32-characters
extra:
FLUXER_KV_URL: redis://:[email protected]:6379/0
@@ -0,0 +1,11 @@
domain: chat.example.test
edge:
expose: gateway
gateway:
parentRefs:
- name: shared
namespace: gateway-system
timeouts:
request: 0s
livekit:
expose: nodePort
@@ -0,0 +1,10 @@
domain: chat.example.test
email: [email protected]
edge:
expose: loadBalancer
loadBalancer:
http3: true
livekit:
expose: loadBalancer
nodeIP: 203.0.113.10
loadBalancerIP: 203.0.113.10
+15
View File
@@ -0,0 +1,15 @@
domain: chat.example.test
networkPolicy:
enabled: false
livekit:
enabled: false
nats:
replicas: 3
initImage:
repository: registry.example.test/busybox
digest: sha256:0000000000000000000000000000000000000000000000000000000000000000
postgres:
image:
repository: registry.example.test/postgres
tag: "16"
runAsUser: 999
@@ -0,0 +1,13 @@
domain: chat.example.test
networkPolicy:
ingressNamespaceSelector:
matchLabels:
kubernetes.io/metadata.name: ingress-nginx
edge:
ingress:
className: nginx
livekit:
nodeSelector:
kubernetes.io/hostname: voice-1
nodeCIDRs:
- 10.0.0.12/32
+64
View File
@@ -0,0 +1,64 @@
{
servers {
trusted_proxies static {$FLUXER_EDGE_TRUSTED_PROXIES:private_ranges}
trusted_proxies_strict
}
{$FLUXER_EDGE_GLOBAL_OPTIONS}
}
{$FLUXER_EDGE_SITE_ADDRESS} {
encode {$FLUXER_EDGE_ENCODE:zstd gzip}
handle /_health {
respond "OK" 200
}
handle_path /api/* {
reverse_proxy api:8080
}
handle /gateway {
rewrite * /
reverse_proxy gateway:8080
}
handle_path /gateway/* {
reverse_proxy gateway:8080
}
handle_path /media/* {
reverse_proxy media-proxy:8080
}
handle_path /livekit/* {
reverse_proxy livekit:7880
}
handle {$FLUXER_ADMIN_BASE_PATH:/admin} {
rewrite * /
reverse_proxy admin:8080
}
handle_path {$FLUXER_ADMIN_BASE_PATH:/admin}/* {
reverse_proxy admin:8080
}
@staticAssets path /web/* /emoji/* /libs/* /avatars/* /badges/* /desktop/*
handle @staticAssets {
reverse_proxy static-proxy:8080
}
handle /.well-known/fluxer {
reverse_proxy api:8080
}
handle {
reverse_proxy app-proxy:8080
}
}
:8088 {
handle /.well-known/fluxer {
reverse_proxy api:8080
}
}
@@ -0,0 +1,32 @@
trap 'exit 0' TERM INT
buckets="$FLUXER_S3_BUCKET_CDN $FLUXER_S3_BUCKET_UPLOADS $FLUXER_S3_BUCKET_REPORTS $FLUXER_S3_BUCKET_HARVESTS"
missing="$buckets"
for attempt in $(seq 1 "$FLUXER_SEAWEEDFS_INIT_ATTEMPTS"); do
if ! nc -z 127.0.0.1 9333 2>/dev/null; then
sleep 2
continue
fi
listed=$(echo "s3.bucket.list" | timeout 10 weed shell -master=127.0.0.1:9333 2>&1)
missing=""
for b in $buckets; do
echo "$listed" | grep -q "^[[:space:]]*$b[[:space:]]" || missing="${missing:+$missing }$b"
done
if [ -z "$missing" ]; then
if ! echo "s3.configure -user=fluxer -access_key=$FLUXER_S3_ACCESS_KEY -secret_key=$FLUXER_S3_SECRET_KEY -actions=Admin,Read,Write,List,Tagging -apply" | timeout 10 weed shell -master=127.0.0.1:9333 >/dev/null 2>&1; then
echo "seaweedfs-init could not configure the S3 identity" >&2
exit 1
fi
echo "buckets ready"
touch /tmp/fluxer-buckets-ready
while :; do
sleep 86400 &
wait $!
done
fi
for b in $missing; do
echo "s3.bucket.create -name $b" | timeout 10 weed shell -master=127.0.0.1:9333 >/dev/null 2>&1
done
sleep 2
done
echo "seaweedfs-init could not verify buckets: $missing" >&2
exit 1
+72
View File
@@ -0,0 +1,72 @@
{{- include "fluxer.validate" . }}
{{- $v := .Values }}
{{- $o := include "fluxer.origin" . }}
Fluxer is installing in namespace {{ .Release.Namespace }}. Run one release per namespace.
Open {{ $o }} once every pod is ready:
kubectl -n {{ .Release.Namespace }} get pods
The admin panel is at {{ $o }}{{ $v.adminBasePath }}.
The setup wizard asks for the setup token before it creates the admin account:
kubectl -n {{ .Release.Namespace }} get secret {{ include "fluxer.secretName" . }} -o jsonpath='{.data.FLUXER_SETUP_TOKEN}' | base64 -d
A Secret with no FLUXER_SETUP_TOKEN has a token the API wrote to its log once:
kubectl -n {{ .Release.Namespace }} logs deployment/api | grep -i 'setup token'
{{- if eq $v.edge.expose "loadBalancer" }}
Point DNS for {{ $v.domain }} at the external address of the edge-public Service:
kubectl -n {{ .Release.Namespace }} get service edge-public
{{- else if eq $v.edge.expose "none" }}
Nothing exposes the instance yet. Send your proxy or tunnel to the Service edge
on port 8080, with the Host header {{ $v.domain }}.
{{- else }}
Point DNS for {{ $v.domain }} at your {{ ternary "ingress controller" "gateway" (eq $v.edge.expose "ingress") }}. Its Service needs
externalTrafficPolicy Local or the PROXY protocol, or every client shares one IP
and registration hits its per-IP limit.
{{- end }}
{{- if $v.livekit.external.url }}
Voice uses your LiveKit server at {{ $v.livekit.external.publicUrl }}. Point its webhook at
{{ $o }}/api/webhooks/livekit, signed with the key in FLUXER_LIVEKIT_API_KEY.
{{- end }}
{{- if $v.livekit.enabled }}
Voice media does not pass through the edge. Open these ports to the internet:
{{- if eq $v.livekit.expose "hostNetwork" }}
{{ $v.livekit.tcpPort }}/tcp and {{ $v.livekit.udpPort }}/udp on the node that runs the livekit pod.
hostNetwork needs a namespace that allows privileged pods. Keep 7880 closed.
{{- else if eq $v.livekit.expose "nodePort" }}
{{ $v.livekit.nodePorts.tcp }}/tcp and {{ $v.livekit.nodePorts.udp }}/udp on the node that runs the livekit pod.
{{- else }}
{{ $v.livekit.tcpPort }}/tcp and {{ $v.livekit.udpPort }}/udp on the load balancer of the Service livekit-rtc,
whose public IP must be {{ $v.livekit.nodeIP }}.
{{- end }}
{{- end }}
Back up the Secret {{ include "fluxer.secretName" . }}{{ if $v.postgres.enabled }}, the database in postgres-0{{ end }}{{ if $v.seaweedfs.enabled }} and the uploads on the volume data-seaweedfs-0{{ end }}.
{{- if $v.secrets.existingSecret }}
After editing the Secret {{ $v.secrets.existingSecret }}, restart the release:
kubectl -n {{ .Release.Namespace }} rollout restart deployment,statefulset -l app.kubernetes.io/instance={{ .Release.Name }}
{{- end }}
{{- if and $v.secrets.generate (not $v.secrets.existingSecret) }}
Argo CD and helm template cannot read the existing Secret and would generate
new secrets on every render. Under them, set secrets.generate=false and use
secrets.existingSecret or give every key in secrets.values.
{{- end }}
To move to the latest v1 images, pull the repository and upgrade with your values file:
helm upgrade {{ .Release.Name }} <chart> -n {{ .Release.Namespace }} -f <your values file> --set-string image.revision=$(date +%s)
Leave out --reuse-values, which keeps the defaults of the old chart version.
Documentation: https://docs.fluxer.app/operator/kubernetes/
+109
View File
@@ -0,0 +1,109 @@
{{- define "fluxer.s3Endpoint" -}}
{{- .Values.s3.endpoint | default (ternary "http://seaweedfs:8333" "" .Values.seaweedfs.enabled) }}
{{- end }}
{{- define "fluxer.configData" -}}
{{- $v := .Values }}
{{- $o := include "fluxer.origin" . }}
{{- $hostOrigin := include "fluxer.hostOrigin" . }}
{{- $media := printf "%s/media" $o }}
{{- $s3 := include "fluxer.s3Endpoint" . }}
{{- $c := dict }}
{{- $_ := set $c "FLUXER_ENV" "production" }}
{{- $_ := set $c "FLUXER_SELF_HOSTED" "true" }}
{{- $_ := set $c "FLUXER_DATABASE_BACKEND" "postgres" }}
{{- $_ := set $c "FLUXER_TRUST_CLIENT_IP_HEADER" "true" }}
{{- $_ := set $c "FLUXER_BASE_DOMAIN" $v.domain }}
{{- $_ := set $c "FLUXER_PUBLIC_SCHEME" $v.scheme }}
{{- $_ := set $c "FLUXER_PUBLIC_PORT" (include "fluxer.string" $v.port) }}
{{- $_ := set $c "FLUXER_PUBLIC_ORIGIN" (trimSuffix "/" ($v.publicOrigin | default "")) }}
{{- $_ := set $c "FLUXER_APP_ENDPOINT" $o }}
{{- $_ := set $c "FLUXER_MARKETING_ENDPOINT" $o }}
{{- $_ := set $c "FLUXER_MEDIA_ENDPOINT" $media }}
{{- $_ := set $c "FLUXER_MEDIA_PROXY_PUBLIC_ENDPOINT" $media }}
{{- $_ := set $c "FLUXER_MEDIA_PROXY_UPLOAD_RELAY_ENDPOINT" $media }}
{{- $_ := set $c "FLUXER_GATEWAY_MEDIA_PROXY_ENDPOINT" $media }}
{{- $_ := set $c "FLUXER_ADMIN_ENDPOINT" (printf "%s%s" $o $v.adminBasePath) }}
{{- $_ := set $c "FLUXER_GATEWAY_STATIC_CDN_ENDPOINT" $o }}
{{- $_ := set $c "FLUXER_MEDIA_PROXY_CORS_ALLOWED_ORIGINS" $hostOrigin }}
{{- $_ := set $c "FLUXER_PASSKEY_RP_ID" $v.domain }}
{{- $_ := set $c "FLUXER_PASSKEY_ADDITIONAL_ALLOWED_ORIGINS" $o }}
{{- if $v.postgres.enabled }}
{{- $_ := set $c "FLUXER_POSTGRES_HOST" "postgres" }}
{{- $_ := set $c "FLUXER_POSTGRES_PORT" "5432" }}
{{- $_ := set $c "FLUXER_POSTGRES_DATABASE" $v.postgres.database }}
{{- $_ := set $c "FLUXER_POSTGRES_USERNAME" $v.postgres.username }}
{{- else }}
{{- with $v.postgres.external }}
{{- $_ := set $c "FLUXER_POSTGRES_HOST" (.host | default "") }}
{{- $_ := set $c "FLUXER_POSTGRES_PORT" (include "fluxer.string" (.port | default "")) }}
{{- $_ := set $c "FLUXER_POSTGRES_DATABASE" (.database | default "") }}
{{- $_ := set $c "FLUXER_POSTGRES_USERNAME" (.username | default "") }}
{{- $_ := set $c "FLUXER_POSTGRES_SSL" (include "fluxer.string" (.ssl | default "")) }}
{{- end }}
{{- end }}
{{- if not $v.valkey.enabled }}
{{- $_ := set $c "FLUXER_KV_URL" ($v.valkey.external.url | default "") }}
{{- end }}
{{- $nats := ternary "nats://nats:4222" ($v.nats.external.url | default "") $v.nats.enabled }}
{{- $_ := set $c "FLUXER_NATS_URL" $nats }}
{{- $_ := set $c "FLUXER_NATS_JETSTREAM_URL" $nats }}
{{- $_ := set $c "FLUXER_SVC_NATS_URL" $nats }}
{{- $_ := set $c "FLUXER_SEARCH_ENGINE" "meilisearch" }}
{{- $_ := set $c "FLUXER_SEARCH_URL" (ternary "http://meilisearch:7700" ($v.meilisearch.external.url | default "") $v.meilisearch.enabled) }}
{{- $_ := set $c "FLUXER_S3_ENDPOINT" $s3 }}
{{- $_ := set $c "FLUXER_S3_PUBLIC_ENDPOINT" ($v.s3.publicEndpoint | default $s3) }}
{{- $_ := set $c "FLUXER_S3_REGION" $v.s3.region }}
{{- $_ := set $c "FLUXER_S3_FORCE_PATH_STYLE" (toString $v.s3.forcePathStyle) }}
{{- $_ := set $c "FLUXER_S3_BUCKET_CDN" $v.s3.buckets.cdn }}
{{- $_ := set $c "FLUXER_S3_BUCKET_UPLOADS" $v.s3.buckets.uploads }}
{{- $_ := set $c "FLUXER_S3_BUCKET_REPORTS" $v.s3.buckets.reports }}
{{- $_ := set $c "FLUXER_S3_BUCKET_HARVESTS" $v.s3.buckets.harvests }}
{{- $livekit := include "fluxer.livekitMode" . }}
{{- $_ := set $c "FLUXER_LIVEKIT_ENABLED" (toString (ne $livekit "off")) }}
{{- if eq $livekit "bundled" }}
{{- $_ := set $c "FLUXER_LIVEKIT_INTERNAL_URL" "http://livekit:7880" }}
{{- $_ := set $c "FLUXER_LIVEKIT_DEFAULT_REGION" $v.livekit.defaultRegion }}
{{- $_ := set $c "FLUXER_LIVEKIT_URL" (printf "%s/livekit" $hostOrigin) }}
{{- else if eq $livekit "external" }}
{{- $_ := set $c "FLUXER_LIVEKIT_INTERNAL_URL" $v.livekit.external.url }}
{{- $_ := set $c "FLUXER_LIVEKIT_DEFAULT_REGION" $v.livekit.defaultRegion }}
{{- $_ := set $c "FLUXER_LIVEKIT_URL" $v.livekit.external.publicUrl }}
{{- end }}
{{- $_ := set $c "FLUXER_API_PRESIGNED_HARVEST_DOWNLOADS_ENABLED" "false" }}
{{- $_ := set $c "FLUXER_EMAIL_FROM_EMAIL" "noreply@localhost" }}
{{- $_ := set $c "FLUXER_VAPID_EMAIL" ($v.email | default (printf "admin@%s" $v.domain)) }}
{{- $_ := set $c "FLUXER_INTERNAL_API_ENDPOINT" "http://api:8080" }}
{{- $_ := set $c "FLUXER_INTERNAL_MEDIA_PROXY_ENDPOINT" "http://media-proxy:8080" }}
{{- range $k, $val := $v.env | default dict }}
{{- $_ := set $c $k (include "fluxer.string" ($val | default "")) }}
{{- end }}
{{- $out := dict }}
{{- range $k, $val := $c }}
{{- if $val }}
{{- $_ := set $out $k $val }}
{{- end }}
{{- end }}
{{- toYaml $out }}
{{- end }}
{{- define "fluxer.appProxyConfigData" -}}
{{- $v := .Values }}
{{- $s3 := include "fluxer.s3Endpoint" . }}
{{- $c := dict }}
{{- $_ := set $c "FLUXER_APP_PROXY_PORT" "8080" }}
{{- $_ := set $c "DISCOVERY_UPSTREAM_URL" "http://api:8080/.well-known/fluxer" }}
{{- $_ := set $c "FLUXER_S3_PUBLIC_ENDPOINT" ($v.s3.publicEndpoint | default $s3) }}
{{- $_ := set $c "FLUXER_S3_BUCKET_UPLOADS" $v.s3.buckets.uploads }}
{{- $_ := set $c "FLUXER_MEDIA_ENDPOINT" (printf "%s/media" (include "fluxer.origin" .)) }}
{{- range $k, $val := $v.appProxy.config | default dict }}
{{- $_ := set $c $k (include "fluxer.string" ($val | default "")) }}
{{- end }}
{{- $out := dict }}
{{- range $k, $val := $c }}
{{- if $val }}
{{- $_ := set $out $k $val }}
{{- end }}
{{- end }}
{{- toYaml $out }}
{{- end }}
+375
View File
@@ -0,0 +1,375 @@
{{- define "fluxer.chart" -}}
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}}
{{- end }}
{{- define "fluxer.selectorLabels" -}}
app.kubernetes.io/name: {{ .name }}
app.kubernetes.io/instance: {{ .root.Release.Name }}
{{- end }}
{{- define "fluxer.podLabels" -}}
{{ include "fluxer.selectorLabels" . }}
{{- with .component }}
app.kubernetes.io/component: {{ . }}
{{- end }}
app.kubernetes.io/part-of: fluxer
{{- end }}
{{- define "fluxer.labels" -}}
{{ include "fluxer.podLabels" . }}
app.kubernetes.io/managed-by: {{ .root.Release.Service }}
app.kubernetes.io/version: {{ .root.Chart.AppVersion | quote }}
helm.sh/chart: {{ include "fluxer.chart" .root }}
{{- end }}
{{- define "fluxer.string" -}}
{{- if and (kindIs "float64" .) (eq . (float64 (int64 .))) }}
{{- int64 . | toString }}
{{- else }}
{{- toString . }}
{{- end }}
{{- end }}
{{- define "fluxer.origin" -}}
{{- $v := .Values }}
{{- if $v.publicOrigin }}
{{- trimSuffix "/" $v.publicOrigin }}
{{- else }}
{{- $default := ternary 443 80 (eq $v.scheme "https") }}
{{- if eq (int $v.port) $default }}
{{- printf "%s://%s" $v.scheme $v.domain }}
{{- else }}
{{- printf "%s://%s:%d" $v.scheme $v.domain (int $v.port) }}
{{- end }}
{{- end }}
{{- end }}
{{- define "fluxer.hostOrigin" -}}
{{- $v := .Values }}
{{- if $v.publicOrigin }}
{{- trimSuffix "/" $v.publicOrigin }}
{{- else }}
{{- printf "%s://%s:%d" $v.scheme $v.domain (int $v.port) }}
{{- end }}
{{- end }}
{{- define "fluxer.secretName" -}}
{{- .Values.secrets.existingSecret | default "fluxer-env" }}
{{- end }}
{{- define "fluxer.thirdPartyImage" -}}
{{- $ref := printf "%s:%s" .repository (include "fluxer.string" .tag) }}
{{- with .digest }}
{{- $ref = printf "%s@%s" $ref . }}
{{- end }}
{{- $ref | quote }}
{{- end }}
{{- define "fluxer.image" -}}
{{- $v := .root.Values.image }}
{{- $img := (.w | default dict).image | default dict }}
{{- $registry := $img.registry | default $v.registry }}
{{- $tag := $img.tag | default (get ($v.tags | default dict) .image) | default $v.tag }}
{{- $ref := printf "%s/%s:%s" $registry .image (include "fluxer.string" $tag) }}
{{- with $img.digest | default (get ($v.digests | default dict) .image) }}
{{- $ref = printf "%s@%s" $ref . }}
{{- end }}
{{- $ref | quote }}
{{- end }}
{{- define "fluxer.pullPolicy" -}}
{{- (((.w | default dict).image | default dict).pullPolicy) | default .root.Values.image.pullPolicy }}
{{- end }}
{{- define "fluxer.pullSecrets" -}}
{{- with .Values.image.pullSecrets }}
imagePullSecrets:
{{- toYaml . | nindent 2 }}
{{- end }}
{{- end }}
{{- define "fluxer.podSecurityContext" -}}
runAsNonRoot: true
{{- with .uid }}
runAsUser: {{ . }}
runAsGroup: {{ $.gid | default . }}
fsGroup: {{ $.gid | default . }}
fsGroupChangePolicy: OnRootMismatch
{{- end }}
seccompProfile:
type: RuntimeDefault
{{- end }}
{{- define "fluxer.containerSecurityContext" -}}
allowPrivilegeEscalation: false
capabilities:
drop:
- ALL
{{- end }}
{{- define "fluxer.scheduling" -}}
{{- $w := .w | default dict }}
{{- with $w.nodeSelector }}
nodeSelector:
{{- toYaml . | nindent 2 }}
{{- end }}
{{- with $w.tolerations }}
tolerations:
{{- toYaml . | nindent 2 }}
{{- end }}
{{- with $w.affinity }}
affinity:
{{- toYaml . | nindent 2 }}
{{- end }}
{{- $spread := list }}
{{- range $w.topologySpreadConstraints }}
{{- $c := deepCopy . }}
{{- if not (hasKey $c "labelSelector") }}
{{- $_ := set $c "labelSelector" (dict "matchLabels" (include "fluxer.selectorLabels" $ | fromYaml)) }}
{{- end }}
{{- $spread = append $spread $c }}
{{- end }}
{{- with $spread }}
topologySpreadConstraints:
{{- toYaml . | nindent 2 }}
{{- end }}
{{- end }}
{{- define "fluxer.pvc" -}}
- metadata:
name: data
spec:
accessModes:
- ReadWriteOnce
{{- with .storageClassName }}
storageClassName: {{ . }}
{{- end }}
resources:
requests:
storage: {{ .size }}
{{- end }}
{{- define "fluxer.vapidPair" -}}
{{- $der := genPrivateKey "ecdsa" | replace "-----BEGIN EC PRIVATE KEY-----" "" | replace "-----END EC PRIVATE KEY-----" "" | nospace | b64dec -}}
private: {{ substr 7 39 $der | b64enc | replace "+" "-" | replace "/" "_" | trimAll "=" }}
public: {{ substr 56 121 $der | b64enc | replace "+" "-" | replace "/" "_" | trimAll "=" }}
{{- end }}
{{- define "fluxer.livekitMode" -}}
{{- if .Values.livekit.enabled }}bundled{{ else if .Values.livekit.external.url }}external{{ else }}off{{ end }}
{{- end }}
{{- define "fluxer.noGenerateKeys" -}}
{{- $keys := list }}
{{- $extra := .Values.secrets.extra | default dict }}
{{- if and (not .Values.postgres.enabled) (not (get $extra "FLUXER_POSTGRES_URL")) }}
{{- $keys = append $keys "FLUXER_POSTGRES_PASSWORD" }}
{{- end }}
{{- if not .Values.meilisearch.enabled }}
{{- $keys = append $keys "FLUXER_SEARCH_API_KEY" }}
{{- end }}
{{- if not .Values.seaweedfs.enabled }}
{{- $keys = append $keys "FLUXER_S3_SECRET_ACCESS_KEY" }}
{{- end }}
{{- if eq (include "fluxer.livekitMode" .) "external" }}
{{- $keys = append $keys "FLUXER_LIVEKIT_API_SECRET" }}
{{- end }}
{{- toJson $keys }}
{{- end }}
{{- define "fluxer.requiredSecretKeys" -}}
{{- $keys := list }}
{{- $extra := .Values.secrets.extra | default dict }}
{{- range $k, $_ := .Values.secrets.values }}
{{- if and (eq $k "FLUXER_POSTGRES_PASSWORD") (get $extra "FLUXER_POSTGRES_URL") }}
{{- else if and (hasPrefix "FLUXER_LIVEKIT_" $k) (eq (include "fluxer.livekitMode" $) "off") }}
{{- else if and (eq $k "FLUXER_VALKEY_PASSWORD") (not $.Values.valkey.enabled) }}
{{- else if and (eq $k "FLUXER_NATS_AUTH_TOKEN") (not $.Values.nats.enabled) }}
{{- else if and (eq $k "FLUXER_SETUP_TOKEN") (or $.Values.secrets.existingSecret (and (not $.Values.secrets.generate) (not (include "fluxer.string" (get $.Values.secrets.values $k | default ""))))) }}
{{- else }}
{{- $keys = append $keys $k }}
{{- end }}
{{- end }}
{{- toJson $keys }}
{{- end }}
{{- define "fluxer.resolveSecrets" -}}
{{- $v := .Values }}
{{- $existing := dict }}
{{- $found := lookup "v1" "Secret" .Release.Namespace (include "fluxer.secretName" .) }}
{{- range $k, $val := ($found.data | default dict) }}
{{- $_ := set $existing $k (b64dec $val) }}
{{- end }}
{{- $out := dict }}
{{- if $v.secrets.existingSecret }}
{{- $out = $existing }}
{{- else }}
{{- $noGenerate := include "fluxer.noGenerateKeys" . | fromJsonArray }}
{{- $explicit := $v.secrets.values | default dict }}
{{- $vapidPublic := include "fluxer.string" (get $explicit "FLUXER_VAPID_PUBLIC_KEY" | default "") }}
{{- $vapidPrivate := include "fluxer.string" (get $explicit "FLUXER_VAPID_PRIVATE_KEY" | default "") }}
{{- if and $vapidPublic $vapidPrivate }}
{{- else if or $vapidPublic $vapidPrivate }}
{{- fail "secrets.values.FLUXER_VAPID_PUBLIC_KEY and secrets.values.FLUXER_VAPID_PRIVATE_KEY must be set together, or both left empty to generate a pair" }}
{{- else if and (get $existing "FLUXER_VAPID_PUBLIC_KEY") (get $existing "FLUXER_VAPID_PRIVATE_KEY") }}
{{- $vapidPublic = get $existing "FLUXER_VAPID_PUBLIC_KEY" }}
{{- $vapidPrivate = get $existing "FLUXER_VAPID_PRIVATE_KEY" }}
{{- else if $v.secrets.generate }}
{{- $pair := include "fluxer.vapidPair" . | fromYaml }}
{{- $vapidPublic = $pair.public }}
{{- $vapidPrivate = $pair.private }}
{{- else }}
{{- fail "secrets.values.FLUXER_VAPID_PUBLIC_KEY and secrets.values.FLUXER_VAPID_PRIVATE_KEY are empty and secrets.generate is false" }}
{{- end }}
{{- $_ := set $out "FLUXER_VAPID_PUBLIC_KEY" $vapidPublic }}
{{- $_ := set $out "FLUXER_VAPID_PRIVATE_KEY" $vapidPrivate }}
{{- $required := include "fluxer.requiredSecretKeys" . | fromJsonArray }}
{{- $outsideToken := include "fluxer.string" (get $explicit "FLUXER_NATS_AUTH_TOKEN" | default "") }}
{{- if and $outsideToken (not $v.nats.enabled) }}
{{- $_ := set $out "FLUXER_NATS_AUTH_TOKEN" $outsideToken }}
{{- end }}
{{- range $k := $required }}
{{- if not (hasPrefix "FLUXER_VAPID_" $k) }}
{{- $given := include "fluxer.string" (get $explicit $k | default "") }}
{{- if $given }}
{{- $_ := set $out $k $given }}
{{- else if has $k $noGenerate }}
{{- fail (printf "secrets.values.%s is required when the matching service is not bundled" $k) }}
{{- else if get $existing $k }}
{{- $_ := set $out $k (get $existing $k) }}
{{- else if $v.secrets.generate }}
{{- if eq $k "FLUXER_MEDIA_PROXY_UPLOAD_RELAY_SECRET_BASE64" }}
{{- $_ := set $out $k (randBytes 32) }}
{{- else }}
{{- $_ := set $out $k (randAlphaNum 64) }}
{{- end }}
{{- else }}
{{- fail (printf "secrets.values.%s is empty and secrets.generate is false" $k) }}
{{- end }}
{{- end }}
{{- end }}
{{- range $k, $val := $v.secrets.extra | default dict }}
{{- $s := include "fluxer.string" ($val | default "") }}
{{- if $s }}
{{- $_ := set $out $k $s }}
{{- end }}
{{- end }}
{{- end }}
{{- toJson $out }}
{{- end }}
{{- define "fluxer.secretData" -}}
{{- if not (hasKey .Values "fluxerResolvedSecrets") }}
{{- $_ := set .Values "fluxerResolvedSecrets" (include "fluxer.resolveSecrets" . | fromJson) }}
{{- end }}
{{- toJson (get .Values "fluxerResolvedSecrets") }}
{{- end }}
{{- define "fluxer.secretEnv" -}}
- name: {{ .name }}
valueFrom:
secretKeyRef:
name: {{ include "fluxer.secretName" .root }}
key: {{ .key }}
{{- end }}
{{- define "fluxer.instanceChecksum" -}}
{{- $parts := list (include "fluxer.configData" .) (include "fluxer.appProxyConfigData" .) (toJson .Values.secrets.values) (toJson (.Values.secrets.extra | default dict)) (include "fluxer.string" .Values.image.revision) (include "fluxer.secretData" .) }}
{{- join "\n" $parts | sha256sum }}
{{- end }}
{{- define "fluxer.envList" -}}
{{- $w := .w | default dict }}
{{- $own := $w.env | default dict }}
{{- $base := .env | default list }}
{{- $names := list }}
{{- range $base }}
{{- $names = append $names .name }}
{{- end }}
{{- $out := list }}
{{- range $base }}
{{- if hasKey $own .name }}
{{- $val := get $own .name }}
{{- if not (kindIs "invalid" $val) }}
{{- $out = append $out (dict "name" .name "value" (include "fluxer.string" $val)) }}
{{- end }}
{{- else }}
{{- $out = append $out . }}
{{- end }}
{{- end }}
{{- range $k := keys $own | sortAlpha }}
{{- $val := get $own $k }}
{{- if and (not (has $k $names)) (not (kindIs "invalid" $val)) }}
{{- $out = append $out (dict "name" $k "value" (include "fluxer.string" $val)) }}
{{- end }}
{{- end }}
{{- $out = concat $out ($w.extraEnv | default list) }}
{{- with $out }}
{{- toYaml . }}
{{- end }}
{{- end }}
{{- define "fluxer.probes" -}}
{{- $own := (.w | default dict).probes | default dict }}
{{- range $probe := list "startup" "liveness" "readiness" }}
{{- $p := get ($.probes | default dict) $probe }}
{{- if hasKey $own $probe }}
{{- $p = get $own $probe }}
{{- end }}
{{- with $p }}
{{ $probe }}Probe:
{{- toYaml . | nindent 2 }}
{{- end }}
{{- end }}
{{- end }}
{{- define "fluxer.waitFor" -}}
{{- with .waitFor }}
initContainers:
- name: wait-for-dependencies
image: {{ include "fluxer.thirdPartyImage" $.root.Values.initImage }}
imagePullPolicy: {{ $.root.Values.initImage.pullPolicy }}
command:
- sh
- -c
- {{ printf "for t in %s; do until nc -z -w 2 ${t%%:*} ${t##*:}; do echo waiting for $t; sleep 2; done; done" (join " " .) | quote }}
resources:
requests:
cpu: 10m
memory: 16Mi
limits:
memory: 32Mi
securityContext:
runAsUser: 65532
runAsGroup: 65532
{{- include "fluxer.containerSecurityContext" . | nindent 6 }}
{{- end }}
{{- end }}
{{- define "fluxer.appWaitFor" -}}
{{- $out := list }}
{{- if and .usesNats .root.Values.nats.enabled }}
{{- $out = append $out "nats:4222" }}
{{- end }}
{{- if and .usesPostgres .root.Values.postgres.enabled }}
{{- $out = append $out "postgres:5432" }}
{{- end }}
{{- if and .usesS3 .root.Values.seaweedfs.enabled }}
{{- $out = append $out "seaweedfs:8333" }}
{{- end }}
{{- if and .usesKv .root.Values.valkey.enabled }}
{{- $out = append $out "valkey:6379" }}
{{- end }}
{{- if .usesSvc }}
{{- range $name := list "gifs" "messages" "snowflakes" "unfurl" "users" }}
{{- $svc := get $.root.Values.svc $name | default dict }}
{{- if gt (int (include "fluxer.replicas" (dict "w" ($svc.router | default dict)))) 0 }}
{{- $out = append $out (printf "%s:8090" $name) }}
{{- end }}
{{- if gt (int (include "fluxer.replicas" (dict "w" ($svc.shard | default dict)))) 0 }}
{{- $out = append $out (printf "%s-shard:8090" $name) }}
{{- end }}
{{- end }}
{{- end }}
{{- toJson $out }}
{{- end }}
+245
View File
@@ -0,0 +1,245 @@
{{- define "fluxer.validate" -}}
{{- if not (hasKey .Values "fluxerValidated") }}
{{- $_ := set .Values "fluxerValidated" true }}
{{- $v := .Values }}
{{- if not (regexMatch "^[a-z0-9]([a-z0-9-]*[a-z0-9])?(\\.[a-z0-9]([a-z0-9-]*[a-z0-9])?)+$" ($v.domain | default "")) }}
{{- fail "domain must be set to the lowercase public hostname of the instance, for example --set domain=chat.example.com" }}
{{- end }}
{{- if not (has $v.scheme (list "http" "https")) }}
{{- fail "scheme must be http or https" }}
{{- end }}
{{- if and $v.publicOrigin (not (regexMatch "^https?://[^/]+$" (trimSuffix "/" $v.publicOrigin))) }}
{{- fail "publicOrigin must be a scheme, host and optional port, for example https://chat.example.com" }}
{{- end }}
{{- if not (regexMatch "^/[^/].*[^/]$|^/[^/]$" $v.adminBasePath) }}
{{- fail "adminBasePath must start with a slash and must not end with one, for example /admin" }}
{{- end }}
{{- $extra := $v.secrets.extra | default dict }}
{{- if and (not $v.postgres.enabled) (not $v.postgres.external.host) (not (get $extra "FLUXER_POSTGRES_URL")) (not $v.secrets.existingSecret) }}
{{- fail "postgres.enabled is false, so set postgres.external.host or secrets.extra.FLUXER_POSTGRES_URL" }}
{{- end }}
{{- if and (not $v.valkey.enabled) (not $v.valkey.external.url) (not (get $extra "FLUXER_KV_URL")) (not $v.secrets.existingSecret) }}
{{- fail "valkey.enabled is false, so set valkey.external.url or secrets.extra.FLUXER_KV_URL" }}
{{- end }}
{{- if and (not $v.nats.enabled) (not $v.nats.external.url) }}
{{- fail "nats.enabled is false, so set nats.external.url" }}
{{- end }}
{{- if and (not $v.meilisearch.enabled) (not $v.meilisearch.external.url) }}
{{- fail "meilisearch.enabled is false, so set meilisearch.external.url" }}
{{- end }}
{{- if and (not $v.seaweedfs.enabled) (not $v.s3.endpoint) }}
{{- fail "seaweedfs.enabled is false, so set s3.endpoint to your S3 store" }}
{{- end }}
{{- if gt (int $v.nats.replicas) 1 }}
{{- if lt (int $v.nats.replicas) 3 }}
{{- fail "nats.replicas must be 1, or 3 or more for a JetStream cluster" }}
{{- end }}
{{- end }}
{{- if gt (int (include "fluxer.replicas" (dict "w" $v.worker))) 1 }}
{{- fail "worker.replicas must be 1, because the worker runs the cron scheduler" }}
{{- end }}
{{- if not (has $v.livekit.expose (list "hostNetwork" "loadBalancer" "nodePort")) }}
{{- fail "livekit.expose must be hostNetwork, loadBalancer or nodePort" }}
{{- end }}
{{- if and $v.livekit.enabled $v.livekit.external.url }}
{{- fail "livekit.external.url names a LiveKit server of your own, so set livekit.enabled to false" }}
{{- end }}
{{- if and $v.livekit.external.url (not (regexMatch "^(wss?|https?)://[^/]+" ($v.livekit.external.publicUrl | default ""))) }}
{{- fail "livekit.external.url is set, so set livekit.external.publicUrl to the address browsers use to reach that LiveKit server, for example wss://livekit.example.com" }}
{{- end }}
{{- if and $v.livekit.enabled (eq $v.livekit.expose "loadBalancer") (not $v.livekit.nodeIP) }}
{{- fail "livekit.expose is loadBalancer, so reserve a public address before installing, set livekit.nodeIP to it, and pin the Service livekit-rtc to it with livekit.loadBalancerIP or livekit.serviceAnnotations. Use hostNetwork or nodePort where a load balancer cannot serve TCP and UDP on one address" }}
{{- end }}
{{- if not (has $v.edge.expose (list "ingress" "gateway" "loadBalancer" "none")) }}
{{- fail "edge.expose must be ingress, gateway, loadBalancer or none" }}
{{- end }}
{{- if not (has $v.edge.ingress.tls.mode (list "certManager" "existingSecret" "none")) }}
{{- fail "edge.ingress.tls.mode must be certManager, existingSecret or none" }}
{{- end }}
{{- if and (eq $v.edge.expose "gateway") (not $v.edge.gateway.parentRefs) }}
{{- fail "edge.expose is gateway, so set edge.gateway.parentRefs to the Gateway the route attaches to" }}
{{- end }}
{{- if and (eq $v.edge.expose "loadBalancer") (ne $v.scheme "https") }}
{{- fail "edge.expose loadBalancer serves HTTPS itself, so scheme must be https" }}
{{- end }}
{{- if and $v.networkPolicy.enabled $v.networkPolicy.ingressNamespaceSelector $v.livekit.enabled (eq $v.livekit.expose "hostNetwork") (not $v.livekit.nodeCIDRs) }}
{{- fail "networkPolicy.ingressNamespaceSelector limits who reaches the edge, and LiveKit on the host network sends its webhook to the edge from a node address. Set livekit.nodeCIDRs to the address of the LiveKit node, for example 10.0.0.12/32" }}
{{- end }}
{{- $placeholder := "(?i)change[_-]?me" }}
{{- $secretName := "_(PASSWORD|SECRET|TOKEN|COOKIE|KEY|KEYS|KEY_BASE|SECRETS?_BASE64)$" }}
{{- $credentialUrl := "://[^/@ ]*:[^/@ ]*@" }}
{{- $plain := dict "env" ($v.env | default dict) "appProxy.config" ($v.appProxy.config | default dict) "edge.env" ($v.edge.env | default dict) }}
{{- range $name := list "api" "worker" "gateway" "mediaProxy" "push" "admin" "appProxy" "staticProxy" }}
{{- $_ := set $plain (printf "%s.env" $name) ((get $v $name).env | default dict) }}
{{- end }}
{{- range $name, $svc := $v.svc }}
{{- range $part := list "router" "shard" }}
{{- $_ := set $plain (printf "svc.%s.%s.env" $name $part) ((get ($svc | default dict) $part | default dict).env | default dict) }}
{{- end }}
{{- end }}
{{- range $where, $map := $plain }}
{{- range $k, $val := $map }}
{{- $s := include "fluxer.string" ($val | default "") }}
{{- if regexMatch $placeholder $s }}
{{- fail (printf "%s.%s is a placeholder. Remove it or set a real value" $where $k) }}
{{- end }}
{{- if or (hasKey $v.secrets.values $k) (regexMatch $secretName $k) }}
{{- fail (printf "%s.%s is a secret and would be stored in plain text. Set it in secrets.extra instead, or reference a Secret of your own from extraEnv" $where $k) }}
{{- end }}
{{- if regexMatch $credentialUrl $s }}
{{- fail (printf "%s.%s is a URL that holds a password and would be stored in plain text. Set it in secrets.extra instead" $where $k) }}
{{- end }}
{{- end }}
{{- end }}
{{- range $k, $val := $v.secrets.values }}
{{- if regexMatch $placeholder (include "fluxer.string" ($val | default "")) }}
{{- fail (printf "secrets.values.%s is a placeholder. Leave it empty to generate a value, or set a real one" $k) }}
{{- end }}
{{- end }}
{{- range $k, $val := $extra }}
{{- if regexMatch $placeholder (include "fluxer.string" ($val | default "")) }}
{{- fail (printf "secrets.extra.%s is a placeholder. Remove it or set a real value" $k) }}
{{- end }}
{{- end }}
{{- $connected := false }}
{{- if $v.clusterChecks }}
{{- $connected = or (lookup "v1" "Namespace" "" .Release.Namespace) (lookup "v1" "Namespace" "" "kube-system") | not | not }}
{{- end }}
{{- $data := include "fluxer.secretData" . | fromJson }}
{{- $secret := include "fluxer.secretName" . }}
{{- if $v.secrets.existingSecret }}
{{- if and $connected (not $data) }}
{{- fail (printf "secrets.existingSecret names the Secret %s, which does not exist in namespace %s. Create it first with every key listed under secrets.values" $secret .Release.Namespace) }}
{{- end }}
{{- if $data }}
{{- $missing := list }}
{{- range $k := include "fluxer.requiredSecretKeys" . | fromJsonArray }}
{{- if not (get $data $k) }}
{{- $missing = append $missing $k }}
{{- end }}
{{- end }}
{{- with $missing }}
{{- fail (printf "Secret %s is missing these keys: %s" $secret (join ", " .)) }}
{{- end }}
{{- $config := include "fluxer.configData" . | fromYaml }}
{{- if $v.valkey.enabled }}
{{- $_ := set $config "FLUXER_KV_URL" "the bundled Valkey" }}
{{- end }}
{{- $shadowed := list }}
{{- range $k := keys $config | sortAlpha }}
{{- if and (hasKey $data $k) (ne (get $data $k) (get $config $k)) }}
{{- $shadowed = append $shadowed $k }}
{{- end }}
{{- end }}
{{- with $shadowed }}
{{- fail (printf "Secret %s holds settings the chart sets itself, and the two disagree: %s. Remove them from the Secret. To keep an outside service, turn the bundled one off, as the External services section of the Kubernetes page describes" $secret (join ", " .)) }}
{{- end }}
{{- end }}
{{- end }}
{{- if $data }}
{{- range $k, $val := $data }}
{{- if regexMatch $placeholder $val }}
{{- fail (printf "%s in the Secret %s is a placeholder. Set a real value" $k $secret) }}
{{- end }}
{{- end }}
{{- with get $data "FLUXER_MEDIA_PROXY_UPLOAD_RELAY_SECRET_BASE64" }}
{{- if or (not (regexMatch "^[A-Za-z0-9+/]+={0,2}$" .)) (lt (len (b64dec .)) 32) }}
{{- fail "FLUXER_MEDIA_PROXY_UPLOAD_RELAY_SECRET_BASE64 must be standard base64 of at least 32 bytes, for example the output of openssl rand -base64 32" }}
{{- end }}
{{- end }}
{{- $long := list "FLUXER_SUDO_MODE_SECRET" "FLUXER_CONNECTION_INITIATION_SECRET" "FLUXER_PROFILE_PSEUDONYM_SECRET" "FLUXER_GATEWAY_RPC_AUTH_TOKEN" "FLUXER_ERLANG_COOKIE" "FLUXER_MEDIA_PROXY_SECRET_KEY" "FLUXER_ADMIN_SECRET_KEY_BASE" "FLUXER_ADMIN_OAUTH_CLIENT_SECRET" "FLUXER_SETUP_TOKEN" }}
{{- if ne (include "fluxer.livekitMode" .) "off" }}
{{- $long = append $long "FLUXER_LIVEKIT_API_SECRET" }}
{{- end }}
{{- if $v.valkey.enabled }}
{{- $long = append $long "FLUXER_VALKEY_PASSWORD" }}
{{- end }}
{{- if $v.nats.enabled }}
{{- $long = append $long "FLUXER_NATS_AUTH_TOKEN" }}
{{- end }}
{{- range $k := $long }}
{{- if and (hasKey $data $k) (lt (len (get $data $k)) 32) }}
{{- fail (printf "%s must be at least 32 characters, for example the output of openssl rand -hex 32" $k) }}
{{- end }}
{{- end }}
{{- if and $v.meilisearch.enabled (hasKey $data "FLUXER_SEARCH_API_KEY") (lt (len (get $data "FLUXER_SEARCH_API_KEY")) 16) }}
{{- fail "FLUXER_SEARCH_API_KEY must be at least 16 characters, or the bundled Meilisearch refuses to start" }}
{{- end }}
{{- if eq (get $data "FLUXER_POSTGRES_PASSWORD" | default "") "fluxer" }}
{{- fail "FLUXER_POSTGRES_PASSWORD must not be fluxer, which the services refuse in production" }}
{{- end }}
{{- $plainOnly := list }}
{{- if $v.seaweedfs.enabled }}
{{- $plainOnly = concat $plainOnly (list "FLUXER_S3_ACCESS_KEY_ID" "FLUXER_S3_SECRET_ACCESS_KEY") }}
{{- end }}
{{- if $v.valkey.enabled }}
{{- $plainOnly = append $plainOnly "FLUXER_VALKEY_PASSWORD" }}
{{- end }}
{{- if $v.nats.enabled }}
{{- $plainOnly = append $plainOnly "FLUXER_NATS_AUTH_TOKEN" }}
{{- end }}
{{- range $k := $plainOnly }}
{{- if and (hasKey $data $k) (not (regexMatch "^[A-Za-z0-9]+$" (get $data $k))) }}
{{- fail (printf "%s must contain letters and digits only, because the bundled service takes it on a command line or in a URL" $k) }}
{{- end }}
{{- end }}
{{- $pub := get $data "FLUXER_VAPID_PUBLIC_KEY" | default "" }}
{{- $priv := get $data "FLUXER_VAPID_PRIVATE_KEY" | default "" }}
{{- if or $pub $priv }}
{{- if or (not (regexMatch "^[A-Za-z0-9_-]{87}$" $pub)) (not (regexMatch "^[A-Za-z0-9_-]{43}$" $priv)) }}
{{- fail "FLUXER_VAPID_PUBLIC_KEY and FLUXER_VAPID_PRIVATE_KEY must be an unpadded base64url P-256 pair of 87 and 43 characters" }}
{{- end }}
{{- end }}
{{- end }}
{{- if $connected }}
{{- if and (eq $v.edge.expose "ingress") (eq $v.edge.ingress.tls.mode "certManager") (not (.Capabilities.APIVersions.Has "cert-manager.io/v1")) }}
{{- fail "edge.ingress.tls.mode is certManager, but cert-manager is not installed. Install cert-manager, or set edge.ingress.tls.mode to existingSecret or none, or set edge.expose to loadBalancer so the edge obtains its own certificate" }}
{{- end }}
{{- if and (eq $v.edge.expose "gateway") (not (.Capabilities.APIVersions.Has "gateway.networking.k8s.io/v1")) }}
{{- fail "edge.expose is gateway, but the Gateway API is not installed in the cluster" }}
{{- end }}
{{- $emptyClass := and (eq $v.edge.expose "loadBalancer") (not $v.edge.loadBalancer.persistence.storageClassName) }}
{{- range $c := list $v.postgres $v.valkey $v.nats $v.meilisearch $v.seaweedfs }}
{{- if and $c.enabled (not $c.persistence.storageClassName) }}
{{- $emptyClass = true }}
{{- end }}
{{- end }}
{{- if $emptyClass }}
{{- $default := false }}
{{- range (lookup "storage.k8s.io/v1" "StorageClass" "" "").items | default list }}
{{- if eq (get (.metadata.annotations | default dict) "storageclass.kubernetes.io/is-default-class") "true" }}
{{- $default = true }}
{{- end }}
{{- end }}
{{- if not $default }}
{{- fail "The cluster has no default StorageClass, so the volumes would never bind. Install a storage provisioner and mark its class as default, or set persistence.storageClassName on postgres, valkey, nats, meilisearch and seaweedfs" }}
{{- end }}
{{- end }}
{{- if and (eq $v.edge.expose "ingress") (not $v.edge.ingress.className) }}
{{- $classes := (lookup "networking.k8s.io/v1" "IngressClass" "" "").items | default list }}
{{- if not $classes }}
{{- fail "The cluster has no IngressClass, so nothing would serve the Ingress. Install an ingress controller, or set edge.expose to loadBalancer, gateway or none. Set clusterChecks to false if your controller serves an Ingress without a class" }}
{{- end }}
{{- $defaults := list }}
{{- range $classes }}
{{- if eq (get (.metadata.annotations | default dict) "ingressclass.kubernetes.io/is-default-class") "true" }}
{{- $defaults = append $defaults .metadata.name }}
{{- end }}
{{- end }}
{{- if not $defaults }}
{{- $names := list }}
{{- range $classes }}
{{- $names = append $names .metadata.name }}
{{- end }}
{{- fail (printf "The cluster has no default IngressClass. Set edge.ingress.className to one of: %s" (join ", " $names)) }}
{{- end }}
{{- if gt (len $defaults) 1 }}
{{- fail (printf "The cluster marks more than one IngressClass as default (%s), so the Ingress may land on the wrong controller. Set edge.ingress.className" (join ", " $defaults)) }}
{{- end }}
{{- end }}
{{- end }}
{{- end }}
{{- end }}
+188
View File
@@ -0,0 +1,188 @@
{{- define "fluxer.replicas" -}}
{{- $w := .w | default dict }}
{{- if kindIs "invalid" $w.replicas }}1{{ else }}{{ $w.replicas | int }}{{ end }}
{{- end }}
{{- define "fluxer.appPod" -}}
{{- $root := .root }}
{{- $w := .w | default dict }}
{{- $readsSecret := and (not .noEnvFrom) (not .configMapOnly) }}
{{- $env := .env | default list }}
{{- if and $readsSecret $root.Values.valkey.enabled }}
{{- $env = prepend $env (dict "name" "FLUXER_KV_URL" "value" "redis://:$(FLUXER_VALKEY_PASSWORD)@valkey:6379/0") }}
{{- end }}
metadata:
labels:
{{- include "fluxer.podLabels" . | nindent 4 }}
annotations:
checksum/instance: {{ include "fluxer.instanceChecksum" $root }}
{{- with $w.podAnnotations }}
{{- toYaml . | nindent 4 }}
{{- end }}
spec:
automountServiceAccountToken: false
enableServiceLinks: false
{{- include "fluxer.pullSecrets" $root | nindent 2 }}
securityContext:
{{- include "fluxer.podSecurityContext" (dict) | nindent 4 }}
{{- include "fluxer.scheduling" . | nindent 2 }}
{{- with $w.terminationGracePeriodSeconds }}
terminationGracePeriodSeconds: {{ . }}
{{- end }}
{{- include "fluxer.waitFor" . | nindent 2 }}
containers:
- name: {{ .containerName | default .name }}
image: {{ include "fluxer.image" . }}
imagePullPolicy: {{ include "fluxer.pullPolicy" . }}
{{- with .command }}
command:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with include "fluxer.envList" (dict "w" $w "env" $env) }}
env:
{{- . | nindent 8 }}
{{- end }}
{{- if not .noEnvFrom }}
envFrom:
{{- if .configMapOnly }}
- configMapRef:
name: {{ .configMapOnly }}
{{- else }}
- configMapRef:
name: fluxer-config
- secretRef:
name: {{ include "fluxer.secretName" $root }}
{{- end }}
{{- end }}
ports:
- name: http
containerPort: {{ .port }}
protocol: TCP
{{- range .extraPorts }}
- name: {{ .name }}
containerPort: {{ .port }}
protocol: TCP
{{- end }}
{{- with include "fluxer.probes" . | trim }}
{{- . | nindent 6 }}
{{- end }}
{{- with .lifecycle }}
lifecycle:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with $w.resources }}
resources:
{{- toYaml . | nindent 8 }}
{{- end }}
securityContext:
{{- include "fluxer.containerSecurityContext" . | nindent 8 }}
{{- with concat (.volumeMounts | default list) ($w.extraVolumeMounts | default list) }}
volumeMounts:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with concat (.volumes | default list) ($w.extraVolumes | default list) }}
volumes:
{{- toYaml . | nindent 4 }}
{{- end }}
{{- end }}
{{- define "fluxer.app" -}}
{{- $root := .root }}
{{- $w := .w | default dict }}
---
apiVersion: apps/v1
kind: {{ .kind | default "Deployment" }}
metadata:
name: {{ .name }}
namespace: {{ $root.Release.Namespace }}
labels:
{{- include "fluxer.labels" . | nindent 4 }}
spec:
{{- if not (and $w.hpa $w.hpa.maxReplicas) }}
replicas: {{ include "fluxer.replicas" . }}
{{- end }}
revisionHistoryLimit: 3
selector:
matchLabels:
{{- include "fluxer.selectorLabels" . | nindent 6 }}
{{- if eq (.kind | default "Deployment") "StatefulSet" }}
serviceName: {{ .serviceName }}
podManagementPolicy: Parallel
{{- else if .strategy }}
strategy:
{{- toYaml .strategy | nindent 4 }}
{{- end }}
template:
{{- include "fluxer.appPod" . | nindent 4 }}
{{- if not .noService }}
---
apiVersion: v1
kind: Service
metadata:
name: {{ .name }}
namespace: {{ $root.Release.Namespace }}
labels:
{{- include "fluxer.labels" . | nindent 4 }}
spec:
type: ClusterIP
{{- if .headless }}
clusterIP: None
publishNotReadyAddresses: true
{{- end }}
selector:
{{- include "fluxer.selectorLabels" . | nindent 4 }}
ports:
- name: http
port: {{ .servicePort | default .port }}
targetPort: http
protocol: TCP
{{- end }}
{{- with $w.pdb }}
{{- if or (not (kindIs "invalid" .minAvailable)) (not (kindIs "invalid" .maxUnavailable)) }}
---
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: {{ $.name }}
namespace: {{ $root.Release.Namespace }}
labels:
{{- include "fluxer.labels" $ | nindent 4 }}
spec:
{{- if not (kindIs "invalid" .minAvailable) }}
minAvailable: {{ .minAvailable }}
{{- end }}
{{- if not (kindIs "invalid" .maxUnavailable) }}
maxUnavailable: {{ .maxUnavailable }}
{{- end }}
selector:
matchLabels:
{{- include "fluxer.selectorLabels" $ | nindent 6 }}
{{- end }}
{{- end }}
{{- with $w.hpa }}
{{- if .maxReplicas }}
---
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: {{ $.name }}
namespace: {{ $root.Release.Namespace }}
labels:
{{- include "fluxer.labels" $ | nindent 4 }}
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: {{ $.kind | default "Deployment" }}
name: {{ $.name }}
minReplicas: {{ .minReplicas | default 1 }}
maxReplicas: {{ .maxReplicas }}
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: {{ .targetCPUUtilizationPercentage | default 80 }}
{{- end }}
{{- end }}
{{- end }}
+90
View File
@@ -0,0 +1,90 @@
{{- include "fluxer.validate" . }}
{{- $v := .Values }}
{{- $o := include "fluxer.origin" . }}
{{- $http := dict "port" "http" }}
{{- $start := dict "periodSeconds" 10 "timeoutSeconds" 5 "failureThreshold" 30 }}
{{- $check := dict "periodSeconds" 10 "timeoutSeconds" 5 "failureThreshold" 3 }}
{{- $health := merge (dict "path" "/_health") $http }}
{{- $healthProbes := dict "startup" (merge (dict "httpGet" $health) $start) "liveness" (merge (dict "httpGet" $health) $check) "readiness" (merge (dict "httpGet" $health) $check) }}
{{- $nodeEnv := list (dict "name" "NODE_ENV" "value" "production") (dict "name" "NODE_OPTIONS" "value" "--enable-source-maps") (dict "name" "NODE_EXTRA_CA_CERTS" "value" "/etc/ssl/certs/ca-certificates.crt") (dict "name" "FLUXER_POSTGRES_MAX_CONNECTIONS" "value" "25") }}
{{- include "fluxer.app" (dict "root" . "name" "api" "component" "api" "w" $v.api "image" "fluxer-api" "port" 8080
"env" (concat (list (dict "name" "FLUXER_API_PORT" "value" "8080") (dict "name" "FLUXER_API_PRESIGNED_ATTACHMENT_UPLOADS_ENABLED" "value" "true")) $nodeEnv)
"probes" $healthProbes
"waitFor" (include "fluxer.appWaitFor" (dict "root" . "usesNats" true "usesPostgres" true "usesS3" true "usesKv" true "usesSvc" true) | fromJsonArray)) }}
{{- $heartbeat := dict "exec" (dict "command" (list "node" "-e" "const age=Date.now()-require('node:fs').statSync('/tmp/fluxer-worker-heartbeat').mtimeMs;if(age>30000){console.error('worker heartbeat is '+Math.round(age)+'ms old');process.exit(1)}")) "initialDelaySeconds" 90 "periodSeconds" 10 "timeoutSeconds" 5 "failureThreshold" 3 }}
{{- include "fluxer.app" (dict "root" . "name" "worker" "component" "worker" "w" $v.worker "image" "fluxer-api" "port" 8080 "noService" true
"command" (list "node" "dist/WorkerEntrypoint.js")
"env" (concat (list (dict "name" "FLUXER_API_WORKER_MODE" "value" "all_lanes") (dict "name" "FLUXER_API_WORKER_ENABLE_CRON_SCHEDULER" "value" "true")) $nodeEnv)
"probes" (dict "liveness" $heartbeat)
"strategy" (dict "type" "Recreate")
"waitFor" (include "fluxer.appWaitFor" (dict "root" . "usesNats" true "usesPostgres" true "usesS3" true "usesKv" true "usesSvc" true) | fromJsonArray)) }}
{{- $gatewayEnv := list
(dict "name" "FLUXER_GATEWAY_PORT" "value" "8080")
(dict "name" "FLUXER_GATEWAY_ROLE" "value" "all")
(dict "name" "POD_IP" "valueFrom" (dict "fieldRef" (dict "apiVersion" "v1" "fieldPath" "status.podIP")))
(dict "name" "FLUXER_ERLANG_NODE_NAME" "value" "fluxer_gateway@$(POD_IP)")
(dict "name" "FLUXER_ERLANG_DIST_PORT" "value" "8081")
(dict "name" "FLUXER_GATEWAY_CLUSTER_ENABLED" "value" "true")
(dict "name" "FLUXER_GATEWAY_CLUSTER_DISCOVERY_DNS_NAME" "value" (printf "gateway-headless.%s.svc.%s" .Release.Namespace $v.clusterDomain))
(dict "name" "FLUXER_GATEWAY_CLUSTER_DISCOVERY_NODE_BASENAME" "value" "fluxer_gateway") }}
{{- $gatewayReady := dict "exec" (dict "command" (list "curl" "-fsS" "-o" "/dev/null" "--max-time" "4" "http://127.0.0.1:8080/_health/ready")) "periodSeconds" 10 "timeoutSeconds" 5 "failureThreshold" 3 }}
{{- $gatewayCtx := dict "root" . "name" "gateway" "component" "gateway" "w" $v.gateway "image" "fluxer-gateway" "port" 8080
"extraPorts" (list (dict "name" "epmd" "port" 4369) (dict "name" "erl-dist" "port" 8081))
"env" $gatewayEnv
"probes" (dict "startup" (merge (dict "httpGet" $health) $start) "liveness" (merge (dict "httpGet" $health) $check) "readiness" $gatewayReady)
"waitFor" (include "fluxer.appWaitFor" (dict "root" . "usesNats" true) | fromJsonArray)
"lifecycle" (dict "preStop" (dict "exec" (dict "command" (list "/bin/sh" "-c" "curl -fsS -o /dev/null --max-time 2 http://127.0.0.1:8080/_health/drain; sleep 5")))) }}
{{- include "fluxer.app" $gatewayCtx }}
---
apiVersion: v1
kind: Service
metadata:
name: gateway-headless
namespace: {{ .Release.Namespace }}
labels:
{{- include "fluxer.labels" $gatewayCtx | nindent 4 }}
spec:
type: ClusterIP
clusterIP: None
publishNotReadyAddresses: true
selector:
{{- include "fluxer.selectorLabels" $gatewayCtx | nindent 4 }}
ports:
- name: http
port: 8080
targetPort: http
protocol: TCP
- name: epmd
port: 4369
targetPort: epmd
protocol: TCP
- name: erl-dist
port: 8081
targetPort: erl-dist
protocol: TCP
{{- include "fluxer.app" (dict "root" . "name" "media-proxy" "component" "media" "w" $v.mediaProxy "image" "fluxer-media-proxy" "port" 8080
"env" (list (dict "name" "FLUXER_MEDIA_PROXY_PORT" "value" "8080") (dict "name" "FLUXER_MEDIA_PROXY_MODE" "value" "upload") (dict "name" "FLUXER_MEDIA_PROXY_STORAGE_BACKEND" "value" "s3") (dict "name" "FLUXER_S3_READ_SIGNED" "value" "true"))
"probes" $healthProbes
"waitFor" (include "fluxer.appWaitFor" (dict "root" . "usesNats" true "usesS3" true) | fromJsonArray)) }}
{{- $pushCheck := dict "exec" (dict "command" (list "/usr/local/bin/fluxer-push" "healthcheck")) }}
{{- include "fluxer.app" (dict "root" . "name" "push" "component" "push" "w" $v.push "image" "fluxer-push" "port" 8126
"env" (list (dict "name" "FLUXER_PUSH_SERVICE_PORT" "value" "8126"))
"waitFor" (include "fluxer.appWaitFor" (dict "root" . "usesNats" true) | fromJsonArray)
"probes" (dict "startup" (merge (deepCopy $pushCheck) (dict "periodSeconds" 10 "timeoutSeconds" 5 "failureThreshold" 12)) "liveness" (merge (deepCopy $pushCheck) $check) "readiness" (merge (deepCopy $pushCheck) $check))) }}
{{- include "fluxer.app" (dict "root" . "name" "admin" "component" "admin" "w" $v.admin "image" "fluxer-admin" "port" 8080
"env" (list (dict "name" "FLUXER_ADMIN_PORT" "value" "8080") (dict "name" "FLUXER_ADMIN_BASE_PATH" "value" $v.adminBasePath) (dict "name" "FLUXER_API_ENDPOINT" "value" "http://api:8080") (dict "name" "FLUXER_STATIC_CDN_ENDPOINT" "value" $o))
"probes" $healthProbes) }}
{{- $ready := merge (dict "path" "/_ready") $http }}
{{- include "fluxer.app" (dict "root" . "name" "app-proxy" "component" "web" "w" $v.appProxy "image" "fluxer-app-proxy-self-hosted" "port" 8080 "configMapOnly" "fluxer-app-proxy"
"probes" (dict "startup" (merge (dict "httpGet" $health) $start) "liveness" (merge (dict "httpGet" $health) $check) "readiness" (merge (dict "httpGet" $ready) $check))) }}
{{- $avatar := merge (dict "path" "/avatars/0.png") $http }}
{{- include "fluxer.app" (dict "root" . "name" "static-proxy" "component" "web" "w" $v.staticProxy "image" "fluxer-static" "port" 8080 "noEnvFrom" true
"probes" (dict "startup" (merge (dict "httpGet" $avatar) $start) "liveness" (merge (dict "httpGet" $avatar) $check) "readiness" (merge (dict "httpGet" $avatar) $check))) }}
@@ -0,0 +1,20 @@
{{- include "fluxer.validate" . }}
apiVersion: v1
kind: ConfigMap
metadata:
name: fluxer-config
namespace: {{ .Release.Namespace }}
labels:
{{- include "fluxer.labels" (dict "root" . "name" "fluxer-config" "component" "config") | nindent 4 }}
data:
{{- include "fluxer.configData" . | nindent 2 }}
---
apiVersion: v1
kind: ConfigMap
metadata:
name: fluxer-app-proxy
namespace: {{ .Release.Namespace }}
labels:
{{- include "fluxer.labels" (dict "root" . "name" "fluxer-app-proxy" "component" "config") | nindent 4 }}
data:
{{- include "fluxer.appProxyConfigData" . | nindent 2 }}
+296
View File
@@ -0,0 +1,296 @@
{{- include "fluxer.validate" . }}
{{- $v := .Values }}
{{- $e := $v.edge }}
{{- $ctx := dict "root" . "name" "edge" "component" "edge" "w" $e }}
{{- $lb := eq $e.expose "loadBalancer" }}
{{- $caddyfile := .Files.Get "files/Caddyfile" }}
{{- $globalOptions := ternary (printf "email %s" $v.email) "" (and $lb (ne $v.email "")) }}
{{- $siteAddress := ternary (printf "https://%s, :8080" $v.domain) ":8080" $lb }}
{{- $trusted := ternary "127.0.0.1/32" "private_ranges" $lb }}
---
apiVersion: v1
kind: ConfigMap
metadata:
name: edge-caddyfile
namespace: {{ .Release.Namespace }}
labels:
{{- include "fluxer.labels" $ctx | nindent 4 }}
data:
Caddyfile: {{ $caddyfile | quote }}
{{- if $lb }}
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: edge-data
namespace: {{ .Release.Namespace }}
labels:
{{- include "fluxer.labels" $ctx | nindent 4 }}
annotations:
helm.sh/resource-policy: keep
spec:
accessModes:
- ReadWriteOnce
{{- with $e.loadBalancer.persistence.storageClassName }}
storageClassName: {{ . }}
{{- end }}
resources:
requests:
storage: {{ $e.loadBalancer.persistence.size }}
{{- end }}
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: edge
namespace: {{ .Release.Namespace }}
labels:
{{- include "fluxer.labels" $ctx | nindent 4 }}
spec:
replicas: {{ ternary 1 ($e.replicas | int) $lb }}
revisionHistoryLimit: 3
{{- if $lb }}
strategy:
type: Recreate
{{- end }}
selector:
matchLabels:
{{- include "fluxer.selectorLabels" $ctx | nindent 6 }}
template:
metadata:
labels:
{{- include "fluxer.podLabels" $ctx | nindent 8 }}
annotations:
checksum/config: {{ printf "%s\n%s\n%s\n%s" $caddyfile $siteAddress $globalOptions (toJson (list $e.env $e.extraEnv)) | sha256sum }}
{{- with $e.podAnnotations }}
{{- toYaml . | nindent 8 }}
{{- end }}
spec:
automountServiceAccountToken: false
enableServiceLinks: false
{{- include "fluxer.pullSecrets" . | nindent 6 }}
securityContext:
{{- include "fluxer.podSecurityContext" (dict "uid" 65532) | nindent 8 }}
{{- if $lb }}
sysctls:
- name: net.ipv4.ip_unprivileged_port_start
value: "0"
{{- end }}
{{- include "fluxer.scheduling" $ctx | nindent 6 }}
containers:
- name: edge
image: {{ include "fluxer.thirdPartyImage" $e.image }}
imagePullPolicy: {{ $e.image.pullPolicy }}
{{- $edgeEnv := list (dict "name" "FLUXER_EDGE_SITE_ADDRESS" "value" $siteAddress) (dict "name" "FLUXER_EDGE_TRUSTED_PROXIES" "value" $trusted) (dict "name" "FLUXER_EDGE_GLOBAL_OPTIONS" "value" $globalOptions) (dict "name" "FLUXER_ADMIN_BASE_PATH" "value" $v.adminBasePath) }}
env:
{{- include "fluxer.envList" (dict "w" $e "env" $edgeEnv) | nindent 12 }}
ports:
- name: http
containerPort: 8080
protocol: TCP
{{- if $lb }}
- name: public-http
containerPort: 80
protocol: TCP
- name: public-https
containerPort: 443
protocol: TCP
- name: public-quic
containerPort: 443
protocol: UDP
{{- end }}
startupProbe:
exec:
command: ["wget", "-q", "-O", "/dev/null", "http://127.0.0.1:2019/config/"]
periodSeconds: 5
failureThreshold: 24
livenessProbe:
exec:
command: ["wget", "-q", "-O", "/dev/null", "http://127.0.0.1:2019/config/"]
periodSeconds: 10
timeoutSeconds: 5
failureThreshold: 6
readinessProbe:
exec:
command: ["wget", "-q", "-O", "/dev/null", "http://127.0.0.1:2019/config/"]
periodSeconds: 10
timeoutSeconds: 5
{{- with $e.resources }}
resources:
{{- toYaml . | nindent 12 }}
{{- end }}
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities:
drop:
- ALL
add:
- NET_BIND_SERVICE
volumeMounts:
- name: caddyfile
mountPath: /etc/caddy
readOnly: true
- name: config
mountPath: /config
- name: data
mountPath: /data
volumes:
- name: caddyfile
configMap:
name: edge-caddyfile
- name: config
emptyDir: {}
- name: data
{{- if $lb }}
persistentVolumeClaim:
claimName: edge-data
{{- else }}
emptyDir: {}
{{- end }}
---
apiVersion: v1
kind: Service
metadata:
name: edge
namespace: {{ .Release.Namespace }}
labels:
{{- include "fluxer.labels" $ctx | nindent 4 }}
spec:
type: ClusterIP
selector:
{{- include "fluxer.selectorLabels" $ctx | nindent 4 }}
ports:
- name: http
port: 8080
targetPort: http
protocol: TCP
{{- if $lb }}
---
apiVersion: v1
kind: Service
metadata:
name: edge-public
namespace: {{ .Release.Namespace }}
labels:
{{- include "fluxer.labels" $ctx | nindent 4 }}
{{- with $e.loadBalancer.annotations }}
annotations:
{{- toYaml . | nindent 4 }}
{{- end }}
spec:
type: LoadBalancer
externalTrafficPolicy: Local
selector:
{{- include "fluxer.selectorLabels" $ctx | nindent 4 }}
ports:
- name: http
port: 80
targetPort: public-http
protocol: TCP
- name: https
port: 443
targetPort: public-https
protocol: TCP
{{- if $e.loadBalancer.http3 }}
- name: quic
port: 443
targetPort: public-quic
protocol: UDP
{{- end }}
{{- end }}
{{- if eq $e.expose "ingress" }}
{{- $tls := $e.ingress.tls }}
{{- $annotations := dict
"nginx.ingress.kubernetes.io/proxy-body-size" "512m"
"nginx.ingress.kubernetes.io/proxy-read-timeout" "3600"
"nginx.ingress.kubernetes.io/proxy-send-timeout" "3600" }}
{{- if eq $tls.mode "certManager" }}
{{- $_ := set $annotations "cert-manager.io/issuer" "fluxer-acme" }}
---
apiVersion: cert-manager.io/v1
kind: Issuer
metadata:
name: fluxer-acme
namespace: {{ .Release.Namespace }}
labels:
{{- include "fluxer.labels" $ctx | nindent 4 }}
spec:
acme:
server: {{ $tls.acmeServer }}
{{- with $v.email }}
email: {{ . }}
{{- end }}
privateKeySecretRef:
name: fluxer-acme-account
solvers:
- http01:
{{- with $e.ingress.className }}
ingress:
ingressClassName: {{ . }}
{{- else }}
ingress: {}
{{- end }}
{{- end }}
{{- $annotations = merge (deepCopy ($e.ingress.annotations | default dict)) $annotations }}
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: fluxer
namespace: {{ .Release.Namespace }}
labels:
{{- include "fluxer.labels" $ctx | nindent 4 }}
{{- with $annotations }}
annotations:
{{- toYaml . | nindent 4 }}
{{- end }}
spec:
{{- with $e.ingress.className }}
ingressClassName: {{ . }}
{{- end }}
{{- if ne $tls.mode "none" }}
tls:
- hosts:
- {{ $v.domain }}
secretName: {{ $tls.secretName }}
{{- end }}
rules:
- host: {{ $v.domain }}
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: edge
port:
name: http
{{- end }}
{{- if eq $e.expose "gateway" }}
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: fluxer
namespace: {{ .Release.Namespace }}
labels:
{{- include "fluxer.labels" $ctx | nindent 4 }}
spec:
parentRefs:
{{- toYaml $e.gateway.parentRefs | nindent 4 }}
hostnames:
- {{ $v.domain }}
rules:
- matches:
- path:
type: PathPrefix
value: /
backendRefs:
- name: edge
port: 8080
{{- with $e.gateway.timeouts }}
timeouts:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- end }}
+156
View File
@@ -0,0 +1,156 @@
{{- include "fluxer.validate" . }}
{{- with .Values.livekit }}
{{- if .enabled }}
{{- $ctx := dict "root" $ "name" "livekit" "component" "voice" "w" . }}
{{- $tcp := .tcpPort | int }}
{{- $udp := .udpPort | int }}
{{- if eq .expose "nodePort" }}
{{- $tcp = .nodePorts.tcp | int }}
{{- $udp = .nodePorts.udp | int }}
{{- end }}
{{- $useExternal := and .useExternalIP (ne .expose "loadBalancer") }}
{{- $secrets := include "fluxer.secretData" $ | fromJson }}
{{- $config := dict
"port" 7880
"log_level" .logLevel
"rtc" (dict "tcp_port" $tcp "udp_port" $udp "use_external_ip" $useExternal "node_ip" (.nodeIP | default "") "stun_servers" .stunServers) }}
{{- $config = mergeOverwrite $config (deepCopy (.extraConfig | default dict)) }}
{{- $configYaml := toYaml $config }}
{{- $root := dict "path" "/" "port" "http" }}
{{- $webhook := ternary "http://edge:8080/api/webhooks/livekit" "http://api:8080/webhooks/livekit" (eq .expose "hostNetwork") }}
---
apiVersion: v1
kind: Service
metadata:
name: livekit
namespace: {{ $.Release.Namespace }}
labels:
{{- include "fluxer.labels" $ctx | nindent 4 }}
spec:
type: ClusterIP
selector:
{{- include "fluxer.selectorLabels" $ctx | nindent 4 }}
ports:
- name: http
port: 7880
targetPort: 7880
protocol: TCP
{{- if ne .expose "hostNetwork" }}
---
apiVersion: v1
kind: Service
metadata:
name: livekit-rtc
namespace: {{ $.Release.Namespace }}
labels:
{{- include "fluxer.labels" $ctx | nindent 4 }}
{{- with .serviceAnnotations }}
annotations:
{{- toYaml . | nindent 4 }}
{{- end }}
spec:
type: {{ ternary "LoadBalancer" "NodePort" (eq .expose "loadBalancer") }}
externalTrafficPolicy: Local
{{- if and (eq .expose "loadBalancer") .loadBalancerIP }}
loadBalancerIP: {{ .loadBalancerIP }}
{{- end }}
selector:
{{- include "fluxer.selectorLabels" $ctx | nindent 4 }}
ports:
- name: rtc-tcp
port: {{ $tcp }}
targetPort: rtc-tcp
protocol: TCP
{{- if eq .expose "nodePort" }}
nodePort: {{ $tcp }}
{{- end }}
- name: rtc-udp
port: {{ $udp }}
targetPort: rtc-udp
protocol: UDP
{{- if eq .expose "nodePort" }}
nodePort: {{ $udp }}
{{- end }}
{{- end }}
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: livekit
namespace: {{ $.Release.Namespace }}
labels:
{{- include "fluxer.labels" $ctx | nindent 4 }}
spec:
replicas: 1
revisionHistoryLimit: 3
strategy:
type: Recreate
selector:
matchLabels:
{{- include "fluxer.selectorLabels" $ctx | nindent 6 }}
template:
metadata:
labels:
{{- include "fluxer.podLabels" $ctx | nindent 8 }}
annotations:
checksum/config: {{ list $configYaml $webhook (get $secrets "FLUXER_LIVEKIT_API_KEY" | default "") (get $secrets "FLUXER_LIVEKIT_API_SECRET" | default "") | toJson | sha256sum }}
spec:
automountServiceAccountToken: false
enableServiceLinks: false
{{- if eq .expose "hostNetwork" }}
hostNetwork: true
dnsPolicy: ClusterFirstWithHostNet
{{- end }}
{{- include "fluxer.pullSecrets" $ | nindent 6 }}
securityContext:
{{- include "fluxer.podSecurityContext" (dict "uid" 65532) | nindent 8 }}
{{- include "fluxer.scheduling" $ctx | nindent 6 }}
containers:
- name: livekit
image: {{ include "fluxer.thirdPartyImage" .image }}
imagePullPolicy: {{ .image.pullPolicy }}
command:
- /bin/sh
- -c
- |
export LIVEKIT_KEYS="${LIVEKIT_API_KEY}: ${LIVEKIT_API_SECRET}"
export LIVEKIT_CONFIG="$(printf '%s\nwebhook:\n api_key: %s\n urls:\n - %s\n' "$LIVEKIT_CONFIG" "$LIVEKIT_API_KEY" "$LIVEKIT_WEBHOOK_URL")"
exec /livekit-server
env:
- name: LIVEKIT_CONFIG
value: {{ $configYaml | quote }}
- name: LIVEKIT_WEBHOOK_URL
value: {{ $webhook }}
{{- include "fluxer.secretEnv" (dict "root" $ "name" "LIVEKIT_API_KEY" "key" "FLUXER_LIVEKIT_API_KEY") | nindent 12 }}
{{- include "fluxer.secretEnv" (dict "root" $ "name" "LIVEKIT_API_SECRET" "key" "FLUXER_LIVEKIT_API_SECRET") | nindent 12 }}
ports:
- name: http
containerPort: 7880
protocol: TCP
- name: rtc-tcp
containerPort: {{ $tcp }}
protocol: TCP
- name: rtc-udp
containerPort: {{ $udp }}
protocol: UDP
startupProbe:
httpGet: {{ toJson $root }}
periodSeconds: 5
failureThreshold: 60
livenessProbe:
httpGet: {{ toJson $root }}
periodSeconds: 10
timeoutSeconds: 5
failureThreshold: 6
readinessProbe:
httpGet: {{ toJson $root }}
periodSeconds: 10
timeoutSeconds: 5
{{- with .resources }}
resources:
{{- toYaml . | nindent 12 }}
{{- end }}
securityContext:
{{- include "fluxer.containerSecurityContext" . | nindent 12 }}
{{- end }}
{{- end }}
@@ -0,0 +1,97 @@
{{- include "fluxer.validate" . }}
{{- with .Values.meilisearch }}
{{- if .enabled }}
{{- $ctx := dict "root" $ "name" "meilisearch" "component" "search" "w" . }}
{{- $health := dict "path" "/health" "port" "http" }}
---
apiVersion: v1
kind: Service
metadata:
name: meilisearch
namespace: {{ $.Release.Namespace }}
labels:
{{- include "fluxer.labels" $ctx | nindent 4 }}
spec:
type: ClusterIP
selector:
{{- include "fluxer.selectorLabels" $ctx | nindent 4 }}
ports:
- name: http
port: 7700
targetPort: http
protocol: TCP
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: meilisearch
namespace: {{ $.Release.Namespace }}
labels:
{{- include "fluxer.labels" $ctx | nindent 4 }}
spec:
replicas: 1
serviceName: meilisearch
podManagementPolicy: Parallel
persistentVolumeClaimRetentionPolicy:
whenDeleted: Retain
whenScaled: Retain
selector:
matchLabels:
{{- include "fluxer.selectorLabels" $ctx | nindent 6 }}
template:
metadata:
labels:
{{- include "fluxer.podLabels" $ctx | nindent 8 }}
spec:
automountServiceAccountToken: false
enableServiceLinks: false
{{- include "fluxer.pullSecrets" $ | nindent 6 }}
securityContext:
{{- include "fluxer.podSecurityContext" (dict "uid" 65532) | nindent 8 }}
{{- include "fluxer.scheduling" $ctx | nindent 6 }}
containers:
- name: meilisearch
image: {{ include "fluxer.thirdPartyImage" .image }}
imagePullPolicy: {{ .image.pullPolicy }}
env:
- name: MEILI_ENV
value: production
- name: MEILI_NO_ANALYTICS
value: "true"
- name: MEILI_UPGRADE_DB
value: "true"
- name: MEILI_MAX_INDEXING_MEMORY
value: {{ .maxIndexingMemory | quote }}
- name: MEILI_MAX_INDEXING_THREADS
value: {{ include "fluxer.string" .maxIndexingThreads | quote }}
{{- include "fluxer.secretEnv" (dict "root" $ "name" "MEILI_MASTER_KEY" "key" "FLUXER_SEARCH_API_KEY") | nindent 12 }}
ports:
- name: http
containerPort: 7700
protocol: TCP
startupProbe:
httpGet: {{ toJson $health }}
periodSeconds: 5
failureThreshold: 120
livenessProbe:
httpGet: {{ toJson $health }}
periodSeconds: 10
timeoutSeconds: 5
failureThreshold: 6
readinessProbe:
httpGet: {{ toJson $health }}
periodSeconds: 10
timeoutSeconds: 5
{{- with .resources }}
resources:
{{- toYaml . | nindent 12 }}
{{- end }}
securityContext:
{{- include "fluxer.containerSecurityContext" . | nindent 12 }}
volumeMounts:
- name: data
mountPath: /meili_data
volumeClaimTemplates:
{{- include "fluxer.pvc" .persistence | nindent 4 }}
{{- end }}
{{- end }}
+169
View File
@@ -0,0 +1,169 @@
{{- include "fluxer.validate" . }}
{{- with .Values.nats }}
{{- if .enabled }}
{{- $ctx := dict "root" $ "name" "nats" "component" "messaging" "w" . }}
{{- $replicas := .replicas | int }}
{{- $conf := include "fluxer.natsConf" (dict "root" $ "replicas" $replicas) }}
{{- $secrets := include "fluxer.secretData" $ | fromJson }}
{{- $healthz := dict "exec" (dict "command" (list "wget" "-q" "-O" "/dev/null" "http://127.0.0.1:8222/healthz")) }}
---
apiVersion: v1
kind: ConfigMap
metadata:
name: nats-config
namespace: {{ $.Release.Namespace }}
labels:
{{- include "fluxer.labels" $ctx | nindent 4 }}
data:
nats.conf: {{ $conf | quote }}
---
apiVersion: v1
kind: Service
metadata:
name: nats
namespace: {{ $.Release.Namespace }}
labels:
{{- include "fluxer.labels" $ctx | nindent 4 }}
spec:
type: ClusterIP
selector:
{{- include "fluxer.selectorLabels" $ctx | nindent 4 }}
ports:
- name: client
port: 4222
targetPort: client
protocol: TCP
---
apiVersion: v1
kind: Service
metadata:
name: nats-headless
namespace: {{ $.Release.Namespace }}
labels:
{{- include "fluxer.labels" $ctx | nindent 4 }}
spec:
type: ClusterIP
clusterIP: None
publishNotReadyAddresses: true
selector:
{{- include "fluxer.selectorLabels" $ctx | nindent 4 }}
ports:
- name: client
port: 4222
targetPort: client
protocol: TCP
- name: cluster
port: 6222
targetPort: cluster
protocol: TCP
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: nats
namespace: {{ $.Release.Namespace }}
labels:
{{- include "fluxer.labels" $ctx | nindent 4 }}
spec:
replicas: {{ $replicas }}
serviceName: nats-headless
podManagementPolicy: Parallel
persistentVolumeClaimRetentionPolicy:
whenDeleted: Retain
whenScaled: Retain
selector:
matchLabels:
{{- include "fluxer.selectorLabels" $ctx | nindent 6 }}
template:
metadata:
labels:
{{- include "fluxer.podLabels" $ctx | nindent 8 }}
annotations:
checksum/config: {{ printf "%s\n%s" $conf (get $secrets "FLUXER_NATS_AUTH_TOKEN" | default "") | sha256sum }}
spec:
automountServiceAccountToken: false
enableServiceLinks: false
{{- include "fluxer.pullSecrets" $ | nindent 6 }}
securityContext:
{{- include "fluxer.podSecurityContext" (dict "uid" 65534) | nindent 8 }}
{{- include "fluxer.scheduling" $ctx | nindent 6 }}
containers:
- name: nats
image: {{ include "fluxer.thirdPartyImage" .image }}
imagePullPolicy: {{ .image.pullPolicy }}
command:
- sh
- -c
- sed "s/__FLUXER_NATS_AUTH_TOKEN__/$FLUXER_NATS_AUTH_TOKEN/g" /etc/nats/nats.conf > /run/nats/nats.conf && exec nats-server --config /run/nats/nats.conf
env:
- name: POD_NAME
valueFrom:
fieldRef:
apiVersion: v1
fieldPath: metadata.name
{{- include "fluxer.secretEnv" (dict "root" $ "name" "FLUXER_NATS_AUTH_TOKEN" "key" "FLUXER_NATS_AUTH_TOKEN") | nindent 12 }}
ports:
- name: client
containerPort: 4222
protocol: TCP
- name: cluster
containerPort: 6222
protocol: TCP
startupProbe:
{{- toYaml (merge (dict "periodSeconds" 5 "timeoutSeconds" 5 "failureThreshold" 60) $healthz) | nindent 12 }}
livenessProbe:
{{- toYaml (merge (dict "periodSeconds" 10 "timeoutSeconds" 5 "failureThreshold" 6) $healthz) | nindent 12 }}
readinessProbe:
{{- toYaml (merge (dict "periodSeconds" 10 "timeoutSeconds" 5) $healthz) | nindent 12 }}
{{- with .resources }}
resources:
{{- toYaml . | nindent 12 }}
{{- end }}
securityContext:
{{- include "fluxer.containerSecurityContext" . | nindent 12 }}
volumeMounts:
- name: config
mountPath: /etc/nats
readOnly: true
- name: rendered
mountPath: /run/nats
- name: data
mountPath: /data
volumes:
- name: config
configMap:
name: nats-config
- name: rendered
emptyDir:
medium: Memory
volumeClaimTemplates:
{{- include "fluxer.pvc" .persistence | nindent 4 }}
{{- end }}
{{- end }}
{{- define "fluxer.natsConf" -}}
server_name: $POD_NAME
listen: 0.0.0.0:4222
http: 127.0.0.1:8222
authorization {
token: "__FLUXER_NATS_AUTH_TOKEN__"
}
jetstream {
store_dir: /data
}
{{- if gt .replicas 1 }}
cluster {
name: fluxer
listen: 0.0.0.0:6222
authorization {
user: fluxer
password: "__FLUXER_NATS_AUTH_TOKEN__"
}
routes: [
{{- range $i := until .replicas }}
nats://fluxer:__FLUXER_NATS_AUTH_TOKEN__@nats-{{ $i }}.nats-headless.{{ $.root.Release.Namespace }}.svc.{{ $.root.Values.clusterDomain }}:6222
{{- end }}
]
}
{{- end }}
{{- end }}
@@ -0,0 +1,83 @@
{{- include "fluxer.validate" . }}
{{- if .Values.networkPolicy.enabled }}
{{- $v := .Values }}
{{- $release := dict "app.kubernetes.io/instance" .Release.Name }}
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: fluxer
namespace: {{ .Release.Namespace }}
labels:
{{- include "fluxer.labels" (dict "root" . "name" "fluxer" "component" "network") | nindent 4 }}
spec:
podSelector:
matchLabels:
{{- toYaml $release | nindent 6 }}
policyTypes:
- Ingress
ingress:
- from:
- podSelector:
matchLabels:
{{- toYaml $release | nindent 14 }}
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: fluxer-edge
namespace: {{ .Release.Namespace }}
labels:
{{- include "fluxer.labels" (dict "root" . "name" "fluxer-edge" "component" "network") | nindent 4 }}
spec:
podSelector:
matchLabels:
{{- include "fluxer.selectorLabels" (dict "root" . "name" "edge") | nindent 6 }}
policyTypes:
- Ingress
ingress:
- ports:
- port: 8080
protocol: TCP
{{- with $v.networkPolicy.ingressNamespaceSelector }}
from:
- namespaceSelector:
{{- toYaml . | nindent 12 }}
{{- if and $v.livekit.enabled (eq $v.livekit.expose "hostNetwork") }}
{{- range $v.livekit.nodeCIDRs }}
- ipBlock:
cidr: {{ . }}
{{- end }}
{{- end }}
{{- end }}
{{- if eq $v.edge.expose "loadBalancer" }}
- ports:
- port: 80
protocol: TCP
- port: 443
protocol: TCP
- port: 443
protocol: UDP
{{- end }}
{{- if and $v.livekit.enabled (ne $v.livekit.expose "hostNetwork") }}
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: fluxer-livekit
namespace: {{ .Release.Namespace }}
labels:
{{- include "fluxer.labels" (dict "root" . "name" "fluxer-livekit" "component" "network") | nindent 4 }}
spec:
podSelector:
matchLabels:
{{- include "fluxer.selectorLabels" (dict "root" . "name" "livekit") | nindent 6 }}
policyTypes:
- Ingress
ingress:
- ports:
- port: {{ ternary $v.livekit.nodePorts.tcp $v.livekit.tcpPort (eq $v.livekit.expose "nodePort") }}
protocol: TCP
- port: {{ ternary $v.livekit.nodePorts.udp $v.livekit.udpPort (eq $v.livekit.expose "nodePort") }}
protocol: UDP
{{- end }}
{{- end }}
+147
View File
@@ -0,0 +1,147 @@
{{- include "fluxer.validate" . }}
{{- with .Values.postgres }}
{{- if .enabled }}
{{- $ctx := dict "root" $ "name" "postgres" "component" "database" "w" . }}
{{- $args := list }}
{{- range $k := keys .parameters | sortAlpha }}
{{- $args = concat $args (list "-c" (printf "%s=%s" $k (include "fluxer.string" (get $.Values.postgres.parameters $k)))) }}
{{- end }}
---
apiVersion: v1
kind: Service
metadata:
name: postgres
namespace: {{ $.Release.Namespace }}
labels:
{{- include "fluxer.labels" $ctx | nindent 4 }}
spec:
type: ClusterIP
selector:
{{- include "fluxer.selectorLabels" $ctx | nindent 4 }}
ports:
- name: postgres
port: 5432
targetPort: postgres
protocol: TCP
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: postgres
namespace: {{ $.Release.Namespace }}
labels:
{{- include "fluxer.labels" $ctx | nindent 4 }}
spec:
replicas: 1
serviceName: postgres
podManagementPolicy: Parallel
persistentVolumeClaimRetentionPolicy:
whenDeleted: Retain
whenScaled: Retain
selector:
matchLabels:
{{- include "fluxer.selectorLabels" $ctx | nindent 6 }}
template:
metadata:
labels:
{{- include "fluxer.podLabels" $ctx | nindent 8 }}
annotations:
checksum/config: {{ toJson $args | sha256sum }}
spec:
automountServiceAccountToken: false
enableServiceLinks: false
terminationGracePeriodSeconds: 60
{{- include "fluxer.pullSecrets" $ | nindent 6 }}
securityContext:
{{- include "fluxer.podSecurityContext" (dict "uid" (.runAsUser | int)) | nindent 8 }}
{{- include "fluxer.scheduling" $ctx | nindent 6 }}
initContainers:
- name: version-guard
image: {{ include "fluxer.thirdPartyImage" .image }}
imagePullPolicy: {{ .image.pullPolicy }}
command:
- sh
- -c
- |
want=$(postgres --version | sed -E 's/[^0-9]*([0-9]+).*/\1/')
if [ -s "$PGDATA/PG_VERSION" ]; then
have=$(cat "$PGDATA/PG_VERSION")
if [ "$have" != "$want" ]; then
echo "The data directory was created by Postgres $have and this image runs Postgres $want. Set postgres.image.tag back to the $have image, or dump the database and restore it into a fresh volume. See the Kubernetes page of the operator docs." >&2
exit 1
fi
fi
env:
- name: PGDATA
value: /var/lib/postgresql/data/pgdata
resources:
requests:
cpu: 10m
memory: 16Mi
limits:
memory: 64Mi
securityContext:
{{- include "fluxer.containerSecurityContext" . | nindent 12 }}
volumeMounts:
- name: data
mountPath: /var/lib/postgresql/data
containers:
- name: postgres
image: {{ include "fluxer.thirdPartyImage" .image }}
imagePullPolicy: {{ .image.pullPolicy }}
args:
{{- toYaml $args | nindent 12 }}
env:
- name: PGDATA
value: /var/lib/postgresql/data/pgdata
- name: POSTGRES_DB
value: {{ .database | quote }}
- name: POSTGRES_USER
value: {{ .username | quote }}
{{- include "fluxer.secretEnv" (dict "root" $ "name" "POSTGRES_PASSWORD" "key" "FLUXER_POSTGRES_PASSWORD") | nindent 12 }}
ports:
- name: postgres
containerPort: 5432
protocol: TCP
startupProbe:
exec:
command: ["sh", "-c", "pg_isready -U \"$POSTGRES_USER\" -d \"$POSTGRES_DB\""]
periodSeconds: 10
timeoutSeconds: 5
failureThreshold: 30
livenessProbe:
exec:
command: ["sh", "-c", "pg_isready -U \"$POSTGRES_USER\" -d \"$POSTGRES_DB\""]
periodSeconds: 10
timeoutSeconds: 5
failureThreshold: 6
readinessProbe:
exec:
command: ["sh", "-c", "pg_isready -U \"$POSTGRES_USER\" -d \"$POSTGRES_DB\""]
periodSeconds: 10
timeoutSeconds: 5
failureThreshold: 3
{{- with .resources }}
resources:
{{- toYaml . | nindent 12 }}
{{- end }}
securityContext:
{{- include "fluxer.containerSecurityContext" . | nindent 12 }}
volumeMounts:
- name: data
mountPath: /var/lib/postgresql/data
- name: shm
mountPath: /dev/shm
- name: run
mountPath: /var/run/postgresql
volumes:
- name: shm
emptyDir:
medium: Memory
sizeLimit: {{ .shmSize }}
- name: run
emptyDir: {}
volumeClaimTemplates:
{{- include "fluxer.pvc" .persistence | nindent 4 }}
{{- end }}
{{- end }}
+136
View File
@@ -0,0 +1,136 @@
{{- include "fluxer.validate" . }}
{{- with .Values.seaweedfs }}
{{- if .enabled }}
{{- $ctx := dict "root" $ "name" "seaweedfs" "component" "storage" "w" . }}
{{- $healthz := dict "path" "/healthz" "port" "s3" }}
{{- $secrets := include "fluxer.secretData" $ | fromJson }}
{{- $init := $.Files.Get "files/seaweedfs-init.sh" }}
---
apiVersion: v1
kind: Service
metadata:
name: seaweedfs
namespace: {{ $.Release.Namespace }}
labels:
{{- include "fluxer.labels" $ctx | nindent 4 }}
spec:
type: ClusterIP
selector:
{{- include "fluxer.selectorLabels" $ctx | nindent 4 }}
ports:
- name: s3
port: 8333
targetPort: s3
protocol: TCP
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: seaweedfs
namespace: {{ $.Release.Namespace }}
labels:
{{- include "fluxer.labels" $ctx | nindent 4 }}
spec:
replicas: 1
serviceName: seaweedfs
podManagementPolicy: Parallel
persistentVolumeClaimRetentionPolicy:
whenDeleted: Retain
whenScaled: Retain
selector:
matchLabels:
{{- include "fluxer.selectorLabels" $ctx | nindent 6 }}
template:
metadata:
labels:
{{- include "fluxer.podLabels" $ctx | nindent 8 }}
annotations:
checksum/init: {{ list $init (get $secrets "FLUXER_S3_ACCESS_KEY_ID" | default "") (get $secrets "FLUXER_S3_SECRET_ACCESS_KEY" | default "") $.Values.s3.buckets | toJson | sha256sum }}
spec:
automountServiceAccountToken: false
enableServiceLinks: false
{{- include "fluxer.pullSecrets" $ | nindent 6 }}
securityContext:
{{- include "fluxer.podSecurityContext" (dict "uid" 1000) | nindent 8 }}
{{- include "fluxer.scheduling" $ctx | nindent 6 }}
containers:
- name: seaweedfs
image: {{ include "fluxer.thirdPartyImage" .image }}
imagePullPolicy: {{ .image.pullPolicy }}
args:
- server
- -s3
- -dir=/data
- -master.telemetry=false
- -ip=127.0.0.1
- -ip.bind=127.0.0.1
- -s3.ip.bind=0.0.0.0
env:
- name: GOMEMLIMIT
value: {{ .gomemlimit | quote }}
- name: WEED_MASTER_VOLUME_GROWTH_COPY_1
value: {{ include "fluxer.string" .volumeGrowth | quote }}
ports:
- name: s3
containerPort: 8333
protocol: TCP
startupProbe:
httpGet: {{ toJson $healthz }}
periodSeconds: 5
timeoutSeconds: 5
failureThreshold: 24
livenessProbe:
httpGet: {{ toJson $healthz }}
periodSeconds: 10
timeoutSeconds: 5
failureThreshold: 6
readinessProbe:
httpGet: {{ toJson $healthz }}
periodSeconds: 10
timeoutSeconds: 5
{{- with .resources }}
resources:
{{- toYaml . | nindent 12 }}
{{- end }}
securityContext:
{{- include "fluxer.containerSecurityContext" . | nindent 12 }}
volumeMounts:
- name: data
mountPath: /data
- name: seaweedfs-init
image: {{ include "fluxer.thirdPartyImage" .image }}
imagePullPolicy: {{ .image.pullPolicy }}
command:
- /bin/sh
- -c
- {{ $init | quote }}
env:
{{- include "fluxer.secretEnv" (dict "root" $ "name" "FLUXER_S3_ACCESS_KEY" "key" "FLUXER_S3_ACCESS_KEY_ID") | nindent 12 }}
{{- include "fluxer.secretEnv" (dict "root" $ "name" "FLUXER_S3_SECRET_KEY" "key" "FLUXER_S3_SECRET_ACCESS_KEY") | nindent 12 }}
- name: FLUXER_S3_BUCKET_CDN
value: {{ $.Values.s3.buckets.cdn | quote }}
- name: FLUXER_S3_BUCKET_UPLOADS
value: {{ $.Values.s3.buckets.uploads | quote }}
- name: FLUXER_S3_BUCKET_REPORTS
value: {{ $.Values.s3.buckets.reports | quote }}
- name: FLUXER_S3_BUCKET_HARVESTS
value: {{ $.Values.s3.buckets.harvests | quote }}
- name: FLUXER_SEAWEEDFS_INIT_ATTEMPTS
value: {{ include "fluxer.string" .initAttempts | quote }}
readinessProbe:
exec:
command: ["test", "-f", "/tmp/fluxer-buckets-ready"]
periodSeconds: 5
timeoutSeconds: 5
resources:
requests:
cpu: 10m
memory: 32Mi
limits:
memory: 128Mi
securityContext:
{{- include "fluxer.containerSecurityContext" . | nindent 12 }}
volumeClaimTemplates:
{{- include "fluxer.pvc" .persistence | nindent 4 }}
{{- end }}
{{- end }}
+17
View File
@@ -0,0 +1,17 @@
{{- include "fluxer.validate" . }}
{{- if not .Values.secrets.existingSecret }}
apiVersion: v1
kind: Secret
metadata:
name: fluxer-env
namespace: {{ .Release.Namespace }}
labels:
{{- include "fluxer.labels" (dict "root" . "name" "fluxer-env" "component" "config") | nindent 4 }}
annotations:
helm.sh/resource-policy: keep
type: Opaque
data:
{{- range $k, $val := include "fluxer.secretData" . | fromJson }}
{{ $k }}: {{ $val | b64enc | quote }}
{{- end }}
{{- end }}
+31
View File
@@ -0,0 +1,31 @@
{{- include "fluxer.validate" . }}
{{- $healthz := dict "path" "/_healthz" "port" "http" }}
{{- $health := dict "path" "/_health" "port" "http" }}
{{- $probes := dict
"startup" (dict "httpGet" $healthz "periodSeconds" 10 "timeoutSeconds" 5 "failureThreshold" 12)
"liveness" (dict "httpGet" $healthz "periodSeconds" 10 "timeoutSeconds" 5 "failureThreshold" 3)
"readiness" (dict "httpGet" $health "periodSeconds" 10 "timeoutSeconds" 5 "failureThreshold" 3) }}
{{- range $name := list "gifs" "messages" "snowflakes" "unfurl" "users" }}
{{- $svc := get $.Values.svc $name | default dict }}
{{- $router := $svc.router | default dict }}
{{- $shard := $svc.shard | default dict }}
{{- $count := include "fluxer.replicas" (dict "w" $shard) }}
{{- $common := list (dict "name" "FLUXER_SVC_NAME" "value" $name) (dict "name" "FLUXER_SVC_PORT" "value" "8090") (dict "name" "FLUXER_SVC_SHARD_COUNT" "value" $count) }}
{{- $shardEnv := concat $common (list (dict "name" "FLUXER_SVC_MODE" "value" "shard") (dict "name" "FLUXER_SVC_SHARD_ID" "valueFrom" (dict "fieldRef" (dict "apiVersion" "v1" "fieldPath" "metadata.labels['apps.kubernetes.io/pod-index']"))) (dict "name" "POD_NAME" "valueFrom" (dict "fieldRef" (dict "apiVersion" "v1" "fieldPath" "metadata.name")))) }}
{{- $usesPostgres := has $name (list "users" "messages") }}
{{- if $usesPostgres }}
{{- $shardEnv = append $shardEnv (dict "name" "FLUXER_POSTGRES_MAX_CONNECTIONS" "value" "20") }}
{{- end }}
{{- if eq $name "unfurl" }}
{{- $shardEnv = append $shardEnv (dict "name" "FLUXER_MEDIA_PROXY_ENDPOINT" "value" "http://media-proxy:8080") }}
{{- end }}
{{- include "fluxer.app" (dict "root" $ "name" $name "component" "svc" "w" $router "image" (printf "fluxer-%s" $name) "port" 8090 "containerName" "router"
"env" (append $common (dict "name" "FLUXER_SVC_MODE" "value" "router"))
"probes" $probes
"waitFor" (include "fluxer.appWaitFor" (dict "root" $ "usesNats" true) | fromJsonArray)) }}
{{- include "fluxer.app" (dict "root" $ "name" (printf "%s-shard" $name) "component" "svc" "w" $shard "image" (printf "fluxer-%s" $name) "port" 8090 "containerName" "shard"
"kind" "StatefulSet" "serviceName" (printf "%s-shard" $name) "headless" true
"env" $shardEnv
"probes" $probes
"waitFor" (include "fluxer.appWaitFor" (dict "root" $ "usesNats" true "usesPostgres" $usesPostgres) | fromJsonArray)) }}
{{- end }}
@@ -0,0 +1,46 @@
{{- include "fluxer.validate" . }}
apiVersion: v1
kind: Pod
metadata:
name: fluxer-test-edge
namespace: {{ .Release.Namespace }}
labels:
{{- include "fluxer.labels" (dict "root" . "name" "fluxer-test" "component" "test") | nindent 4 }}
annotations:
helm.sh/hook: test
helm.sh/hook-delete-policy: before-hook-creation,hook-succeeded
spec:
restartPolicy: Never
automountServiceAccountToken: false
enableServiceLinks: false
{{- include "fluxer.pullSecrets" . | nindent 2 }}
securityContext:
{{- include "fluxer.podSecurityContext" (dict "uid" 65532) | nindent 4 }}
containers:
- name: check
image: {{ include "fluxer.thirdPartyImage" .Values.edge.image }}
imagePullPolicy: {{ .Values.edge.image.pullPolicy }}
command:
- sh
- -c
- |
for i in $(seq 1 30); do
if wget -q -O /dev/null http://edge:8080/_health && wget -q -O - --header "Host: $DOMAIN" http://edge:8080/.well-known/fluxer > /tmp/discovery && grep -q "$DOMAIN" /tmp/discovery; then
echo "edge and discovery answer for $DOMAIN"
exit 0
fi
sleep 2
done
echo "edge or discovery did not answer for $DOMAIN within 60 seconds" >&2
exit 1
env:
- name: DOMAIN
value: {{ .Values.domain | quote }}
resources:
requests:
cpu: 10m
memory: 16Mi
limits:
memory: 64Mi
securityContext:
{{- include "fluxer.containerSecurityContext" . | nindent 8 }}
+84
View File
@@ -0,0 +1,84 @@
{{- include "fluxer.validate" . }}
{{- with .Values.valkey }}
{{- if .enabled }}
{{- $ctx := dict "root" $ "name" "valkey" "component" "cache" "w" . }}
{{- $args := list "valkey-server" "--appendonly" "yes" "--appendfsync" .appendfsync "--dir" "/data" "--maxmemory" .maxmemory "--maxmemory-policy" .maxmemoryPolicy "--requirepass" "$(REDISCLI_AUTH)" }}
{{- $ping := dict "exec" (dict "command" (list "sh" "-c" "valkey-cli ping | grep -q PONG")) "periodSeconds" 10 "timeoutSeconds" 5 }}
{{- $secrets := include "fluxer.secretData" $ | fromJson }}
---
apiVersion: v1
kind: Service
metadata:
name: valkey
namespace: {{ $.Release.Namespace }}
labels:
{{- include "fluxer.labels" $ctx | nindent 4 }}
spec:
type: ClusterIP
selector:
{{- include "fluxer.selectorLabels" $ctx | nindent 4 }}
ports:
- name: redis
port: 6379
targetPort: redis
protocol: TCP
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: valkey
namespace: {{ $.Release.Namespace }}
labels:
{{- include "fluxer.labels" $ctx | nindent 4 }}
spec:
replicas: 1
serviceName: valkey
podManagementPolicy: Parallel
persistentVolumeClaimRetentionPolicy:
whenDeleted: Retain
whenScaled: Retain
selector:
matchLabels:
{{- include "fluxer.selectorLabels" $ctx | nindent 6 }}
template:
metadata:
labels:
{{- include "fluxer.podLabels" $ctx | nindent 8 }}
annotations:
checksum/config: {{ printf "%s\n%s" (toJson $args) (get $secrets "FLUXER_VALKEY_PASSWORD" | default "") | sha256sum }}
spec:
automountServiceAccountToken: false
enableServiceLinks: false
{{- include "fluxer.pullSecrets" $ | nindent 6 }}
securityContext:
{{- include "fluxer.podSecurityContext" (dict "uid" 999 "gid" 1000) | nindent 8 }}
{{- include "fluxer.scheduling" $ctx | nindent 6 }}
containers:
- name: valkey
image: {{ include "fluxer.thirdPartyImage" .image }}
imagePullPolicy: {{ .image.pullPolicy }}
command:
{{- toYaml $args | nindent 12 }}
env:
{{- include "fluxer.secretEnv" (dict "root" $ "name" "REDISCLI_AUTH" "key" "FLUXER_VALKEY_PASSWORD") | nindent 12 }}
ports:
- name: redis
containerPort: 6379
protocol: TCP
livenessProbe:
{{- toYaml (merge (dict "failureThreshold" 6 "initialDelaySeconds" 10) $ping) | nindent 12 }}
readinessProbe:
{{- toYaml (merge (dict "failureThreshold" 3) $ping) | nindent 12 }}
{{- with .resources }}
resources:
{{- toYaml . | nindent 12 }}
{{- end }}
securityContext:
{{- include "fluxer.containerSecurityContext" . | nindent 12 }}
volumeMounts:
- name: data
mountPath: /data
volumeClaimTemplates:
{{- include "fluxer.pvc" .persistence | nindent 4 }}
{{- end }}
{{- end }}
+975
View File
@@ -0,0 +1,975 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "Fluxer chart values",
"definitions": {
"workload": {
"type": "object",
"properties": {
"replicas": {
"type": "integer",
"minimum": 0
},
"image": {
"type": "object",
"properties": {
"registry": {
"type": "string"
},
"tag": {
"type": ["string", "number"]
},
"digest": {
"type": "string"
},
"pullPolicy": {
"type": "string",
"enum": ["Always", "IfNotPresent", "Never"]
}
},
"additionalProperties": false
},
"resources": {
"type": "object"
},
"env": {
"type": "object",
"additionalProperties": {
"type": ["string", "number", "boolean", "null"]
}
},
"extraEnv": {
"type": "array"
},
"probes": {
"type": "object",
"properties": {
"startup": {
"type": ["object", "null"]
},
"liveness": {
"type": ["object", "null"]
},
"readiness": {
"type": ["object", "null"]
}
},
"additionalProperties": false
},
"podAnnotations": {
"type": "object"
},
"nodeSelector": {
"type": "object"
},
"tolerations": {
"type": "array"
},
"affinity": {
"type": "object"
},
"topologySpreadConstraints": {
"type": "array"
},
"pdb": {
"type": "object",
"properties": {
"minAvailable": {
"type": ["string", "integer"]
},
"maxUnavailable": {
"type": ["string", "integer"]
}
},
"additionalProperties": false
},
"hpa": {
"type": "object",
"properties": {
"minReplicas": {
"type": "integer",
"minimum": 1
},
"maxReplicas": {
"type": "integer",
"minimum": 1
},
"targetCPUUtilizationPercentage": {
"type": "integer",
"minimum": 1
}
},
"additionalProperties": false
},
"extraVolumes": {
"type": "array"
},
"extraVolumeMounts": {
"type": "array"
},
"terminationGracePeriodSeconds": {
"type": "integer",
"minimum": 0
}
},
"additionalProperties": false
},
"thirdPartyImage": {
"type": "object",
"properties": {
"repository": {
"type": "string",
"minLength": 1
},
"tag": {
"type": ["string", "number"]
},
"digest": {
"type": "string"
},
"pullPolicy": {
"type": "string",
"enum": ["Always", "IfNotPresent", "Never"]
}
},
"additionalProperties": false
},
"persistence": {
"type": "object",
"properties": {
"size": {
"type": "string",
"minLength": 1
},
"storageClassName": {
"type": "string"
}
},
"additionalProperties": false
},
"svc": {
"type": "object",
"properties": {
"router": {
"$ref": "#/definitions/workload"
},
"shard": {
"$ref": "#/definitions/workload"
}
},
"additionalProperties": false
}
},
"type": "object",
"required": ["domain"],
"additionalProperties": false,
"properties": {
"global": {
"type": "object"
},
"enabled": {
"type": "boolean"
},
"domain": {
"type": "string",
"description": "Public hostname of the instance, for example chat.example.com"
},
"scheme": {
"type": "string",
"enum": ["http", "https"]
},
"port": {
"type": "integer",
"minimum": 1,
"maximum": 65535
},
"publicOrigin": {
"type": "string"
},
"adminBasePath": {
"type": "string"
},
"email": {
"type": "string"
},
"clusterDomain": {
"type": "string",
"minLength": 1
},
"clusterChecks": {
"type": "boolean"
},
"image": {
"type": "object",
"properties": {
"registry": {
"type": "string",
"minLength": 1
},
"tag": {
"type": ["string", "number"]
},
"pullPolicy": {
"type": "string",
"enum": ["Always", "IfNotPresent", "Never"]
},
"tags": {
"type": "object",
"additionalProperties": {
"type": ["string", "number", "boolean", "null"]
}
},
"digests": {
"type": "object",
"additionalProperties": {
"type": ["string", "number", "boolean", "null"]
}
},
"pullSecrets": {
"type": "array"
},
"revision": {
"type": ["string", "number"]
}
},
"additionalProperties": false
},
"initImage": {
"$ref": "#/definitions/thirdPartyImage"
},
"secrets": {
"type": "object",
"properties": {
"existingSecret": {
"type": "string"
},
"generate": {
"type": "boolean"
},
"values": {
"type": "object",
"properties": {
"FLUXER_POSTGRES_PASSWORD": {
"type": ["string", "null"]
},
"FLUXER_SEARCH_API_KEY": {
"type": ["string", "null"]
},
"FLUXER_S3_ACCESS_KEY_ID": {
"type": ["string", "null"]
},
"FLUXER_S3_SECRET_ACCESS_KEY": {
"type": ["string", "null"]
},
"FLUXER_SUDO_MODE_SECRET": {
"type": ["string", "null"]
},
"FLUXER_CONNECTION_INITIATION_SECRET": {
"type": ["string", "null"]
},
"FLUXER_PROFILE_PSEUDONYM_SECRET": {
"type": ["string", "null"]
},
"FLUXER_GATEWAY_RPC_AUTH_TOKEN": {
"type": ["string", "null"]
},
"FLUXER_VALKEY_PASSWORD": {
"type": ["string", "null"]
},
"FLUXER_NATS_AUTH_TOKEN": {
"type": ["string", "null"]
},
"FLUXER_ERLANG_COOKIE": {
"type": ["string", "null"]
},
"FLUXER_MEDIA_PROXY_SECRET_KEY": {
"type": ["string", "null"]
},
"FLUXER_MEDIA_PROXY_UPLOAD_RELAY_SECRET_BASE64": {
"type": ["string", "null"]
},
"FLUXER_ADMIN_SECRET_KEY_BASE": {
"type": ["string", "null"]
},
"FLUXER_ADMIN_OAUTH_CLIENT_SECRET": {
"type": ["string", "null"]
},
"FLUXER_SETUP_TOKEN": {
"type": ["string", "null"]
},
"FLUXER_LIVEKIT_API_KEY": {
"type": ["string", "null"]
},
"FLUXER_LIVEKIT_API_SECRET": {
"type": ["string", "null"]
},
"FLUXER_VAPID_PUBLIC_KEY": {
"type": ["string", "null"]
},
"FLUXER_VAPID_PRIVATE_KEY": {
"type": ["string", "null"]
}
},
"additionalProperties": false
},
"extra": {
"type": "object",
"additionalProperties": {
"type": ["string", "null"]
}
}
},
"additionalProperties": false
},
"env": {
"type": "object",
"additionalProperties": {
"type": ["string", "number", "boolean", "null"]
}
},
"postgres": {
"type": "object",
"properties": {
"enabled": {
"type": "boolean"
},
"image": {
"$ref": "#/definitions/thirdPartyImage"
},
"persistence": {
"$ref": "#/definitions/persistence"
},
"resources": {
"type": "object"
},
"nodeSelector": {
"type": "object"
},
"tolerations": {
"type": "array"
},
"affinity": {
"type": "object"
},
"external": {
"type": "object",
"properties": {
"host": {
"type": "string"
},
"port": {
"type": "integer",
"minimum": 1,
"maximum": 65535
},
"database": {
"type": "string"
},
"username": {
"type": "string"
},
"ssl": {
"type": ["string", "boolean"]
}
},
"additionalProperties": false
},
"database": {
"type": "string",
"minLength": 1
},
"username": {
"type": "string",
"minLength": 1
},
"runAsUser": {
"type": "integer",
"minimum": 1
},
"parameters": {
"type": "object",
"additionalProperties": {
"type": ["string", "number", "boolean", "null"]
}
},
"shmSize": {
"type": "string"
}
},
"additionalProperties": false
},
"valkey": {
"type": "object",
"properties": {
"enabled": {
"type": "boolean"
},
"image": {
"$ref": "#/definitions/thirdPartyImage"
},
"persistence": {
"$ref": "#/definitions/persistence"
},
"resources": {
"type": "object"
},
"nodeSelector": {
"type": "object"
},
"tolerations": {
"type": "array"
},
"affinity": {
"type": "object"
},
"external": {
"type": "object",
"properties": {
"url": {
"type": "string"
}
},
"additionalProperties": false
},
"maxmemory": {
"type": "string"
},
"maxmemoryPolicy": {
"type": "string"
},
"appendfsync": {
"type": "string",
"enum": ["always", "everysec", "no"]
}
},
"additionalProperties": false
},
"nats": {
"type": "object",
"properties": {
"enabled": {
"type": "boolean"
},
"image": {
"$ref": "#/definitions/thirdPartyImage"
},
"persistence": {
"$ref": "#/definitions/persistence"
},
"resources": {
"type": "object"
},
"nodeSelector": {
"type": "object"
},
"tolerations": {
"type": "array"
},
"affinity": {
"type": "object"
},
"external": {
"type": "object",
"properties": {
"url": {
"type": "string"
}
},
"additionalProperties": false
},
"replicas": {
"type": "integer",
"minimum": 1
}
},
"additionalProperties": false
},
"meilisearch": {
"type": "object",
"properties": {
"enabled": {
"type": "boolean"
},
"image": {
"$ref": "#/definitions/thirdPartyImage"
},
"persistence": {
"$ref": "#/definitions/persistence"
},
"resources": {
"type": "object"
},
"nodeSelector": {
"type": "object"
},
"tolerations": {
"type": "array"
},
"affinity": {
"type": "object"
},
"external": {
"type": "object",
"properties": {
"url": {
"type": "string"
}
},
"additionalProperties": false
},
"maxIndexingMemory": {
"type": "string"
},
"maxIndexingThreads": {
"type": "integer",
"minimum": 1
}
},
"additionalProperties": false
},
"seaweedfs": {
"type": "object",
"properties": {
"enabled": {
"type": "boolean"
},
"image": {
"$ref": "#/definitions/thirdPartyImage"
},
"persistence": {
"$ref": "#/definitions/persistence"
},
"resources": {
"type": "object"
},
"nodeSelector": {
"type": "object"
},
"tolerations": {
"type": "array"
},
"affinity": {
"type": "object"
},
"volumeGrowth": {
"type": "integer",
"minimum": 1
},
"gomemlimit": {
"type": "string"
},
"initAttempts": {
"type": "integer",
"minimum": 1
}
},
"additionalProperties": false
},
"s3": {
"type": "object",
"properties": {
"endpoint": {
"type": "string"
},
"publicEndpoint": {
"type": "string"
},
"region": {
"type": "string",
"minLength": 1
},
"forcePathStyle": {
"type": "boolean"
},
"buckets": {
"type": "object",
"properties": {
"cdn": {
"type": "string",
"minLength": 1
},
"uploads": {
"type": "string",
"minLength": 1
},
"reports": {
"type": "string",
"minLength": 1
},
"harvests": {
"type": "string",
"minLength": 1
}
},
"additionalProperties": false
}
},
"additionalProperties": false
},
"livekit": {
"type": "object",
"properties": {
"enabled": {
"type": "boolean"
},
"image": {
"$ref": "#/definitions/thirdPartyImage"
},
"external": {
"type": "object",
"properties": {
"url": {
"type": "string"
},
"publicUrl": {
"type": "string"
}
},
"additionalProperties": false
},
"expose": {
"type": "string",
"enum": ["hostNetwork", "loadBalancer", "nodePort"]
},
"nodeIP": {
"type": "string"
},
"loadBalancerIP": {
"type": "string"
},
"useExternalIP": {
"type": "boolean"
},
"tcpPort": {
"type": "integer",
"minimum": 1,
"maximum": 65535
},
"udpPort": {
"type": "integer",
"minimum": 1,
"maximum": 65535
},
"nodePorts": {
"type": "object",
"properties": {
"tcp": {
"type": "integer",
"minimum": 1,
"maximum": 65535
},
"udp": {
"type": "integer",
"minimum": 1,
"maximum": 65535
}
},
"additionalProperties": false
},
"stunServers": {
"type": "array",
"items": {
"type": "string"
}
},
"logLevel": {
"type": "string"
},
"extraConfig": {
"type": "object"
},
"defaultRegion": {
"type": "string"
},
"nodeCIDRs": {
"type": "array",
"items": {
"type": "string"
}
},
"serviceAnnotations": {
"type": "object"
},
"resources": {
"type": "object"
},
"nodeSelector": {
"type": "object"
},
"tolerations": {
"type": "array"
},
"affinity": {
"type": "object"
}
},
"additionalProperties": false
},
"api": {
"$ref": "#/definitions/workload"
},
"worker": {
"$ref": "#/definitions/workload"
},
"gateway": {
"$ref": "#/definitions/workload"
},
"mediaProxy": {
"$ref": "#/definitions/workload"
},
"push": {
"$ref": "#/definitions/workload"
},
"admin": {
"$ref": "#/definitions/workload"
},
"appProxy": {
"type": "object",
"properties": {
"replicas": {
"type": "integer",
"minimum": 0
},
"image": {
"type": "object",
"properties": {
"registry": {
"type": "string"
},
"tag": {
"type": ["string", "number"]
},
"digest": {
"type": "string"
},
"pullPolicy": {
"type": "string",
"enum": ["Always", "IfNotPresent", "Never"]
}
},
"additionalProperties": false
},
"resources": {
"type": "object"
},
"env": {
"type": "object",
"additionalProperties": {
"type": ["string", "number", "boolean", "null"]
}
},
"extraEnv": {
"type": "array"
},
"probes": {
"type": "object",
"properties": {
"startup": {
"type": ["object", "null"]
},
"liveness": {
"type": ["object", "null"]
},
"readiness": {
"type": ["object", "null"]
}
},
"additionalProperties": false
},
"podAnnotations": {
"type": "object"
},
"nodeSelector": {
"type": "object"
},
"tolerations": {
"type": "array"
},
"affinity": {
"type": "object"
},
"topologySpreadConstraints": {
"type": "array"
},
"pdb": {
"type": "object",
"properties": {
"minAvailable": {
"type": ["string", "integer"]
},
"maxUnavailable": {
"type": ["string", "integer"]
}
},
"additionalProperties": false
},
"hpa": {
"type": "object",
"properties": {
"minReplicas": {
"type": "integer",
"minimum": 1
},
"maxReplicas": {
"type": "integer",
"minimum": 1
},
"targetCPUUtilizationPercentage": {
"type": "integer",
"minimum": 1
}
},
"additionalProperties": false
},
"extraVolumes": {
"type": "array"
},
"extraVolumeMounts": {
"type": "array"
},
"terminationGracePeriodSeconds": {
"type": "integer",
"minimum": 0
},
"config": {
"type": "object",
"additionalProperties": {
"type": ["string", "number", "boolean", "null"]
}
}
},
"additionalProperties": false
},
"staticProxy": {
"$ref": "#/definitions/workload"
},
"svc": {
"type": "object",
"properties": {
"gifs": {
"$ref": "#/definitions/svc"
},
"messages": {
"$ref": "#/definitions/svc"
},
"snowflakes": {
"$ref": "#/definitions/svc"
},
"unfurl": {
"$ref": "#/definitions/svc"
},
"users": {
"$ref": "#/definitions/svc"
}
},
"additionalProperties": false
},
"edge": {
"type": "object",
"properties": {
"image": {
"$ref": "#/definitions/thirdPartyImage"
},
"expose": {
"type": "string",
"enum": ["ingress", "gateway", "loadBalancer", "none"]
},
"ingress": {
"type": "object",
"properties": {
"className": {
"type": "string"
},
"annotations": {
"type": "object"
},
"tls": {
"type": "object",
"properties": {
"mode": {
"type": "string",
"enum": ["certManager", "existingSecret", "none"]
},
"secretName": {
"type": "string"
},
"acmeServer": {
"type": "string"
}
},
"additionalProperties": false
}
},
"additionalProperties": false
},
"gateway": {
"type": "object",
"properties": {
"parentRefs": {
"type": "array"
},
"timeouts": {
"type": "object"
}
},
"additionalProperties": false
},
"loadBalancer": {
"type": "object",
"properties": {
"http3": {
"type": "boolean"
},
"annotations": {
"type": "object"
},
"persistence": {
"$ref": "#/definitions/persistence"
}
},
"additionalProperties": false
},
"replicas": {
"type": "integer",
"minimum": 1
},
"env": {
"type": "object",
"additionalProperties": {
"type": ["string", "number", "boolean", "null"]
}
},
"extraEnv": {
"type": "array"
},
"resources": {
"type": "object"
},
"podAnnotations": {
"type": "object"
},
"nodeSelector": {
"type": "object"
},
"tolerations": {
"type": "array"
},
"affinity": {
"type": "object"
}
},
"additionalProperties": false
},
"networkPolicy": {
"type": "object",
"properties": {
"enabled": {
"type": "boolean"
},
"ingressNamespaceSelector": {
"type": "object"
}
},
"additionalProperties": false
}
}
}
+710
View File
@@ -0,0 +1,710 @@
# The public hostname of the instance, lowercase, for example chat.example.com.
# Required. Point DNS for it at your ingress controller or at the edge load balancer.
domain: ""
# The scheme browsers use to reach the instance.
scheme: https
# The port browsers use to reach the instance.
port: 443
# The origin browsers see, with no trailing slash. Set it only when browsers
# reach the instance on an origin that domain, scheme and port do not describe.
# It wins over those three in every derived URL.
publicOrigin: ""
# The path the admin panel is served under, with a leading slash and no
# trailing slash.
adminBasePath: /admin
# A contact address. It becomes FLUXER_VAPID_EMAIL for web push and the ACME
# account email. Empty uses admin@<domain> for web push and no ACME email.
email: ""
# The cluster DNS suffix, used in the gateway discovery name.
clusterDomain: cluster.local
# Look up namespaces, StorageClasses, IngressClasses and installed APIs before
# installing, to fail early on a cluster that cannot run the release. Set false
# when your account cannot read those cluster-scoped objects.
clusterChecks: true
image:
# The registry and owner the Fluxer images come from.
registry: ghcr.io/fluxerapp
# The tag for every Fluxer image. v1 moves with each release.
tag: v1
# Always pulls the moving tag whenever a pod starts, including a pod that
# restarts by itself. Pin with digests for rollouts you control.
pullPolicy: Always
# Per-image tag overrides, keyed by image name, for example fluxer-api: 2026.1012.120000.
tags: {}
# Per-image digests, keyed by image name, for example fluxer-api: sha256:0123.
# A digest pins the image whatever the tag.
digests: {}
# Image pull secrets for every pod, as a list of {name: <secret>}.
pullSecrets: []
# Any new value restarts every Fluxer pod, which pulls the moving tag again.
# Add --set-string image.revision=$(date +%s) to a helm upgrade that passes
# your values file to move to the latest v1.
revision: ""
# The image of the init container that waits for the backing services.
initImage:
repository: busybox
tag: "1.37"
# A digest pins the image whatever the tag.
digest: ""
pullPolicy: IfNotPresent
secrets:
# The name of a Secret you manage yourself. The chart then renders no Secret
# and never changes this one. It needs every key listed under values below.
# After editing it, run kubectl rollout restart for the deployments and
# statefulsets of the release.
existingSecret: ""
# Generate every empty key below on first install and keep it on upgrade.
# Set false under Argo CD or helm template, which cannot read the existing
# Secret, and give every key here or use existingSecret.
generate: true
# Keys of the fluxer-env Secret. Empty means generate once, then keep.
# Generated values are never rotated by an upgrade. Back up the Secret.
values:
# Password of the bundled Postgres role. Changing it after the first install
# does not change the stored role password.
FLUXER_POSTGRES_PASSWORD: ""
# Meilisearch master key.
FLUXER_SEARCH_API_KEY: ""
# S3 access key id. The bundled store installs it on every upgrade.
FLUXER_S3_ACCESS_KEY_ID: fluxer
# S3 secret key. Letters and digits only for the bundled store.
FLUXER_S3_SECRET_ACCESS_KEY: ""
# Signs sudo mode tokens.
FLUXER_SUDO_MODE_SECRET: ""
# Signs connection initiation tokens.
FLUXER_CONNECTION_INITIATION_SECRET: ""
# Derives hidden profile names. Changing it renames every hidden profile.
FLUXER_PROFILE_PSEUDONYM_SECRET: ""
# Authenticates RPC between the gateway, push and the api.
FLUXER_GATEWAY_RPC_AUTH_TOKEN: ""
# Password of the bundled Valkey. Letters and digits only.
FLUXER_VALKEY_PASSWORD: ""
# Token of the bundled NATS, letters and digits only. With an outside NATS,
# its token if it has one.
FLUXER_NATS_AUTH_TOKEN: ""
# Erlang distribution cookie of the gateway.
FLUXER_ERLANG_COOKIE: ""
# Signs media proxy URLs.
FLUXER_MEDIA_PROXY_SECRET_KEY: ""
# Standard base64 of at least 32 random bytes. Signs upload relay tokens.
FLUXER_MEDIA_PROXY_UPLOAD_RELAY_SECRET_BASE64: ""
# Signs admin panel sessions.
FLUXER_ADMIN_SECRET_KEY_BASE: ""
# Client secret of the built-in admin OAuth application.
FLUXER_ADMIN_OAUTH_CLIENT_SECRET: ""
# Setup token the wizard asks for before the first admin account. With
# existingSecret, or with generate false and no value here, the API makes
# one on its first start and writes it to its log once.
FLUXER_SETUP_TOKEN: ""
# LiveKit API key.
FLUXER_LIVEKIT_API_KEY: fluxer
# LiveKit API secret, at least 32 characters.
FLUXER_LIVEKIT_API_SECRET: ""
# Web push key pair, base64url. Set both or neither.
FLUXER_VAPID_PUBLIC_KEY: ""
FLUXER_VAPID_PRIVATE_KEY: ""
# Optional secret-bearing settings, added to the Secret as given. The keys are
# the variable names on the configuration page of the operator docs.
extra: {}
# A full Postgres URL. Wins over the host, port, database and password.
# FLUXER_POSTGRES_URL: postgres://fluxer:[email protected]:5432/fluxer
# PEM text of the CA that signed the Postgres server certificate.
# FLUXER_POSTGRES_SSL_CA: ""
# The URL of an outside key-value store that holds a password.
# FLUXER_KV_URL: redis://:[email protected]:6379/0
# The password of an outside search server.
# FLUXER_SEARCH_PASSWORD: ""
# A temporary S3 session token.
# FLUXER_S3_SESSION_TOKEN: ""
# Comma-separated base64 secrets that sign attachment URLs.
# FLUXER_MEDIA_PROXY_ATTACHMENT_URL_SECRETS_BASE64: ""
# SMTP password for outgoing email.
# FLUXER_EMAIL_SMTP_PASSWORD: ""
# Secret that authenticates email provider webhooks.
# FLUXER_EMAIL_WEBHOOK_SECRET: ""
# Stripe secret key.
# FLUXER_STRIPE_SECRET_KEY: ""
# Stripe webhook signing secret.
# FLUXER_STRIPE_WEBHOOK_SECRET: ""
# Key of the donation proxy.
# FLUXER_API_DONATION_PROXY_KEY: ""
# Bearer token of the HTTP cache purge endpoint.
# FLUXER_CACHE_PURGE_HTTP_TOKEN: ""
# Klipy GIF API key.
# FLUXER_KLIPY_API_KEY: ""
# YouTube Data API key.
# FLUXER_YOUTUBE_API_KEY: ""
# Bluesky OAuth signing keys.
# FLUXER_AUTH_BLUESKY_KEYS: ""
# APNs private key for iOS push.
# FLUXER_PUSH_APNS_PRIVATE_KEY: ""
# FCM private key for Android push.
# FLUXER_PUSH_FCM_PRIVATE_KEY: ""
# Extra non-secret settings for every Fluxer service, as NAME: value. The names
# and meanings are on the configuration page of the operator docs. An empty
# value is left out. The chart refuses a secret here. Secrets belong in
# secrets.extra.
env: {}
postgres:
# Run Postgres in the release. Set false to use your own server.
enabled: true
image:
repository: postgres
# A new major needs a dump and restore. The pod refuses a data directory
# from another major.
tag: 16-alpine
digest: ""
pullPolicy: IfNotPresent
# The user and group id of the postgres user in the image. 70 in the Alpine
# images, 999 in the Debian ones.
runAsUser: 70
# Your own server, used when enabled is false. Its password goes in
# secrets.values.FLUXER_POSTGRES_PASSWORD, or a URL in secrets.extra.FLUXER_POSTGRES_URL.
# The database and the role have to exist already.
external:
host: ""
port: 5432
database: fluxer
username: fluxer
# true or false. Empty keeps the service default.
ssl: ""
# The database and role the bundled server creates.
database: fluxer
username: fluxer
# Server settings, passed as -c name=value.
parameters:
max_connections: "150"
shared_buffers: 512MB
effective_cache_size: 2GB
work_mem: 8MB
maintenance_work_mem: 256MB
autovacuum_work_mem: 128MB
random_page_cost: "1.1"
effective_io_concurrency: "200"
default_statistics_target: "200"
jit: "off"
min_wal_size: 512MB
max_wal_size: 2GB
checkpoint_completion_target: "0.9"
wal_buffers: 16MB
wal_compression: zstd
bgwriter_delay: 50ms
bgwriter_lru_maxpages: "1000"
autovacuum_vacuum_scale_factor: "0.05"
autovacuum_analyze_scale_factor: "0.02"
autovacuum_vacuum_cost_limit: "2000"
track_io_timing: "on"
shared_preload_libraries: pg_stat_statements
# Size of the in-memory /dev/shm. Parallel vacuum needs it.
shmSize: 1Gi
# The size and the class are fixed once the volume exists. To grow a volume
# later, edit the PersistentVolumeClaim, not this value.
persistence:
size: 20Gi
# Empty uses the cluster default StorageClass.
storageClassName: ""
resources:
requests:
cpu: 100m
memory: 1Gi
limits:
memory: 5Gi
nodeSelector: {}
tolerations: []
affinity: {}
valkey:
# Run Valkey in the release. Set false and give external.url to use your own.
enabled: true
image:
repository: valkey/valkey
tag: 9.1-alpine
digest: ""
pullPolicy: IfNotPresent
external:
# A URL without a password. One with a password goes in secrets.extra.FLUXER_KV_URL.
url: ""
maxmemory: 192mb
maxmemoryPolicy: noeviction
appendfsync: everysec
persistence:
size: 1Gi
storageClassName: ""
resources:
requests:
cpu: 20m
memory: 64Mi
limits:
memory: 256Mi
nodeSelector: {}
tolerations: []
affinity: {}
nats:
# Run NATS with JetStream in the release. Set false and give external.url to use your own.
enabled: true
image:
repository: nats
tag: 2.14-alpine
digest: ""
pullPolicy: IfNotPresent
external:
# Its token, if any, goes in secrets.values.FLUXER_NATS_AUTH_TOKEN.
url: ""
# 1, or 3 or more for a JetStream cluster.
replicas: 1
persistence:
size: 10Gi
storageClassName: ""
resources:
requests:
cpu: 20m
memory: 64Mi
limits:
memory: 256Mi
nodeSelector: {}
tolerations: []
affinity: {}
meilisearch:
# Run Meilisearch in the release. Set false and give external.url to use your own.
enabled: true
image:
repository: getmeili/meilisearch
tag: v1.53
digest: ""
pullPolicy: IfNotPresent
external:
# Its key goes in secrets.values.FLUXER_SEARCH_API_KEY.
url: ""
maxIndexingMemory: 256mb
maxIndexingThreads: 2
persistence:
size: 10Gi
storageClassName: ""
resources:
requests:
cpu: 50m
memory: 256Mi
limits:
memory: 1536Mi
nodeSelector: {}
tolerations: []
affinity: {}
seaweedfs:
# Run SeaweedFS as the S3 store and create the buckets. Only its S3 port
# listens outside the pod. Set false and give s3.endpoint to use your own
# store, with the buckets already created.
enabled: true
image:
repository: chrislusf/seaweedfs
tag: "4.47"
digest: ""
pullPolicy: IfNotPresent
# Volumes grown per bucket. 1 keeps the store usable on small disks.
volumeGrowth: 1
gomemlimit: 1536MiB
# Tries, 2 seconds apart, the bucket setup makes before it starts over.
initAttempts: 60
persistence:
size: 20Gi
storageClassName: ""
resources:
requests:
cpu: 50m
memory: 256Mi
limits:
memory: 2Gi
nodeSelector: {}
tolerations: []
affinity: {}
s3:
# The S3 endpoint the services use. Empty uses the bundled SeaweedFS.
endpoint: ""
# The S3 endpoint browsers use for direct uploads. Empty uses endpoint.
publicEndpoint: ""
region: us-east-1
forcePathStyle: true
buckets:
cdn: fluxer
uploads: fluxer-uploads
reports: fluxer-reports
harvests: fluxer-harvests
livekit:
# Run LiveKit for voice and video. false with no external.url turns voice off.
enabled: true
# A LiveKit server of your own, used when enabled is false. Its key and
# secret go in secrets.values.FLUXER_LIVEKIT_API_KEY and FLUXER_LIVEKIT_API_SECRET.
# Point its webhook at <origin>/api/webhooks/livekit, signed with the same key.
external:
# The address the api uses to reach it, for example http://livekit.voice.svc:7880.
url: ""
# The address browsers use to reach it, for example wss://livekit.example.com.
publicUrl: ""
image:
repository: livekit/livekit-server
tag: v1.12.0
digest: ""
pullPolicy: IfNotPresent
# How clients reach the media ports. hostNetwork binds them on the node and
# needs a privileged namespace. loadBalancer and nodePort use the Service
# livekit-rtc, which serves TCP and UDP on one address.
expose: hostNetwork
# The public IP LiveKit advertises. Required for loadBalancer, where it is the
# address of the load balancer. Empty finds it over STUN.
nodeIP: ""
# The address the loadBalancer Service asks for. Reserve it with your
# provider first. Some providers take it from an annotation instead.
loadBalancerIP: ""
# Find the public IP over STUN.
useExternalIP: true
tcpPort: 7881
udpPort: 7882
# The node ports in nodePort mode. LiveKit advertises these too.
nodePorts:
tcp: 30881
udp: 30882
stunServers:
- stun.l.google.com:19302
- stun1.l.google.com:19302
logLevel: info
# More LiveKit configuration, merged over the chart's, for example
# {rtc: {ips: {includes: [10.0.0.0/8]}}} to advertise only the node network.
extraConfig: {}
# The voice region seeded on every api start, as JSON.
defaultRegion: '{"id":"default","name":"Default","emoji":"🌍","latitude":0,"longitude":0}'
# Node addresses allowed to reach the edge in hostNetwork mode, for the
# LiveKit webhook. Needed only with networkPolicy.ingressNamespaceSelector.
# Keep it as narrow as the LiveKit node, for example 10.0.0.12/32 together
# with nodeSelector.
nodeCIDRs: []
# Annotations for the Service livekit-rtc.
serviceAnnotations: {}
resources:
requests:
cpu: 50m
memory: 128Mi
limits:
memory: 512Mi
nodeSelector: {}
tolerations: []
affinity: {}
# Every Fluxer workload below takes the same keys:
# replicas, image (registry, tag, digest, pullPolicy), resources, env (NAME: value),
# extraEnv (a list of Kubernetes env entries), probes (startup, liveness or
# readiness, each replacing the default probe whole), podAnnotations,
# nodeSelector, tolerations, affinity, topologySpreadConstraints, pdb
# (minAvailable or maxUnavailable), hpa (minReplicas, maxReplicas,
# targetCPUUtilizationPercentage), extraVolumes and extraVolumeMounts.
api:
replicas: 1
resources:
requests:
cpu: 100m
memory: 768Mi
limits:
memory: 2560Mi
env: {}
extraEnv: []
probes: {}
podAnnotations: {}
nodeSelector: {}
tolerations: []
affinity: {}
topologySpreadConstraints: []
pdb: {}
hpa: {}
extraVolumes: []
extraVolumeMounts: []
worker:
# Must stay 1. The worker runs the cron scheduler.
replicas: 1
resources:
requests:
cpu: 100m
memory: 768Mi
limits:
memory: 2560Mi
env: {}
extraEnv: []
probes: {}
podAnnotations: {}
nodeSelector: {}
tolerations: []
affinity: {}
topologySpreadConstraints: []
pdb: {}
hpa: {}
extraVolumes: []
extraVolumeMounts: []
gateway:
replicas: 1
terminationGracePeriodSeconds: 30
resources:
requests:
cpu: 50m
memory: 256Mi
limits:
memory: 1Gi
env: {}
extraEnv: []
probes: {}
podAnnotations: {}
nodeSelector: {}
tolerations: []
affinity: {}
topologySpreadConstraints: []
pdb: {}
hpa: {}
extraVolumes: []
extraVolumeMounts: []
mediaProxy:
replicas: 1
resources:
requests:
cpu: 50m
memory: 128Mi
limits:
memory: 512Mi
env: {}
extraEnv: []
probes: {}
podAnnotations: {}
nodeSelector: {}
tolerations: []
affinity: {}
topologySpreadConstraints: []
pdb: {}
hpa: {}
extraVolumes: []
extraVolumeMounts: []
push:
replicas: 1
resources:
requests:
cpu: 10m
memory: 32Mi
limits:
memory: 256Mi
env: {}
extraEnv: []
probes: {}
podAnnotations: {}
nodeSelector: {}
tolerations: []
affinity: {}
topologySpreadConstraints: []
pdb: {}
hpa: {}
extraVolumes: []
extraVolumeMounts: []
admin:
replicas: 1
resources:
requests:
cpu: 10m
memory: 64Mi
limits:
memory: 256Mi
env: {}
extraEnv: []
probes: {}
podAnnotations: {}
nodeSelector: {}
tolerations: []
affinity: {}
topologySpreadConstraints: []
pdb: {}
hpa: {}
extraVolumes: []
extraVolumeMounts: []
appProxy:
replicas: 1
# Settings for app-proxy only: branding, CSP extras, the index upstream,
# same-origin hosts and manifest scope, as NAME: value. For example
# FLUXER_APP_PRODUCT_NAME, FLUXER_CSP_EXTRA_CONNECT_SRC or RUST_LOG.
config: {}
resources:
requests:
cpu: 20m
memory: 64Mi
limits:
memory: 256Mi
env: {}
extraEnv: []
probes: {}
podAnnotations: {}
nodeSelector: {}
tolerations: []
affinity: {}
topologySpreadConstraints: []
pdb: {}
hpa: {}
extraVolumes: []
extraVolumeMounts: []
staticProxy:
replicas: 1
resources:
requests:
cpu: 10m
memory: 32Mi
limits:
memory: 256Mi
env: {}
extraEnv: []
probes: {}
podAnnotations: {}
nodeSelector: {}
tolerations: []
affinity: {}
topologySpreadConstraints: []
pdb: {}
hpa: {}
extraVolumes: []
extraVolumeMounts: []
# The internal services. Each runs a router Deployment and a shard StatefulSet.
# router and shard take the workload keys above. shard.replicas sets the shard count.
svc:
gifs:
router:
replicas: 1
resources:
requests: {cpu: 10m, memory: 32Mi}
limits: {memory: 128Mi}
shard:
replicas: 1
resources:
requests: {cpu: 20m, memory: 64Mi}
limits: {memory: 256Mi}
messages:
router:
replicas: 1
resources:
requests: {cpu: 10m, memory: 32Mi}
limits: {memory: 128Mi}
shard:
replicas: 1
resources:
requests: {cpu: 20m, memory: 64Mi}
limits: {memory: 256Mi}
snowflakes:
router:
replicas: 1
resources:
requests: {cpu: 10m, memory: 32Mi}
limits: {memory: 128Mi}
shard:
replicas: 1
resources:
requests: {cpu: 20m, memory: 64Mi}
limits: {memory: 256Mi}
unfurl:
router:
replicas: 1
resources:
requests: {cpu: 10m, memory: 32Mi}
limits: {memory: 128Mi}
shard:
replicas: 1
resources:
requests: {cpu: 20m, memory: 64Mi}
limits: {memory: 256Mi}
users:
router:
replicas: 1
resources:
requests: {cpu: 10m, memory: 32Mi}
limits: {memory: 128Mi}
shard:
replicas: 1
resources:
requests: {cpu: 20m, memory: 64Mi}
limits: {memory: 256Mi}
edge:
image:
repository: caddy
tag: 2.11-alpine
digest: ""
pullPolicy: IfNotPresent
# How traffic reaches the edge. ingress puts an Ingress in front of it,
# gateway an HTTPRoute, loadBalancer a LoadBalancer Service with its own TLS,
# and none leaves only the ClusterIP Service edge on port 8080.
expose: ingress
ingress:
# The IngressClass. Empty uses the cluster default.
className: ""
# Extra annotations. The chart always adds the ingress-nginx body size and
# timeout annotations, which other controllers ignore.
annotations: {}
tls:
# certManager issues a certificate through a namespaced ACME Issuer.
# existingSecret uses secretName. none serves plain HTTP behind the controller.
mode: certManager
secretName: fluxer-tls
acmeServer: https://acme-v02.api.letsencrypt.org/directory
gateway:
# The Gateway the HTTPRoute attaches to, as Gateway API parentRefs.
parentRefs: []
# HTTPRoute timeouts, for example {request: 0s}. Envoy-based gateways cut
# requests at 15 seconds without it.
timeouts: {}
loadBalancer:
# Also publish UDP 443 for HTTP/3. Some clouds cannot mix TCP and UDP on one Service.
http3: false
annotations: {}
# Size of the volume that keeps the certificates.
persistence:
size: 1Gi
storageClassName: ""
replicas: 1
# Edge settings as NAME: value, overriding the chart's, for example
# FLUXER_EDGE_GLOBAL_OPTIONS or FLUXER_EDGE_ENCODE.
env: {}
extraEnv: []
resources:
requests:
cpu: 20m
memory: 64Mi
limits:
memory: 256Mi
podAnnotations: {}
nodeSelector: {}
tolerations: []
affinity: {}
networkPolicy:
# Admit traffic to the release pods only from the release itself, to the edge
# from anywhere, and to the LiveKit media ports. Network plugins without
# NetworkPolicy support ignore it. Add a NetworkPolicy of your own to let
# another namespace in, for example a metrics scraper.
enabled: true
# Namespaces allowed to reach the edge on port 8080, as a namespaceSelector,
# for example {matchLabels: {kubernetes.io/metadata.name: ingress-nginx}}.
# Empty allows any source.
ingressNamespaceSelector: {}
Loaded 100 of 7261 files, more files were not shown because too many files have changed in this diff. Show more