From c2498911abe94e820dc1dcd8cd189f009b16facf Mon Sep 17 00:00:00 2001 From: qwer-xyz <50532012+qwer-xyz@users.noreply.github.com> Date: Sat, 20 Jun 2026 14:22:31 +0800 Subject: [PATCH] first commit --- .dockerignore | 56 + .env.example | 185 + .gitattributes | 14 + .github/workflows/agent-release.yml | 190 + .github/workflows/ci.yml | 81 + .github/workflows/docker.yml | 65 + .github/workflows/release.yml | 257 + .gitignore | 93 + Dockerfile | 85 + LICENSE | 28 + README.md | 194 + TransparentProxy.md | 104 + agent/.gitignore | 4 + agent/README.md | 174 + agent/VERSION | 1 + agent/cmd/incudal-agent/main.go | 110 + agent/config.example.yaml | 8 + agent/go.mod | 3 + agent/internal/config/config.go | 158 + agent/internal/config/config_test.go | 51 + agent/internal/panel/client.go | 119 + agent/internal/protocol/signing.go | 52 + agent/internal/protocol/signing_test.go | 49 + agent/internal/report/incus.go | 362 + agent/internal/report/report.go | 298 + agent/internal/report/report_test.go | 166 + agent/internal/upgrade/upgrade.go | 400 + agent/internal/upgrade/upgrade_test.go | 230 + agent/scripts/build-release.sh | 82 + client/eslint.config.js | 106 + client/index.html | 53 + client/package.json | 54 + client/postcss.config.js | 7 + client/public/badges/dark/abyss.svg | 102 + client/public/badges/dark/aether.svg | 102 + client/public/badges/dark/apex.svg | 102 + client/public/badges/dark/aquarius.svg | 80 + client/public/badges/dark/ares.svg | 102 + client/public/badges/dark/aries.svg | 80 + client/public/badges/dark/astral.svg | 102 + client/public/badges/dark/barrage.svg | 102 + client/public/badges/dark/barrier.svg | 102 + client/public/badges/dark/bastion.svg | 102 + client/public/badges/dark/blackhole.svg | 102 + client/public/badges/dark/cancer.svg | 80 + client/public/badges/dark/capricorn.svg | 80 + client/public/badges/dark/citadel.svg | 102 + client/public/badges/dark/core.svg | 102 + client/public/badges/dark/cronus.svg | 102 + client/public/badges/dark/crosshair.svg | 102 + client/public/badges/dark/defender.svg | 102 + client/public/badges/dark/dog.svg | 80 + client/public/badges/dark/dragon.svg | 80 + client/public/badges/dark/dynamo.svg | 102 + client/public/badges/dark/earth.svg | 102 + client/public/badges/dark/elite.svg | 102 + client/public/badges/dark/enigma.svg | 102 + client/public/badges/dark/fission.svg | 102 + client/public/badges/dark/fortress.svg | 102 + client/public/badges/dark/fusion.svg | 102 + client/public/badges/dark/galaxy.svg | 102 + client/public/badges/dark/gemini.svg | 80 + client/public/badges/dark/genesis.svg | 102 + client/public/badges/dark/glyph.svg | 102 + client/public/badges/dark/goat.svg | 80 + client/public/badges/dark/guardian.svg | 102 + client/public/badges/dark/horse.svg | 80 + client/public/badges/dark/ignition.svg | 102 + client/public/badges/dark/intercept.svg | 102 + client/public/badges/dark/jupiter.svg | 102 + client/public/badges/dark/leo.svg | 80 + client/public/badges/dark/libra.svg | 80 + client/public/badges/dark/lockon.svg | 102 + client/public/badges/dark/matrix.svg | 102 + client/public/badges/dark/mercury.svg | 102 + client/public/badges/dark/monkey.svg | 80 + client/public/badges/dark/moon.svg | 102 + client/public/badges/dark/mystic.svg | 102 + client/public/badges/dark/nebula.svg | 102 + client/public/badges/dark/neptune.svg | 102 + client/public/badges/dark/nexus.svg | 102 + client/public/badges/dark/omega.svg | 102 + client/public/badges/dark/oracle.svg | 102 + client/public/badges/dark/overload.svg | 102 + client/public/badges/dark/overwatch.svg | 102 + client/public/badges/dark/ox.svg | 80 + client/public/badges/dark/paladin.svg | 102 + client/public/badges/dark/paradox.svg | 102 + client/public/badges/dark/phalanx.svg | 102 + client/public/badges/dark/pig.svg | 80 + client/public/badges/dark/pisces.svg | 80 + client/public/badges/dark/plasma.svg | 102 + client/public/badges/dark/pluto.svg | 102 + client/public/badges/dark/pulsar.svg | 102 + client/public/badges/dark/pulse.svg | 102 + client/public/badges/dark/quantum.svg | 102 + client/public/badges/dark/rabbit.svg | 80 + client/public/badges/dark/radar.svg | 102 + client/public/badges/dark/radiance.svg | 102 + client/public/badges/dark/rat.svg | 80 + client/public/badges/dark/recon.svg | 102 + client/public/badges/dark/rooster.svg | 80 + client/public/badges/dark/rune.svg | 102 + client/public/badges/dark/sagittarius.svg | 80 + client/public/badges/dark/scorpio.svg | 80 + client/public/badges/dark/sentinel.svg | 102 + client/public/badges/dark/sigil.svg | 102 + client/public/badges/dark/snake.svg | 80 + client/public/badges/dark/sniper.svg | 102 + client/public/badges/dark/solaris.svg | 102 + client/public/badges/dark/strike.svg | 102 + client/public/badges/dark/taurus.svg | 80 + client/public/badges/dark/tiger.svg | 80 + client/public/badges/dark/titan.svg | 102 + client/public/badges/dark/tracer.svg | 102 + client/public/badges/dark/ultra.svg | 102 + client/public/badges/dark/uranus.svg | 102 + client/public/badges/dark/vanguard.svg | 102 + client/public/badges/dark/virgo.svg | 80 + client/public/badges/dark/vortex.svg | 102 + client/public/badges/dark/ward.svg | 102 + client/public/badges/dark/wormhole.svg | 102 + client/public/badges/light/abyss.svg | 102 + client/public/badges/light/aether.svg | 102 + client/public/badges/light/apex.svg | 102 + client/public/badges/light/aquarius.svg | 80 + client/public/badges/light/ares.svg | 102 + client/public/badges/light/aries.svg | 80 + client/public/badges/light/astral.svg | 102 + client/public/badges/light/barrage.svg | 102 + client/public/badges/light/barrier.svg | 102 + client/public/badges/light/bastion.svg | 102 + client/public/badges/light/blackhole.svg | 102 + client/public/badges/light/cancer.svg | 80 + client/public/badges/light/capricorn.svg | 80 + client/public/badges/light/citadel.svg | 102 + client/public/badges/light/core.svg | 102 + client/public/badges/light/cronus.svg | 102 + client/public/badges/light/crosshair.svg | 102 + client/public/badges/light/defender.svg | 102 + client/public/badges/light/dog.svg | 80 + client/public/badges/light/dragon.svg | 80 + client/public/badges/light/dynamo.svg | 102 + client/public/badges/light/earth.svg | 102 + client/public/badges/light/elite.svg | 102 + client/public/badges/light/enigma.svg | 102 + client/public/badges/light/fission.svg | 102 + client/public/badges/light/fortress.svg | 102 + client/public/badges/light/fusion.svg | 102 + client/public/badges/light/galaxy.svg | 102 + client/public/badges/light/gemini.svg | 80 + client/public/badges/light/genesis.svg | 102 + client/public/badges/light/glyph.svg | 102 + client/public/badges/light/goat.svg | 80 + client/public/badges/light/guardian.svg | 102 + client/public/badges/light/horse.svg | 80 + client/public/badges/light/ignition.svg | 102 + client/public/badges/light/intercept.svg | 102 + client/public/badges/light/jupiter.svg | 102 + client/public/badges/light/leo.svg | 80 + client/public/badges/light/libra.svg | 80 + client/public/badges/light/lockon.svg | 102 + client/public/badges/light/matrix.svg | 102 + client/public/badges/light/mercury.svg | 102 + client/public/badges/light/monkey.svg | 80 + client/public/badges/light/moon.svg | 102 + client/public/badges/light/mystic.svg | 102 + client/public/badges/light/nebula.svg | 102 + client/public/badges/light/neptune.svg | 102 + client/public/badges/light/nexus.svg | 102 + client/public/badges/light/omega.svg | 102 + client/public/badges/light/oracle.svg | 102 + client/public/badges/light/overload.svg | 102 + client/public/badges/light/overwatch.svg | 102 + client/public/badges/light/ox.svg | 80 + client/public/badges/light/paladin.svg | 102 + client/public/badges/light/paradox.svg | 102 + client/public/badges/light/phalanx.svg | 102 + client/public/badges/light/pig.svg | 80 + client/public/badges/light/pisces.svg | 80 + client/public/badges/light/plasma.svg | 102 + client/public/badges/light/pluto.svg | 102 + client/public/badges/light/pulsar.svg | 102 + client/public/badges/light/pulse.svg | 102 + client/public/badges/light/quantum.svg | 102 + client/public/badges/light/rabbit.svg | 80 + client/public/badges/light/radar.svg | 102 + client/public/badges/light/radiance.svg | 102 + client/public/badges/light/rat.svg | 80 + client/public/badges/light/recon.svg | 102 + client/public/badges/light/rooster.svg | 80 + client/public/badges/light/rune.svg | 102 + client/public/badges/light/sagittarius.svg | 80 + client/public/badges/light/scorpio.svg | 80 + client/public/badges/light/sentinel.svg | 102 + client/public/badges/light/sigil.svg | 102 + client/public/badges/light/snake.svg | 80 + client/public/badges/light/sniper.svg | 102 + client/public/badges/light/solaris.svg | 102 + client/public/badges/light/strike.svg | 102 + client/public/badges/light/taurus.svg | 80 + client/public/badges/light/tiger.svg | 80 + client/public/badges/light/titan.svg | 102 + client/public/badges/light/tracer.svg | 102 + client/public/badges/light/ultra.svg | 102 + client/public/badges/light/uranus.svg | 102 + client/public/badges/light/vanguard.svg | 102 + client/public/badges/light/virgo.svg | 80 + client/public/badges/light/vortex.svg | 102 + client/public/badges/light/ward.svg | 102 + client/public/badges/light/wormhole.svg | 102 + client/public/icons/peer.svg | 115 + client/public/icons/prime.svg | 144 + client/public/icons/pro.svg | 114 + client/public/incudal_logo.webp | Bin 0 -> 56512 bytes client/public/sw.js | 138 + client/public/tos/en.md | 3 + client/public/tos/zh.md | 3 + client/src/App.vue | 187 + client/src/api/index.ts | 5250 +++++++++++ client/src/components/BackupManager.vue | 996 ++ client/src/components/BadgeImage.vue | 61 + client/src/components/CheckinModal.vue | 841 ++ client/src/components/FlagIcon.vue | 37 + client/src/components/InstanceDisplayIcon.vue | 32 + client/src/components/InstanceSelector.vue | 193 + client/src/components/LazyImage.vue | 92 + client/src/components/LoadingSpinner.vue | 61 + client/src/components/NotificationBell.vue | 272 + .../components/PackageQuotaReleaseModal.vue | 414 + .../src/components/PopupAnnouncementModal.vue | 414 + .../components/SensitiveVerificationModal.vue | 270 + client/src/components/Skeleton.vue | 131 + client/src/components/SkeletonLoader.vue | 306 + client/src/components/SnapshotManager.vue | 442 + client/src/components/TermsOfServiceModal.vue | 282 + client/src/components/Toast.vue | 86 + client/src/components/ToastContainer.vue | 23 + client/src/components/TurnstileWidget.vue | 106 + client/src/components/UserAvatar.vue | 131 + client/src/components/VirtualList.vue | 130 + .../components/admin/BillingOverviewIcon.vue | 181 + .../admin/VipBenefitHallSettings.vue | 486 + .../components/admin/VipLevelRulesEditor.vue | 399 + .../entertainment/BadgeBatchRewardModal.vue | 103 + .../components/entertainment/BadgeCenter.vue | 609 ++ .../entertainment/BadgeRewardModal.vue | 130 + .../entertainment/VipBenefitHall.vue | 453 + .../extensions/InitCommandDetailModal.vue | 227 + .../extensions/InitCommandModal.vue | 325 + .../extensions/InitCommandSelector.vue | 268 + .../src/components/host/BatchConfigModal.vue | 793 ++ client/src/components/host/HostCaddyTab.vue | 595 ++ .../components/host/HostCreateInstanceTab.vue | 714 ++ client/src/components/host/HostImagesTab.vue | 294 + client/src/components/host/HostInfoTab.vue | 1039 ++ .../src/components/host/HostInstancesList.vue | 329 + .../src/components/host/HostInstancesTab.vue | 2034 ++++ client/src/components/host/HostOpsTab.vue | 2080 ++++ .../components/host/HostRedeemCodesTab.vue | 928 ++ client/src/components/host/HostStorageTab.vue | 801 ++ .../src/components/host/MigrateHostModal.vue | 480 + .../src/components/host/MyHostConfigTab.vue | 667 ++ .../src/components/icons/AnnouncementIcon.vue | 128 + client/src/components/icons/DistroIcon.vue | 95 + .../components/instance/ChangeHostCard.vue | 325 + .../src/components/instance/HostSelector.vue | 182 + .../src/components/instance/ImageSelector.vue | 245 + .../instance/InstanceBadgeModal.vue | 451 + .../components/instance/InstanceConfigTab.vue | 685 ++ .../components/instance/InstanceInfoTab.vue | 655 ++ .../components/instance/InstanceLogsTab.vue | 424 + .../instance/InstanceNetworkTab.vue | 696 ++ .../components/instance/InstanceOrderMenu.vue | 409 + .../components/instance/InstanceQuotaTab.vue | 125 + .../components/instance/InstanceSitesTab.vue | 989 ++ .../components/instance/PackageSelector.vue | 428 + .../src/components/instance/PlanSelector.vue | 374 + .../components/instance/RegionSelector.vue | 227 + .../components/instance/ResourceSlider.vue | 281 + .../components/instance/ResourceSliders.vue | 185 + .../components/instance/SSHKeySelector.vue | 151 + .../instance/TerminalAccessoryBar.vue | 296 + .../instance/TerminalContextMenu.vue | 192 + .../src/components/instance/TerminalModal.vue | 1802 ++++ .../src/components/instance/TrafficStats.vue | 357 + .../instance/modals/AddPortModal.vue | 380 + .../instance/modals/ApplyAffCodeModal.vue | 177 + .../instance/modals/ChangePlanModal.vue | 696 ++ .../instance/modals/ConfigEditModal.vue | 425 + .../instance/modals/DestroyInstanceModal.vue | 478 + .../instance/modals/PortConflictModal.vue | 265 + .../instance/modals/RebuildModal.vue | 361 + .../instance/modals/RecreateModal.vue | 357 + .../components/instance/modals/RenewModal.vue | 396 + .../instance/modals/TransferModal.vue | 303 + client/src/components/layout/AppLayout.vue | 348 + client/src/components/layout/SideNav.vue | 385 + .../src/components/profile/AccountSection.vue | 214 + .../src/components/profile/BillingSection.vue | 653 ++ .../components/profile/ChangeEmailModal.vue | 468 + .../profile/LoginHistorySection.vue | 232 + .../profile/NotificationSection.vue | 456 + .../src/components/profile/OAuthSection.vue | 224 + .../components/profile/PasswordSection.vue | 170 + .../src/components/profile/SSHKeysSection.vue | 383 + .../src/components/profile/StorageSection.vue | 331 + .../profile/TelegramBindingSection.vue | 191 + .../components/profile/TwoFactorSection.vue | 344 + .../src/components/public/PublicAuthShell.vue | 39 + .../components/public/PublicSiteFooter.vue | 173 + .../components/public/PublicSiteHeader.vue | 315 + .../components/public/PublicSiteLayout.vue | 44 + .../terminal/TerminalSavedCommandsSidebar.vue | 448 + .../tickets/TicketImageLightbox.vue | 212 + .../tickets/TicketImageUploader.vue | 164 + .../tickets/TicketInstanceOwnerCard.vue | 874 ++ client/src/composables/useBrand.ts | 17 + client/src/composables/useM3Auth.ts | 69 + client/src/composables/usePageSeo.ts | 136 + client/src/composables/useTerminal.ts | 705 ++ client/src/composables/useTurnstile.ts | 311 + client/src/constants/adminSettings.ts | 62 + client/src/data/funnyQuotes.ts | 625 ++ client/src/env.d.ts | 8 + client/src/lib/terminal-core.ts | 214 + client/src/locales/en.ts | 7443 +++++++++++++++ client/src/locales/index.ts | 88 + client/src/locales/zh-CN.ts | 7460 +++++++++++++++ client/src/locales/zh-TW.ts | 7777 +++++++++++++++ client/src/main.ts | 81 + client/src/router/index.ts | 551 ++ client/src/stores/auth.ts | 149 + client/src/stores/badges.ts | 62 + client/src/stores/config.ts | 104 + client/src/stores/inbox.ts | 100 + client/src/stores/restoreTask.ts | 219 + client/src/stores/terminal.ts | 332 + client/src/stores/theme.ts | 81 + client/src/stores/toast.ts | 59 + client/src/styles/main.css | 1433 +++ client/src/types/api.ts | 1729 ++++ client/src/types/router.ts | 17 + client/src/types/store.ts | 29 + client/src/utils/billing.ts | 70 + client/src/utils/countryDisplay.ts | 132 + client/src/utils/errorHandler.ts | 61 + client/src/utils/formatters.ts | 166 + client/src/utils/freeSiteFun.ts | 112 + client/src/utils/inboxHelper.ts | 261 + client/src/utils/markdown.ts | 587 ++ client/src/utils/publicCatalog.ts | 136 + client/src/utils/turnstile.ts | 52 + client/src/utils/validation.ts | 403 + client/src/utils/vipBadge.ts | 56 + client/src/views/DashboardView.vue | 799 ++ client/src/views/EntertainmentView.vue | 2044 ++++ client/src/views/ExtensionsView.vue | 339 + client/src/views/ForgotPasswordView.vue | 305 + client/src/views/FriendsView.vue | 1297 +++ client/src/views/HelpView.vue | 750 ++ client/src/views/HostingWalletView.vue | 1492 +++ client/src/views/InboxView.vue | 434 + client/src/views/InstanceCreateView.vue | 1373 +++ client/src/views/InstanceDetailView.vue | 3606 +++++++ client/src/views/InstancesView.vue | 2645 ++++++ client/src/views/InvitesView.vue | 331 + client/src/views/LoginView.vue | 415 + client/src/views/LogsView.vue | 356 + client/src/views/MailDomainView.vue | 428 + client/src/views/MailView.vue | 864 ++ client/src/views/MarketView.vue | 1146 +++ client/src/views/NotFoundView.vue | 28 + client/src/views/PortalView.vue | 575 ++ client/src/views/ProfileView.vue | 63 + client/src/views/RegisterView.vue | 607 ++ client/src/views/TerminalView.vue | 2167 +++++ client/src/views/TicketsView.vue | 1198 +++ client/src/views/TransfersView.vue | 703 ++ client/src/views/WalletView.vue | 2273 +++++ .../views/admin/AdminInstanceCreateView.vue | 833 ++ client/src/views/admin/AdminMailView.vue | 942 ++ client/src/views/admin/AffReviewView.vue | 292 + client/src/views/admin/BillingView.vue | 2577 +++++ client/src/views/admin/BroadcastView.vue | 366 + client/src/views/admin/EntertainmentView.vue | 1637 ++++ client/src/views/admin/HelpManageView.vue | 1125 +++ client/src/views/admin/HostingView.vue | 635 ++ client/src/views/admin/ImagesView.vue | 449 + client/src/views/admin/OAuthConfigView.vue | 348 + .../src/views/admin/PaymentProvidersView.vue | 744 ++ client/src/views/admin/StatisticsView.vue | 679 ++ client/src/views/admin/SystemConfigView.vue | 1828 ++++ client/src/views/admin/TelegramConfigView.vue | 1261 +++ client/src/views/admin/UsersView.vue | 2806 ++++++ .../src/views/resources/MyHostCreateView.vue | 596 ++ .../src/views/resources/MyHostDetailView.vue | 658 ++ client/src/views/resources/MyHostsView.vue | 677 ++ client/src/views/resources/MyPackagesView.vue | 1862 ++++ .../src/views/resources/PackageFormView.vue | 1501 +++ client/tailwind.config.js | 85 + client/tsconfig.json | 44 + client/tsconfig.node.json | 13 + client/vite.config.ts | 109 + docker-compose.dev.yml | 76 + docker-compose.yml | 74 + package-lock.json | 332 + package.json | 22 + pnpm-lock.yaml | 8322 +++++++++++++++++ pnpm-workspace.yaml | 4 + scripts/generate-badges-from-html.mjs | 315 + scripts/init-env.sh | 97 + scripts/install-docker.sh | 759 ++ scripts/install-panel.sh | 1155 +++ scripts/local-ci-macos.sh | 73 + scripts/local-ci.ps1 | 74 + scripts/watch-migrations.mjs | 122 + server/certs/.gitkeep | 4 + server/docker-entrypoint.sh | 10 + server/package-lock.json | 6829 ++++++++++++++ server/package.json | 77 + server/prisma.config.ts | 15 + .../migration.sql | 17 + .../20251206045256_init/migration.sql | 535 ++ .../migration.sql | 3 + .../migration.sql | 2 + .../migration.sql | 18 + .../migration.sql | 16 + .../migration.sql | 52 + .../migration.sql | 2 + .../migration.sql | 30 + .../migration.sql | 61 + .../migration.sql | 2 + .../migration.sql | 38 + .../migration.sql | 2 + .../migration.sql | 2 + .../migration.sql | 107 + .../migration.sql | 6 + .../migration.sql | 63 + .../migration.sql | 14 + .../migration.sql | 67 + .../migration.sql | 11 + .../migration.sql | 59 + .../migration.sql | 76 + .../migration.sql | 60 + .../migration.sql | 1 + .../migration.sql | 14 + .../migration.sql | 1 + .../migration.sql | 32 + .../migration.sql | 56 + .../migration.sql | 2 + .../migration.sql | 12 + .../migration.sql | 7 + .../migration.sql | 2 + .../migration.sql | 51 + .../migration.sql | 85 + .../migration.sql | 31 + .../migration.sql | 8 + .../migration.sql | 3 + .../migration.sql | 2 + .../migration.sql | 2 + .../migration.sql | 27 + .../migration.sql | 46 + .../migration.sql | 39 + .../migration.sql | 12 + .../migration.sql | 2 + .../migration.sql | 5 + .../migration.sql | 4 + .../migration.sql | 2 + .../migration.sql | 2 + .../migration.sql | 7 + .../migration.sql | 2 + .../migration.sql | 22 + .../migration.sql | 5 + .../migration.sql | 40 + .../migration.sql | 2 + .../migration.sql | 24 + .../migration.sql | 11 + .../migration.sql | 2 + .../migration.sql | 5 + .../migration.sql | 99 + .../migration.sql | 1 + .../migration.sql | 57 + .../migration.sql | 64 + .../migration.sql | 5 + .../migration.sql | 11 + .../migration.sql | 18 + .../migration.sql | 26 + .../migration.sql | 2 + .../migration.sql | 2 + .../20260111_add_billing_system/migration.sql | 209 + .../migration.sql | 9 + .../migration.sql | 152 + .../migration.sql | 18 + .../migration.sql | 5 + .../migration.sql | 2 + .../migration.sql | 18 + .../migration.sql | 2 + .../migration.sql | 145 + .../migration.sql | 2 + .../migration.sql | 2 + .../migration.sql | 2 + .../migration.sql | 6 + .../migration.sql | 3 + .../migration.sql | 2 + .../migration.sql | 47 + .../migration.sql | 8 + .../migration.sql | 29 + .../migration.sql | 53 + .../migration.sql | 2 + .../migration.sql | 2 + .../migration.sql | 13 + .../migration.sql | 2 + .../migration.sql | 4 + .../migration.sql | 4 + .../migration.sql | 2 + .../migration.sql | 112 + .../migration.sql | 32 + .../migration.sql | 28 + .../migration.sql | 2 + .../migration.sql | 2 + .../migration.sql | 26 + .../migration.sql | 29 + .../migration.sql | 73 + .../migration.sql | 4 + .../migration.sql | 7 + .../migration.sql | 6 + .../migration.sql | 18 + .../migration.sql | 32 + .../migration.sql | 2 + .../migration.sql | 4 + .../migration.sql | 2 + .../migration.sql | 5 + .../migration.sql | 36 + .../migration.sql | 3 + .../migration.sql | 3 + .../migration.sql | 234 + .../migration.sql | 21 + .../migration.sql | 25 + .../migration.sql | 1 + .../migration.sql | 3 + .../migration.sql | 46 + .../migration.sql | 9 + .../migration.sql | 9 + .../migration.sql | 103 + .../migration.sql | 27 + .../migration.sql | 47 + .../migration.sql | 1 + .../migration.sql | 7 + .../migration.sql | 2 + .../migration.sql | 2 + .../migration.sql | 157 + .../migration.sql | 20 + .../migration.sql | 10 + .../migration.sql | 19 + .../migration.sql | 12 + .../migration.sql | 21 + .../migration.sql | 5 + .../migration.sql | 4 + .../migration.sql | 38 + .../migration.sql | 11 + .../migration.sql | 182 + .../migration.sql | 86 + .../migration.sql | 11 + .../migration.sql | 157 + server/prisma/migrations/migration_lock.toml | 3 + server/prisma/schema.prisma | 2978 ++++++ .../backfill-instance-network-addresses.ts | 164 + .../scripts/fix-migrated-instance-billing.ts | 122 + server/scripts/seed-bulk-local-data.ts | 1690 ++++ server/scripts/seed-test-data.ts | 975 ++ server/scripts/test-agent-auth.ts | 47 + server/scripts/test-security-config.ts | 79 + server/src/app.ts | 951 ++ server/src/config/badges.generated.json | 1426 +++ server/src/config/badges.generated.ts | 1426 +++ server/src/config/badges.ts | 26 + server/src/config/rate-limit.ts | 710 ++ server/src/config/system-images.ts | 297 + server/src/db/advisory-locks.ts | 36 + server/src/db/aff.ts | 1127 +++ server/src/db/announcements.ts | 170 + server/src/db/backup-upload-tasks.ts | 227 + server/src/db/backups.ts | 202 + server/src/db/badges.ts | 1121 +++ server/src/db/balance.ts | 427 + server/src/db/billing-operations.ts | 1481 +++ server/src/db/billing-records.ts | 502 + server/src/db/checkin.ts | 524 ++ server/src/db/custom-init-commands.ts | 309 + server/src/db/email-verification.ts | 116 + server/src/db/friendships.ts | 503 + server/src/db/help.ts | 270 + server/src/db/host-addresses.ts | 319 + .../src/db/host-notification-email-tasks.ts | 239 + server/src/db/hosting-blocks.ts | 135 + server/src/db/hosts.ts | 922 ++ server/src/db/images.ts | 539 ++ server/src/db/inbox.ts | 338 + server/src/db/index.ts | 186 + server/src/db/init-prisma.ts | 233 + server/src/db/instance-suspend.ts | 266 + server/src/db/instance-tasks.ts | 210 + server/src/db/instances.ts | 753 ++ server/src/db/invite-codes.ts | 313 + server/src/db/ip-addresses.ts | 220 + server/src/db/ipv6-subnets.ts | 88 + server/src/db/login-records.ts | 454 + server/src/db/logs.ts | 388 + server/src/db/lottery.ts | 759 ++ server/src/db/mail.ts | 556 ++ server/src/db/notifications.ts | 292 + server/src/db/oauth.ts | 250 + server/src/db/optimizations.ts | 321 + server/src/db/package-hosts.ts | 135 + server/src/db/package-plans.ts | 388 + server/src/db/package-shares.ts | 687 ++ server/src/db/packages.ts | 1459 +++ server/src/db/pagination.ts | 845 ++ server/src/db/payment-providers.ts | 385 + server/src/db/points.ts | 316 + server/src/db/port-mappings.ts | 389 + server/src/db/prisma.ts | 186 + server/src/db/proxy-sites.ts | 229 + server/src/db/quota-operations.ts | 559 ++ server/src/db/recharge-records.ts | 532 ++ server/src/db/redeem-codes.ts | 392 + server/src/db/reset-database.ts | 142 + server/src/db/resource-pool.ts | 283 + server/src/db/restore-tasks.ts | 72 + server/src/db/snapshots.ts | 178 + server/src/db/ssh-keys.ts | 78 + server/src/db/storage-configs.ts | 142 + server/src/db/storage-pools.ts | 229 + server/src/db/system-config.ts | 337 + server/src/db/terminal-saved-commands.ts | 307 + server/src/db/tickets.ts | 1170 +++ server/src/db/traffic.ts | 850 ++ server/src/db/transfers.ts | 556 ++ server/src/db/users.ts | 798 ++ server/src/lib/action-ticket.ts | 174 + server/src/lib/agent-auth.ts | 168 + server/src/lib/architecture.ts | 20 + server/src/lib/billing-calc.ts | 314 + server/src/lib/caddy-client.ts | 279 + server/src/lib/cloud-init-status.ts | 293 + server/src/lib/config-cache.ts | 108 + server/src/lib/cookie-config.ts | 136 + server/src/lib/cpu-allowance.ts | 70 + server/src/lib/distributed-lock.ts | 267 + server/src/lib/download-token.ts | 167 + server/src/lib/email-domain.ts | 111 + server/src/lib/epay.ts | 441 + server/src/lib/errors.ts | 674 ++ server/src/lib/geoip.ts | 233 + server/src/lib/heleket.ts | 352 + server/src/lib/host-agent-credentials.ts | 202 + server/src/lib/hosting-access.ts | 164 + server/src/lib/incus-config-container.ts | 635 ++ server/src/lib/incus-config-generator.ts | 56 + server/src/lib/incus-config-vm.ts | 656 ++ server/src/lib/incus/host-health.ts | 147 + server/src/lib/incus/incus-backups.ts | 176 + server/src/lib/incus/incus-client.ts | 217 + server/src/lib/incus/incus-images.ts | 205 + server/src/lib/incus/incus-instances.ts | 982 ++ server/src/lib/incus/incus-network.ts | 21 + server/src/lib/incus/incus-pool.ts | 227 + server/src/lib/incus/incus-restore.ts | 188 + server/src/lib/incus/incus-snapshots.ts | 79 + server/src/lib/incus/incus-storage.ts | 109 + server/src/lib/incus/incus-traffic.ts | 241 + server/src/lib/incus/incus-utils.ts | 119 + server/src/lib/incus/index.ts | 43 + server/src/lib/instance-audit.ts | 502 + server/src/lib/instance-network-sync.ts | 376 + server/src/lib/instance-swap.ts | 43 + server/src/lib/invite-pricing.ts | 273 + server/src/lib/ip-calculator.ts | 256 + server/src/lib/log-sanitizer.ts | 188 + server/src/lib/lottery-notifier.ts | 249 + server/src/lib/lsky.ts | 361 + server/src/lib/mailer.ts | 2097 +++++ server/src/lib/managed-instance-provision.ts | 156 + server/src/lib/network-address.ts | 92 + server/src/lib/network-payload-builder.ts | 441 + server/src/lib/notifier.ts | 1531 +++ server/src/lib/operation-verification.ts | 394 + server/src/lib/outbound-security.ts | 165 + server/src/lib/permission.ts | 437 + server/src/lib/proxy/IProxyStrategy.ts | 31 + server/src/lib/proxy/KvmProxyStrategy.ts | 81 + server/src/lib/proxy/LxcProxyStrategy.ts | 38 + server/src/lib/proxy/index.ts | 14 + server/src/lib/recharge-payment-details.ts | 165 + server/src/lib/recharge-provider-snapshot.ts | 62 + server/src/lib/redirect-validator.ts | 60 + server/src/lib/release-notifier.ts | 91 + server/src/lib/retry.ts | 288 + server/src/lib/security-config.ts | 90 + server/src/lib/security.ts | 1573 ++++ server/src/lib/terminal-proxy.ts | 828 ++ server/src/lib/timezone.ts | 168 + server/src/lib/traffic-multiplier.ts | 71 + server/src/lib/turnstile.ts | 134 + server/src/lib/vm-network-identifiers.ts | 32 + server/src/lib/websocket-security.ts | 217 + server/src/routes/admin-billing.ts | 3879 ++++++++ server/src/routes/admin-entertainment.ts | 1190 +++ server/src/routes/admin-hosting.ts | 547 ++ .../src/routes/admin-notification-channels.ts | 236 + server/src/routes/admin-statistics.ts | 326 + server/src/routes/aff.ts | 458 + server/src/routes/agent.ts | 1300 +++ server/src/routes/announcements.ts | 106 + server/src/routes/auth.ts | 1369 +++ server/src/routes/backups.ts | 1358 +++ server/src/routes/balance.ts | 355 + server/src/routes/batch-config.ts | 647 ++ server/src/routes/checkin.ts | 362 + server/src/routes/custom-init-commands.ts | 298 + server/src/routes/entertainment.ts | 709 ++ server/src/routes/friends.ts | 516 + server/src/routes/help.ts | 418 + server/src/routes/hosting.ts | 792 ++ server/src/routes/hosts.ts | 8102 ++++++++++++++++ server/src/routes/images.ts | 194 + server/src/routes/inbox.ts | 555 ++ server/src/routes/instance-billing.ts | 1150 +++ server/src/routes/instance-destroy.ts | 1273 +++ server/src/routes/instances.ts | 6375 +++++++++++++ server/src/routes/ip-addresses.ts | 813 ++ server/src/routes/logs.ts | 89 + server/src/routes/mail.ts | 1363 +++ server/src/routes/notifications.ts | 462 + server/src/routes/oauth.ts | 585 ++ server/src/routes/packages.ts | 2543 +++++ server/src/routes/proxy-sites.ts | 1017 ++ server/src/routes/recharge.ts | 2192 +++++ server/src/routes/redeem-codes.ts | 401 + server/src/routes/resource-pool.ts | 249 + server/src/routes/snapshots.ts | 428 + server/src/routes/ssh-keys.ts | 312 + server/src/routes/storage-configs.ts | 366 + server/src/routes/system-config.ts | 661 ++ server/src/routes/telegram.ts | 977 ++ server/src/routes/terminal-saved-commands.ts | 163 + server/src/routes/terminal.ts | 409 + server/src/routes/tickets.ts | 934 ++ server/src/routes/traffic.ts | 431 + server/src/routes/transfers.ts | 1083 +++ server/src/routes/user-invites.ts | 198 + server/src/routes/users.ts | 1655 ++++ server/src/routes/verification.ts | 266 + server/src/routes/vip-benefits.ts | 123 + server/src/routes/vip-levels.ts | 81 + server/src/services/agent-instance-report.ts | 398 + server/src/services/auto-policy-scheduler.ts | 296 + server/src/services/billing-scheduler.ts | 627 ++ server/src/services/cranemail.ts | 222 + server/src/services/host-address-monitor.ts | 480 + server/src/services/hosting-scheduler.ts | 177 + .../services/instance-traffic-collector.ts | 273 + server/src/services/mail-expiry-scheduler.ts | 76 + server/src/services/smartermail.ts | 235 + server/src/services/status-scheduler.ts | 394 + server/src/services/system-monitor.ts | 246 + .../services/ticket-auto-close-scheduler.ts | 98 + server/src/services/traffic-bandwidth.ts | 66 + server/src/services/traffic-notifier.ts | 300 + server/src/services/traffic-scheduler.ts | 694 ++ server/src/services/traffic-utils.ts | 176 + server/src/services/vip-benefits.ts | 733 ++ server/src/services/vip-levels.ts | 777 ++ server/src/storage/factory.ts | 70 + server/src/storage/index.ts | 9 + server/src/storage/providers/FtpProvider.ts | 98 + server/src/storage/providers/SftpProvider.ts | 104 + .../src/storage/providers/WebDavProvider.ts | 97 + server/src/storage/types.ts | 51 + server/src/types/api.ts | 324 + server/src/types/database.ts | 395 + server/src/types/fastify.d.ts | 46 + server/src/types/incus.ts | 246 + server/src/workers/backupUploadWorker.ts | 418 + server/src/workers/db-failure-backoff.ts | 48 + .../workers/hostNotificationEmailWorker.ts | 109 + server/src/workers/instanceTaskWorker.ts | 2595 +++++ server/src/workers/restoreTaskWorker.ts | 536 ++ server/templates/agent-install.sh | 375 + server/templates/caddy.sh | 139 + server/templates/install.sh | 2818 ++++++ server/tsconfig.json | 33 + 793 files changed, 291660 insertions(+) create mode 100644 .dockerignore create mode 100644 .env.example create mode 100644 .gitattributes create mode 100644 .github/workflows/agent-release.yml create mode 100644 .github/workflows/ci.yml create mode 100644 .github/workflows/docker.yml create mode 100644 .github/workflows/release.yml create mode 100644 .gitignore create mode 100644 Dockerfile create mode 100644 LICENSE create mode 100644 README.md create mode 100644 TransparentProxy.md create mode 100644 agent/.gitignore create mode 100644 agent/README.md create mode 100644 agent/VERSION create mode 100644 agent/cmd/incudal-agent/main.go create mode 100644 agent/config.example.yaml create mode 100644 agent/go.mod create mode 100644 agent/internal/config/config.go create mode 100644 agent/internal/config/config_test.go create mode 100644 agent/internal/panel/client.go create mode 100644 agent/internal/protocol/signing.go create mode 100644 agent/internal/protocol/signing_test.go create mode 100644 agent/internal/report/incus.go create mode 100644 agent/internal/report/report.go create mode 100644 agent/internal/report/report_test.go create mode 100644 agent/internal/upgrade/upgrade.go create mode 100644 agent/internal/upgrade/upgrade_test.go create mode 100644 agent/scripts/build-release.sh create mode 100644 client/eslint.config.js create mode 100644 client/index.html create mode 100644 client/package.json create mode 100644 client/postcss.config.js create mode 100644 client/public/badges/dark/abyss.svg create mode 100644 client/public/badges/dark/aether.svg create mode 100644 client/public/badges/dark/apex.svg create mode 100644 client/public/badges/dark/aquarius.svg create mode 100644 client/public/badges/dark/ares.svg create mode 100644 client/public/badges/dark/aries.svg create mode 100644 client/public/badges/dark/astral.svg create mode 100644 client/public/badges/dark/barrage.svg create mode 100644 client/public/badges/dark/barrier.svg create mode 100644 client/public/badges/dark/bastion.svg create mode 100644 client/public/badges/dark/blackhole.svg create mode 100644 client/public/badges/dark/cancer.svg create mode 100644 client/public/badges/dark/capricorn.svg create mode 100644 client/public/badges/dark/citadel.svg create mode 100644 client/public/badges/dark/core.svg create mode 100644 client/public/badges/dark/cronus.svg create mode 100644 client/public/badges/dark/crosshair.svg create mode 100644 client/public/badges/dark/defender.svg create mode 100644 client/public/badges/dark/dog.svg create mode 100644 client/public/badges/dark/dragon.svg create mode 100644 client/public/badges/dark/dynamo.svg create mode 100644 client/public/badges/dark/earth.svg create mode 100644 client/public/badges/dark/elite.svg create mode 100644 client/public/badges/dark/enigma.svg create mode 100644 client/public/badges/dark/fission.svg create mode 100644 client/public/badges/dark/fortress.svg create mode 100644 client/public/badges/dark/fusion.svg create mode 100644 client/public/badges/dark/galaxy.svg create mode 100644 client/public/badges/dark/gemini.svg create mode 100644 client/public/badges/dark/genesis.svg create mode 100644 client/public/badges/dark/glyph.svg create mode 100644 client/public/badges/dark/goat.svg create mode 100644 client/public/badges/dark/guardian.svg create mode 100644 client/public/badges/dark/horse.svg create mode 100644 client/public/badges/dark/ignition.svg create mode 100644 client/public/badges/dark/intercept.svg create mode 100644 client/public/badges/dark/jupiter.svg create mode 100644 client/public/badges/dark/leo.svg create mode 100644 client/public/badges/dark/libra.svg create mode 100644 client/public/badges/dark/lockon.svg create mode 100644 client/public/badges/dark/matrix.svg create mode 100644 client/public/badges/dark/mercury.svg create mode 100644 client/public/badges/dark/monkey.svg create mode 100644 client/public/badges/dark/moon.svg create mode 100644 client/public/badges/dark/mystic.svg create mode 100644 client/public/badges/dark/nebula.svg create mode 100644 client/public/badges/dark/neptune.svg create mode 100644 client/public/badges/dark/nexus.svg create mode 100644 client/public/badges/dark/omega.svg create mode 100644 client/public/badges/dark/oracle.svg create mode 100644 client/public/badges/dark/overload.svg create mode 100644 client/public/badges/dark/overwatch.svg create mode 100644 client/public/badges/dark/ox.svg create mode 100644 client/public/badges/dark/paladin.svg create mode 100644 client/public/badges/dark/paradox.svg create mode 100644 client/public/badges/dark/phalanx.svg create mode 100644 client/public/badges/dark/pig.svg create mode 100644 client/public/badges/dark/pisces.svg create mode 100644 client/public/badges/dark/plasma.svg create mode 100644 client/public/badges/dark/pluto.svg create mode 100644 client/public/badges/dark/pulsar.svg create mode 100644 client/public/badges/dark/pulse.svg create mode 100644 client/public/badges/dark/quantum.svg create mode 100644 client/public/badges/dark/rabbit.svg create mode 100644 client/public/badges/dark/radar.svg create mode 100644 client/public/badges/dark/radiance.svg create mode 100644 client/public/badges/dark/rat.svg create mode 100644 client/public/badges/dark/recon.svg create mode 100644 client/public/badges/dark/rooster.svg create mode 100644 client/public/badges/dark/rune.svg create mode 100644 client/public/badges/dark/sagittarius.svg create mode 100644 client/public/badges/dark/scorpio.svg create mode 100644 client/public/badges/dark/sentinel.svg create mode 100644 client/public/badges/dark/sigil.svg create mode 100644 client/public/badges/dark/snake.svg create mode 100644 client/public/badges/dark/sniper.svg create mode 100644 client/public/badges/dark/solaris.svg create mode 100644 client/public/badges/dark/strike.svg create mode 100644 client/public/badges/dark/taurus.svg create mode 100644 client/public/badges/dark/tiger.svg create mode 100644 client/public/badges/dark/titan.svg create mode 100644 client/public/badges/dark/tracer.svg create mode 100644 client/public/badges/dark/ultra.svg create mode 100644 client/public/badges/dark/uranus.svg create mode 100644 client/public/badges/dark/vanguard.svg create mode 100644 client/public/badges/dark/virgo.svg create mode 100644 client/public/badges/dark/vortex.svg create mode 100644 client/public/badges/dark/ward.svg create mode 100644 client/public/badges/dark/wormhole.svg create mode 100644 client/public/badges/light/abyss.svg create mode 100644 client/public/badges/light/aether.svg create mode 100644 client/public/badges/light/apex.svg create mode 100644 client/public/badges/light/aquarius.svg create mode 100644 client/public/badges/light/ares.svg create mode 100644 client/public/badges/light/aries.svg create mode 100644 client/public/badges/light/astral.svg create mode 100644 client/public/badges/light/barrage.svg create mode 100644 client/public/badges/light/barrier.svg create mode 100644 client/public/badges/light/bastion.svg create mode 100644 client/public/badges/light/blackhole.svg create mode 100644 client/public/badges/light/cancer.svg create mode 100644 client/public/badges/light/capricorn.svg create mode 100644 client/public/badges/light/citadel.svg create mode 100644 client/public/badges/light/core.svg create mode 100644 client/public/badges/light/cronus.svg create mode 100644 client/public/badges/light/crosshair.svg create mode 100644 client/public/badges/light/defender.svg create mode 100644 client/public/badges/light/dog.svg create mode 100644 client/public/badges/light/dragon.svg create mode 100644 client/public/badges/light/dynamo.svg create mode 100644 client/public/badges/light/earth.svg create mode 100644 client/public/badges/light/elite.svg create mode 100644 client/public/badges/light/enigma.svg create mode 100644 client/public/badges/light/fission.svg create mode 100644 client/public/badges/light/fortress.svg create mode 100644 client/public/badges/light/fusion.svg create mode 100644 client/public/badges/light/galaxy.svg create mode 100644 client/public/badges/light/gemini.svg create mode 100644 client/public/badges/light/genesis.svg create mode 100644 client/public/badges/light/glyph.svg create mode 100644 client/public/badges/light/goat.svg create mode 100644 client/public/badges/light/guardian.svg create mode 100644 client/public/badges/light/horse.svg create mode 100644 client/public/badges/light/ignition.svg create mode 100644 client/public/badges/light/intercept.svg create mode 100644 client/public/badges/light/jupiter.svg create mode 100644 client/public/badges/light/leo.svg create mode 100644 client/public/badges/light/libra.svg create mode 100644 client/public/badges/light/lockon.svg create mode 100644 client/public/badges/light/matrix.svg create mode 100644 client/public/badges/light/mercury.svg create mode 100644 client/public/badges/light/monkey.svg create mode 100644 client/public/badges/light/moon.svg create mode 100644 client/public/badges/light/mystic.svg create mode 100644 client/public/badges/light/nebula.svg create mode 100644 client/public/badges/light/neptune.svg create mode 100644 client/public/badges/light/nexus.svg create mode 100644 client/public/badges/light/omega.svg create mode 100644 client/public/badges/light/oracle.svg create mode 100644 client/public/badges/light/overload.svg create mode 100644 client/public/badges/light/overwatch.svg create mode 100644 client/public/badges/light/ox.svg create mode 100644 client/public/badges/light/paladin.svg create mode 100644 client/public/badges/light/paradox.svg create mode 100644 client/public/badges/light/phalanx.svg create mode 100644 client/public/badges/light/pig.svg create mode 100644 client/public/badges/light/pisces.svg create mode 100644 client/public/badges/light/plasma.svg create mode 100644 client/public/badges/light/pluto.svg create mode 100644 client/public/badges/light/pulsar.svg create mode 100644 client/public/badges/light/pulse.svg create mode 100644 client/public/badges/light/quantum.svg create mode 100644 client/public/badges/light/rabbit.svg create mode 100644 client/public/badges/light/radar.svg create mode 100644 client/public/badges/light/radiance.svg create mode 100644 client/public/badges/light/rat.svg create mode 100644 client/public/badges/light/recon.svg create mode 100644 client/public/badges/light/rooster.svg create mode 100644 client/public/badges/light/rune.svg create mode 100644 client/public/badges/light/sagittarius.svg create mode 100644 client/public/badges/light/scorpio.svg create mode 100644 client/public/badges/light/sentinel.svg create mode 100644 client/public/badges/light/sigil.svg create mode 100644 client/public/badges/light/snake.svg create mode 100644 client/public/badges/light/sniper.svg create mode 100644 client/public/badges/light/solaris.svg create mode 100644 client/public/badges/light/strike.svg create mode 100644 client/public/badges/light/taurus.svg create mode 100644 client/public/badges/light/tiger.svg create mode 100644 client/public/badges/light/titan.svg create mode 100644 client/public/badges/light/tracer.svg create mode 100644 client/public/badges/light/ultra.svg create mode 100644 client/public/badges/light/uranus.svg create mode 100644 client/public/badges/light/vanguard.svg create mode 100644 client/public/badges/light/virgo.svg create mode 100644 client/public/badges/light/vortex.svg create mode 100644 client/public/badges/light/ward.svg create mode 100644 client/public/badges/light/wormhole.svg create mode 100644 client/public/icons/peer.svg create mode 100644 client/public/icons/prime.svg create mode 100644 client/public/icons/pro.svg create mode 100644 client/public/incudal_logo.webp create mode 100644 client/public/sw.js create mode 100644 client/public/tos/en.md create mode 100644 client/public/tos/zh.md create mode 100644 client/src/App.vue create mode 100644 client/src/api/index.ts create mode 100644 client/src/components/BackupManager.vue create mode 100644 client/src/components/BadgeImage.vue create mode 100644 client/src/components/CheckinModal.vue create mode 100644 client/src/components/FlagIcon.vue create mode 100644 client/src/components/InstanceDisplayIcon.vue create mode 100644 client/src/components/InstanceSelector.vue create mode 100644 client/src/components/LazyImage.vue create mode 100644 client/src/components/LoadingSpinner.vue create mode 100644 client/src/components/NotificationBell.vue create mode 100644 client/src/components/PackageQuotaReleaseModal.vue create mode 100644 client/src/components/PopupAnnouncementModal.vue create mode 100644 client/src/components/SensitiveVerificationModal.vue create mode 100644 client/src/components/Skeleton.vue create mode 100644 client/src/components/SkeletonLoader.vue create mode 100644 client/src/components/SnapshotManager.vue create mode 100644 client/src/components/TermsOfServiceModal.vue create mode 100644 client/src/components/Toast.vue create mode 100644 client/src/components/ToastContainer.vue create mode 100644 client/src/components/TurnstileWidget.vue create mode 100644 client/src/components/UserAvatar.vue create mode 100644 client/src/components/VirtualList.vue create mode 100644 client/src/components/admin/BillingOverviewIcon.vue create mode 100644 client/src/components/admin/VipBenefitHallSettings.vue create mode 100644 client/src/components/admin/VipLevelRulesEditor.vue create mode 100644 client/src/components/entertainment/BadgeBatchRewardModal.vue create mode 100644 client/src/components/entertainment/BadgeCenter.vue create mode 100644 client/src/components/entertainment/BadgeRewardModal.vue create mode 100644 client/src/components/entertainment/VipBenefitHall.vue create mode 100644 client/src/components/extensions/InitCommandDetailModal.vue create mode 100644 client/src/components/extensions/InitCommandModal.vue create mode 100644 client/src/components/extensions/InitCommandSelector.vue create mode 100644 client/src/components/host/BatchConfigModal.vue create mode 100644 client/src/components/host/HostCaddyTab.vue create mode 100644 client/src/components/host/HostCreateInstanceTab.vue create mode 100644 client/src/components/host/HostImagesTab.vue create mode 100644 client/src/components/host/HostInfoTab.vue create mode 100644 client/src/components/host/HostInstancesList.vue create mode 100644 client/src/components/host/HostInstancesTab.vue create mode 100644 client/src/components/host/HostOpsTab.vue create mode 100644 client/src/components/host/HostRedeemCodesTab.vue create mode 100644 client/src/components/host/HostStorageTab.vue create mode 100644 client/src/components/host/MigrateHostModal.vue create mode 100644 client/src/components/host/MyHostConfigTab.vue create mode 100644 client/src/components/icons/AnnouncementIcon.vue create mode 100644 client/src/components/icons/DistroIcon.vue create mode 100644 client/src/components/instance/ChangeHostCard.vue create mode 100644 client/src/components/instance/HostSelector.vue create mode 100644 client/src/components/instance/ImageSelector.vue create mode 100644 client/src/components/instance/InstanceBadgeModal.vue create mode 100644 client/src/components/instance/InstanceConfigTab.vue create mode 100644 client/src/components/instance/InstanceInfoTab.vue create mode 100644 client/src/components/instance/InstanceLogsTab.vue create mode 100644 client/src/components/instance/InstanceNetworkTab.vue create mode 100644 client/src/components/instance/InstanceOrderMenu.vue create mode 100644 client/src/components/instance/InstanceQuotaTab.vue create mode 100644 client/src/components/instance/InstanceSitesTab.vue create mode 100644 client/src/components/instance/PackageSelector.vue create mode 100644 client/src/components/instance/PlanSelector.vue create mode 100644 client/src/components/instance/RegionSelector.vue create mode 100644 client/src/components/instance/ResourceSlider.vue create mode 100644 client/src/components/instance/ResourceSliders.vue create mode 100644 client/src/components/instance/SSHKeySelector.vue create mode 100644 client/src/components/instance/TerminalAccessoryBar.vue create mode 100644 client/src/components/instance/TerminalContextMenu.vue create mode 100644 client/src/components/instance/TerminalModal.vue create mode 100644 client/src/components/instance/TrafficStats.vue create mode 100644 client/src/components/instance/modals/AddPortModal.vue create mode 100644 client/src/components/instance/modals/ApplyAffCodeModal.vue create mode 100644 client/src/components/instance/modals/ChangePlanModal.vue create mode 100644 client/src/components/instance/modals/ConfigEditModal.vue create mode 100644 client/src/components/instance/modals/DestroyInstanceModal.vue create mode 100644 client/src/components/instance/modals/PortConflictModal.vue create mode 100644 client/src/components/instance/modals/RebuildModal.vue create mode 100644 client/src/components/instance/modals/RecreateModal.vue create mode 100644 client/src/components/instance/modals/RenewModal.vue create mode 100644 client/src/components/instance/modals/TransferModal.vue create mode 100644 client/src/components/layout/AppLayout.vue create mode 100644 client/src/components/layout/SideNav.vue create mode 100644 client/src/components/profile/AccountSection.vue create mode 100644 client/src/components/profile/BillingSection.vue create mode 100644 client/src/components/profile/ChangeEmailModal.vue create mode 100644 client/src/components/profile/LoginHistorySection.vue create mode 100644 client/src/components/profile/NotificationSection.vue create mode 100644 client/src/components/profile/OAuthSection.vue create mode 100644 client/src/components/profile/PasswordSection.vue create mode 100644 client/src/components/profile/SSHKeysSection.vue create mode 100644 client/src/components/profile/StorageSection.vue create mode 100644 client/src/components/profile/TelegramBindingSection.vue create mode 100644 client/src/components/profile/TwoFactorSection.vue create mode 100644 client/src/components/public/PublicAuthShell.vue create mode 100644 client/src/components/public/PublicSiteFooter.vue create mode 100644 client/src/components/public/PublicSiteHeader.vue create mode 100644 client/src/components/public/PublicSiteLayout.vue create mode 100644 client/src/components/terminal/TerminalSavedCommandsSidebar.vue create mode 100644 client/src/components/tickets/TicketImageLightbox.vue create mode 100644 client/src/components/tickets/TicketImageUploader.vue create mode 100644 client/src/components/tickets/TicketInstanceOwnerCard.vue create mode 100644 client/src/composables/useBrand.ts create mode 100644 client/src/composables/useM3Auth.ts create mode 100644 client/src/composables/usePageSeo.ts create mode 100644 client/src/composables/useTerminal.ts create mode 100644 client/src/composables/useTurnstile.ts create mode 100644 client/src/constants/adminSettings.ts create mode 100644 client/src/data/funnyQuotes.ts create mode 100644 client/src/env.d.ts create mode 100644 client/src/lib/terminal-core.ts create mode 100644 client/src/locales/en.ts create mode 100644 client/src/locales/index.ts create mode 100644 client/src/locales/zh-CN.ts create mode 100644 client/src/locales/zh-TW.ts create mode 100644 client/src/main.ts create mode 100644 client/src/router/index.ts create mode 100644 client/src/stores/auth.ts create mode 100644 client/src/stores/badges.ts create mode 100644 client/src/stores/config.ts create mode 100644 client/src/stores/inbox.ts create mode 100644 client/src/stores/restoreTask.ts create mode 100644 client/src/stores/terminal.ts create mode 100644 client/src/stores/theme.ts create mode 100644 client/src/stores/toast.ts create mode 100644 client/src/styles/main.css create mode 100644 client/src/types/api.ts create mode 100644 client/src/types/router.ts create mode 100644 client/src/types/store.ts create mode 100644 client/src/utils/billing.ts create mode 100644 client/src/utils/countryDisplay.ts create mode 100644 client/src/utils/errorHandler.ts create mode 100644 client/src/utils/formatters.ts create mode 100644 client/src/utils/freeSiteFun.ts create mode 100644 client/src/utils/inboxHelper.ts create mode 100644 client/src/utils/markdown.ts create mode 100644 client/src/utils/publicCatalog.ts create mode 100644 client/src/utils/turnstile.ts create mode 100644 client/src/utils/validation.ts create mode 100644 client/src/utils/vipBadge.ts create mode 100644 client/src/views/DashboardView.vue create mode 100644 client/src/views/EntertainmentView.vue create mode 100644 client/src/views/ExtensionsView.vue create mode 100644 client/src/views/ForgotPasswordView.vue create mode 100644 client/src/views/FriendsView.vue create mode 100644 client/src/views/HelpView.vue create mode 100644 client/src/views/HostingWalletView.vue create mode 100644 client/src/views/InboxView.vue create mode 100644 client/src/views/InstanceCreateView.vue create mode 100644 client/src/views/InstanceDetailView.vue create mode 100644 client/src/views/InstancesView.vue create mode 100644 client/src/views/InvitesView.vue create mode 100644 client/src/views/LoginView.vue create mode 100644 client/src/views/LogsView.vue create mode 100644 client/src/views/MailDomainView.vue create mode 100644 client/src/views/MailView.vue create mode 100644 client/src/views/MarketView.vue create mode 100644 client/src/views/NotFoundView.vue create mode 100644 client/src/views/PortalView.vue create mode 100644 client/src/views/ProfileView.vue create mode 100644 client/src/views/RegisterView.vue create mode 100644 client/src/views/TerminalView.vue create mode 100644 client/src/views/TicketsView.vue create mode 100644 client/src/views/TransfersView.vue create mode 100644 client/src/views/WalletView.vue create mode 100644 client/src/views/admin/AdminInstanceCreateView.vue create mode 100644 client/src/views/admin/AdminMailView.vue create mode 100644 client/src/views/admin/AffReviewView.vue create mode 100644 client/src/views/admin/BillingView.vue create mode 100644 client/src/views/admin/BroadcastView.vue create mode 100644 client/src/views/admin/EntertainmentView.vue create mode 100644 client/src/views/admin/HelpManageView.vue create mode 100644 client/src/views/admin/HostingView.vue create mode 100644 client/src/views/admin/ImagesView.vue create mode 100644 client/src/views/admin/OAuthConfigView.vue create mode 100644 client/src/views/admin/PaymentProvidersView.vue create mode 100644 client/src/views/admin/StatisticsView.vue create mode 100644 client/src/views/admin/SystemConfigView.vue create mode 100644 client/src/views/admin/TelegramConfigView.vue create mode 100644 client/src/views/admin/UsersView.vue create mode 100644 client/src/views/resources/MyHostCreateView.vue create mode 100644 client/src/views/resources/MyHostDetailView.vue create mode 100644 client/src/views/resources/MyHostsView.vue create mode 100644 client/src/views/resources/MyPackagesView.vue create mode 100644 client/src/views/resources/PackageFormView.vue create mode 100644 client/tailwind.config.js create mode 100644 client/tsconfig.json create mode 100644 client/tsconfig.node.json create mode 100644 client/vite.config.ts create mode 100644 docker-compose.dev.yml create mode 100644 docker-compose.yml create mode 100644 package-lock.json create mode 100644 package.json create mode 100644 pnpm-lock.yaml create mode 100644 pnpm-workspace.yaml create mode 100644 scripts/generate-badges-from-html.mjs create mode 100644 scripts/init-env.sh create mode 100644 scripts/install-docker.sh create mode 100644 scripts/install-panel.sh create mode 100644 scripts/local-ci-macos.sh create mode 100644 scripts/local-ci.ps1 create mode 100644 scripts/watch-migrations.mjs create mode 100644 server/certs/.gitkeep create mode 100644 server/docker-entrypoint.sh create mode 100644 server/package-lock.json create mode 100644 server/package.json create mode 100644 server/prisma.config.ts create mode 100644 server/prisma/migrations/20241209000000_add_email_verification_codes/migration.sql create mode 100644 server/prisma/migrations/20251206045256_init/migration.sql create mode 100644 server/prisma/migrations/20251206110301_add_2fa_fields/migration.sql create mode 100644 server/prisma/migrations/20251206120241_add_2fa_recovery_codes/migration.sql create mode 100644 server/prisma/migrations/20251207072151_add_host_init_fields/migration.sql create mode 100644 server/prisma/migrations/20251207123655_add_system_config/migration.sql create mode 100644 server/prisma/migrations/20251208050057_add_traffic_quota/migration.sql create mode 100644 server/prisma/migrations/20251208054106_add_package_traffic_limit/migration.sql create mode 100644 server/prisma/migrations/20251208072207_add_restore_task_model/migration.sql create mode 100644 server/prisma/migrations/20251208084341_add_storage_and_upload_tasks/migration.sql create mode 100644 server/prisma/migrations/20251208090039_add_storage_config_index/migration.sql create mode 100644 server/prisma/migrations/20251208120000_add_instance_transfer/migration.sql create mode 100644 server/prisma/migrations/20251208140941_add_avatar_style/migration.sql create mode 100644 server/prisma/migrations/20251208154011_add_port_mapping_remark/migration.sql create mode 100644 server/prisma/migrations/20251209000000_add_user_ownership_and_friendship/migration.sql create mode 100644 server/prisma/migrations/20251209022207_add_friendship_remark_and_rejected/migration.sql create mode 100644 server/prisma/migrations/20251209064035_add_package_shares/migration.sql create mode 100644 server/prisma/migrations/20251209073625_add_host_install_config/migration.sql create mode 100644 server/prisma/migrations/20251209110000_refactor_quota_system/migration.sql create mode 100644 server/prisma/migrations/20251209120000_cleanup_old_quota_configs/migration.sql create mode 100644 server/prisma/migrations/20251210133511_replace_node_group_with_package_host/migration.sql create mode 100644 server/prisma/migrations/20251211000000_simplify_host_images/migration.sql create mode 100644 server/prisma/migrations/20251211045923_add_package_advanced_config/migration.sql create mode 100644 server/prisma/migrations/20251211045937_add_package_advanced_config/migration.sql create mode 100644 server/prisma/migrations/20251211060512_remove_host_images/migration.sql create mode 100644 server/prisma/migrations/20251211060523_remove_host_images/migration.sql create mode 100644 server/prisma/migrations/20251211071708_add_ip_address_table/migration.sql create mode 100644 server/prisma/migrations/20251211145415_add_token_invalidation/migration.sql create mode 100644 server/prisma/migrations/20251211151239_add_friendship_removed_status/migration.sql create mode 100644 server/prisma/migrations/20251212152400_remove_cpu_memory_disk_total_fields/migration.sql create mode 100644 server/prisma/migrations/20251212205000_add_queue_progress_and_retry/migration.sql create mode 100644 server/prisma/migrations/20251212221500_add_cert_download_count/migration.sql create mode 100644 server/prisma/migrations/20251212221600_add_operation_verification_and_locks/migration.sql create mode 100644 server/prisma/migrations/20251213000000_add_network_mode_and_system_images/migration.sql create mode 100644 server/prisma/migrations/20251213100000_refactor_auto_policy_intervals/migration.sql create mode 100644 server/prisma/migrations/20251214084943_add_port_mapping_unique_constraint/migration.sql create mode 100644 server/prisma/migrations/20251214090959_add_package_share_quota_fields/migration.sql create mode 100644 server/prisma/migrations/20251214115302_add_package_instance_type/migration.sql create mode 100644 server/prisma/migrations/20251214200000_add_system_image_instance_type/migration.sql create mode 100644 server/prisma/migrations/20251215081921_ipv6_routed_mode/migration.sql create mode 100644 server/prisma/migrations/20251216162514_add_storage_pools/migration.sql create mode 100644 server/prisma/migrations/20251217135047_add_caddy_proxy_sites/migration.sql create mode 100644 server/prisma/migrations/20251217142200_add_site_limit/migration.sql create mode 100644 server/prisma/migrations/20251218042321_add_https_enabled_to_proxy_sites/migration.sql create mode 100644 server/prisma/migrations/20251221104032_add_package_quota_and_update_defaults/migration.sql create mode 100644 server/prisma/migrations/20251221125025_add_global_shared_to_package/migration.sql create mode 100644 server/prisma/migrations/20251221135737_add_allow_instance_deletion_to_package/migration.sql create mode 100644 server/prisma/migrations/20251221141834_change_allow_instance_deletion_default_to_false/migration.sql create mode 100644 server/prisma/migrations/20251221194038_remove_instance_quota_and_add_pinned/migration.sql create mode 100644 server/prisma/migrations/20251227014516_add_ban_reason/migration.sql create mode 100644 server/prisma/migrations/20251227054559_add_inbox_message/migration.sql create mode 100644 server/prisma/migrations/20251227063752_add_package_release_channel/migration.sql create mode 100644 server/prisma/migrations/20251227085332_add_instance_task/migration.sql create mode 100644 server/prisma/migrations/20251227113416_add_proxy_site_remark/migration.sql create mode 100644 server/prisma/migrations/20251227123500_add_login_record/migration.sql create mode 100644 server/prisma/migrations/20251227151951_add_last_synced_at/migration.sql create mode 100644 server/prisma/migrations/20251228075452_add_ssh_key_id_to_instance_task/migration.sql create mode 100644 server/prisma/migrations/20251229000000_add_transfer_processing_status/migration.sql create mode 100644 server/prisma/migrations/20260101100908_add_ticket_system/migration.sql create mode 100644 server/prisma/migrations/20260101100917_add_ticket_system/migration.sql create mode 100644 server/prisma/migrations/20260101163409_add_checkin_system/migration.sql create mode 100644 server/prisma/migrations/20260103130305_add_redeem_codes/migration.sql create mode 100644 server/prisma/migrations/20260103140651_add_redeem_code_batch_id/migration.sql create mode 100644 server/prisma/migrations/20260103141836_add_batch_id_to_usage/migration.sql create mode 100644 server/prisma/migrations/20260103_add_billing_and_suspend_fields/migration.sql create mode 100644 server/prisma/migrations/20260106000000_add_custom_init_commands/migration.sql create mode 100644 server/prisma/migrations/20260106190000_add_last_ipv6_reassign_at/migration.sql create mode 100644 server/prisma/migrations/20260110000000_add_host_transfer_enabled/migration.sql create mode 100644 server/prisma/migrations/20260111_add_billing_system/migration.sql create mode 100644 server/prisma/migrations/20260113000000_replace_stock_with_sla_guarantee/migration.sql create mode 100644 server/prisma/migrations/20260115000000_add_aff_system/migration.sql create mode 100644 server/prisma/migrations/20260117000000_add_payment_callback_table/migration.sql create mode 100644 server/prisma/migrations/20260117000000_ticket_optional_host/migration.sql create mode 100644 server/prisma/migrations/20260118000000_add_last_plan_change_at/migration.sql create mode 100644 server/prisma/migrations/20260121000000_add_global_aff_code/migration.sql create mode 100644 server/prisma/migrations/20260130000000_add_host_traffic_reset_day/migration.sql create mode 100644 server/prisma/migrations/20260130200000_add_entertainment_lottery_tables/migration.sql create mode 100644 server/prisma/migrations/20260202000000_add_checkin_to_points_log_type/migration.sql create mode 100644 server/prisma/migrations/20260210000000_add_recreate_task_type/migration.sql create mode 100644 server/prisma/migrations/20260210000001_add_recreate_operation_type/migration.sql create mode 100644 server/prisma/migrations/20260222140000_add_transfer_fee/migration.sql create mode 100644 server/prisma/migrations/20260222150000_add_transfer_fee_to_billing_record_type/migration.sql create mode 100644 server/prisma/migrations/20260222160000_add_host_announcement/migration.sql create mode 100644 server/prisma/migrations/20260222170000_add_resource_pool/migration.sql create mode 100644 server/prisma/migrations/20260222180000_add_lottery_resource_prizes_and_system_redeem/migration.sql create mode 100644 server/prisma/migrations/20260222190000_add_user_destroy_record/migration.sql create mode 100644 server/prisma/migrations/20260222200000_add_hosting_balance_system/migration.sql create mode 100644 server/prisma/migrations/20260222210000_add_hosting_deduction_type/migration.sql create mode 100644 server/prisma/migrations/20260222220000_add_hosting_deduction_enum/migration.sql create mode 100644 server/prisma/migrations/20260309000000_add_hosting_action_type/migration.sql create mode 100644 server/prisma/migrations/20260310000000_add_host_enable_resource_pool/migration.sql create mode 100644 server/prisma/migrations/20260311000000_add_hosting_withdraw_balance_log_type/migration.sql create mode 100644 server/prisma/migrations/20260312000000_add_admin_adjust_action_type/migration.sql create mode 100644 server/prisma/migrations/20260312000001_add_host_probe_url/migration.sql create mode 100644 server/prisma/migrations/20260313000000_add_mail_module/migration.sql create mode 100644 server/prisma/migrations/20260314000000_add_mail_aff_support/migration.sql create mode 100644 server/prisma/migrations/20260318000000_add_hosting_log_snapshot_fields/migration.sql create mode 100644 server/prisma/migrations/20260324000000_add_notification_channel_is_global/migration.sql create mode 100644 server/prisma/migrations/20260328000000_add_io_limit_mode/migration.sql create mode 100644 server/prisma/migrations/20260406090000_add_host_architecture_and_allowed_images/migration.sql create mode 100644 server/prisma/migrations/20260406110000_add_host_notification_email_queue/migration.sql create mode 100644 server/prisma/migrations/20260407100000_add_host_address_registry/migration.sql create mode 100644 server/prisma/migrations/20260407103000_add_host_notification_preferences/migration.sql create mode 100644 server/prisma/migrations/20260409123000_add_instance_cloud_init_state/migration.sql create mode 100644 server/prisma/migrations/20260410093000_add_instance_swap_support/migration.sql create mode 100644 server/prisma/migrations/20260412093000_add_terminal_saved_commands/migration.sql create mode 100644 server/prisma/migrations/20260412113000_add_badge_system/migration.sql create mode 100644 server/prisma/migrations/20260413143000_add_lottery_badge_prize_type/migration.sql create mode 100644 server/prisma/migrations/20260413150000_expand_network_mode_enum/migration.sql create mode 100644 server/prisma/migrations/20260414040844_add_host_nat_public_ipv6/migration.sql create mode 100644 server/prisma/migrations/20260416000000_add_storage_pool_selection/migration.sql create mode 100644 server/prisma/migrations/20260417110000_add_ticket_message_attachments/migration.sql create mode 100644 server/prisma/migrations/20260421090000_add_host_bind_addresses/migration.sql create mode 100644 server/prisma/migrations/20260424103000_relax_ip_address_uniqueness/migration.sql create mode 100644 server/prisma/migrations/20260424113000_restore_scoped_ip_uniqueness/migration.sql create mode 100644 server/prisma/migrations/20260424114000_add_hosting_feature_visibility/migration.sql create mode 100644 server/prisma/migrations/20260425114500_add_hosting_user_blocks/migration.sql create mode 100644 server/prisma/migrations/20260426102000_add_heleket_payment_provider_type/migration.sql create mode 100644 server/prisma/migrations/20260426134000_add_recharge_snapshot_and_payment_details/migration.sql create mode 100644 server/prisma/migrations/20260427090000_add_telegram_user_binding/migration.sql create mode 100644 server/prisma/migrations/20260427143000_add_telegram_group_join_configs/migration.sql create mode 100644 server/prisma/migrations/20260427170000_add_telegram_vip_group_configs/migration.sql create mode 100644 server/prisma/migrations/20260427190000_add_instance_audit_tables/migration.sql create mode 100644 server/prisma/migrations/20260427193000_add_instance_audit_builtin_rule_overrides/migration.sql create mode 100644 server/prisma/migrations/20260428110000_add_host_agent_identity/migration.sql create mode 100644 server/prisma/migrations/20260428233000_add_change_host_task_type/migration.sql create mode 100644 server/prisma/migrations/20260429080000_add_host_agent_install_token/migration.sql create mode 100644 server/prisma/migrations/20260429120000_add_package_plan_sold_out/migration.sql create mode 100644 server/prisma/migrations/20260501090000_add_package_host_traffic_multiplier/migration.sql create mode 100644 server/prisma/migrations/20260501133000_add_log_instance_id/migration.sql create mode 100644 server/prisma/migrations/20260504120000_add_hosting_zones/migration.sql create mode 100644 server/prisma/migrations/20260504123000_rename_hosting_zone_logo_url/migration.sql create mode 100644 server/prisma/migrations/20260504160000_add_instance_display_order/migration.sql create mode 100644 server/prisma/migrations/20260505090000_add_package_prerequisite/migration.sql create mode 100644 server/prisma/migrations/20260505170000_harden_instance_destroy_idempotency/migration.sql create mode 100644 server/prisma/migrations/20260522090000_add_user_invite_costs/migration.sql create mode 100644 server/prisma/migrations/20260522100000_enable_package_instance_deletion_default/migration.sql create mode 100644 server/prisma/migrations/20260614120000_add_vip_level_rules/migration.sql create mode 100644 server/prisma/migrations/20260615090000_add_user_vip_metric_config/migration.sql create mode 100644 server/prisma/migrations/20260616120000_add_vip_benefit_hall/migration.sql create mode 100644 server/prisma/migrations/20260619193000_refresh_default_system_images/migration.sql create mode 100644 server/prisma/migrations/20260619210000_add_hosting_market_entry_config/migration.sql create mode 100644 server/prisma/migrations/20260620110000_add_dynamic_badge_catalog/migration.sql create mode 100644 server/prisma/migrations/migration_lock.toml create mode 100644 server/prisma/schema.prisma create mode 100644 server/scripts/backfill-instance-network-addresses.ts create mode 100644 server/scripts/fix-migrated-instance-billing.ts create mode 100644 server/scripts/seed-bulk-local-data.ts create mode 100644 server/scripts/seed-test-data.ts create mode 100644 server/scripts/test-agent-auth.ts create mode 100644 server/scripts/test-security-config.ts create mode 100644 server/src/app.ts create mode 100644 server/src/config/badges.generated.json create mode 100644 server/src/config/badges.generated.ts create mode 100644 server/src/config/badges.ts create mode 100644 server/src/config/rate-limit.ts create mode 100644 server/src/config/system-images.ts create mode 100644 server/src/db/advisory-locks.ts create mode 100644 server/src/db/aff.ts create mode 100644 server/src/db/announcements.ts create mode 100644 server/src/db/backup-upload-tasks.ts create mode 100644 server/src/db/backups.ts create mode 100644 server/src/db/badges.ts create mode 100644 server/src/db/balance.ts create mode 100644 server/src/db/billing-operations.ts create mode 100644 server/src/db/billing-records.ts create mode 100644 server/src/db/checkin.ts create mode 100644 server/src/db/custom-init-commands.ts create mode 100644 server/src/db/email-verification.ts create mode 100644 server/src/db/friendships.ts create mode 100644 server/src/db/help.ts create mode 100644 server/src/db/host-addresses.ts create mode 100644 server/src/db/host-notification-email-tasks.ts create mode 100644 server/src/db/hosting-blocks.ts create mode 100644 server/src/db/hosts.ts create mode 100644 server/src/db/images.ts create mode 100644 server/src/db/inbox.ts create mode 100644 server/src/db/index.ts create mode 100644 server/src/db/init-prisma.ts create mode 100644 server/src/db/instance-suspend.ts create mode 100644 server/src/db/instance-tasks.ts create mode 100644 server/src/db/instances.ts create mode 100644 server/src/db/invite-codes.ts create mode 100644 server/src/db/ip-addresses.ts create mode 100644 server/src/db/ipv6-subnets.ts create mode 100644 server/src/db/login-records.ts create mode 100644 server/src/db/logs.ts create mode 100644 server/src/db/lottery.ts create mode 100644 server/src/db/mail.ts create mode 100644 server/src/db/notifications.ts create mode 100644 server/src/db/oauth.ts create mode 100644 server/src/db/optimizations.ts create mode 100644 server/src/db/package-hosts.ts create mode 100644 server/src/db/package-plans.ts create mode 100644 server/src/db/package-shares.ts create mode 100644 server/src/db/packages.ts create mode 100644 server/src/db/pagination.ts create mode 100644 server/src/db/payment-providers.ts create mode 100644 server/src/db/points.ts create mode 100644 server/src/db/port-mappings.ts create mode 100644 server/src/db/prisma.ts create mode 100644 server/src/db/proxy-sites.ts create mode 100644 server/src/db/quota-operations.ts create mode 100644 server/src/db/recharge-records.ts create mode 100644 server/src/db/redeem-codes.ts create mode 100644 server/src/db/reset-database.ts create mode 100644 server/src/db/resource-pool.ts create mode 100644 server/src/db/restore-tasks.ts create mode 100644 server/src/db/snapshots.ts create mode 100644 server/src/db/ssh-keys.ts create mode 100644 server/src/db/storage-configs.ts create mode 100644 server/src/db/storage-pools.ts create mode 100644 server/src/db/system-config.ts create mode 100644 server/src/db/terminal-saved-commands.ts create mode 100644 server/src/db/tickets.ts create mode 100644 server/src/db/traffic.ts create mode 100644 server/src/db/transfers.ts create mode 100644 server/src/db/users.ts create mode 100644 server/src/lib/action-ticket.ts create mode 100644 server/src/lib/agent-auth.ts create mode 100644 server/src/lib/architecture.ts create mode 100644 server/src/lib/billing-calc.ts create mode 100644 server/src/lib/caddy-client.ts create mode 100644 server/src/lib/cloud-init-status.ts create mode 100644 server/src/lib/config-cache.ts create mode 100644 server/src/lib/cookie-config.ts create mode 100644 server/src/lib/cpu-allowance.ts create mode 100644 server/src/lib/distributed-lock.ts create mode 100644 server/src/lib/download-token.ts create mode 100644 server/src/lib/email-domain.ts create mode 100644 server/src/lib/epay.ts create mode 100644 server/src/lib/errors.ts create mode 100644 server/src/lib/geoip.ts create mode 100644 server/src/lib/heleket.ts create mode 100644 server/src/lib/host-agent-credentials.ts create mode 100644 server/src/lib/hosting-access.ts create mode 100644 server/src/lib/incus-config-container.ts create mode 100644 server/src/lib/incus-config-generator.ts create mode 100644 server/src/lib/incus-config-vm.ts create mode 100644 server/src/lib/incus/host-health.ts create mode 100644 server/src/lib/incus/incus-backups.ts create mode 100644 server/src/lib/incus/incus-client.ts create mode 100644 server/src/lib/incus/incus-images.ts create mode 100644 server/src/lib/incus/incus-instances.ts create mode 100644 server/src/lib/incus/incus-network.ts create mode 100644 server/src/lib/incus/incus-pool.ts create mode 100644 server/src/lib/incus/incus-restore.ts create mode 100644 server/src/lib/incus/incus-snapshots.ts create mode 100644 server/src/lib/incus/incus-storage.ts create mode 100644 server/src/lib/incus/incus-traffic.ts create mode 100644 server/src/lib/incus/incus-utils.ts create mode 100644 server/src/lib/incus/index.ts create mode 100644 server/src/lib/instance-audit.ts create mode 100644 server/src/lib/instance-network-sync.ts create mode 100644 server/src/lib/instance-swap.ts create mode 100644 server/src/lib/invite-pricing.ts create mode 100644 server/src/lib/ip-calculator.ts create mode 100644 server/src/lib/log-sanitizer.ts create mode 100644 server/src/lib/lottery-notifier.ts create mode 100644 server/src/lib/lsky.ts create mode 100644 server/src/lib/mailer.ts create mode 100644 server/src/lib/managed-instance-provision.ts create mode 100644 server/src/lib/network-address.ts create mode 100644 server/src/lib/network-payload-builder.ts create mode 100644 server/src/lib/notifier.ts create mode 100644 server/src/lib/operation-verification.ts create mode 100644 server/src/lib/outbound-security.ts create mode 100644 server/src/lib/permission.ts create mode 100644 server/src/lib/proxy/IProxyStrategy.ts create mode 100644 server/src/lib/proxy/KvmProxyStrategy.ts create mode 100644 server/src/lib/proxy/LxcProxyStrategy.ts create mode 100644 server/src/lib/proxy/index.ts create mode 100644 server/src/lib/recharge-payment-details.ts create mode 100644 server/src/lib/recharge-provider-snapshot.ts create mode 100644 server/src/lib/redirect-validator.ts create mode 100644 server/src/lib/release-notifier.ts create mode 100644 server/src/lib/retry.ts create mode 100644 server/src/lib/security-config.ts create mode 100644 server/src/lib/security.ts create mode 100644 server/src/lib/terminal-proxy.ts create mode 100644 server/src/lib/timezone.ts create mode 100644 server/src/lib/traffic-multiplier.ts create mode 100644 server/src/lib/turnstile.ts create mode 100644 server/src/lib/vm-network-identifiers.ts create mode 100644 server/src/lib/websocket-security.ts create mode 100644 server/src/routes/admin-billing.ts create mode 100644 server/src/routes/admin-entertainment.ts create mode 100644 server/src/routes/admin-hosting.ts create mode 100644 server/src/routes/admin-notification-channels.ts create mode 100644 server/src/routes/admin-statistics.ts create mode 100644 server/src/routes/aff.ts create mode 100644 server/src/routes/agent.ts create mode 100644 server/src/routes/announcements.ts create mode 100644 server/src/routes/auth.ts create mode 100644 server/src/routes/backups.ts create mode 100644 server/src/routes/balance.ts create mode 100644 server/src/routes/batch-config.ts create mode 100644 server/src/routes/checkin.ts create mode 100644 server/src/routes/custom-init-commands.ts create mode 100644 server/src/routes/entertainment.ts create mode 100644 server/src/routes/friends.ts create mode 100644 server/src/routes/help.ts create mode 100644 server/src/routes/hosting.ts create mode 100644 server/src/routes/hosts.ts create mode 100644 server/src/routes/images.ts create mode 100644 server/src/routes/inbox.ts create mode 100644 server/src/routes/instance-billing.ts create mode 100644 server/src/routes/instance-destroy.ts create mode 100644 server/src/routes/instances.ts create mode 100644 server/src/routes/ip-addresses.ts create mode 100644 server/src/routes/logs.ts create mode 100644 server/src/routes/mail.ts create mode 100644 server/src/routes/notifications.ts create mode 100644 server/src/routes/oauth.ts create mode 100644 server/src/routes/packages.ts create mode 100644 server/src/routes/proxy-sites.ts create mode 100644 server/src/routes/recharge.ts create mode 100644 server/src/routes/redeem-codes.ts create mode 100644 server/src/routes/resource-pool.ts create mode 100644 server/src/routes/snapshots.ts create mode 100644 server/src/routes/ssh-keys.ts create mode 100644 server/src/routes/storage-configs.ts create mode 100644 server/src/routes/system-config.ts create mode 100644 server/src/routes/telegram.ts create mode 100644 server/src/routes/terminal-saved-commands.ts create mode 100644 server/src/routes/terminal.ts create mode 100644 server/src/routes/tickets.ts create mode 100644 server/src/routes/traffic.ts create mode 100644 server/src/routes/transfers.ts create mode 100644 server/src/routes/user-invites.ts create mode 100644 server/src/routes/users.ts create mode 100644 server/src/routes/verification.ts create mode 100644 server/src/routes/vip-benefits.ts create mode 100644 server/src/routes/vip-levels.ts create mode 100644 server/src/services/agent-instance-report.ts create mode 100644 server/src/services/auto-policy-scheduler.ts create mode 100644 server/src/services/billing-scheduler.ts create mode 100644 server/src/services/cranemail.ts create mode 100644 server/src/services/host-address-monitor.ts create mode 100644 server/src/services/hosting-scheduler.ts create mode 100644 server/src/services/instance-traffic-collector.ts create mode 100644 server/src/services/mail-expiry-scheduler.ts create mode 100644 server/src/services/smartermail.ts create mode 100644 server/src/services/status-scheduler.ts create mode 100644 server/src/services/system-monitor.ts create mode 100644 server/src/services/ticket-auto-close-scheduler.ts create mode 100644 server/src/services/traffic-bandwidth.ts create mode 100644 server/src/services/traffic-notifier.ts create mode 100644 server/src/services/traffic-scheduler.ts create mode 100644 server/src/services/traffic-utils.ts create mode 100644 server/src/services/vip-benefits.ts create mode 100644 server/src/services/vip-levels.ts create mode 100644 server/src/storage/factory.ts create mode 100644 server/src/storage/index.ts create mode 100644 server/src/storage/providers/FtpProvider.ts create mode 100644 server/src/storage/providers/SftpProvider.ts create mode 100644 server/src/storage/providers/WebDavProvider.ts create mode 100644 server/src/storage/types.ts create mode 100644 server/src/types/api.ts create mode 100644 server/src/types/database.ts create mode 100644 server/src/types/fastify.d.ts create mode 100644 server/src/types/incus.ts create mode 100644 server/src/workers/backupUploadWorker.ts create mode 100644 server/src/workers/db-failure-backoff.ts create mode 100644 server/src/workers/hostNotificationEmailWorker.ts create mode 100644 server/src/workers/instanceTaskWorker.ts create mode 100644 server/src/workers/restoreTaskWorker.ts create mode 100644 server/templates/agent-install.sh create mode 100644 server/templates/caddy.sh create mode 100644 server/templates/install.sh create mode 100644 server/tsconfig.json diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..125e57a --- /dev/null +++ b/.dockerignore @@ -0,0 +1,56 @@ +# Dependencies (will be installed in container) +node_modules/ +**/node_modules/ + +# Build outputs (will be built in container) +dist/ +**/dist/ +!agent/dist/ +!agent/dist/incudal-agent-linux-amd64 +!agent/dist/incudal-agent-linux-amd64.gz +!agent/dist/incudal-agent-linux-arm64 +!agent/dist/incudal-agent-linux-arm64.gz +!agent/dist/manifest.json + +# Git +.git/ +.gitignore + +# IDE +.vscode/ +.idea/ +.cursor/ +.kiro/ + +# Environment files +.env +.env.* +server/.env + +# Certificates +server/certs/*.key +server/certs/*.crt +server/certs/*.pem + +# Docs +docs/ +*.md +!README.md + +# Tests +coverage/ +*.test.ts +*.spec.ts + +# OS +.DS_Store +Thumbs.db + +# Logs +*.log +logs/ + +# Temporary +tmp/ +temp/ +.cache/ diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..bdd2288 --- /dev/null +++ b/.env.example @@ -0,0 +1,185 @@ + +根据分析 `docker-compose.yml`、`.env.example` 和代码中使用的环境变量,以下是完整的环境变量清单: + +--- + +## Docker Compose 部署环境变量 + +### 必需变量(必须配置) + +| 变量名 | 说明 | 示例值 | 生成方式 | +|--------|------|--------|----------| +| `POSTGRES_PASSWORD` | PostgreSQL 数据库密码 | `BJ4hbYQTLsz97zzJUGg8RFiq` | `openssl rand -base64 24` | +| `JWT_SECRET` | JWT 签名密钥(≥32字符,至少 3 类字符) | `A1!b7e9c4d0...` | `printf 'A1!%s\n' "$(openssl rand -hex 48)" \| cut -c1-64` | +| `ENCRYPTION_KEY` | 敏感数据加密密钥(≥32字符,需稳定保存) | `XpPc3tcVSmy4/...` | `openssl rand -base64 32` | + +--- + +### 可选变量(有默认值) + +| 变量名 | 说明 | 默认值 | +|--------|------|--------| +| `POSTGRES_USER` | 数据库用户名 | `incudal` | +| `POSTGRES_DB` | 数据库名称 | `incudal` | +| `REDIS_PASSWORD` | Redis 密码 | 空(无密码) | +| `APP_PORT` | 外部暴露端口 | `3000` | +| `ADMIN_PASSWORD` | 首次初始化管理员密码 | `admin123` | +| `LOG_LEVEL` | 日志级别 | `info` | +| `DISABLE_REQUEST_LOG` | 禁用请求日志 | `true` | +| `COOKIE_SECRET` | Cookie 签名密钥 | 空 | +| `FRONTEND_URL` | 前端 URL(CORS 白名单 + 页面跳转) | 空 | +| `PAYMENT_CALLBACK_BASE_URL` | 支付回调使用的后端公网 URL | 空(默认回退到 `FRONTEND_URL`) | +| `PAYMENT_CALLBACK_IP_WHITELIST` | 支付回调来源 IP 白名单(逗号分隔,留空时 Heleket 默认收紧到官方 IP) | 空 | +| `PAYMENT_CALLBACK_SKIP_IP_WHITELIST` | 跳过支付回调 IP 白名单校验(仅开发调试) | `false` | +| `ALERT_WEBHOOK_URL` | 系统告警 Webhook URL | 空 | +| `DB_POOL_MAX` | PostgreSQL 连接池最大连接数 | `20` | +| `DB_POOL_MIN` | PostgreSQL 连接池最小连接数 | `5` | +| `DB_CONNECTION_TIMEOUT` | 获取数据库连接超时(毫秒) | `5000` | +| `DB_IDLE_TIMEOUT` | 数据库空闲连接超时(毫秒) | `30000` | +| `DB_STATEMENT_TIMEOUT` | PostgreSQL 语句超时(毫秒) | `30000` | +| `DB_QUERY_TIMEOUT` | PostgreSQL 查询超时(毫秒) | `30000` | +| `TRAFFIC_CONCURRENCY_PER_HOST` | 每宿主机流量采集并发数 | `10` | +| `TRAFFIC_HOST_CONCURRENCY` | 同时处理宿主机数量 | `3` | +| `DB_WORKER_BACKOFF_MS` | worker 遇到数据库异常后的退避时间(毫秒) | `15000` | + +--- + +### 最小 .env 配置示例 + +可以直接自动生成或补齐缺失项: + +```bash +bash scripts/init-env.sh +``` + +脚本只补齐缺失或空值的变量,不覆盖已有非空配置。 + +```bash +# ============ 必需配置 ============ +# 数据库密码 - 使用 openssl rand -base64 24 生成 +POSTGRES_PASSWORD=your_strong_password_here + +# JWT 密钥 - 使用下面命令生成: +# printf 'A1!%s\n' "$(openssl rand -hex 48)" | cut -c1-64 +JWT_SECRET=your_mixed_64_char_secret_here + +# 敏感数据加密密钥 - 使用 openssl rand -base64 32 生成 +# 注意:上线后必须稳定保存,不能随意更换 +ENCRYPTION_KEY=your_base64_32_byte_encryption_key_here + +# ============ 建议配置 ============ +# 管理员初始密码(首次启动后建议立即修改) +ADMIN_PASSWORD=your_admin_password + +# Redis 密码(可选但推荐) +REDIS_PASSWORD=your_redis_password +``` + +--- + +### 完整 .env 配置示例(生产环境推荐) + +```bash +# ============ 数据库配置 ============ +POSTGRES_USER=incudal +POSTGRES_PASSWORD=BJ4hbYQTLsz97zzJUGg8RFiq +POSTGRES_DB=incudal + +# ============ Redis 配置 ============ +REDIS_PASSWORD=redis_password_here + +# ============ 安全配置(必须修改!)============ +# JWT 密钥 - 生成方式: +# printf 'A1!%s\n' "$(openssl rand -hex 48)" | cut -c1-64 +JWT_SECRET=A1!b7e9c4d0f2a8e6b9d1c3f5a7b8d0e2c4f6a9b1d3e5c7a8f0b2d4e6c8 + +# Cookie 签名密钥(可选,增强安全性) +COOKIE_SECRET=another_random_string_here + +# 敏感数据加密密钥(2FA 等)- 生成方式: openssl rand -base64 32 +ENCRYPTION_KEY=XpPc3tcVSmy4/pxpLL40jVfMDJyayR+bE1ibamEWlys= + +# ============ 应用配置 ============ +APP_PORT=3000 +ADMIN_PASSWORD=QpXEZrBkDosvu7TFMcAJjbv7 +LOG_LEVEL=info +DISABLE_REQUEST_LOG=true + +# ============ CORS 配置 + 页面跳转(必配!)============ +FRONTEND_URL=https://your-domain.com + +# ============ 支付回调(前后端分域时必配)============ +# 第三方支付平台回调到后端时使用的公网地址 +# 例如前端为 https://panel.example.com,后端为 https://api.example.com +# 则这里应配置为 https://api.example.com +PAYMENT_CALLBACK_BASE_URL=https://api.your-domain.com + +# 支付回调来源 IP 白名单(逗号分隔) +# 留空时 Heleket 默认仅允许官方文档中的 webhook IP:31.133.220.8 +# PAYMENT_CALLBACK_IP_WHITELIST=31.133.220.8 + +# 仅开发调试时可临时跳过支付回调 IP 白名单校验 +# PAYMENT_CALLBACK_SKIP_IP_WHITELIST=false + +# ============ 监控告警(可选)============ +# 系统异常时发送 Webhook 告警 +# ALERT_WEBHOOK_URL=https://your-webhook-url + +# ============ 数据库稳定性调优(可选)============ +DB_POOL_MAX=30 +DB_POOL_MIN=2 +DB_CONNECTION_TIMEOUT=10000 +DB_IDLE_TIMEOUT=15000 +DB_STATEMENT_TIMEOUT=30000 +DB_QUERY_TIMEOUT=30000 +TRAFFIC_CONCURRENCY_PER_HOST=10 +TRAFFIC_HOST_CONCURRENCY=3 +DB_WORKER_BACKOFF_MS=15000 +``` + +--- + +### 部署命令 + +```bash +# 1. 创建 .env 文件 +cp .env.example .env + +# 2. 编辑 .env,配置上述变量 +nano .env + +# 3. 启动服务 +docker-compose up -d + +# 4. 查看日志 +docker-compose logs -f app +``` + +--- + +### 变量来源说明 + +``` +┌─────────────────────────────────────────────────────────────────┐ +│ 环境变量流向图 │ +├─────────────────────────────────────────────────────────────────┤ +│ │ +│ .env 文件 │ +│ │ │ +│ ├─── POSTGRES_* ──────► db 容器 (PostgreSQL) │ +│ │ │ +│ ├─── REDIS_PASSWORD ──► redis 容器 │ +│ │ │ +│ └─── 其他变量 ────────► app 容器 (Incudal) │ +│ │ │ +│ ├── DATABASE_URL (自动组装) │ +│ ├── REDIS_URL (自动组装) │ +│ ├── JWT_SECRET │ +│ ├── ENCRYPTION_KEY │ +│ ├── ADMIN_PASSWORD │ +│ └── LOG_LEVEL 等 │ +│ │ +└─────────────────────────────────────────────────────────────────┘ +``` + +**重点**:`DATABASE_URL` 和 `REDIS_URL` 由 docker-compose.yml 自动组装,无需在 .env 中配置。 diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..96407ee --- /dev/null +++ b/.gitattributes @@ -0,0 +1,14 @@ +# Auto detect text files and perform LF normalization +* text=auto + +# Shell scripts must always use LF +*.sh text eol=lf + +# Docker files must use LF +Dockerfile text eol=lf +docker-compose*.yml text eol=lf + +# Keep Windows batch files with CRLF +*.bat text eol=crlf +*.cmd text eol=crlf +*.ps1 text eol=crlf diff --git a/.github/workflows/agent-release.yml b/.github/workflows/agent-release.yml new file mode 100644 index 0000000..2f0ac4e --- /dev/null +++ b/.github/workflows/agent-release.yml @@ -0,0 +1,190 @@ +name: Agent Build & Release + +on: + push: + branches: + - main + - dev + paths: + - 'agent/**' + - 'server/templates/agent-install.sh' + - 'server/src/routes/agent.ts' + - 'server/src/lib/agent-auth.ts' + - 'server/src/lib/host-agent-credentials.ts' + - 'server/src/services/agent-instance-report.ts' + - '.github/workflows/agent-release.yml' + pull_request: + branches: + - main + paths: + - 'agent/**' + - 'server/templates/agent-install.sh' + - 'server/src/routes/agent.ts' + - 'server/src/lib/agent-auth.ts' + - 'server/src/lib/host-agent-credentials.ts' + - 'server/src/services/agent-instance-report.ts' + - '.github/workflows/agent-release.yml' + workflow_dispatch: + inputs: + publish_release: + description: 'Publish an Agent GitHub Release using the version in agent/VERSION.' + required: false + default: false + type: boolean + +env: + GO_VERSION: '1.22.x' + +jobs: + version: + name: Resolve Agent Version + runs-on: ubuntu-latest + permissions: + contents: read + outputs: + version: ${{ steps.version.outputs.version }} + version_changed: ${{ steps.changed.outputs.version_changed }} + should_release: ${{ steps.release.outputs.should_release }} + + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Read Agent version + id: version + shell: bash + run: | + set -euo pipefail + VERSION="$(tr -d '[:space:]' < agent/VERSION)" + + if [[ ! "${VERSION}" =~ ^v[0-9]+\.[0-9]+\.[0-9]+(-[0-9A-Za-z.-]+)?$ ]]; then + echo "Invalid Agent version: ${VERSION}" >&2 + echo "Expected vMAJOR.MINOR.PATCH or vMAJOR.MINOR.PATCH-suffix" >&2 + exit 1 + fi + + echo "version=${VERSION}" >> "${GITHUB_OUTPUT}" + echo "Agent version: ${VERSION}" + + - name: Detect agent/VERSION change + id: changed + shell: bash + run: | + set -euo pipefail + + VERSION_CHANGED=false + if [[ "${GITHUB_EVENT_NAME}" == "push" ]]; then + BEFORE="${{ github.event.before }}" + AFTER="${{ github.sha }}" + + if [[ -z "${BEFORE}" || "${BEFORE}" =~ ^0+$ ]]; then + VERSION_CHANGED=true + elif git diff --name-only "${BEFORE}" "${AFTER}" | grep -qx 'agent/VERSION'; then + VERSION_CHANGED=true + fi + fi + + echo "version_changed=${VERSION_CHANGED}" >> "${GITHUB_OUTPUT}" + echo "agent/VERSION changed: ${VERSION_CHANGED}" + + - name: Decide release publishing + id: release + shell: bash + run: | + set -euo pipefail + + SHOULD_RELEASE=false + if [[ "${GITHUB_EVENT_NAME}" == "push" && "${{ steps.changed.outputs.version_changed }}" == "true" ]]; then + SHOULD_RELEASE=true + elif [[ "${GITHUB_EVENT_NAME}" == "workflow_dispatch" && "${{ inputs.publish_release }}" == "true" ]]; then + SHOULD_RELEASE=true + fi + + echo "should_release=${SHOULD_RELEASE}" >> "${GITHUB_OUTPUT}" + echo "should release: ${SHOULD_RELEASE}" + + build-agent: + name: Build Agent + runs-on: ubuntu-latest + needs: + - version + permissions: + contents: read + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Go + uses: actions/setup-go@v5 + with: + go-version: ${{ env.GO_VERSION }} + cache-dependency-path: agent/go.mod + + - name: Test Agent + working-directory: agent + run: go test ./... + + - name: Build Agent release files + run: VERSION="${{ needs.version.outputs.version }}" bash agent/scripts/build-release.sh + + - name: Prepare release assets + shell: bash + run: | + set -euo pipefail + VERSION="${{ needs.version.outputs.version }}" + mkdir -p agent/release + + cp agent/dist/incudal-agent-linux-amd64 "agent/release/incudal-agent-x86_64-${VERSION}" + cp agent/dist/incudal-agent-linux-arm64 "agent/release/incudal-agent-aarch64-${VERSION}" + + - name: Verify release assets + shell: bash + run: | + set -euo pipefail + VERSION="${{ needs.version.outputs.version }}" + test -s "agent/release/incudal-agent-x86_64-${VERSION}" + test -s "agent/release/incudal-agent-aarch64-${VERSION}" + + - name: Upload Agent artifacts + uses: actions/upload-artifact@v4 + with: + name: incudal-agent-${{ needs.version.outputs.version }} + path: | + agent/release/incudal-agent-x86_64-${{ needs.version.outputs.version }} + agent/release/incudal-agent-aarch64-${{ needs.version.outputs.version }} + retention-days: 30 + + release-agent: + name: Publish Agent Release + runs-on: ubuntu-latest + needs: + - version + - build-agent + if: needs.version.outputs.should_release == 'true' + permissions: + contents: write + + steps: + - name: Download Agent artifacts + uses: actions/download-artifact@v4 + with: + name: incudal-agent-${{ needs.version.outputs.version }} + path: agent-release + + - name: Publish GitHub Release + uses: softprops/action-gh-release@v2 + with: + tag_name: agent-${{ needs.version.outputs.version }} + target_commitish: ${{ github.sha }} + name: Incudal Agent ${{ needs.version.outputs.version }} + files: | + agent-release/incudal-agent-x86_64-${{ needs.version.outputs.version }} + agent-release/incudal-agent-aarch64-${{ needs.version.outputs.version }} + generate_release_notes: true + draft: false + prerelease: ${{ contains(needs.version.outputs.version, '-') }} + fail_on_unmatched_files: true + overwrite_files: true diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..1d0ae42 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,81 @@ +name: CI + +on: + push: + branches: [main, dev] + pull_request: + branches: [main] + +jobs: + lint-and-typecheck: + name: Lint & Type Check + runs-on: ubuntu-latest + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup pnpm + uses: pnpm/action-setup@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: '22' + cache: 'pnpm' + + - name: Install dependencies + run: pnpm install --frozen-lockfile --ignore-scripts + + - name: Generate Prisma Client + run: pnpm --filter server exec prisma generate + env: + DATABASE_URL: postgresql://user:pass@localhost:5432/db + + - name: Lint + run: pnpm lint + + - name: Type check (client) + run: pnpm --filter client type-check + + - name: Type check (server) + run: pnpm --filter server type-check + + - name: Setup Go + uses: actions/setup-go@v5 + with: + go-version: '1.22.x' + cache-dependency-path: agent/go.mod + + - name: Test Agent + working-directory: agent + run: go test ./... + + build: + name: Build + runs-on: ubuntu-latest + needs: lint-and-typecheck + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup pnpm + uses: pnpm/action-setup@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: '22' + cache: 'pnpm' + + - name: Install dependencies + run: pnpm install --frozen-lockfile --ignore-scripts + + - name: Generate Prisma Client + run: pnpm --filter server exec prisma generate + env: + DATABASE_URL: postgresql://user:pass@localhost:5432/db + + - name: Build + run: pnpm build diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml new file mode 100644 index 0000000..0799b0b --- /dev/null +++ b/.github/workflows/docker.yml @@ -0,0 +1,65 @@ +name: Docker Build & Push + +on: + push: + tags: + - 'v*' + workflow_dispatch: + inputs: + tag: + description: 'Image tag (e.g., latest, v1.0.0)' + required: false + default: 'latest' + +env: + REGISTRY: ghcr.io + IMAGE_NAME: ${{ github.repository }} + +jobs: + build-and-push: + name: Build & Push Docker Image + runs-on: ubuntu-latest + permissions: + contents: read + packages: write + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Login to GitHub Container Registry + uses: docker/login-action@v3 + with: + registry: ${{ env.REGISTRY }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Extract metadata for Docker + id: meta + uses: docker/metadata-action@v5 + with: + images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} + tags: | + # For tags: v1.0.0 -> 1.0.0, latest + type=semver,pattern={{version}} + type=semver,pattern={{major}}.{{minor}} + type=semver,pattern={{major}} + # For manual trigger + type=raw,value=${{ github.event.inputs.tag }},enable=${{ github.event_name == 'workflow_dispatch' }} + # Always tag latest for version tags + type=raw,value=latest,enable=${{ startsWith(github.ref, 'refs/tags/v') }} + + - name: Build and push Docker image + uses: docker/build-push-action@v6 + with: + context: . + push: true + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + cache-from: type=gha + cache-to: type=gha,mode=max + platforms: linux/amd64 + diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..c448902 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,257 @@ +# ============================================================================ +# Incudal 预构建产物包发布工作流 +# 在推送版本 tag (v*) 或手动触发时,自动构建前后端并打包为 .tar.gz 发行包 +# 支持 amd64 和 arm64 双架构 +# ============================================================================ + +name: Build & Release + +on: + push: + tags: + - 'v*' + workflow_dispatch: + inputs: + tag: + description: '发行版本号(例如 v1.0.0),留空则使用 git SHA 作为预览版' + required: false + default: '' + +env: + NODE_VERSION: '22' + +jobs: + # ===== 构建发行包(多架构矩阵) ===== + build: + name: 构建 (${{ matrix.arch }}) + runs-on: ${{ matrix.runner }} + strategy: + matrix: + include: + - arch: amd64 + runner: ubuntu-latest + - arch: arm64 + runner: ubuntu-24.04-arm + permissions: + contents: write + + steps: + - name: 检出代码 + uses: actions/checkout@v4 + + - name: 安装 pnpm + uses: pnpm/action-setup@v4 + + - name: 安装 Node.js + uses: actions/setup-node@v4 + with: + node-version: ${{ env.NODE_VERSION }} + cache: 'pnpm' + + # ===== 安装依赖 ===== + - name: 安装全部依赖 + run: pnpm install --frozen-lockfile --ignore-scripts + + - name: 重新构建 esbuild(Vite 构建需要) + run: pnpm rebuild esbuild + + # ===== 生成 Prisma Client ===== + - name: 生成 Prisma Client + run: pnpm --filter server exec prisma generate + env: + DATABASE_URL: postgresql://user:pass@localhost:5432/db + + # ===== 构建前端 ===== + - name: 构建前端 (Vite) + run: pnpm --filter client build + + # ===== 构建后端 ===== + - name: 编译后端 (TypeScript) + run: pnpm --filter server build + + # ===== 准备发行目录 ===== + - name: 准备发行目录结构 + run: | + mkdir -p release/client/dist + mkdir -p release/server + + # 复制前端构建产物 + cp -r client/dist/* release/client/dist/ + + # 复制后端构建产物 + cp -r server/dist release/server/ + cp -r server/prisma release/server/ + cp server/prisma.config.ts release/server/ + cp server/package.json release/server/ + cp -r server/templates release/server/ + + # 复制根配置文件 + cp package.json release/ + cp pnpm-workspace.yaml release/ + + # ===== 安装生产依赖 ===== + - name: 安装后端生产依赖 + working-directory: release/server + run: | + # 在发行目录中只安装生产依赖 + npm install --omit=dev --ignore-scripts + + # 在当前架构的 runner 上原生生成 Prisma Client + DATABASE_URL="postgresql://user:pass@localhost:5432/db" npx prisma generate + + # ===== 创建启动辅助脚本 ===== + - name: 创建管理脚本 + run: | + cat > release/incudal.sh << 'SCRIPT' + #!/bin/bash + # Incudal 快捷管理脚本 + set -e + APP_DIR="$(cd "$(dirname "$0")" && pwd)" + + case "${1:-}" in + start) + echo "🚀 启动 Incudal..." + cd "$APP_DIR/server" + npx prisma migrate deploy + cd "$APP_DIR" + exec node server/dist/app.js + ;; + migrate) + echo "🔄 执行数据库迁移..." + cd "$APP_DIR/server" + npx prisma migrate deploy + echo "✅ 迁移完成" + ;; + version) + echo "Incudal $(node -e "console.log(require('./package.json').version)")" + ;; + *) + echo "用法: $0 {start|migrate|version}" + exit 1 + ;; + esac + SCRIPT + chmod +x release/incudal.sh + + # ===== 确定版本号 ===== + - name: 确定版本号 + id: version + run: | + if [[ "${{ github.event_name }}" == "push" && "${{ github.ref_type }}" == "tag" ]]; then + VERSION="${{ github.ref_name }}" + elif [[ -n "${{ github.event.inputs.tag }}" ]]; then + VERSION="${{ github.event.inputs.tag }}" + else + VERSION="preview-$(echo ${{ github.sha }} | head -c 7)" + fi + echo "version=$VERSION" >> $GITHUB_OUTPUT + echo "📦 版本号: $VERSION" + + # ===== 打包 ===== + - name: 打包为 tar.gz + run: | + VERSION="${{ steps.version.outputs.version }}" + cd release + tar -czf "../incudal-${VERSION}-linux-${{ matrix.arch }}.tar.gz" . + cd .. + ls -lh "incudal-${VERSION}-linux-${{ matrix.arch }}.tar.gz" + echo "📦 发行包大小: $(du -h "incudal-${VERSION}-linux-${{ matrix.arch }}.tar.gz" | cut -f1)" + + # ===== 上传构建产物 ===== + - name: 上传构建产物 + uses: actions/upload-artifact@v4 + with: + name: incudal-${{ steps.version.outputs.version }}-linux-${{ matrix.arch }} + path: incudal-${{ steps.version.outputs.version }}-linux-${{ matrix.arch }}.tar.gz + retention-days: 30 + + # ===== 发布 Release(等待所有架构构建完成) ===== + release: + name: 发布 Release + needs: + - build + if: startsWith(github.ref, 'refs/tags/v') + runs-on: ubuntu-latest + permissions: + contents: write + + steps: + - name: 确定版本号 + id: version + run: echo "version=${{ github.ref_name }}" >> $GITHUB_OUTPUT + + # 下载所有架构的构建产物 + - name: 下载 amd64 产物 + uses: actions/download-artifact@v4 + with: + name: incudal-${{ steps.version.outputs.version }}-linux-amd64 + + - name: 下载 arm64 产物 + uses: actions/download-artifact@v4 + with: + name: incudal-${{ steps.version.outputs.version }}-linux-arm64 + + - name: 列出发行文件 + run: ls -lh incudal-*.tar.gz + + # 创建 Release 并上传所有架构的产物 + - name: 创建 GitHub Release + uses: softprops/action-gh-release@v2 + with: + files: | + incudal-*.tar.gz + generate_release_notes: true + draft: false + prerelease: ${{ contains(github.ref_name, 'alpha') || contains(github.ref_name, 'beta') || contains(github.ref_name, 'rc') }} + + # ===== 构建并推送 Docker 镜像(多架构) ===== + docker: + name: 构建 Docker 镜像 + if: startsWith(github.ref, 'refs/tags/v') + runs-on: ubuntu-latest + permissions: + contents: read + packages: write + + steps: + - name: 检出代码 + uses: actions/checkout@v4 + + # 设置 QEMU(用于跨架构构建 arm64) + - name: 设置 QEMU + uses: docker/setup-qemu-action@v3 + + # 设置 Docker Buildx(多架构构建引擎) + - name: 设置 Docker Buildx + uses: docker/setup-buildx-action@v3 + + # 登录 GitHub Container Registry + - name: 登录 GHCR + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + # 提取版本号和元数据 + - name: 提取 Docker 元数据 + id: meta + uses: docker/metadata-action@v5 + with: + images: ghcr.io/${{ github.repository }} + tags: | + type=semver,pattern={{version}} + type=semver,pattern={{major}}.{{minor}} + type=raw,value=latest + + # 构建并推送多架构镜像 + - name: 构建并推送 Docker 镜像 + uses: docker/build-push-action@v6 + with: + context: . + platforms: linux/amd64,linux/arm64 + push: true + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + cache-from: type=gha + cache-to: type=gha,mode=max diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..348dd9e --- /dev/null +++ b/.gitignore @@ -0,0 +1,93 @@ +# ===================================================== +# Incudal 项目 .gitignore +# ===================================================== + +# ==================== 依赖 ==================== +node_modules/ +.pnpm-store/ + +# ==================== 构建产物 ==================== +dist/ +build/ +client/dist/ +server/dist/ +!agent/dist/ +!agent/dist/incudal-agent-linux-amd64 +!agent/dist/incudal-agent-linux-amd64.gz +!agent/dist/incudal-agent-linux-arm64 +!agent/dist/incudal-agent-linux-arm64.gz + +# ==================== 环境变量 ==================== +.env +.env.local +.env.*.local +server/.env + +# ==================== Prisma ==================== +server/src/generated/ +**/generated/prisma/ + +# ==================== 日志 ==================== +logs/ +*.log +npm-debug.log* +pnpm-debug.log* +yarn-debug.log* +yarn-error.log* + +# ==================== IDE/编辑器 ==================== +.vscode/ +.idea/ +*.swp +*.swo +*.sublime-workspace +*.sublime-project + +# ==================== 操作系统 ==================== +.DS_Store +.DS_Store? +._* +.Spotlight-V100 +.Trashes +ehthumbs.db +Thumbs.db +Desktop.ini + +# ==================== 证书 (敏感) ==================== +server/certs/*.key +server/certs/*.crt +server/certs/*.pem +!server/certs/.gitkeep + +# ==================== 数据库 ==================== +*.db +*.sqlite +*.sqlite3 + +# ==================== Docker ==================== +.docker/ + +# ==================== 临时文件 ==================== +tmp/ +temp/ +.cache/ +*.tmp +*.temp +*.pid + +# ==================== Vite ==================== +client/node_modules/.vite/ +client/node_modules/.vite-temp/ + +# ==================== TypeScript ==================== +*.tsbuildinfo + +# ==================== 测试覆盖率 ==================== +coverage/ +.nyc_output/ + +# ==================== 文档 (内部) ==================== +docs/ +SDK/ +SDKNEW/ +namecrane_modules/ diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..941ccbb --- /dev/null +++ b/Dockerfile @@ -0,0 +1,85 @@ +# ===================================================== +# Incudal 多阶段构建 Dockerfile +# ===================================================== + +# Stage 1: 依赖安装 +FROM node:22-alpine AS deps +RUN corepack enable && corepack prepare pnpm@9 --activate +WORKDIR /app + +COPY package.json pnpm-lock.yaml pnpm-workspace.yaml ./ +COPY client/package.json ./client/ +COPY server/package.json ./server/ + +RUN pnpm install --frozen-lockfile --ignore-scripts + +# esbuild 依赖安装脚本不能被跳过,否则 Vite 在构建阶段无法启动二进制 +RUN pnpm rebuild esbuild + +# Stage 2: 构建前端 +FROM node:22-alpine AS builder-client +RUN corepack enable && corepack prepare pnpm@9 --activate +WORKDIR /app + +COPY --from=deps /app/node_modules ./node_modules +COPY --from=deps /app/client/node_modules ./client/node_modules +COPY client ./client +COPY package.json pnpm-lock.yaml pnpm-workspace.yaml ./ + +RUN pnpm --filter client build + +# Stage 3: 构建后端 +FROM node:22-alpine AS builder-server +RUN corepack enable && corepack prepare pnpm@9 --activate +WORKDIR /app + +COPY --from=deps /app/node_modules ./node_modules +COPY --from=deps /app/server/node_modules ./server/node_modules +COPY server ./server +COPY package.json pnpm-lock.yaml pnpm-workspace.yaml ./ + +# 生成 Prisma Client 并构建 +WORKDIR /app/server +RUN DATABASE_URL="postgresql://user:pass@localhost:5432/db" npx prisma generate +WORKDIR /app +RUN pnpm --filter server build + +# Stage 4: 生产镜像 +FROM node:22-alpine AS runner +WORKDIR /app + +ENV NODE_ENV=production + +# 创建非 root 用户 +RUN addgroup --system --gid 1001 nodejs && \ + adduser --system --uid 1001 incudal + +# 复制依赖 (包含生成的 Prisma Client) +COPY --from=builder-server /app/node_modules ./node_modules +COPY --from=builder-server /app/server/node_modules ./server/node_modules + +# 复制构建产物 +COPY --from=builder-server /app/server/dist ./server/dist +COPY --from=builder-client /app/client/dist ./client/dist + +# 复制必要配置文件 +COPY server/package.json ./server/ +COPY server/prisma.config.ts ./server/ +COPY server/prisma ./server/prisma +COPY server/templates ./server/templates +COPY server/scripts ./server/scripts +COPY server/src ./server/src +COPY package.json pnpm-workspace.yaml ./ + +# 复制启动脚本 +COPY server/docker-entrypoint.sh ./server/ +RUN chmod +x ./server/docker-entrypoint.sh + +# 创建证书目录 +RUN mkdir -p server/certs && chown -R incudal:nodejs server/certs + +USER incudal + +EXPOSE 3000 + +ENTRYPOINT ["./server/docker-entrypoint.sh"] diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..68fc42b --- /dev/null +++ b/LICENSE @@ -0,0 +1,28 @@ +BSD 3-Clause License + +Copyright (c) 2026, qwer-xyz + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..1fed7b7 --- /dev/null +++ b/README.md @@ -0,0 +1,194 @@ +
Incudal基于 Incus 的 LXC / KVM NAT VPS 销售、交付与管理面板。
+ +## 项目简介 + +Incudal 基于 Incus 的 NAT VPS 销售与管理面板。 +项目支持 LXC / KVM 实例、套餐与镜像管理、账务计费、节点托管、用户后台、管理员后台以及宿主机 Agent。 + +> 演示站:https://demo.incudal.com9Id>O<9MSSg9~A)zm)OS7A0 z-wknqpN7n(l=el>6b*fB3jOVgKSm~pQ*vx$F6@7_U70oPO>4?-eWnf>MCHJ>+fRi8 zTV-}JkC(gLsc&Eze$7K1P}|9>VZJ(BJbA&qg%eVMVfj)p7p@0bX?9?jJRQ@ywL^{8 zvjvn_+C4=7WI4$pK|V*u#!aE^@A?hDIEOiAk7pN4hRTb;6!8Dhqbz0ov%J1--z5c! zlVCsa!BD!+i-M!#gbGiBJqLj}|Na6}%Uk|%^A!z` nRK^ldc#SoM@>=@QNQY)10OXMC%YPA<^_M@#EX0F1 zpOf3D%b@8gVX{0ALJ0>`2i^n;99}kJdAIg?pI7LAPyh-mna2V~KtqCkgG|>JS-R#3 zr;Q54EL%Z)b !@T0HKI)D$*ld%c8aDVY$4z#gR@J<6-{fP$*Q1t8NSb>I|NF zzzXf#FEp@WpE3au4bU(uEBPfG4SN@rQJ4noB u|vUnUVxb$bgeuNA|NOF+zBz z44{m@$4l<`1|UkriOm)QFp+*o@t%2)8&cJ4AKB>!wX`5 oRc0XA)CmOb41kK8%`gJD<5e49NTl}s--w)kenT(j$cvqlCl$a;HM zU&%Zc#xWw7ofi|@Hdx9tYVP_AEh;jFj5^ZE7%+smE1f-vIkRMhkrU@u!{=v`JwjJ9 ziBdPI2k _M<_6pZ?xViY-yI^TD&<8e8^ zm{s^!Szk}3|7-{AH+-_lsKKL=lteVh9$Z$a*0-`|R!^MFj4U~=A~~Z^uPik7O1@c^ zPDTHSI}p(OE?Y=TMSs;HFqa^&5Ct11nGe@iOorBZbQztL=L?PfmfAk62E!D9JBsVT z2u_r7vYAxb<5=m|i+)%iZX_arapH&W%ajaG7*Wa&h%f3Ms5yvmC?maPwr1+^_v=Zx zsyhgz)QBX90Ik)lHX2yPjBnBkw9odvYn;!5rngn96hO<0LOj~geL&yt_1;fuW9AKq z2lB_$^0FPznMW#gTXVG#Mc-M2PR7MtA&bmI0pH7V+pG*tFYei^>Rh&R#9MvUJ`a=7 zFG}x)MO7&xT+X)5#H-5O9Ct3>n2pCReWd~GPR;W`l9woZh{N9*w@sLt^nP$?K0UCr zmxEH-AQah?`aD4$@k?beSVwYr-hy0tt-;{f54l`%B9Q@cfJW*r)&86FFf1iXvewqR z&*)2Mx^`Zh5&M3|e; zV{oM-kdhy}Caf!C#>e}|NuLsm(BzTC32AIaP__rxxQPrfRQ4rz%DLc7M3egF# zvuv$1{T7T(*+OFh_cP`X4*vpdZBP!+V}5#zRXBO2S<=l ~_xgK)|9?frIL za)iNh(AYN+j `|D-7b*=FEpq@8!F~{KvRQ15Kd(8sX3V@(t}4wUfh9d6 zbbrt7s;BKA)Baffx&r7YhIvVKOV;&K35B|Q1Y(v2MnjJJK9Z%-SYph9L@1h` X%f3>>(G%C*eq-Dsna^NgNdaGZ(pE<6+yldt%j+1NtE)$v zu8`;MM-mfKWrhk_?Be)YfU1#X!?b=%n-J9hYXF(>QWc(q!@0tpLA(6qv+JExR`TdA zGa@m>bld(=fN^Q+Uj5aa1$7{idgT^d5>{*V+SRjsGhXn$={kndmyZdFD$bf90-+{i z85<4Cm-+Y1D^K;t!}aByEP39g%ZrxRe&_e>WwZFm@@4wPB4-}K^Wd-6w%M8<=4vhl za5!&h#sfUR5!@^HO;Zr^?u_TS!xL4a`Gk`qeA_%{c1r;1ARQn-J5B~geR1Go1|!@8 zI(DkOtIGCETaL2vVL@Rf(odr9y;kpy5f}C?6!14)adyQA{a*d~o5tWIDVB$v7>d94 z*jmff%&D(E%GG&^CX*#qH)O~b`C|Vj<)+HRn$IUq@}S;0R6au-Q$iA1%{t#&UUXRW zdgmt3Nrt;}OQ68b=}(7o?XR@|gv#6Ya#F}?^BXafq^Hd==@o(325e# {gW_nU6Z@9GcYs0c$H5>X&Kgh{e?f6OG?ltioOrmOCjX=aLca0G&GUn^xCOrxn7Lyd(7 zC)d2<08 ~I;2R8+*j)H0<>9Z$$d&H1|67~ri^G_|5#R+et1(jyc(LT>$ z3>rqZmN5Vz{k_v44Sm$ ScVa27c<27c+IbKc4&5ckPvWqNzb3t7rjF zau`I}rGs+CVTIz3F=4$5m{O9+c(aXji4znzFdLPh`*22Ubo8X&=Agq&+?_<#G<2{j zn@-7axL@h}Nz06hSosLoT#XxH3Rr?6Vm`)&9(`zxr7&LbUa%)iSl*Ie<&-+ycBI>S zmPRWy7cetB$HbH?5O4&7{l+H0WqW9R_b%Gs>0-#{rM{uJE5Mvbtu4gK$@C)0p>6G3 z%?E%YxGbLzM7*eRdG@&BbM+siCtp-UeWb}i`X{b8d9#_NwmtEo4x9LoRCfXN-Nr3$ z!Oo_ci=xgF!IcTtk)TR^9&Y!|QzNdwI 8vTWcK9xuPI}fnvxluNckr za~ _3@4YZYGIW!ZPm1a*JwtDa(ob?b`zO*hLQ;`W|D 2!?r^5|FG;slxp@QJc$nE@yHBs&XJ;38!`lp&W4T3Fy+c7B zzG}f!y>{%T!H+np!M2=aG@XYatMEA27_*lTJ?G-l6S;k$C@c|$6cT|%d@o>y@06qa zRo!n+`tLj-7K}UzJ##bW6Qb^9!|e{<1HYt@xfnhE C&L$R zP7`j`MsF;(^`0%59Fpcdjcr6%I1ki+*Vs-yv43P04Jdw@Y)0rD%RE${^~YVc9dn1V zbxL2|$k~Y6B@IkY1?r|SK@okD%}Xh4u4r^WM|!!`M;_8BuvZoqDkW*}RWz`)#j!gW zPNrL>cpvPKWE1Q9ASLSiShJa&4yhbHDhqAKnM^!ee{;Tr0D)sZKVATIa94`erAxhf zZbu#!`=xl`Pa_}W9R)jdt+xi^*Y%(sH3!Z6w(1?LB^s- JVk?>)LN zkzj)<8yocq!gV_DWT?^PejcIq%R9o+!rqq?ZozZHS4+;jfva%B0muJa@&=sGheNLf z#|ay4I(pjy@G!-hfUDuq>+b)L66*6LLfMv@umQ)fv~kqS9p(|v(S&nX^jz0_hGPqc z#Q(KAqiZ_)zrWx7JIHY7e>HX0w!wNl{{L65!}+J44#EYRply>wqP(h>YC^~(JW;$H z4$b~Vb&uvJ{Qvo9$YO5?!RRwAUlITLJ-x@Ms-E87Pmk`dPd&G%soeZOTu+y1Y}{UD z7C3Wr{`pDP&z2pGP;Pi@m-s=4Sozz&TV_FU4v1^DpUpWN#Kq;`o+hE{{KGBU%GJxR zk9m-KhZ`!;w!hZDe5kUO7JkEd_cl^^_N`oY$$>Apz9kP?Le6@96RtsJ7hUf%zy@D! zS?b^fm+ntYJd<|pqbe0a6WrbaXcX=uT0({LlHJ!L>eq60CkVXc%_Pd${jS9-qv{8B zU2CB9hSjB$mk O1m3Q&qrB_a6)tMuF>vKirRJ)Yd4rbc38+lzYhEYN5zKOr%z%jK4vC&~aLd;F`T z^)CU&S)M00#RGFbMF8+aWSDwl$W70AQ9ZqJ;BfL!5PK3dA{ogfTYb~1Uus}7>(?FV zNB{{Fb43fprijV3hHUj|KYTpqHuXdN0Ufr0>ob7jHF7n@EYD(sDZS4$V5FE8eTRiz z T?7fmxEIfwNvpz@-T z-oFKcrg4to+cEFq3-8bhX5fo{YIeMT*P8pLz^MNMW0NP8=-EFK=H4i3mtFOSk<=!y zc_0dGn!sik`wKm&XM1gj^*)7t_q$^=CUvnujDB}Gu_5(tXM zk25QsOFClR5=EMtEAx1{bO1eolN;cuTJ>;q4!>nrZb*-Absw*j8P;4>h_ 37c*l?PN~c{5WdY;N45Fw-4rgwzEwF zsDweaqKI~%kgLCGROPlS1~&FCJ%JD_ib @CdpPmGwIT< Y*tt|n(``NdVh z;G c>=?FD-onmVZ0$Q{q^JtL0>R5C(2BZ7Q37^xEl9)4k|2zJlWh=>_$ z+A1t#G3$l49@kKXCxhu(FLvHDZZD4@ifk_1=Zf!g+T8BBqwdCSA-sXkL~G*&Ypz>W zQt6`aaGSqjLQ{QQ(}uH@I1 P>9DT011?KYx?1Zg6PGlq99 zo?NX@#}#N_4OaW!p#ZABX#i)Q+idQBd+U3U(_cdjEAh|MOg)p|^U_z@W{V8b+z| z&8+kgNfCY`PMEIVOQmRQ3&}>mp_YO<746r@N(*O~#49+yOj@W$&py-rnZxVJsB^B) zF?h>s0`o1Mi4X<#(T0u!^qC~TuU!Z($G!hs(p&LuTPdP| G5i5Jw%%eD~^ha_21N-rj$}KQlG5s-Mk4M3QqzV&~_?H|FBjy zR@%zP78W^6Km3oF|2|0%Pm*nKYq9j~zVMvfP0HYdqiAdYyNOTmwZqOAhwl@u%x5Lv z&gX`q-b3jYJM@vaBopmZoS&7UvuwN;`o>B333UvNrU$GOXAK-#?428(Sy_6ud;X4b zhm{8Cy!IqmvZd*@ )+&W=3^%>Zak9F^_^>|j7F&%tBe`#|s=#`RyRL#{DqbgOI zX}|brn^YQi zt!L#n$~Y?XPT}Am>uaG&AH{acTgjY65LD1Y(L{w)^gF4&k+tB9U)f9spWN<&ArQ`0 zvZsS@wW#bz3OKITGc+Q8eyiLrv#-#lGG6Z5HlK>tAa0MmNTICw=G~E^WxNN^T_lg9 zT>FMQo0u`^NIEv+IA%x!$5 #62+Ux5M~EaTVIFJ^K%EzRr2V`-@ov4<4=g*|AVNHx9WKp96}s< zHXQ#cDPY4^G^~B5n~ou}_Z>$C+Yr$k+X!RAeT}8gn})mexiexf==nK924=-!VeO+c zWC(7aU%F0%iJ$iW72z#2qd3uDx0Q(d#IG}IU-Lv>h3>H_EafuTtLbGNu4LY&-+K`d zWqAQx{eaUYVqyMy5)^nR0ZFrPJgCvW)`4bajQ_OA7b>3I{>7hLAjsXCIC2|KsGwxI zYc*o8!lf;LU2iqtdzU_-m05wa)$Y&3|23Mqm&dNKbXsWtJDS`VFPYqKCODM%31+wU z5{uyGoW1fC{&bgi&%!eida=K $ zsl F|MC?aP6wgz)x$-u>{!K=1Cd_x@ zKw u)_=8kT%_KaA5@M)~h%h?lkqB}SdNU$MDX z$cgs+22pBIJyiPLMCg8*llkTxH}dQQ$AKt04`>9WT=mv%MvYGNPp6f$@vsS*hb4%l zDE4u0MSzEOV3!$>vcAKPpjK@j35-&W>Kji};1p4UyO+r3a$ 8;4JPF6$-W2QDtENuvG4Jjio|%sKQ8gH zfHR3;60EF(y% <_|i#P+!OV+{&8Zk%@YAO7XZo2#)Lr%DiNeB;u* z5~zXVgP(6`o!s_(*P$ZV=E#zrSdssYEmW42k5pGE*u@|vPU!n68~X8rZzB|j-~n>S ze3m0>$!=?InNnd~a^Eh1pHXmTf?4`+o$HU~+;uN6_q|sS g|a0+0VGV zTN|ApcQ!DnRm >`Q15ZM4r&Lfg7a8SFaOnF{%Gon%dg+9 z;tkEEVrKrZeYHE9`$1BGyr?@g*b++4*n8dEQvJ$6zZ5q2 bM zvViNp)ln%_n~=ok3Zw0H)`9eUKn>{X%^vk$%h#VOg}r~%^1i`G&}tMH(bnjbo0_*n zlUXHRn_J}p?po%^yZhn@`pb#7sW~P!-a)U8zr$z80rZDJL;h>&jZ+b3#x&_?IUKu^ zIo<7YiW;Zqc(v{25_%R1?I4>JG89dFy96zGNd&vSe>QABwwO=*7PI|+I7H@sw;l-k z1)1 gmU6)U`kw%5=ilXRc%T8K0ia|#SL`u+Vo)(U{O*V0CR z3EdqKsSy&GDq`VlX^`8FtgNI;S{)y1D1Dr$*;x9g;_J;yyaau@2%>j0yiI +w4UUPxC(;M~p;1&vEbaFdOFNhuf_!vREbyBz?x7(;cNdj}tkpSVQ+D#;Lk*wD zR0@{|*tE{hc)=(hB6<(0VnoIiw)H|{Os#M=>raZsOs+VVE|!6owAOcu?J={lL2UkU zMmM7i{52BraFHvC7<7&OY2@ir8mIZkdDsO;9$#4|S+{l+I@D4mB%sqhNM~X0$;Sy1 z)E%jNb9quAR<6vTQo8=GZNYci=2KD}aWD4$<9^p~zWN$6W2f!YUnPjadg`6rw+w+@ zUFa>d!6)F|becQW_Hl3Hqe7as7;WYJoF|TbhDMEdzy1Kva20T+1Ag>rIWB3x!G?8V zPhExABmHg5(UA=~*AEAE%Lbha--%mAwHzF($zSc31nH?D@@HG~<{Rn++0v%7^Ilod z7$8*C8;FhqNA$_(#l7HdPbdxeNyI{z`vavN^GAX(yGyQ5@?g)Pg`Zj<=UJF=r0tn? zgu~C?f`On%kE|_w_Q5&xH@2Sl(+ BU))Jv&S)HJb1JvLKM-wn;Yma?PG`H?~C_;y}5Kh ziYHPjN*fyLGt+$4WT4Mnh413Ms`hWsk7EAq>vXi!FRK6YPvI+<%sOEOc_ (fJ&0)Al6h`Odr|KhJ*BfCXrHx|Td4q?CEn%8nPGkl(4!q?J# z=r=?+`D3*QJ?t&fsn2{`&hU _}uehGoVKstUoD&%l%X1ZkSlk z2KSum4FM2QN@j?B;SCka7XLi2vbJ^nC6D?+jG9sj|1bU|jgbAxuo!QBDH*ZGjn2WB zwz;1Q;=lNI_S2~qn!OZ^HQ1#Vp7g$V;TBF#l{ yA;e^wtQ0`zBJ~ z =SsGV2!4Dbpo9+}UtDPW+T2eYG@IMis^vIir|SWb+%dHE)4pgmiz}{GT|22i z*t;NWK@szOazouN8&qA}bNF TSr@o4% z!|aNdMD8OVBqxM-ESOdujEaC)*`JU-%PQzoNN=@U`ur;#0L;K#BcQyC)q?CeOhQ3y z`{iCH{5~r^yMndhML&-Ms1JWRy;@{w=sr1F{bnQcz6!|b;MVu@5UEKFIBBUcL<<@D z-lR9}FGhZir6y0}yz~u{teRJ0h^?8^eI)tY9;C;>p&oXP?Ycnw7MdA0WNZf54{s_Q z8Xg74rs3vpW-}x)Z@fy^-Oqx@9t1IPSWf;K6qDXwDOvtArgQmk13sJuu~N#wtjJn| zpW&*g^0Xb}1h~xxy>Ymw5LyJimXVV*V>{q5QYo(JM}3L)_oAC|iBNb703@a?ezid4 z8glc|r2B8TKCd0-sMGFh2uw`>(mR`nrbv;HxBc4dOTMNSBii4pM%E9Y&sK3AV`0H= zypc~aP~T!d6on_2eIfqQX>s4T+U0(lkG@?|S!l?Lg*_m`sHyjGYVN3FSUK(EEescy zaZxmE671!8@#GsfosrKitx`Ask?9wJ&Okg98_v^{Gb nyb_g2tBc zN!Yxx_-7{JX()gJd)K7P9?x-NiC-JpDrsrBj8z+pcj Py zJ2lSjU-}Aic)@n$;v6qvXok7C^u;F|^77?e+@LrX5yISsa8@+P-`OOQ zm{3dmQL3Mk_cE7>FX5ReV4-fUU`EE#Oht#j^V`|usnP6$##bS33)H$g>M9KNn;%|g z4`-1IC|E5xt~liP&DNh3ZCY?J1AvO)uUYB>$q_Vup^G!dE)KeCX-%0Ld9HFPjJIj4 z7(NgWpyT&qsdL`K3l5{xu9LdQli3-lc T zikx^cMQ#Bu-YS$RfLa-!V%4R}0zNn6NrG1Wd}i^qPdTmKedU~t@t7B2AbuBYdm|&? zYGWvkf%=?Gw4ID{1gnZy5l6wRPOCSKZA}fWcFOUG=r0&B=kU~E-QQd7NQtfY;u1Fy zOtgFu+c7cecM-raMXQZ4`v+r9C87-nr@|6>EIc9z0H1!EpvL5)h2z4 B%2)0y)oeDqLiP zy7~e7X^uQCq0Tv;I?@=VkGu0m&QSo6$S`5$h9j=1cH(wJY))%fl*5UlN*dEVx{U`L zJ_}*KB+j^8;lYMWJwdoE6V`l+?fUYS_qx6|>XsQ86NoH$z(+VFKryH6M{IscNc+yN z{o&%z2#j$AP*m4y|96(Ji%Wb#tdp_KMHxJjS#NL|n2AJjAC;G)1!~QlCsK_kG|xG| zY|%GER)@=jH-?7d%VuNq`Nys&E6B)_02spor0D|QNY|&aWMpP6G0 L74}ASo_)xn zW#XeLnO2HQLg|JO&Ra#8jWzD6=yy VUJKr}NpYUmG;)Sb&n2Ztyp2%jrC+uGA>AM@g#W}Dv*;n+~ zup`DGv`P=S2pba;;>G!~*M|L6IM;s?Jcuz$h#hU{<2>=(MSuEC$2YXv9=Ys5dV9si z^@Q8TF<&!eLh`irf{c_G!xRbhar^VNpLJ9}vaPIbIZ{XJE9Pf7T=FdIvV`ULn6_W2 z888Bcu*9s6HeA!pL)$h+WmKC=G%H{4 @+?UeL6v=S2wKr3ZT=}~!sqf!Lqj ZYXwA0SO>x}p?~k#V`zzCXx;__Nz#`95&{L2V7;P82 z^VUoMq@avhe6zvm<~a{Pm`7lVL^O8LXDHas^jjQ;d$a(TeYSm `(POeSOl9>$ut*+{u{paNvTZqZ58}nip4&3mf~=TsSo1j2-Sulq z`n{e8_@8`AFX7RJKz8=0>s%fBm5mC^FHZesNaZvIy0OY3mbmmYuV4B#rDe<~i*+W4 zhDq7VR@>=k34_1%88EEDH{AVq&ih0Ld tM4 &OU{uxU)y;-;)y6(^B7H7JQUWH9$uYYb8vIsT(kNceL6{744vrciW-#Kr9c=Ya zDvqbD#@qQ@6UQYFLI?e0*Hlz$v_Y$$ti}Iz>w7!R{%6%S@^PuO@)Q)|)GaZr T`^26D+(+vH-R$k=@W*G=!zI!Lo;etaQ z6Ez(=D7NLB?6td8#_BW6r{KH&egCCK>5;tuFCv!syuCcnJj0OAXuIpg3|%2=B?{rx z*Kh*d=G)Qowv^d>*VyI@%-7d!B-Grni~UN%+@}R&!z+2}c6Mi^Z^i8C)I413ZciMsldSCgGJDU41yqSBEbCHcC`H1Y%?44+Q|9bf3RJ^O86QTpeedsH@#D<#vC z;sIUD1@j0dkgdw|_XV60q^H8cuY;s9s)>%>E&Bx$L<~#@v<&r8R$y)WkYIOHrTaz? zng&X?;R{b-ZxCE!z*Z0$Fs3KZT lO>N>}26yR?6TWe|}+^CK=TowD3W<(J|j) zL%X}cVo%hHPaVM^>F4COn=OGJR3004`Aa4C9CGJ2rzA~+?=Y~fwW3Ta5tDB=_r>)| zsa~pHO!n#o94^gfq|;rKBQg9^EZVB%@U>fq30=00u^fYn{w~Aly&c(2AcfL->^-Wr zpLuf)`!Qvr`ZYD431^Q6xB$YsDrHeLNek7ht@>{njjWn3%W_Rd9#Au#YkuI2ydYZ8 z#@#wAf!+GeQ&- $;f!V^X?x!zP4J*%gaGvEZ z+D%)P{IRBUI-V0}eKfDZhJQGTI3Y>b*vnyd*6dhJ-)KRu*#tEyQ#8dNVv2Vm!QaK$ zjIxVg@eN&yh^PYwzIrHGwnsn`h5VbFq!^4o7k6o2IPGQ<+ZuYsS_31i%gnR?Zsl)z z884h-lg;Q6FG#wR4!P>inr77OObu)HURC5Cj?XI~Fsn%r3K@9o*)3; Iesa=M?PP~~7Q|tepY2LCfA8gX;wRU$3rD?2T3O|6b2u%DnXlLlaoI&$8H)CWzneWU602}YEN}>6oIr=V zM4~Dz5thdU`9Ra)oqPvE*6GZ=g#`kNq5&&00HTs;zPDPmWsTp4`C!=S#cHh0Yegm~ zd5oq4QJ&2AV^kG0VeV;@zR;?LMsltMl0ITx|W++I}WPLDu*o+uR&)5BDSH z`wGGWUhXYorY4>=^27c)@P#%l1STOY*WeKT`{((^QWP|eS$`y&e~23c1Tlcr(#p*o znXbQ`oMHU2x3@N5{8&+bZv;x3A&kbCvoMlS=CI1$q=TTP*Ajk<3H{rq^#8?Ym-}B9 zSF>Y@1S-g_pvs(QS2HQ9_W6sS9qS#JqRcbZMA6Fw2mop1PA;za|FHMYL6Sx5zF^t5 zZQHilUAAo-U9K)0U0tp&+qSFAwz_O1C(nCFZ|=G0OvL;#F%dT-W5v#l%(eHzr{9O= zIjjmZYo1lgAiO)2AT*8Bk8nq=DZI9bBb`5ZWJ~n6yP_=un|O(8G_oY*sVL6}M{7}? z-P4x9J$@3-GHQ;kWbL7 cu+GdzkDG4$+Pa9q8rk zVoDH(J71Wm|0A9~5utk?sPG$^wtMf{>fS=Khy>-=Cz<^uV}7_YU}<7MEJ!dFc-^W2 z{NYAGDac^}N7+FQl};1l1)BH4Bds3iF=tQ-h~{*}n7# phgBg=!T&$GpRz6N@; z&|Xn&4G>_{m(M$C&0jDg-5vz i$67Q!G!`|B6Geip=Yz)2e{Clj1jdsdt! z_Y*!^&=(ERNwa>Dg!QM>*2WmH^j~RF%Kjc8*HCJ_k|g0=gzNXO_J!=KFb$Wd@hwEO zK~cBm2nkUK_n(r=hG^2klV8>q?~so2H^4H2Ti7Q`+=}!uCS%W-dX7UGLwAy0qI<;y zwv(Y-J>kr|vVf@s;t+zamRrJ$eGY92gt1_`^N*69Ps?;k_XA!Y~3}C|kq;H#J*6 zIG17l`K|Q?pu|QHDImx! k7l$CgYljYgkkpie=? zk|gQeLM!eWG$SZ`vYZzQjfmJ4bB*$3D~(DPP(;?b)o>RUF&lnM)CcShE;Yu46f+UA zdp|ZaY{~h6cppA~T&wsJKq~E*bYLCR{+uHWr#$HPxlaQAu=H0NeQ98ec#@olTD|L5 z^8VR86I}jqu5wTNtJdDv0{Pn!t}X!}(2@TIo7?bVDO{9PCLD#%eiemChNJdyDjRQ) zrU-92w(a5})tk2OI*7&?KS(u)f?A`?d1QqTJ4|E$@mIwso48DP5*pSm*rdeebG#?# zkZL4n^_7o>B6B}V@%cB`H&c3|4i0qb;HB2PIPPwKt)QYJ$S9DPP +gjLwijOw z0hwMZu<{^e!mWRf6fnHpGnaI`H!NkX=|#aijyLBMJ@V6OqHU 34_S3v0aX{h4=ANti__5oP!m)@U-$q?BL0bhpUAfl$b0 zq79MtDXcO&DqB0N$}AW2Um31hdL@_@q|k$dM&}s zD}MWJo_@2*8Dp=i=X5A9J~-2?pokV!Y_+Q9iDOtbW4RcgYI=1CcTNr%C3EDuZrD*m zxnE#ZQ-LphSjY(3Nn;NOw`zY!jH7;5Q0usHZMAKrW(MO&EBZ5^C2##!uQaB=r}|}$ z_ska&IlLAP42&iAU~(y)x@k}qW~F#UnB^+H6Wgf3jYb4)|M{qf_)le&SRd2oL+_e^ z*FZWJM27WLqsDPTkW>4wvt7wR6&(yOhOw%=S$$&Z{N3T~?YG09-iCZ2m~L*HA=dVp zGy>;y_M5J6qI8%Uqj~Q~M@McLR34em>c^KXza&I Xs29bqoj}(KVNT8tqaTQyrQeT1Z|Ay`REx09y8WiR> zB3%i8JtIzRylGjm&gh9`c&Pc|^I_gs(A^nBE6$cV(A&KSc*_B}1MZ$cQ~7bpKJ)a; ztmpIbtr(i3{$OQaDQ@6;tOA2Kye4qjS$ZY*n1R*-iwKP0 ab*eWI7k8PNF2-q;w~I}i&mfsuf0esecPjDYD_e`j3{ z7y<0;TdBI+t{5VRm_ib_@_^g79*Tot32am}@hrqkZNAbDD$K9~_WeuUR!!I-eF39z z1-Zv}7T4Q93K&UB@jm%8zAQ{c-$|1Nnf24+#O6=Z&V+OfTgy-)gQtkMIq3)?%l2Vs z7+%O7aflMaf7i-^Q6SvmjOT4NUl097-l%nC&Bk94uz;xALE$Oos)Im|TBdJt@*Nha zG{t07b-mo}LIl%r0Sj(qOs+IN51rxd)7S~V) {Z zcln%;O%dK`oC;x-@esh<#&}o&;<4d$(O~GnZ5AH2+eoAIXZz+uezS=Q&b)M_tBoHT z4BumH)baKD6%RYCLBITFF1 ;9w32mt%jlGsIYG*@C6K9c@joNCsbhnNXZhqxKd~ z2`2|;LLENEI?79uUQf1S2TaIG4yfn7*&bHPtE 3xik z`-g+Lk+QQEQQy3E`#+Yy^1vl_+^_g -C-hb0(;ahuQ2$`qHqfli;Ec02+T9 z7 aUIm?+H`hzka!gZJ~VO{#wc7x_-`^PGgZ6EK^5mQtNnpEnov zJ+80=F^Wk25?S7`PaI`%Nb)`&Qhz+UVMHn$-W^33T8si~Bfa+=Z$N>)i#$Gg$H&GM zI{B42zvKHtgCZpN{_ar-BAtXA!BRdciz)o+SN<{W* b7oi|%8xsigPPyB)&KI?X-fqXF`DD?M}$82XF;8n-lSm>5=Qe@8+PJ`dEmA? zCI?%Zk$etWZ)ULASV#g*X4TV|B#hr%ch@wV)$CS;X+gow{0$W@%AyL50`!ml?>v!A z-V4J>NS~IoJgdvT7yb;e*wdwdMQ{X=48Y&M33YV=wPFACU&Mh|1$IRD@t`T|k8@w` zaj_wv)vJ;%YJ|Qde6oHXOj9bm0aeq$qotXKq)*a%clMEC)!yyJ*pti-=9u6XvJXOQ z7-Pxts|>+kw1i qa7K3BXsQlp-JMDaCe+JTI=lb0`eOa0uwPc;>N6#2f3(W!7 zOb63`K_cvaiEW-i4i1a}!sksNB2b#+)v*X3#qpla$YC(>w(lfPoE0P@?w5=#-BS}a z`?1vxOOg`~SM59GumT1$mt)JFXJsOt?d5G<>F`)*B>^va|KGmNKtX`@>_|4ggMeIf zK;(kbF+edu2_?$>RG=*CsU>wO8@NV;wRQkhw7?lAnX7SkB=Uo}7ihbTAOZ8b$D)JO zj5sjK5Zn1?(*TR^fVwxLBSF8Kcj*VfHF1q$-S&r2+xz^p@D*f6L$Uf zPT-4w^v?EPNA{=k2lor|WlwvLXixh`>5cHw=TF1%K+MmU58rp<*Z9kA0>^ @c*NIu#&6W1?JvN5XZuj zUoSa9gZm#F>P}m; 9tAj)+$kly;88~+TM(UeMZVP5ve?{IxQt2+DD8^-{ww4rjnSn|5K9~q=Rn@ zl?*sHZ53wW1a =hdkb z(E0Mr!=!mL(GWC&B0Q{jqk5pX|EIvwwq8QdrQ1S`yi;5g`w42%o$nHJBza>LMK9He z-7F?e<=-@s_~TTY{&{OiRmmxctn=;D8kyR&eG Yh(!>ian4kL@0Y-Gm(lb~^V;;4S z9TtW?g-~`n4e_*?iOIImhdUq J+VYig`RwE-?F|E)NgE4^nkdly$Uv1})H z`K?9l#Wi|E*k`=3g~y%F<`&|i=gb(cT@t!V2jb0c1Ow1ijE8~kYlx__aIg zCg_M7yS6qv anWx-M$~ z9-(QZpmj_6mi^Cth-jUxj+&AIoxf0kHcUvw7>0seZ+&n!h2GyA9dDvkhT7TgLcm+= zuaj*_XW40Ok>T9nCX{6Agn}z?*u+c-Q!=#92=D{dgHYBG3RyqL`qQ7XBv>2$e49_d z-^gQz;|bnWKzi;-*q~pi>HWp*s?wDhI(6jR{9%JK!v><0+z4%lZv1_%#HD}#@R*B= z(!%;@`ZsZR*lUO)eqxvY@WY_=m9d*99u*$%-~sE2V_t9;%3cyrP=L%kVTuub$Kzr6 zmGF}g7B R}YxYS8J zgYLlBE=Y;@y4* N?D<$Bo7?Bpdo{2z@EFmw!To4+n>~x8p3|R6Bv6O4=zS8`4117rZAPI0TnD2 zHg=QGdM#Yx_Vben=UW|BR(h~;D7!?LaVSZpV15BDbMayPt_R_uA6d%vw|H_$KO=4) z+9l!jvOiWmKjl6s+dRJ5 ~HQ_{V;c$>ILR6!{2{!R}Pw2ZS0>i;`a zI*{~o;du$xgKqnh8{$U;yu}@4L^DAk!4)mrMKJ1^7!HyLPX{Qs`{xY(jz5WWYIR$4 zRHE5E$4k`T*!rntt%oALS?%I55U9X^`&& zYm~}#30!5Xs^k%AUT*TSa!K!(*gCZhQm~L_Nov%U>ZiZi=Vg`fe#86goK^7mey)$h zNDNlO|1WKZy3YWuh}ICoyxDI9G8Q74ntWk4# * zehc4+gZ7=;?4EjR{d?$| ^=nr z;XnVP5SRI^HSeA^3Y_Dhw4`t*VyrkOe?>+k@tR@E_}0UIkYjm8n5~Qwj=`0Mw(+up z8DswPS8g)8sh`RhGlsuHvFFY>LG&J+V{-b6P5`e6V~gu5>Z)&Z_wG6vcVlU71H(#B z_~j|IKB8X2CZll8pL`(`hQqLDw^uz{_#>0+#xYE(nOdm572KX7q2HM3THj?SGZm2u zQGwVHQ}vA$-YY4o_cn4L91j04vqi9yR=qi6!Se34nA3hzFOX| s8TzB+Xq#l1%)kwE-Aw8e%srWFe zj_A!48n|R)5>GT2xx^--X)t~s&A(JJ{w9e(dZnT?(iR+diI1=iq|~eEVnC=d%@AWn zAHVILxHct-QxT)=AyW*QYWp_wxVn6~s?P(A6A{aK2^=Wxg#d{8y=)#!dm%N914|gb zVfO&aLMO9QC@x2SL`AtUNOtm%C|jhBo&;^eU-=Ov1~wI?k;fv?NGeU6zcLc=1m0Av zvqg8B$TAdUMaMxo?sZ#?&TJkVKi-C*3gsanF^SFmjMJ#v77Mq8o8X5i@<+EwL6;DM z0=&+70_s#5azo*JKAPJ~Q~c-PlW>Dep>*@*@Km7 m?Cj3H65)I2&u{Vx%je|pqexeNN>rv0< Q;-*Oe zCa;iu>LFYMB$aIvG?~X8GD{lli-p#kVJi=a0{?a$@ujIag)8aBg)mnhKnhuN@ivE& zaF!<( euK|jbEY5d2FT;Z-;OW?D-a}dz5%koi> z1hJ*etLBM62wBza>T%?jifyt4R&q+opk;YcnBmG`e_)nI`AsHZX6nj;AePbrBYB{O zM=b7^y;*+DXt=57tW+=dfp0zUeGPUfzKahGWW7b!%9e2m5MFcw@CBMXV {3^0o#i2KOapXIZvnXuLn37FY+jqXh*^GiN8i1=(9wo8(O zY{cUdJOJ(aft59Ik%@^D!AB@>5i3k@p5WRvSy6y{PE?q4mBII{zF#Wu~MdxoR* z68#7C!gDYEK8)Bl(fp1@e8U>|)?~<+LN9WbY+scx(%AXb?6I6V&0J@2=QJ;hBDVy} zts#|>yHkmx8O3)wdsTtFmVVJEhs$GAD&0{-ggFLq$!0k6V1@Mgt>@view?dRH>7W$ zybN_8uHFcicGEwK5= YcH_4Ir6Uol|80Sa7(~>fZK%oK(3#XD zZi9?~lkmY2w5;@Qa?twIW*Fc5&X7HBM6O%A^biptGJ;Fp#-BTHUeqSW>p@`PvbF76 zG}dc1$IAt*?li;*ldoh?=T_OEHf>^Nr>xNVl@O^e9e47p;dEw0<)mzcZ^JLN&}(bW z@N_(K=ot=j*vjY%Br!hnfd?CGozJRzE&` iA^rC&YFIkyS9bt1_H(@fM^8vLj+jEKy9SvE4i?2|@3!!$*F6)Z{b-Q4C@ zY)!@1h&3K~TKp7NR!EC%0jIfG7a1u@3YOy`Ri+(GZ3yZ1VQFSJ<>v*;IOQ<4Zj*SS z3Xq&%^7QHA+)jJ3Nm1T5r_?`Sq>R6XuXqr9!nbU~v16`xcD1{fsr1L&l=S!{YRv;> zb{l`D^o@OEV!1dFTz&vZ&FAkcc%9|wEoRW@kbVx*YUdA}soLqkp0f-P_tX&Xu- z$;QyC*QfG`e;2XQqoN+yetXM0V^tQ1sA#Ogv (N-vPi>s(V=<;R?x1~4#2%mA(xW|*zCvDJt)QoEcR_E z8S1!$4rS8uwIt%gN=vwqCS<>gjK=(-ol8f+;MfcT&jy?`9pM>&gcAJ4Jot-^08yw( zIW3(3QSPx=DXn=MDIGsI&e0|pf`j+Fft5EsV=#G>y^S;z7W^vNBsj?=_b^!_kj8C3 zKueQYIgW5;yzsS3H6q^#gY#qPWWuO689h9m!f3N8Fn)ZIv&(|=3x(-iKKRw){>x`A z$=~h?W%u>#s=vTpKEX?gP=>42ACL%{#LLq%P9oFYyC~(!=H^0D=@LrM$EPm!m)_Z! zKR+KrWQI+{$QR3VB5Xi@SX>Bj-z3ABAO4QL8~^w*N0OkHw2>QF7kshg(SJbB6l4z9 z@wA!uu6P4s9QQZ%^e=!NN6^|@q`^UPC-e7@O8eDP%Z5PlX#m56n>Q9sG*+?*L&Q5I zo6d5I%k`neFV_acA6;G!)hbjb4j_CF=?(<2f_T3STKG@54~}ltK1vz%W4`TgSMd!5 z0-0kL6m_2bd^f47&Li6z5Rv)^KGZ}boO8?iayBl&0?YQ^jz0+@3?hU)FPtg5*)(-Q zPNQ&B5R!e$UXfX{h=RC^d;D_xno+n=o`b=bb>Z# SEu?*+C4=m6TpRAu z3x?M4WaeI?al)~PAUlRR>i+jp*b)MfYt65}Zr#5bkid`9fY~s;4~GiQ>;BTYV}z4O zq74zV?i_~>b+rCnClKJ_1qG~ 0$nl@4}NO-B9S-0#)r+f{Ki$*Kxb_{}N`6bk0>eQEV*xqN#>4agvp> zvD{9w!_s~3_{ g-$N3kqsfIT X7V{CtX1qNVpdB7MRe(T5_)oBXkBf?)9chThRnfD zB9gO%n5nRnk|_E_=s(lQKT(VB&I*RIKD}COVsGnIet@^PsZhfpIR*Rk*{qP+wWE_< z1{HCfXQwjY*l8MFv|BlP;}UN94=3r!f(1XMtlobQq06y+bEEn_s-{d94SLFH1TD+u z7Z d?}HmuEEjd85qq-}_}&%3EwS;v&X_ zdH5Prq@0hfzo1V_ED6Ouo)D|P>oOmO^(z8G9RUGNE_9~@P60V051aANEM0$vXZ{yP z^Isa9)<9?}E@8{n1Y+Y~z_JvU2Pn}Q=E74G*%xgbq$Z0?04)(ttD+29K{crxRY7JZ zK*ps3E=K6_+`sq3uyLLM@(ibn@ySWi;la%-5eO>vIy<}wrw6h|%jW%bN@EgrAnaDY z&;YXk1ZBMT#kipar2ZAB`0xBXqp1{os+LvIf k3q(uodhrED;lK R%q3&SE?Lgxk9HdeCmtHX!;8MWPh=J6!Q^95_OllCzhz zM^lo9Lqfj=bsJf`oJd8VUNo_$38XJt8~%Z>{g<`-C(hJ)xM_c)!GhO$p|&q;yvGIt z6aSrnh`i#O@&dg1pTxsIw*J4kqN`d#THu3DU7cb492fe!vwOP&Wc|V-PxSu;0RLkm z|J9XFwE+HKjo!bruK&0q|Ei<^`R;$_@9$2Bf8Go%ARr%Wc)%kF|6jjAW5y616tunt zFJ-dw6i(azvFjv5EDZwod$(7!`q}1PB71!IoA`43Nn>R`ll@mi*%-6bpg)!QTm<*D zy|qr;Z%%m>A)fQXsSv6q5BS3_AW%@6$>r*Y&_B<>(P;dx`g}k$BRr=)(f1sm7aH@a z`WQ;@A4gOADd`J<#r-UPhr`~0X|G;JNfK-!7iI}Z6>&82-J}U~{UKq^V;0mH=Tc ZM2d>^jn;tU7kO7Pk `1qa8Umw@6zxburh0bxr7bj2kjx(_+!O`6HJF9NgB zN{W6#! zv#4Yj&7!eheRY-sIcu vxdpX-8 z1z-)*fKwZijsyYMlGrcwo76LUlHAhjKKojDHr-_V1CtH1HNNLYb@Bx)W$X;u!BK>G zIBSOXPjb2-`m$GhLJ%FjVXVTZjU~#}_1AYH_Z(;E!u6kkt}}VnG`ZaoV2!TxIy6vs z{m~NzVZp(jiN{`ZH?4k=O4pV^-;RFcx2)stpDOdF)d}Uy cxP$tt0J;_Y(1K~Vqs4hMiS3&BTDsXO8E8AhLWeBRaKxwGHhYi$K zPL@hfA|Wm6<+gIn=wC;*M}_0EFNV@bjxRxIx~HO@A7WykX3~aToeZ2jaK*&x$$npP zu HfACO0%3HqZIEMo_IoaVBZDk` iGG&`_jn(_ zU7_nPJ!6O=2zly<9bmrYvS-lVchHnEYFlJNo}&xXYTDp{Z|y+!Uf^3g%9A4`6L{G7 z8`iKI>Kc$k+1zanEGr?LL>#yp9_vMj@KINF_hHi)*)LVlZWuGy1342dM~$wE=Hs`m zT?Kke>-8v7R0@zc&1*eRX@VB*M!cxPBDuM={V=Prfrtr^?PHL- Gmu*1m226ddFpvtg!g8GGRG zbzK`C&KC$PB74tDn!>FonB5yN-J@GYagcnTT%jZyi82_VQfg+QDN6PoTI4Pr3o^Xn z+ho3Cpbn;{X*2=>;bB}&J{Fao@vF(eAztgbm A05RtnTM_1&<3jSSIN_`(sxEp( zn%u?ts$ErA>|B)42C-~Y1w~IKOfsIXssl=62aVYB99_=R?9#1A8zLPrdhbPYNMhIs z%)FlA%!WEDdX}0V=Vm>6St9%yQjV36XtY +SuNNmiyCu{N$-%4@2;=X3T@RLMkIR7PqW|qs>Q9)1nnt*qlaBi3Yq_n1!F$@6+ zyOY4t))ts#K3IVe2gykVWs|C2b-iuOQX6>F%T0v@c-K(#-j$hne~IlWyZ)*EVB*WX zhB`tGaVUow{{4dBw2vUYhi{v1?sbEish^3H6cs<+Qb8^jP-5wMG5E~OqjDGv`a9XD zci7R|d|SA>`S3;g%p=Nwd_}I*{>pSp{)6`s=N5QU3uK5M^Cxb0QB6n1R0Ro~pC2+Q z i;%tk( zG4C1DmR_?;!$oiM1My^eo?tk6@ehqF##dXjH)QUF{gF3czps<7)r&u3n1}P}?Xt~k zdTNHWZgNH2LyAUyZf6C9&=e|{;acs_rP7nIeJU*Gxs|_@& 29-j`ZI9gLp$9b zw;op-!0u{}xMr)^yz13Zfht+ Bo0{jk04<~ExiI7_PZ+bho? Uv8i7FPt?Z15e$ gRgKFKoetA_0v7_5<$+-jE7PBLk(Jq6o!&_EDy>NNVFT~-0; z$94 SqAiD;;q<1B4>ewbP?E4 ztV@s`R+F81VptY2Jpi%Q_S5Q616;}>U^R)p@aZKIS~cjv#MYW4 %%H z@9DD2cLm$UX4d3C1ckWgeT$8@*I@Tw&}(fc{j3Fof-tDh 9vMX)<0Zxv<2<#O(_O$7Qq03|1q; z)0}#C>Rpssu46YG)JLTdYD3v}nWK)0bX6FPr;9FMEmtrG2nZUH$?(T$mItM{{*dU) zo5`q8d~GtUOlb5pedo4gtVZ`FBTxq;!3}bx(FrtgH_bAFO*yWjH>o%Nl442F8)dW9 z1ae=lYjI7|!D_V5p<1G7*};x)I*BM!fjMu^LJO56xzHu3zBNIkGrXR&qm-X3A#EJ4 zS1_yvO`PrPL?x{Q!4T{*HO}_k6r;rG<2J17_dHKp@dLTg7dop#+)tkAa%$nqTJy{I zQOja*s`QItaz%>`IDWuHozqcgLW4tFw$%zn$Bdg(#gL-~8JHfyYF2<7L+tHkS}OTI zWsqld9frwmd4DhMWvml$tpke)f62^TIawj9b~TP=qs3~`L1(wiCUZgW)(~~U `w&miPTECnPt7p)jB?igbC|VmU83 zR^eSdpQ;Vobg%Ku)#UE<`(;yipq}^6F833E)Jd5{D^+G{FJnX?%p)3S7IE_XNV2&| z+#TpjOv}V__rv( xLWvg=d>_6MQ!}h2pYBVivNEJzefOssS zzp`ntZ@ZhbMTQ^o)&Bt_I8|u3+Mn8N{7CiODftsYiT}<@ENmoqKH|)pO42Gj5i~qj z<<+1!aVe23&0rGYHZxF^63jrsJIjyq&E Tb?N{Ly0|zkp-8Jz$HUIOLGukD56< zUoXGdig^#*`pRKIMFO=Q%EdB>CQj(LbA+kRp#3*;*j4-5AsQE8AeeSWII5v?U4UR@ zULe9Bk;H5MqA^QdZ?uvb&grW@C5#}5+U~YS<_|_V%XIbWeD{bDF%hF6jCN`_Up^K1 z0Xk|fn>^Y`hbvNY|KH+5?c2ceu*Tof%14SO^xm?wTliii(eiK=4G~VCqIXVo)ws18 zpLTLx$dXDQcW^e2%C7INiuT_PMw}n})F!;YMsX emgxshEt Vky zN!-lawWFb~Q>J&k73gk!2=o}D^BwVqn9f8l{ZtsH$K3c*LY@M9N_S1NG3B qn^WD}Ez6{QLFv%4l?OqF zIWtgeZQ-*dUCgY6%na@>JvM{i5BoPI)y`-+i5|!t`07 hx-F!>v3kSg^5Bs+;^s`Mr%e&b>8up z B0l8zLh<~>;|yj>T8rUg|Anu>LmoP zn2AODl>>-T=Y `ztP}0Xs#-4?A^FHru7cnxeh=VXb?DBU5d1grbR0=W@xEQ)yw9qns z+P;8sc8CPRfZ6fdNV1^fV>liZ4;d!^a%#nUscmz37-3>mnjGp
9aH>;A()PuTA}Wfu}gu*z&~pla~>yUHXlM| zl3iT_;phHgm}Z&3Pg_`&uMs}Q!V*$9NGT^*GE5I9usgJkVnQSuO6Q9y={ar(tqa;k zNI;3x?D3{Ug+Lk#WREPB@k- GOOr)NQdCdh_KeFsP1v17(a`#?vYgLPcOc0 zm$6fZe1$6J{eUIc{DR4Up#!n}@Cm|Vxuc67%5C|6qRfJ4#w$TB*C0EZgWlg|=FGdB zu&BnKdl3vXvhuy}JYS2Uv$xA%5FV z{WvllrC_THzu?bzRex3e3%d*1eB)YhAFrf%*MasQ+G^|OjyfiAN^*^mJd9JEYVN{o zDC&ZD9Ke8tt@H~_5R>AB8D zd* qkz{6Jpq&<<`e ze~xjt+-LRDur(Zb ^Uk);VFu@20u&yM-%g`1~X@2;1F7FB;XwR=H`V7Va0+jWykHDHSf_@X&&B8=MVh z;8*$B=OVg T)Euiwt#s(<1C~em-d{IQlYRPInV7oA8*%e>R7UAQprj?RLgB=M zeC`+_Vrtm@x!|3@K$CaKT;hrbr@}$c$E&Zfdd*)I<)b?nl{R&EA$|Es#(fja&X18) zcyA4az`T $jF&h5p(4VU&GpIb1$d*E~+>$;KwPe?4fdD4M^!AfMOZ73a$>bJ>(I zk5H;Od(!13h{OLQPSHqyq3?zXaLvB6V0K+39WTvesauCQZ>(?Bw#9oEDb*Mc9maAY zlu2D&a~qE-WPmhOrj~aozAP}yIc(9#5%xzxs>Utc3>z91nN|lq48ZK0y3oRfU?4sx zZN_DzLey`vBlPR?NumJyN8HaKH^&+`b3;0AbjEB4Jw_B7m G~?>xW{;OZq%&X3@3Vqy?wM%eF&9jKLtyL9W-7OJja5 z-`ZO;WXrJXB~^`OVBRF?g~F;)18v@=c8}ab!137L=u0N;tS?(qUVP(8F++qe^{ s@jX3E`S)yCp$-Ni!=o(2LCf9V`i56in|T3Bs|OWCR&z0pc7=WTq@Ra03-+a# z&kxAWuSqE$FA?rRjZ-D1Y80sHSBU1m%!_rYEASYXW+0dJl%yeeo^P?arw5>o*`~kz zuVY37L=|k(@teF5kKX^lyw7SUiu_?URE@w40w0N?>Ad+O1=hzhVmB< !B(+Q$MRKGXxxhi^GL+Q3DzLYd=tmlhGH=i zO`=-jz^)wwDkirP9PMOGmErBQiBNjLC=1I7)-P!oojv9p2*qSiC?VZ;>7B2PC@A4i zelyj@+9(cBN=gR807&g!SEnMUKrJ1&@^YF}*;(c|-U&3-g-9_8Ehep8?Ohm>knLnX z-z9Xz_rP|igTS$eP{M#`2TK`o7Jsyz@#je6ht%A&s3}}ER8+dYt_{lX8}Q?GjyIfi z{*<%j)N%N31mR!7lh?My73f&5dnCgT5&GSQq$nd)iZWP}ywYb W|=oa+@$9v8__Ga zyhMmPUwC}ncWyi S7@ZIzWQU%0&`eVEmuaMM_G;fVtJJ@P+<===}%BV(N+|k`m zsQDAPs~{i?)97Q3Bca-Tw0iLipwa|!c>~ctTZ6GND@aSW6rY1lr#}0LTBL2l(@a)R z>~m4z1gtAYPYy&;;;csOuy~K=(8Y1n`)OdD>?#R<)+>vBRl6%g}Av~1P` zy7;bWNjQ%2Dz$@HDS>;@u1`v^x&|6c)+0_>Q_b-SB~iv8Zfza}((h<5N%L$LC%Q{t zuEoU*iBQ2au3p?fGRluU8QS}fJRXSQcWvv*a+xz)nuJ+Jz2tEB(eA3Xu(@888P1sQ z7sGFi&9rPu?0IplVRYPc$!xt_I}U3EH`q;Gqa&!SU|PywqRn&ng>R=XfRz%(8E&|K zA~HSVd)+1VncMkf5FHHH*M<1uIYKid?J-EuS}%d^+6?vsg$A=TTc~(P&EaT~YVv8% z0IZSQm8cTs7ty5*$i}i)hPj=+-RoEVJu@l8!9kZ+Et^5-I=X{8vy(%HnA6&43& Hx&fHqh9Wc)y?&tXU9aq?sRm<)hB?L5* zNp&n@uvXLFmirTgeF=&FRd@N#8o5Fsi>I*?@d5`_0v62Km>t}pI+j_5LyPP0ZP4^c zq(S?J(I|syB9pwJlwSxf%J|~(2+6R-ElkMk uvBt>EA*Pye25s{YTAeL>DAtUvVkUJ3^*Lo;mHo#V9{UXi%DhjNS336aU2jY2d z2-zmAvQt4$`H35ko7XmYE!N1fwHkP*?SfIekK_T<)MqQk8jc3`ku+r`vkkCU4cLJn zOJEn_QOgx=lp%EHLgsVAeESaC`N6A2Hu0TkIT2toYhRem^79mzk$ 78=%uay7XUv%z`r6hUGD(lH7Ai` zhTuT0;P(BjsryFNX1AoCepNFON}4BxVen-Bqk?Bg%mXSbiGLN PuwTJ?p83hd@wY*o{Qi9+~H2UdodUH-B0<4~;wr z*hoC?o_IlIp_o__z%H+isp;%Dc0J4AR??5~)(s%GfD%7?Giko-(Dw^hDA~O_&Wii) z*$m`&L4P+RLg{2Y_nPFJk?s77lJPw{e0sHb?XWCR07_7sjgX-L%u~JPhJ6W1yYlOv z>= _P#Rmg+&&j*Hl zk)3@=TWeqKu;Xhm<1y)~6#%+wL^zHw<0sjfzZys!cc-bF+Kf@CJepZcFw$ams^p9q zpTtb;#2j?<{OH&Z3V-`{HIa9G50pw9;hPTVL3W+DSfPxZC3Z@U=6k@2#qG4VDfk1Z zpL+fGqlwW293t)Ap(xAk`td;*7xiU@xi!NT?6$c2BtPb%!J^e3{vN=;L5kWl1PtR_ zh?AXr(y-*br#l~tUvTtp+Tk6g1VtZ9D?W5=n^?>!1Ku~pl(s+PtJ;k-r`=mdNg|+z z2{pV$wrHHn@^Jy++NJUj#CW}nG>i-MK&qHaxJ>Lv$^NN;9J; zwLr z8p0^(g|X3U_W5$LL&nbAMw&o#pCF_P(#Qav(wvMke;lqMVgY6x$(^KOSo~+)VI%fm1G<1WX^YAL$E))^ cCd^Gn>t^m?q zF&Y@r&9l;AW>VVFGbbY!DS=Rsu`c6dmtU18B;2Mfruqhmg^}M#SY3)SS5+X)B(9tw zZOK#F&Tikv5*-#V#frU9v_&0lFoX8GxOdo+rA{`Cl2`QGK1Ct+M8(8z&=(IqnpPZ? zrb7f`2q=2;yBh7qaPX|u%Mkg%3j7Z+DFP_%uC8o#0 < cO=tDpxIT rF?a^ag =n|5VbFai^B IiGz`g&p+XDb_`3kRJLaa=*pN29A%9rk?x3-`_tIzL@!;;Ec@ zPf5l7Pz&aG5SF%FtGG^8Ls{qfJowAj8rj#gvrvb5!{qb875VtKLKfSAQlGGfl|q9x zW$!fRlFT?}_o!@A-Op7rdxUZnpU0i^&7sJvPHp~9M4}KTq}ejLnOG7*_+g&5QZt2S z3ra3dqWGUN&yIJS_kcO`)zj6L>TG%H&lVp>Bv`P00eMnK{{3wI;wB7^)PoRe9B{>2 z3lUD0cMeMX$&qRRYH4wL%3|F2%hv7R&(%pA2_u~N{@Ts3;sv-IOI}DJJ_PAXoJTe! zUtt nr6K?R${3JN`8#mf7cSyr~z^gK;7Ias?u*G@W9`8gr*JsmsuvN>%xg2 zY))1h4*Y>dj}|Q*h%6w*FqsG{?!6hZe`ClL)f1EmRcJqFH`Ly?q)eiQ{?myT14saK zO}5_)kSqOrJ4Bipp>B3epta*GpX<66FC37Eu=5(M;g@$|3m98CVjlCMfCl{K293_f z3VsvK1x^uX{*Ymae=^S<;{XUtrKP7n(Oop1p;7WFZ+NB1gt|mZ$oPGMt)ohZfAy$c zt*5jGq<5X7Rjvz>CGhXz_q5*!E;xsjgnU-xmpjp4ed zYKDtS`;%I5%Qk -f>;UVX&otIgs5d#9^HN+<~ms z#p9E=A~6Q)kgL&|5lhbG);mGJ>4kvK|0#;E9w(6Dx#WQn%cTBqb-P{od8StRmXCf! zwATdY$_)Y?Cm&jK!zHQA)r)a9L^=}GpSxI@cuxdRC7_La0t_JpAMO>g@Q!%J4J0NX zZdYjQfnv^|1NvclX||~2vxsZ2iJLAgA=B<-4~@V2OB7|C!%WD4sA?oUid&HsVZ|6( zLxI<-3=Rzf*G-!|VBqz{mZ4T3NjZ2p2PcBC+yS7&RRxEeneuC@y_$+k!FT;a6DkN~ zL>x5L ;d~M+_>`YOt>(tt3-J%ZOM40Hu7@KM~m~#ONcp$6Sq9O0n3l@P~%L z2Swl(!G`_tqo)lO{nCxJvRKoIT2dM-F=u2{w)dF3L|rRZsfVmO>wabB)we#cd2OTx zUg5%VK(Htb^pRss6a)t_2GeTyRhS)RTGlX-LVl1mA}W%JlUcz`m+_}t@GnBbI*qhG zsy)iKx-CuH?3&zYF?x8GeU(-Xhe%_Y2mAk&P&WinDi7aTii6{~73uG}-mE@y@r#bf%Dx9aM^H+$r#dWY5c zA=R-z_PEMBQkc87)%)DH?!g?C1OK1ny^SJEH#oovh|?aIwjN8XL>@Srmam8Bk>JzL zh$1RMrDUx;`L@*bS-J7YzL*}+&SUQI9>S9uJ|U#?OwSHd;Wjgo$-v}Uu$G0;$0WrF zVuaVRN8-v)OALaJ$`JyPyedh`6t#NE zu;{K~&irHCRjgI-m+%$p7xWvnEl~EX79|f28L2!lVsVgDqQnFsObH>X1J8#(2fb6E zCkNEUViKOc#+@f`c!CEO>67b3f-Y(_6dA)ivkYHsYtOW2fh1T7H=U~rb)+FjL4aGB zvDNdqNbzfmIxV=vInCvwdh&RX#W>y5b5ZBxn8`zowoMw9j?Q#UIY}XS^8|;>UtX*} zPp#LMuBDJXw5>=s>5CUf*1tRUoeD iSF<=E0-4|@pU+P%&B+wDpFwAz>+19 zd_+zL5yo51o!Smh)P{d)Wk$QFSduU-(251?tx+jRKj}`NYxoLrx7dPn1Rs-T62CX9 zNBt`@R%;SZOB4|;SX@RY6>T7Rov3B!<0D=5IokG BKXS;f;8K`UVj+->JP(vdun=NLOs7bAqFXj zHB>o^KN&+dAO&5KqZ$Au{Z2+E-JgfjYp(sr%N## 2=0A=i%fxE}thC$gudqw)qv`x*rw-wp@JHrjEshj sofV7{vp zKBj5YJBS5q`_GJd+y6;l(6l}X3kD0 }g@8hCZAoU_K)CboYotvEt)b zZ2IyrAiYAZ`V4tB)_Pg56`Zo3-Qcoi{l0cQ%tdOqQjt+imJY1IV9@-qQn_1&V@E_l zRW}|}{57S7=`~3#zAuiEkTGk}@|0$MfFD5zg%%D!1DY=t$nyIKl3Q~?ElkV*CJ9A! z+b`xmsMRP*GX-D8ns5qMrjtyJX7vr==4rc1tOnp)Ec`KZ*Bi3%B0et)Y4jxA&K;So zJ4B}X%pMS;0+9}L>x{<7+G2XDQ+)vmLE4GViQ{$d3Swp_AyvC%aULxX*la?Wv;NA0 zb^=YmYTDW9RHV+Wy|QIv7dK*cgmp8LR`7#~5d`9}$#_9d9)2rOp%1yR3-i^$GXifB ziU)5&G+m}s$qA+kbOZ2BHw7o?g%l?Q >`mNnz(l>8D<#By|lBp z`zG(J_TmhVlicCxqVd4r;FOLFV&yzO?O{vzwUICEs5@|R^l*yZ6r8PW)8Zkl?N`N2 zX!?Or9|7a7s!68&Z&&$t-G6Tzv%tg3-R2ZnNdW?dg4;`(4$h1Sh#;3EUbbxrsS`C1 zjxX<+BTC52Z$crZw%x{fxdL)$Nta}&Jfld>P3Jlg0AK7jOnzn}09ZAX@A;R99>>I8 zu^O~(rjY2orjV=RQA2yem ojve9cQ?n*UDWhx(09F)m<)o+l5_?&O0$hjQ5c#F2G z(hn>{Ld+ahn9`voa!(+Wt5+mv2HFJQhA!8fE5Pyy|KqRquE})SzK>=V^7eC0Bf>GcHhI9DY)2-$eOsRpYW$ z%;=FtZ4tm7lM-&wXDC#VUZ(jngbcM#?aY+{FTr%sJz}g&_`Y$7-Wbs|avHD7#I;t5 zOq8SH_yXl4rwy#(9K|1B=!0&P-3h<~m~n6ueUAmxm40WinmZ%ySiE`bI{$WwS6#@A z3PYj! Dg_3;t&Pkoh#b^vZwXXJeTPQ7bfu?YUvSg4m<1q%*vU2kD zuNj%zK!?3^m?|cw)74I|{%tlns|?)D$K|r?z?4mPf>(SQaqutUqS^itPki`yd%z=P z1J63nj ISxI28+HhWo_?oZfwdCl4i`z=}(e-cFGCqHaq!1wUl#|4;Y-y%-1sR=bk{ zK$0ni8lIS)^bHYw=pUFMp?(d0R74$T5KOKcU!$%d!NB1bu+E<_!lFW Xg zL62O^J6i6B^Sb<7r3hh$XzKz #dN)u BwDL2X6mG} z0TWVeq4yO2%DCusfH&EeHEvy>(nvm@G(Hwjh;sgz!B2i~*3wYSx?xf#!)=8Nmcu<9 z1uOX$p;cX!EUGQB5y&ncl>Xb@9*9-fXt-6Xb2ge~J**tIkvf|xtdu&2rsM!zqn%mE z>|@T=BD>aFXGaMHL-CCf0~ywZD ZgSE2UhBbmD*qK37!O@*jw=xlFObek&w!?_2>Q$Rk={-x 90m+Aw>uNQF{(^tE^Qu&vbhz=yt<{YG|D^OOknl^kH)t;aAHQcf zaD5?G?}g-=vSpe!t-8_#uIth##h@uWBO>5#qcm{ImEs^prY _w&rN4nA6DAzqCjtIF-++tlz z#=+zQj3~$N5`a)p?<3A(4(X%tsM4%+GObH7+#oWxVB9qSvjz81qW2Fu@|jhtX>Dpv zn+bSkYr@$T+v|0hywG(H^$Q&{Ef8B>PY)c#AI+^_!K&)q#CnOjd<%lBD%Gs}Z)fUU zOOeaAv{?NcpM=z*pzqmR$*Pg|41U#k*p7%s#MG G-K3~p3!-~KV74g%A$#eY>x@W BEXct~E8z@{omzj8Cu^1U3JtlYx=U#K--|;X6 zO965o;ic#S1*TwR!8w9*RFVt$&$XpaiUEW9gih1|{B>!=(L|83Z2}zER1~u1wX>1Y zB174HPoy%bzC;;*JUH0i7*R-?HnPiFvNP}kGhD0<)IhB#cyo<}(_aMEFYX|(Rr^mQ z+;FvrYT0^F3fM~U3s#6;AcWI$ zaX@|mUl0IOly0QlU|ZVK7g6Eyi<@&2t+F`kn-Qx!e~U?&l y%Uckj!4Mw=`G{2_2QBB!44W|nr_H;< z1zG=Z84^PMIt<>)ij17`rS$}^i>z6K3s8Lha_4}c82(%uMl^f*&qKq}3}Z*StEbgd z9YI}P`{mpkrp_Oh(6VV;Ma6ZcSr;Wh@KMnK`lD%I1|)z0aX{Z#HnaU2&31OH=P5zZ zHqJAMaY7HLQGLi3&Tt&&YwLc`WIl2JgguS^?{R(wn=b+XJM&3ND+7^YgUuc%dkSHV z^c|UQ+lws{1SCz^JqT+$Tuf;Ih{T>S%{aQ-GFXfntVcCST=^JL!`fJmq$&0|;Xr4X zpjr4{#f8+^n+KKH-|{WZC?tI>eCa)h(pahzL63+#LuQGeCqDVK2Z0%Wq~V1XBxfwf z5Zq>TUGT4cs7CE 4f;*7OyqR`&S zr0Ypsx8WOd0d53rNUCVm-5N^&M{HMbef18F&u6>O>p4$1^&4x>MnJirDH-fq?Z -s-m%b^AB~x5Gj@XV`B>X~4!)_(%|>^>4$SgSA*T;M>{~w>8RX z$?ToJzl||e(iZz{2)c2#hgXX+hzerr?#n{o(oD9n6Q%zEh(zZ7A+V{X&OJa~G^F)r z8}eLjCH9mxAfNCSK1-+3fRMx84MflFdd0D31=<=cPpFhN=ap^Lpm4&q2m^yHdXI5i zpHl h*=6_0GoRzby#Nl}@7`l$zknWLGAjeC+*Z1LbwZaM7Q2569GT zxF|lNe_^Z5CBqq(P+>}$hwK?A$neax79(=R{0OHpKvsMG${QGsnENc-O!TWZmJtTk z2+26%zk{XqfND6$o2&VWIV4;CRDp`Y2ndKqOUFs6dy=eFhB+#u#hW`jWeyjgT02CG z1T#XTo+f?Jy^BZNbF8#VKKkDPPOq(mhZI?Wy{rE{DWV~%B_s$GiU7?QrhZpDn5jDG zGCserZ5u>TepArB wb6~S>AQPEL3n)k!l~-rVR=8%@R#-leoG%QXt({XE^j}7`d7ClthY7 zpIw2X-|j4&m%X8cpkNm*k{}v!lR4yB(razDn^Am;nOVG1a+1(tI}o pu&zTKi7 zi6Gi^{TV@XR7q{tW3-?H!1A4Y@1mFckhaI#={tAPq{0;xa{(aphdo250wAF~-WmB@ z*`BqNOFP|65~8=X&cfY-rrpZ($X_s62M|L){1;vYbCxs>I?z3`vQ4Rmo?{A|IGTY; zE%o9t+kT$i9-gE8eL#YE`293-frew8v WixNb>liMZ*ak?me9?2T*+3;v1W(*Qf0im`5csB=#dKaswgtAJK+AqK-iyfMa( zB30hQ@zH5fof+GjNHmk3faQ^17x3IxXNTQk%0oa7Y%i0)`AqkJYr`-F!Oi~^jG3$1 z^F`B2AVL?asdpk-=_S@M+?`C{T%ar7&a+)hx_sR! Whiz?W;q=?E1CV#l@If{^qM6Ork>iFx z18q7rM>5hm>%n()nuPf9+TTYEen#Xu!7VH)@HM)Xv_g><#zb%E%xsEsXcfxnxG~*i zE#k986UY4N6N)`VZp`xipzu8TtaOw(CvBIN71=*)`twT@$mwVmoRfP!?4r1U*+5Zm zZt(U7oN_93`Zsl~aH7gg{g~`pLo0kTe1a4pZu5Ngh!o_4B<40n0$W9{ii;CKs>#`$ zB2lw+YE%Q5iil}(#N}%CTL?k4n-!HK&0mZZU^;Kc^_g|GHJ>xd$GG(=Wn~t_U75Io z+pWeZh!Yx(uo*nByp8nMz~!SzF0T^$D|RnHrME)DE7!xH;neVb?u+(Sj2rZS|06x{ zGYpCk`BD4g@1FvDcO$P5WGasNZ71vOM!?X|U}kw>(cqjIAMP@nWz~jS(4!JZBLX*W z!eu(@{HYc%2KihLO-xjp6juV8KwD;aaoni8kX-(yJ%u8@l$o78XA#H`Za7j4C8`nw zKN`)c(8#ed`HThUq+EA1lD>nhD$6}xD1i3NK4^>!vpy0=SAC2EU|hdS4EB1XUrmA= zj~3ACd5qx=BtWZ4XXdBi)pBb@op206Y!AlZfgBV&HJSv-@(c$uHhmAkod(GDSMeN@ zI&SJ~Zo_&m%tGKG%hMsLBW9w#9fX$2);{18qvm~{!{-yB4c{kJgCXL_OVLJ;INmS7 z%el`lAyJ9^{VVW$j=c<4>y)jBY^Zi!RQ|=`>0#EHoDzAggCmAb9^8 WMbvX>3TT!>kK$Qg1_0e*LR%N5Eocgx?vMIy{hv96PsZS&A$rNSjW15 zw%JmRi$vs&2t&PXPAr>SudpT_>6rRaZ8YQM-+=l1h;?i*bdoOi&AY(xe_rN`mH?CG zw2p<{kZydJ@I J5PBIZ;xLb{+#oJM z11l?>fGtuYxNR?Aeq@cqOx{C$#AQZZ4nx^4o_+F{aQOyV==T&6d_v0VmHuHPGEU#o z?D5(X^*7gZ92j>iZq5@fw;U8YwysPTWmE}!wvZne+LRFj1TWKi5e!LmMu0gR%sLZ3 z+nOL-Yd^LmxeZV4g1*2aIITzy!Xv8;v~ZgqM7dNZD$-%`(~P8#{D-)Na3d; maLSPCOZoneHkR#SGui@)2D4ZJJ@BQ(A&lri{UF1L+Mi4Q6nwaz3l#THjz@6NkZC zIDtaSr2^G0(v*R=WtcQiNyKMqprq4Sy+Fn}-}Svo&+~Pvmb!-cI3S8}y^Ym``aWL? zgO6x?t@qzeiUrp}6eJ>F@?CeMYUa_4g3v#=7dxiObu(k HLY$ctn?0r &v_~?I({@e}c^Eq$7W%FB xl&>1e(I|q9;IqTmy1mq* zlW~=v?6f=UC1zx)gz;Bif6B0ur9$zJuR}hv(+gv+)7?BW@gj2}iEfTFY_1L%D{O+y zgC+XUsy02sGyXSlTJs$bfRkMLNIcQb3RA7hJ~9{GYt~QCodu%B`Ud@{eV7*M_a3H0 z2L>+P`7)z&hK&*C;7E{QzVv#)T_`x2M3~QBPMp#yl|mt;2hW|MDAN7q)86q} =rVp2s7cAT=??*I`r) HrFWs}ZSkQZ( z)^q~M(4@|?AXkeY3~ei{+i=t_Xsf!m )PlZDaZQT?h88m4oFW6}ZvXI3$YV>WcLE54z!c`&RMv=N zWT@QQ(Cz5Fvs;9g2f=ei;~l)+l3A&Kzs~Da)F~k~R#cm657TRq4O6IO$4!)zDxj*& zMK)Qo?;1%GuPQ1g8=hxj@{&2!>D855uEe8{tHgQ$nU$G)cNTWI%*<}Y &hmp zZPD+rC1|*!e{u=oQw8V~#gqlb5Tine{bKSGe>Id=R4px5^<}w+){cGTY&n>pFtkM0 zF*xNDr-|(e$jcow+-CxFu6Y*5By{RD)XgnG>3@M-RuNE#+9s5!C$JNn(e}eH;tMqN zQ5y+d(eX=&-xkOOno|iw6navsz|2YEheTxlKsQDc#1iX=jNu9@!In^C{`7c_&D7iG zaB7?n)`ys6=^nM?Wm~xw*O;2A@!#%^yAhA&tdya9>#TQbxS;N Ux}CsE##e& @g#h~(SG)E}Kfp`a zQk$B5Z2^EX@`tL!N+L3h)3ZyC%lr6PX%wK 33E3+&+W}-w0C^mPI}h zKa7eI*6Wtu>T4U0a4?A8({9=r7T!i@0shN!;vyBsFwn>(2gE)5xU)L3*Gw@8z~$CL zBVvp&Ms7~7QNz-dRl~6(i83Cll!Ur*hp%dJy4yCDt1PrO&s^BzQVpgk65idVHfE(z z7FTBc_whx7VV#gWqj(?F1Z!K#f}?|Z%D_K@byBF;(eBUj1ED@>;}Gz6`cEZFs_s;A z$RvwYVz!=O?iRv@FnXIchECcOCVXHAG!F09hfdvBimoHuF}cqiDFYs!ZQrRtFXU@g zV-T)|3jS1OnwCk@`ptW;KykX{BT>M+4to9ia$>mos3JbH(p--@y3|>$OQ_8J3*`Fk zB%D0JdRBw9q=T?WCov_foNgffaQtt=u9j~n?(<2h6v+*YngQNz0*#V3eW3c*w|v@e zwqLB70r@s1v2=*AorOonxuo1+(v3vNU-%1a7b~Od!MlFtqB46VBy!P}liFefd_^Lm zCt?!<@fjab!cWC!B^cQHNsa}jCYETsgPwf4BS>>57Od2&P|dP7 )+axThumt%Y7V*r%rOGbqzKzjiA7Inl9!W$ebxlo#!UE=jw4%s7&&e2~ zj)Ryph l-1?Cg=qq%2(9=#VHUU`+M`!~= zzP1A-XM4G!{X53R_{A-M46YH_NYHBCa#8w w7kA{!uOeZQ&vaSZa<5cWdVDR4Y_cU*y_TJ0V?hM_{UF zRjFRVBBm4*r54I=07GeJ^{`cuUnC3TBUt7wmhi5FIm-*Sci{^qTa? `OF&bG)jdt_2L-oBRiyi3e1;d*)^LIFBZ`=gJtnTO(dV+5wy zKN$^K!3<%P$qWy ;k_Fu=ZXKSs(gI=#X-${>J#0^D`tfFjjjKbd% zHlrYg?x)OsU qF 8>LkHD@&q(tZpA=RxrF%80`P;V=j{LIU8*c`&fBb!BI#rDXlAUHefme ztmNax1aiS1KERGOZ|ce}9W#HXrg(2OD1M2$Q&tI1w?v97KUFwuE#X}z9|9VwZj}Kj z@R_$d6vtp{JyQJJr+5kRZv~|Q4**9F7K&g>a*G=T! 4bwv@a{rf~q{7Pp0{13q=_1}Biclr1B z@3OH9^zvSLW !_%!g(l z1&?}%QP@A$wEuJoVH6=F!rP>f0HEA>ZK)T0R%x6$%9|${L)V5!gJ=iNN@s-}@u3`~ zq&@dYqm)mR81u;!4(ss4NbGSR!~j<>J!%+Wbva6h91x!lMP9Hdxij9T+3HwGoP-L0 zkhbV~>RufA VFq&x H~W%=2C8VEGctn>SX7Jl}; z3{10QIVdfa?SqmOMEwrX?46pb&