From e19aff248db712dabbba7388d0ab1ecce22dd0ca Mon Sep 17 00:00:00 2001 From: julien Date: Mon, 19 Jan 2026 08:34:20 +0100 Subject: [PATCH] Added gitea-mirror --- Divers/gitea-mirror/.dockerignore | 64 + Divers/gitea-mirror/.env.example | 186 + Divers/gitea-mirror/.envrc | 1 + Divers/gitea-mirror/.gitignore | 39 + Divers/gitea-mirror/AGENTS.md | 46 + Divers/gitea-mirror/CHANGELOG.md | 474 ++ Divers/gitea-mirror/CLAUDE.md | 317 ++ Divers/gitea-mirror/CONTRIBUTING.md | 182 + Divers/gitea-mirror/DISTRIBUTION_SUMMARY.md | 169 + Divers/gitea-mirror/Dockerfile | 60 + Divers/gitea-mirror/LICENSE | 619 +++ Divers/gitea-mirror/NIX.md | 189 + Divers/gitea-mirror/README.md | 454 ++ Divers/gitea-mirror/astro.config.mjs | 22 + Divers/gitea-mirror/bun.lock | 2086 ++++++++ Divers/gitea-mirror/bunfig.toml | 6 + Divers/gitea-mirror/certs/README.md | 236 + Divers/gitea-mirror/components.json | 21 + Divers/gitea-mirror/data/README.md | 32 + Divers/gitea-mirror/docker-compose.alt.yml | 61 + Divers/gitea-mirror/docker-compose.dev.yml | 114 + Divers/gitea-mirror/docker-compose.yml | 85 + Divers/gitea-mirror/docker-entrypoint.sh | 227 + .../gitea-mirror/docs/DEVELOPMENT_WORKFLOW.md | 354 ++ .../docs/ENVIRONMENT_VARIABLES.md | 416 ++ Divers/gitea-mirror/docs/GRACEFUL_SHUTDOWN.md | 249 + Divers/gitea-mirror/docs/NIX_DEPLOYMENT.md | 486 ++ Divers/gitea-mirror/docs/NIX_DISTRIBUTION.md | 322 ++ Divers/gitea-mirror/docs/README.md | 39 + .../docs/RECOVERY_IMPROVEMENTS.md | 170 + Divers/gitea-mirror/docs/SSO-OIDC-SETUP.md | 226 + Divers/gitea-mirror/docs/SSO_TESTING.md | 193 + Divers/gitea-mirror/drizzle.config.ts | 16 + Divers/gitea-mirror/drizzle/0000_init.sql | 180 + .../drizzle/0001_polite_exodus.sql | 64 + .../drizzle/0002_bored_captain_cross.sql | 10 + .../drizzle/0003_open_spacker_dave.sql | 3 + .../drizzle/0004_grey_butterfly.sql | 18 + .../drizzle/0005_polite_preak.sql | 11 + .../drizzle/0006_military_la_nuit.sql | 4 + .../drizzle/0007_whole_hellion.sql | 18 + .../drizzle/0008_serious_thena.sql | 1 + .../drizzle/meta/0000_snapshot.json | 1290 +++++ .../drizzle/meta/0001_snapshot.json | 1722 ++++++ .../drizzle/meta/0002_snapshot.json | 1784 ++++++ .../drizzle/meta/0003_snapshot.json | 1805 +++++++ .../drizzle/meta/0004_snapshot.json | 1933 +++++++ .../drizzle/meta/0005_snapshot.json | 1941 +++++++ .../drizzle/meta/0006_snapshot.json | 1969 +++++++ .../drizzle/meta/0007_snapshot.json | 1999 +++++++ .../drizzle/meta/0008_snapshot.json | 2006 +++++++ .../gitea-mirror/drizzle/meta/_journal.json | 69 + Divers/gitea-mirror/env.d.ts | 9 + Divers/gitea-mirror/flake.lock | 61 + Divers/gitea-mirror/flake.nix | 395 ++ .../gitea-mirror/helm/gitea-mirror/.yamllint | 21 + .../gitea-mirror/helm/gitea-mirror/Chart.yaml | 12 + .../gitea-mirror/helm/gitea-mirror/README.md | 307 ++ .../helm/gitea-mirror/templates/_helpers.tpl | 59 + .../gitea-mirror/templates/configmap.yaml | 38 + .../gitea-mirror/templates/deployment.yaml | 143 + .../gitea-mirror/templates/httproute.yaml | 77 + .../helm/gitea-mirror/templates/ingress.yaml | 40 + .../helm/gitea-mirror/templates/pvc.yaml | 26 + .../helm/gitea-mirror/templates/secret.yaml | 14 + .../helm/gitea-mirror/templates/service.yaml | 34 + .../templates/serviceaccount.yaml | 17 + .../helm/gitea-mirror/values.yaml | 151 + Divers/gitea-mirror/package.json | 115 + .../gitea-mirror/public/apple-touch-icon.png | Bin 0 -> 31669 bytes Divers/gitea-mirror/public/favicon.ico | Bin 0 -> 15086 bytes Divers/gitea-mirror/public/favicon.svg | 3 + Divers/gitea-mirror/public/logo.png | Bin 0 -> 24463 bytes Divers/gitea-mirror/scripts/README-dev.md | 56 + Divers/gitea-mirror/scripts/README-docker.md | 78 + Divers/gitea-mirror/scripts/README-lxc.md | 137 + Divers/gitea-mirror/scripts/README.md | 140 + Divers/gitea-mirror/scripts/build-docker.sh | 104 + .../scripts/cleanup-duplicate-repos.ts | 129 + .../scripts/docker-diagnostics.sh | 125 + .../scripts/fix-interrupted-jobs.ts | 69 + .../scripts/generate-better-auth-schema.ts | 110 + Divers/gitea-mirror/scripts/gitea-app.ini | 68 + .../scripts/gitea-create-admin.sh | 14 + Divers/gitea-mirror/scripts/gitea-dev-init.sh | 32 + .../scripts/gitea-mirror-lxc-local.sh | 86 + .../gitea-mirror/scripts/investigate-repo.ts | 178 + Divers/gitea-mirror/scripts/manage-db.ts | 239 + .../scripts/remove-duplicate-events.ts | 44 + .../scripts/repair-mirrored-repos.ts | 279 + Divers/gitea-mirror/scripts/run-migration.ts | 31 + .../scripts/setup-authentik-test.sh | 184 + .../scripts/startup-env-config.ts | 52 + .../gitea-mirror/scripts/startup-recovery.ts | 113 + .../scripts/test-graceful-shutdown.ts | 237 + Divers/gitea-mirror/scripts/test-recovery.ts | 183 + .../gitea-mirror/src/components/NotFound.tsx | 80 + .../src/components/activity/ActivityList.tsx | 352 ++ .../src/components/activity/ActivityLog.tsx | 752 +++ .../activity/ActivityNameCombobox.tsx | 91 + .../src/components/auth/LoginForm.tsx | 297 + .../src/components/auth/LoginPage.tsx | 10 + .../src/components/auth/SignupForm.tsx | 156 + .../src/components/auth/SignupPage.tsx | 10 + .../components/config/AdvancedOptionsForm.tsx | 90 + .../components/config/AutomationSettings.tsx | 446 ++ .../src/components/config/ConfigTabs.tsx | 703 +++ .../components/config/GitHubConfigForm.tsx | 220 + .../config/GitHubMirrorSettings.tsx | 641 +++ .../src/components/config/GiteaConfigForm.tsx | 275 + .../config/OrganizationConfiguration.tsx | 175 + .../config/OrganizationStrategy.tsx | 437 ++ .../src/components/config/SSOSettings.tsx | 727 +++ .../components/config/ScheduleConfigForm.tsx | 157 + .../src/components/dashboard/Dashboard.tsx | 342 ++ .../components/dashboard/RecentActivity.tsx | 63 + .../components/dashboard/RepositoryList.tsx | 156 + .../src/components/dashboard/StatusCard.tsx | 33 + .../src/components/layout/Header.tsx | 173 + .../src/components/layout/MainLayout.tsx | 166 + .../src/components/layout/Providers.tsx | 16 + .../src/components/layout/Sidebar.tsx | 207 + .../src/components/layout/SponsorCard.tsx | 72 + .../src/components/layout/VersionInfo.tsx | 49 + .../src/components/oauth/ConsentPage.tsx | 307 ++ .../organizations/AddOrganizationDialog.tsx | 160 + .../organizations/MirrorDestinationEditor.tsx | 193 + .../components/organizations/Organization.tsx | 907 ++++ .../organizations/OrganizationsList.tsx | 685 +++ .../repositories/AddRepositoryDialog.tsx | 151 + .../repositories/InlineDestinationEditor.tsx | 187 + .../components/repositories/Repository.tsx | 1399 +++++ .../repositories/RepositoryComboboxes.tsx | 141 + .../repositories/RepositoryTable.tsx | 910 ++++ .../components/sponsors/GitHubSponsors.tsx | 105 + .../src/components/theme/ModeToggle.tsx | 52 + .../src/components/theme/ThemeScript.astro | 21 + .../src/components/ui/accordion.tsx | 64 + .../gitea-mirror/src/components/ui/alert.tsx | 70 + .../gitea-mirror/src/components/ui/avatar.tsx | 48 + .../gitea-mirror/src/components/ui/badge.tsx | 46 + .../gitea-mirror/src/components/ui/button.tsx | 59 + .../gitea-mirror/src/components/ui/card.tsx | 79 + .../src/components/ui/checkbox.tsx | 28 + .../src/components/ui/collapsible.tsx | 31 + .../src/components/ui/command.tsx | 175 + .../gitea-mirror/src/components/ui/dialog.tsx | 135 + .../gitea-mirror/src/components/ui/drawer.tsx | 133 + .../src/components/ui/dropdown-menu.tsx | 255 + .../src/components/ui/hover-card.tsx | 42 + .../gitea-mirror/src/components/ui/input.tsx | 22 + .../gitea-mirror/src/components/ui/label.tsx | 26 + .../src/components/ui/multi-select.tsx | 137 + .../src/components/ui/popover.tsx | 46 + .../src/components/ui/progress.tsx | 30 + .../src/components/ui/radio-group.tsx | 43 + .../gitea-mirror/src/components/ui/radio.tsx | 44 + .../src/components/ui/scroll-area.tsx | 56 + .../gitea-mirror/src/components/ui/select.tsx | 183 + .../src/components/ui/separator.tsx | 26 + .../src/components/ui/skeleton.tsx | 15 + .../gitea-mirror/src/components/ui/sonner.tsx | 24 + .../gitea-mirror/src/components/ui/switch.tsx | 29 + .../gitea-mirror/src/components/ui/tabs.tsx | 53 + .../src/components/ui/textarea.tsx | 18 + .../src/components/ui/tooltip.tsx | 59 + Divers/gitea-mirror/src/content/config.ts | 4 + Divers/gitea-mirror/src/data/Sidebar.ts | 16 + .../gitea-mirror/src/hooks/useAuth-legacy.ts | 147 + Divers/gitea-mirror/src/hooks/useAuth.ts | 143 + .../gitea-mirror/src/hooks/useAuthMethods.ts | 65 + .../gitea-mirror/src/hooks/useConfigStatus.ts | 154 + .../gitea-mirror/src/hooks/useFilterParams.ts | 59 + .../gitea-mirror/src/hooks/useGiteaConfig.ts | 73 + .../gitea-mirror/src/hooks/useLiveRefresh.ts | 102 + .../src/hooks/usePageVisibility.ts | 28 + Divers/gitea-mirror/src/hooks/useSEE.ts | 111 + Divers/gitea-mirror/src/hooks/useSyncRepo.ts | 100 + Divers/gitea-mirror/src/layouts/main.astro | 21 + Divers/gitea-mirror/src/lib/api.ts | 171 + Divers/gitea-mirror/src/lib/auth-client.ts | 64 + Divers/gitea-mirror/src/lib/auth-header.ts | 139 + .../src/lib/auth-multi-url.test.ts | 190 + Divers/gitea-mirror/src/lib/auth.ts | 176 + .../gitea-mirror/src/lib/cleanup-service.ts | 258 + Divers/gitea-mirror/src/lib/config.ts | 28 + Divers/gitea-mirror/src/lib/db/adapter.ts | 102 + Divers/gitea-mirror/src/lib/db/index.test.ts | 42 + Divers/gitea-mirror/src/lib/db/index.ts | 87 + Divers/gitea-mirror/src/lib/db/schema.ts | 699 +++ .../gitea-mirror/src/lib/deployment-mode.ts | 22 + .../gitea-mirror/src/lib/env-config-loader.ts | 378 ++ Divers/gitea-mirror/src/lib/events.ts | 258 + .../gitea-mirror/src/lib/events/realtime.ts | 256 + .../src/lib/gitea-auth-validator.ts | 202 + .../src/lib/gitea-enhanced.test.ts | 755 +++ Divers/gitea-mirror/src/lib/gitea-enhanced.ts | 785 +++ Divers/gitea-mirror/src/lib/gitea-lfs.test.ts | 110 + .../src/lib/gitea-org-creation.test.ts | 272 + Divers/gitea-mirror/src/lib/gitea-org-fix.ts | 271 + .../src/lib/gitea-starred-repos.test.ts | 229 + Divers/gitea-mirror/src/lib/gitea.test.ts | 495 ++ Divers/gitea-mirror/src/lib/gitea.ts | 2899 ++++++++++ Divers/gitea-mirror/src/lib/github.ts | 454 ++ Divers/gitea-mirror/src/lib/helpers.ts | 311 ++ Divers/gitea-mirror/src/lib/http-client.ts | 245 + Divers/gitea-mirror/src/lib/metadata-state.ts | 75 + .../src/lib/mirror-sync-errors.test.ts | 382 ++ .../src/lib/mirror-sync-fix.test.ts | 392 ++ .../gitea-mirror/src/lib/modules/registry.ts | 184 + .../gitea-mirror/src/lib/modules/types.d.ts | 86 + .../src/lib/modules/types.d.ts.map | 1 + Divers/gitea-mirror/src/lib/modules/types.js | 5 + Divers/gitea-mirror/src/lib/modules/types.ts | 110 + .../gitea-mirror/src/lib/polyfills/buffer.ts | 5 + .../src/lib/rate-limit-manager.ts | 422 ++ Divers/gitea-mirror/src/lib/recovery.ts | 509 ++ .../gitea-mirror/src/lib/repo-utils.test.ts | 75 + Divers/gitea-mirror/src/lib/repo-utils.ts | 71 + .../src/lib/repository-cleanup-service.ts | 443 ++ .../src/lib/scheduler-service.test.ts | 82 + .../gitea-mirror/src/lib/scheduler-service.ts | 736 +++ .../gitea-mirror/src/lib/shutdown-manager.ts | 240 + .../gitea-mirror/src/lib/signal-handlers.ts | 141 + .../src/lib/sso/oidc-config.test.ts | 75 + .../gitea-mirror/src/lib/sso/oidc-config.ts | 205 + .../src/lib/starred-repos-handler.ts | 290 + Divers/gitea-mirror/src/lib/utils.test.ts | 172 + Divers/gitea-mirror/src/lib/utils.ts | 340 ++ .../src/lib/utils/auth-helpers.ts | 58 + .../src/lib/utils/concurrency.test.ts | 167 + .../gitea-mirror/src/lib/utils/concurrency.ts | 365 ++ .../src/lib/utils/config-defaults.ts | 135 + .../src/lib/utils/config-encryption.ts | 52 + .../src/lib/utils/config-mapper.ts | 282 + .../src/lib/utils/duration-parser.test.ts | 94 + .../src/lib/utils/duration-parser.ts | 251 + .../gitea-mirror/src/lib/utils/encryption.ts | 169 + .../src/lib/utils/mirror-strategies.ts | 93 + .../src/lib/utils/oauth-validation.test.ts | 85 + .../src/lib/utils/oauth-validation.ts | 59 + Divers/gitea-mirror/src/middleware.ts | 221 + Divers/gitea-mirror/src/pages/404.astro | 37 + Divers/gitea-mirror/src/pages/activity.astro | 65 + .../src/pages/api/activities/cleanup.ts | 93 + .../src/pages/api/activities/index.ts | 61 + .../src/pages/api/auth/[...all].ts | 37 + .../src/pages/api/auth/check-users.ts | 30 + .../gitea-mirror/src/pages/api/auth/debug.ts | 79 + .../src/pages/api/auth/header-status.ts | 16 + .../src/pages/api/auth/oauth2/register.ts | 139 + .../src/pages/api/auth/sso/register.ts | 246 + .../src/pages/api/auth/sso/sp-metadata.ts | 64 + .../src/pages/api/cleanup/auto.ts | 59 + .../src/pages/api/cleanup/trigger.ts | 130 + .../src/pages/api/config/index.ts | 297 + .../src/pages/api/dashboard/index.ts | 90 + .../src/pages/api/events/index.ts | 69 + .../pages/api/gitea/test-connection.test.ts | 186 + .../src/pages/api/gitea/test-connection.ts | 121 + .../src/pages/api/github/organizations.ts | 127 + .../src/pages/api/github/repositories.ts | 80 + .../pages/api/github/test-connection.test.ts | 135 + .../src/pages/api/github/test-connection.ts | 89 + Divers/gitea-mirror/src/pages/api/health.ts | 232 + .../src/pages/api/job/mirror-org.test.ts | 109 + .../src/pages/api/job/mirror-org.ts | 158 + .../src/pages/api/job/mirror-repo.test.ts | 185 + .../src/pages/api/job/mirror-repo.ts | 225 + .../src/pages/api/job/retry-repo.ts | 221 + .../src/pages/api/job/schedule-sync-repo.ts | 158 + .../src/pages/api/job/sync-repo.ts | 146 + .../src/pages/api/organizations/[id].ts | 120 + .../pages/api/organizations/[id]/status.ts | 81 + .../src/pages/api/rate-limit/index.ts | 104 + .../src/pages/api/repositories/[id].ts | 114 + .../src/pages/api/repositories/[id]/status.ts | 82 + .../gitea-mirror/src/pages/api/sse/index.ts | 102 + .../src/pages/api/sso/applications.ts | 176 + .../src/pages/api/sso/discover.ts | 115 + .../src/pages/api/sso/providers.ts | 329 ++ .../src/pages/api/sso/providers/public.ts | 22 + .../gitea-mirror/src/pages/api/sync/index.ts | 196 + .../src/pages/api/sync/organization.ts | 230 + .../src/pages/api/sync/repository.ts | 185 + .../gitea-mirror/src/pages/api/test-event.ts | 50 + .../gitea-mirror/src/pages/auth-error.astro | 47 + Divers/gitea-mirror/src/pages/config.astro | 24 + .../src/pages/docs/advanced.astro | 467 ++ .../src/pages/docs/architecture.astro | 339 ++ .../src/pages/docs/authentication.astro | 535 ++ .../src/pages/docs/ca-certificates.astro | 475 ++ .../src/pages/docs/configuration.astro | 512 ++ .../gitea-mirror/src/pages/docs/index.astro | 88 + .../src/pages/docs/quickstart.astro | 437 ++ Divers/gitea-mirror/src/pages/index.astro | 71 + Divers/gitea-mirror/src/pages/login.astro | 36 + .../src/pages/oauth/consent.astro | 28 + .../src/pages/organizations.astro | 22 + .../gitea-mirror/src/pages/repositories.astro | 21 + Divers/gitea-mirror/src/pages/signup.astro | 40 + Divers/gitea-mirror/src/styles/docs.css | 63 + Divers/gitea-mirror/src/styles/global.css | 158 + Divers/gitea-mirror/src/tests/mock-fetch.ts | 56 + Divers/gitea-mirror/src/tests/setup.bun.ts | 112 + .../gitea-mirror/src/tests/test-gitea-auth.ts | 161 + .../src/tests/test-metadata-mirroring.ts | 173 + Divers/gitea-mirror/src/types/Repository.ts | 92 + Divers/gitea-mirror/src/types/Sidebar.ts | 14 + Divers/gitea-mirror/src/types/activities.ts | 10 + Divers/gitea-mirror/src/types/config.ts | 107 + Divers/gitea-mirror/src/types/dashboard.ts | 25 + Divers/gitea-mirror/src/types/filter.ts | 12 + Divers/gitea-mirror/src/types/mirror.ts | 30 + .../gitea-mirror/src/types/organizations.ts | 56 + .../src/types/repository-status.test.ts | 10 + Divers/gitea-mirror/src/types/retry.ts | 13 + Divers/gitea-mirror/src/types/sync.ts | 24 + Divers/gitea-mirror/src/types/user.ts | 8 + Divers/gitea-mirror/tsconfig.json | 21 + Divers/gitea-mirror/www/.gitignore | 24 + Divers/gitea-mirror/www/.npmrc | 2 + Divers/gitea-mirror/www/CLAUDE.md | 92 + Divers/gitea-mirror/www/README.md | 30 + Divers/gitea-mirror/www/astro.config.mjs | 14 + Divers/gitea-mirror/www/bun.lock | 1037 ++++ Divers/gitea-mirror/www/components.json | 21 + Divers/gitea-mirror/www/docs/SEO_KEYWORDS.md | 628 +++ Divers/gitea-mirror/www/package.json | 36 + Divers/gitea-mirror/www/pnpm-lock.yaml | 4758 +++++++++++++++++ .../www/public/apple-touch-icon.png | Bin 0 -> 31669 bytes .../www/public/assets/activity.png | Bin 0 -> 854469 bytes .../www/public/assets/activity_mobile.png | Bin 0 -> 283092 bytes .../www/public/assets/configuration-2.png | Bin 0 -> 1009906 bytes .../www/public/assets/configuration.png | Bin 0 -> 926827 bytes .../public/assets/configuration_mobile.png | Bin 0 -> 276769 bytes .../www/public/assets/dashboard.png | Bin 0 -> 930080 bytes .../www/public/assets/dashboard_mobile.png | Bin 0 -> 246800 bytes .../www/public/assets/hero_logo.webp | Bin 0 -> 130846 bytes .../www/public/assets/logo-new.png | Bin 0 -> 44606 bytes .../gitea-mirror/www/public/assets/logo.png | Bin 0 -> 24463 bytes .../www/public/assets/organisation.png | Bin 0 -> 845024 bytes .../www/public/assets/organisation_mobile.png | Bin 0 -> 252109 bytes .../www/public/assets/repositories.png | Bin 0 -> 974772 bytes .../www/public/assets/repositories_mobile.png | Bin 0 -> 242365 bytes Divers/gitea-mirror/www/public/favicon.ico | Bin 0 -> 15086 bytes Divers/gitea-mirror/www/public/favicon.svg | 3 + Divers/gitea-mirror/www/public/og-image.png | Bin 0 -> 337645 bytes Divers/gitea-mirror/www/public/robots.txt | 8 + Divers/gitea-mirror/www/public/sitemap.xml | 9 + .../www/src/components/Button.astro | 19 + .../gitea-mirror/www/src/components/CTA.tsx | 60 + .../gitea-mirror/www/src/components/FAQ.astro | 138 + .../www/src/components/Features.astro | 90 + .../www/src/components/Footer.astro | 83 + .../www/src/components/GitHubButton.tsx | 60 + .../www/src/components/GitHubStats.tsx | 59 + .../www/src/components/Header.tsx | 72 + .../gitea-mirror/www/src/components/Hero.tsx | 98 + .../www/src/components/Installation.tsx | 169 + .../www/src/components/PromoBanner.tsx | 51 + .../www/src/components/Screenshots.astro | 245 + .../www/src/components/ShaderBackground.astro | 140 + .../www/src/components/ThemeToggle.tsx | 40 + .../www/src/components/UseCases.astro | 74 + .../www/src/components/UseCasesList.tsx | 79 + .../www/src/components/ui/button.tsx | 58 + .../www/src/layouts/UseCaseIndexLayout.astro | 115 + .../www/src/layouts/UseCaseLayout.astro | 115 + .../gitea-mirror/www/src/layouts/main.astro | 16 + Divers/gitea-mirror/www/src/lib/use-cases.ts | 75 + Divers/gitea-mirror/www/src/lib/utils.ts | 6 + .../pages/comparison/github-backup-tools.mdx | 254 + Divers/gitea-mirror/www/src/pages/index.astro | 224 + .../use-cases/backup-github-repositories.mdx | 120 + .../use-cases/deploy-with-helm-chart.mdx | 148 + .../use-cases/github-backup-automation.mdx | 93 + .../www/src/pages/use-cases/index.mdx | 120 + .../use-cases/preserve-github-history.mdx | 97 + .../pages/use-cases/proxmox-lxc-homelab.mdx | 110 + .../use-cases/starred-repos-collection.mdx | 89 + .../sync-github-to-self-hosted-gitea.mdx | 97 + .../use-cases/vendor-lock-in-prevention.mdx | 77 + Divers/gitea-mirror/www/src/styles/global.css | 226 + Divers/gitea-mirror/www/tsconfig.json | 20 + 385 files changed, 81357 insertions(+) create mode 100644 Divers/gitea-mirror/.dockerignore create mode 100644 Divers/gitea-mirror/.env.example create mode 100644 Divers/gitea-mirror/.envrc create mode 100644 Divers/gitea-mirror/.gitignore create mode 100644 Divers/gitea-mirror/AGENTS.md create mode 100644 Divers/gitea-mirror/CHANGELOG.md create mode 100644 Divers/gitea-mirror/CLAUDE.md create mode 100644 Divers/gitea-mirror/CONTRIBUTING.md create mode 100644 Divers/gitea-mirror/DISTRIBUTION_SUMMARY.md create mode 100644 Divers/gitea-mirror/Dockerfile create mode 100644 Divers/gitea-mirror/LICENSE create mode 100644 Divers/gitea-mirror/NIX.md create mode 100644 Divers/gitea-mirror/README.md create mode 100644 Divers/gitea-mirror/astro.config.mjs create mode 100644 Divers/gitea-mirror/bun.lock create mode 100644 Divers/gitea-mirror/bunfig.toml create mode 100644 Divers/gitea-mirror/certs/README.md create mode 100644 Divers/gitea-mirror/components.json create mode 100644 Divers/gitea-mirror/data/README.md create mode 100644 Divers/gitea-mirror/docker-compose.alt.yml create mode 100644 Divers/gitea-mirror/docker-compose.dev.yml create mode 100644 Divers/gitea-mirror/docker-compose.yml create mode 100644 Divers/gitea-mirror/docker-entrypoint.sh create mode 100644 Divers/gitea-mirror/docs/DEVELOPMENT_WORKFLOW.md create mode 100644 Divers/gitea-mirror/docs/ENVIRONMENT_VARIABLES.md create mode 100644 Divers/gitea-mirror/docs/GRACEFUL_SHUTDOWN.md create mode 100644 Divers/gitea-mirror/docs/NIX_DEPLOYMENT.md create mode 100644 Divers/gitea-mirror/docs/NIX_DISTRIBUTION.md create mode 100644 Divers/gitea-mirror/docs/README.md create mode 100644 Divers/gitea-mirror/docs/RECOVERY_IMPROVEMENTS.md create mode 100644 Divers/gitea-mirror/docs/SSO-OIDC-SETUP.md create mode 100644 Divers/gitea-mirror/docs/SSO_TESTING.md create mode 100644 Divers/gitea-mirror/drizzle.config.ts create mode 100644 Divers/gitea-mirror/drizzle/0000_init.sql create mode 100644 Divers/gitea-mirror/drizzle/0001_polite_exodus.sql create mode 100644 Divers/gitea-mirror/drizzle/0002_bored_captain_cross.sql create mode 100644 Divers/gitea-mirror/drizzle/0003_open_spacker_dave.sql create mode 100644 Divers/gitea-mirror/drizzle/0004_grey_butterfly.sql create mode 100644 Divers/gitea-mirror/drizzle/0005_polite_preak.sql create mode 100644 Divers/gitea-mirror/drizzle/0006_military_la_nuit.sql create mode 100644 Divers/gitea-mirror/drizzle/0007_whole_hellion.sql create mode 100644 Divers/gitea-mirror/drizzle/0008_serious_thena.sql create mode 100644 Divers/gitea-mirror/drizzle/meta/0000_snapshot.json create mode 100644 Divers/gitea-mirror/drizzle/meta/0001_snapshot.json create mode 100644 Divers/gitea-mirror/drizzle/meta/0002_snapshot.json create mode 100644 Divers/gitea-mirror/drizzle/meta/0003_snapshot.json create mode 100644 Divers/gitea-mirror/drizzle/meta/0004_snapshot.json create mode 100644 Divers/gitea-mirror/drizzle/meta/0005_snapshot.json create mode 100644 Divers/gitea-mirror/drizzle/meta/0006_snapshot.json create mode 100644 Divers/gitea-mirror/drizzle/meta/0007_snapshot.json create mode 100644 Divers/gitea-mirror/drizzle/meta/0008_snapshot.json create mode 100644 Divers/gitea-mirror/drizzle/meta/_journal.json create mode 100644 Divers/gitea-mirror/env.d.ts create mode 100644 Divers/gitea-mirror/flake.lock create mode 100644 Divers/gitea-mirror/flake.nix create mode 100644 Divers/gitea-mirror/helm/gitea-mirror/.yamllint create mode 100644 Divers/gitea-mirror/helm/gitea-mirror/Chart.yaml create mode 100644 Divers/gitea-mirror/helm/gitea-mirror/README.md create mode 100644 Divers/gitea-mirror/helm/gitea-mirror/templates/_helpers.tpl create mode 100644 Divers/gitea-mirror/helm/gitea-mirror/templates/configmap.yaml create mode 100644 Divers/gitea-mirror/helm/gitea-mirror/templates/deployment.yaml create mode 100644 Divers/gitea-mirror/helm/gitea-mirror/templates/httproute.yaml create mode 100644 Divers/gitea-mirror/helm/gitea-mirror/templates/ingress.yaml create mode 100644 Divers/gitea-mirror/helm/gitea-mirror/templates/pvc.yaml create mode 100644 Divers/gitea-mirror/helm/gitea-mirror/templates/secret.yaml create mode 100644 Divers/gitea-mirror/helm/gitea-mirror/templates/service.yaml create mode 100644 Divers/gitea-mirror/helm/gitea-mirror/templates/serviceaccount.yaml create mode 100644 Divers/gitea-mirror/helm/gitea-mirror/values.yaml create mode 100644 Divers/gitea-mirror/package.json create mode 100644 Divers/gitea-mirror/public/apple-touch-icon.png create mode 100644 Divers/gitea-mirror/public/favicon.ico create mode 100644 Divers/gitea-mirror/public/favicon.svg create mode 100644 Divers/gitea-mirror/public/logo.png create mode 100644 Divers/gitea-mirror/scripts/README-dev.md create mode 100644 Divers/gitea-mirror/scripts/README-docker.md create mode 100644 Divers/gitea-mirror/scripts/README-lxc.md create mode 100644 Divers/gitea-mirror/scripts/README.md create mode 100755 Divers/gitea-mirror/scripts/build-docker.sh create mode 100644 Divers/gitea-mirror/scripts/cleanup-duplicate-repos.ts create mode 100644 Divers/gitea-mirror/scripts/docker-diagnostics.sh create mode 100644 Divers/gitea-mirror/scripts/fix-interrupted-jobs.ts create mode 100644 Divers/gitea-mirror/scripts/generate-better-auth-schema.ts create mode 100644 Divers/gitea-mirror/scripts/gitea-app.ini create mode 100755 Divers/gitea-mirror/scripts/gitea-create-admin.sh create mode 100755 Divers/gitea-mirror/scripts/gitea-dev-init.sh create mode 100755 Divers/gitea-mirror/scripts/gitea-mirror-lxc-local.sh create mode 100644 Divers/gitea-mirror/scripts/investigate-repo.ts create mode 100644 Divers/gitea-mirror/scripts/manage-db.ts create mode 100644 Divers/gitea-mirror/scripts/remove-duplicate-events.ts create mode 100644 Divers/gitea-mirror/scripts/repair-mirrored-repos.ts create mode 100644 Divers/gitea-mirror/scripts/run-migration.ts create mode 100755 Divers/gitea-mirror/scripts/setup-authentik-test.sh create mode 100644 Divers/gitea-mirror/scripts/startup-env-config.ts create mode 100644 Divers/gitea-mirror/scripts/startup-recovery.ts create mode 100644 Divers/gitea-mirror/scripts/test-graceful-shutdown.ts create mode 100644 Divers/gitea-mirror/scripts/test-recovery.ts create mode 100644 Divers/gitea-mirror/src/components/NotFound.tsx create mode 100644 Divers/gitea-mirror/src/components/activity/ActivityList.tsx create mode 100644 Divers/gitea-mirror/src/components/activity/ActivityLog.tsx create mode 100644 Divers/gitea-mirror/src/components/activity/ActivityNameCombobox.tsx create mode 100644 Divers/gitea-mirror/src/components/auth/LoginForm.tsx create mode 100644 Divers/gitea-mirror/src/components/auth/LoginPage.tsx create mode 100644 Divers/gitea-mirror/src/components/auth/SignupForm.tsx create mode 100644 Divers/gitea-mirror/src/components/auth/SignupPage.tsx create mode 100644 Divers/gitea-mirror/src/components/config/AdvancedOptionsForm.tsx create mode 100644 Divers/gitea-mirror/src/components/config/AutomationSettings.tsx create mode 100644 Divers/gitea-mirror/src/components/config/ConfigTabs.tsx create mode 100644 Divers/gitea-mirror/src/components/config/GitHubConfigForm.tsx create mode 100644 Divers/gitea-mirror/src/components/config/GitHubMirrorSettings.tsx create mode 100644 Divers/gitea-mirror/src/components/config/GiteaConfigForm.tsx create mode 100644 Divers/gitea-mirror/src/components/config/OrganizationConfiguration.tsx create mode 100644 Divers/gitea-mirror/src/components/config/OrganizationStrategy.tsx create mode 100644 Divers/gitea-mirror/src/components/config/SSOSettings.tsx create mode 100644 Divers/gitea-mirror/src/components/config/ScheduleConfigForm.tsx create mode 100644 Divers/gitea-mirror/src/components/dashboard/Dashboard.tsx create mode 100644 Divers/gitea-mirror/src/components/dashboard/RecentActivity.tsx create mode 100644 Divers/gitea-mirror/src/components/dashboard/RepositoryList.tsx create mode 100644 Divers/gitea-mirror/src/components/dashboard/StatusCard.tsx create mode 100644 Divers/gitea-mirror/src/components/layout/Header.tsx create mode 100644 Divers/gitea-mirror/src/components/layout/MainLayout.tsx create mode 100644 Divers/gitea-mirror/src/components/layout/Providers.tsx create mode 100644 Divers/gitea-mirror/src/components/layout/Sidebar.tsx create mode 100644 Divers/gitea-mirror/src/components/layout/SponsorCard.tsx create mode 100644 Divers/gitea-mirror/src/components/layout/VersionInfo.tsx create mode 100644 Divers/gitea-mirror/src/components/oauth/ConsentPage.tsx create mode 100644 Divers/gitea-mirror/src/components/organizations/AddOrganizationDialog.tsx create mode 100644 Divers/gitea-mirror/src/components/organizations/MirrorDestinationEditor.tsx create mode 100644 Divers/gitea-mirror/src/components/organizations/Organization.tsx create mode 100644 Divers/gitea-mirror/src/components/organizations/OrganizationsList.tsx create mode 100644 Divers/gitea-mirror/src/components/repositories/AddRepositoryDialog.tsx create mode 100644 Divers/gitea-mirror/src/components/repositories/InlineDestinationEditor.tsx create mode 100644 Divers/gitea-mirror/src/components/repositories/Repository.tsx create mode 100644 Divers/gitea-mirror/src/components/repositories/RepositoryComboboxes.tsx create mode 100644 Divers/gitea-mirror/src/components/repositories/RepositoryTable.tsx create mode 100644 Divers/gitea-mirror/src/components/sponsors/GitHubSponsors.tsx create mode 100644 Divers/gitea-mirror/src/components/theme/ModeToggle.tsx create mode 100644 Divers/gitea-mirror/src/components/theme/ThemeScript.astro create mode 100644 Divers/gitea-mirror/src/components/ui/accordion.tsx create mode 100644 Divers/gitea-mirror/src/components/ui/alert.tsx create mode 100644 Divers/gitea-mirror/src/components/ui/avatar.tsx create mode 100644 Divers/gitea-mirror/src/components/ui/badge.tsx create mode 100644 Divers/gitea-mirror/src/components/ui/button.tsx create mode 100644 Divers/gitea-mirror/src/components/ui/card.tsx create mode 100644 Divers/gitea-mirror/src/components/ui/checkbox.tsx create mode 100644 Divers/gitea-mirror/src/components/ui/collapsible.tsx create mode 100644 Divers/gitea-mirror/src/components/ui/command.tsx create mode 100644 Divers/gitea-mirror/src/components/ui/dialog.tsx create mode 100644 Divers/gitea-mirror/src/components/ui/drawer.tsx create mode 100644 Divers/gitea-mirror/src/components/ui/dropdown-menu.tsx create mode 100644 Divers/gitea-mirror/src/components/ui/hover-card.tsx create mode 100644 Divers/gitea-mirror/src/components/ui/input.tsx create mode 100644 Divers/gitea-mirror/src/components/ui/label.tsx create mode 100644 Divers/gitea-mirror/src/components/ui/multi-select.tsx create mode 100644 Divers/gitea-mirror/src/components/ui/popover.tsx create mode 100644 Divers/gitea-mirror/src/components/ui/progress.tsx create mode 100644 Divers/gitea-mirror/src/components/ui/radio-group.tsx create mode 100644 Divers/gitea-mirror/src/components/ui/radio.tsx create mode 100644 Divers/gitea-mirror/src/components/ui/scroll-area.tsx create mode 100644 Divers/gitea-mirror/src/components/ui/select.tsx create mode 100644 Divers/gitea-mirror/src/components/ui/separator.tsx create mode 100644 Divers/gitea-mirror/src/components/ui/skeleton.tsx create mode 100644 Divers/gitea-mirror/src/components/ui/sonner.tsx create mode 100644 Divers/gitea-mirror/src/components/ui/switch.tsx create mode 100644 Divers/gitea-mirror/src/components/ui/tabs.tsx create mode 100644 Divers/gitea-mirror/src/components/ui/textarea.tsx create mode 100644 Divers/gitea-mirror/src/components/ui/tooltip.tsx create mode 100644 Divers/gitea-mirror/src/content/config.ts create mode 100644 Divers/gitea-mirror/src/data/Sidebar.ts create mode 100644 Divers/gitea-mirror/src/hooks/useAuth-legacy.ts create mode 100644 Divers/gitea-mirror/src/hooks/useAuth.ts create mode 100644 Divers/gitea-mirror/src/hooks/useAuthMethods.ts create mode 100644 Divers/gitea-mirror/src/hooks/useConfigStatus.ts create mode 100644 Divers/gitea-mirror/src/hooks/useFilterParams.ts create mode 100644 Divers/gitea-mirror/src/hooks/useGiteaConfig.ts create mode 100644 Divers/gitea-mirror/src/hooks/useLiveRefresh.ts create mode 100644 Divers/gitea-mirror/src/hooks/usePageVisibility.ts create mode 100644 Divers/gitea-mirror/src/hooks/useSEE.ts create mode 100644 Divers/gitea-mirror/src/hooks/useSyncRepo.ts create mode 100644 Divers/gitea-mirror/src/layouts/main.astro create mode 100644 Divers/gitea-mirror/src/lib/api.ts create mode 100644 Divers/gitea-mirror/src/lib/auth-client.ts create mode 100644 Divers/gitea-mirror/src/lib/auth-header.ts create mode 100644 Divers/gitea-mirror/src/lib/auth-multi-url.test.ts create mode 100644 Divers/gitea-mirror/src/lib/auth.ts create mode 100644 Divers/gitea-mirror/src/lib/cleanup-service.ts create mode 100644 Divers/gitea-mirror/src/lib/config.ts create mode 100644 Divers/gitea-mirror/src/lib/db/adapter.ts create mode 100644 Divers/gitea-mirror/src/lib/db/index.test.ts create mode 100644 Divers/gitea-mirror/src/lib/db/index.ts create mode 100644 Divers/gitea-mirror/src/lib/db/schema.ts create mode 100644 Divers/gitea-mirror/src/lib/deployment-mode.ts create mode 100644 Divers/gitea-mirror/src/lib/env-config-loader.ts create mode 100644 Divers/gitea-mirror/src/lib/events.ts create mode 100644 Divers/gitea-mirror/src/lib/events/realtime.ts create mode 100644 Divers/gitea-mirror/src/lib/gitea-auth-validator.ts create mode 100644 Divers/gitea-mirror/src/lib/gitea-enhanced.test.ts create mode 100644 Divers/gitea-mirror/src/lib/gitea-enhanced.ts create mode 100644 Divers/gitea-mirror/src/lib/gitea-lfs.test.ts create mode 100644 Divers/gitea-mirror/src/lib/gitea-org-creation.test.ts create mode 100644 Divers/gitea-mirror/src/lib/gitea-org-fix.ts create mode 100644 Divers/gitea-mirror/src/lib/gitea-starred-repos.test.ts create mode 100644 Divers/gitea-mirror/src/lib/gitea.test.ts create mode 100644 Divers/gitea-mirror/src/lib/gitea.ts create mode 100644 Divers/gitea-mirror/src/lib/github.ts create mode 100644 Divers/gitea-mirror/src/lib/helpers.ts create mode 100644 Divers/gitea-mirror/src/lib/http-client.ts create mode 100644 Divers/gitea-mirror/src/lib/metadata-state.ts create mode 100644 Divers/gitea-mirror/src/lib/mirror-sync-errors.test.ts create mode 100644 Divers/gitea-mirror/src/lib/mirror-sync-fix.test.ts create mode 100644 Divers/gitea-mirror/src/lib/modules/registry.ts create mode 100644 Divers/gitea-mirror/src/lib/modules/types.d.ts create mode 100644 Divers/gitea-mirror/src/lib/modules/types.d.ts.map create mode 100644 Divers/gitea-mirror/src/lib/modules/types.js create mode 100644 Divers/gitea-mirror/src/lib/modules/types.ts create mode 100644 Divers/gitea-mirror/src/lib/polyfills/buffer.ts create mode 100644 Divers/gitea-mirror/src/lib/rate-limit-manager.ts create mode 100644 Divers/gitea-mirror/src/lib/recovery.ts create mode 100644 Divers/gitea-mirror/src/lib/repo-utils.test.ts create mode 100644 Divers/gitea-mirror/src/lib/repo-utils.ts create mode 100644 Divers/gitea-mirror/src/lib/repository-cleanup-service.ts create mode 100644 Divers/gitea-mirror/src/lib/scheduler-service.test.ts create mode 100644 Divers/gitea-mirror/src/lib/scheduler-service.ts create mode 100644 Divers/gitea-mirror/src/lib/shutdown-manager.ts create mode 100644 Divers/gitea-mirror/src/lib/signal-handlers.ts create mode 100644 Divers/gitea-mirror/src/lib/sso/oidc-config.test.ts create mode 100644 Divers/gitea-mirror/src/lib/sso/oidc-config.ts create mode 100644 Divers/gitea-mirror/src/lib/starred-repos-handler.ts create mode 100644 Divers/gitea-mirror/src/lib/utils.test.ts create mode 100644 Divers/gitea-mirror/src/lib/utils.ts create mode 100644 Divers/gitea-mirror/src/lib/utils/auth-helpers.ts create mode 100644 Divers/gitea-mirror/src/lib/utils/concurrency.test.ts create mode 100644 Divers/gitea-mirror/src/lib/utils/concurrency.ts create mode 100644 Divers/gitea-mirror/src/lib/utils/config-defaults.ts create mode 100644 Divers/gitea-mirror/src/lib/utils/config-encryption.ts create mode 100644 Divers/gitea-mirror/src/lib/utils/config-mapper.ts create mode 100644 Divers/gitea-mirror/src/lib/utils/duration-parser.test.ts create mode 100644 Divers/gitea-mirror/src/lib/utils/duration-parser.ts create mode 100644 Divers/gitea-mirror/src/lib/utils/encryption.ts create mode 100644 Divers/gitea-mirror/src/lib/utils/mirror-strategies.ts create mode 100644 Divers/gitea-mirror/src/lib/utils/oauth-validation.test.ts create mode 100644 Divers/gitea-mirror/src/lib/utils/oauth-validation.ts create mode 100644 Divers/gitea-mirror/src/middleware.ts create mode 100644 Divers/gitea-mirror/src/pages/404.astro create mode 100644 Divers/gitea-mirror/src/pages/activity.astro create mode 100644 Divers/gitea-mirror/src/pages/api/activities/cleanup.ts create mode 100644 Divers/gitea-mirror/src/pages/api/activities/index.ts create mode 100644 Divers/gitea-mirror/src/pages/api/auth/[...all].ts create mode 100644 Divers/gitea-mirror/src/pages/api/auth/check-users.ts create mode 100644 Divers/gitea-mirror/src/pages/api/auth/debug.ts create mode 100644 Divers/gitea-mirror/src/pages/api/auth/header-status.ts create mode 100644 Divers/gitea-mirror/src/pages/api/auth/oauth2/register.ts create mode 100644 Divers/gitea-mirror/src/pages/api/auth/sso/register.ts create mode 100644 Divers/gitea-mirror/src/pages/api/auth/sso/sp-metadata.ts create mode 100644 Divers/gitea-mirror/src/pages/api/cleanup/auto.ts create mode 100644 Divers/gitea-mirror/src/pages/api/cleanup/trigger.ts create mode 100644 Divers/gitea-mirror/src/pages/api/config/index.ts create mode 100644 Divers/gitea-mirror/src/pages/api/dashboard/index.ts create mode 100644 Divers/gitea-mirror/src/pages/api/events/index.ts create mode 100644 Divers/gitea-mirror/src/pages/api/gitea/test-connection.test.ts create mode 100644 Divers/gitea-mirror/src/pages/api/gitea/test-connection.ts create mode 100644 Divers/gitea-mirror/src/pages/api/github/organizations.ts create mode 100644 Divers/gitea-mirror/src/pages/api/github/repositories.ts create mode 100644 Divers/gitea-mirror/src/pages/api/github/test-connection.test.ts create mode 100644 Divers/gitea-mirror/src/pages/api/github/test-connection.ts create mode 100644 Divers/gitea-mirror/src/pages/api/health.ts create mode 100644 Divers/gitea-mirror/src/pages/api/job/mirror-org.test.ts create mode 100644 Divers/gitea-mirror/src/pages/api/job/mirror-org.ts create mode 100644 Divers/gitea-mirror/src/pages/api/job/mirror-repo.test.ts create mode 100644 Divers/gitea-mirror/src/pages/api/job/mirror-repo.ts create mode 100644 Divers/gitea-mirror/src/pages/api/job/retry-repo.ts create mode 100644 Divers/gitea-mirror/src/pages/api/job/schedule-sync-repo.ts create mode 100644 Divers/gitea-mirror/src/pages/api/job/sync-repo.ts create mode 100644 Divers/gitea-mirror/src/pages/api/organizations/[id].ts create mode 100644 Divers/gitea-mirror/src/pages/api/organizations/[id]/status.ts create mode 100644 Divers/gitea-mirror/src/pages/api/rate-limit/index.ts create mode 100644 Divers/gitea-mirror/src/pages/api/repositories/[id].ts create mode 100644 Divers/gitea-mirror/src/pages/api/repositories/[id]/status.ts create mode 100644 Divers/gitea-mirror/src/pages/api/sse/index.ts create mode 100644 Divers/gitea-mirror/src/pages/api/sso/applications.ts create mode 100644 Divers/gitea-mirror/src/pages/api/sso/discover.ts create mode 100644 Divers/gitea-mirror/src/pages/api/sso/providers.ts create mode 100644 Divers/gitea-mirror/src/pages/api/sso/providers/public.ts create mode 100644 Divers/gitea-mirror/src/pages/api/sync/index.ts create mode 100644 Divers/gitea-mirror/src/pages/api/sync/organization.ts create mode 100644 Divers/gitea-mirror/src/pages/api/sync/repository.ts create mode 100644 Divers/gitea-mirror/src/pages/api/test-event.ts create mode 100644 Divers/gitea-mirror/src/pages/auth-error.astro create mode 100644 Divers/gitea-mirror/src/pages/config.astro create mode 100644 Divers/gitea-mirror/src/pages/docs/advanced.astro create mode 100644 Divers/gitea-mirror/src/pages/docs/architecture.astro create mode 100644 Divers/gitea-mirror/src/pages/docs/authentication.astro create mode 100644 Divers/gitea-mirror/src/pages/docs/ca-certificates.astro create mode 100644 Divers/gitea-mirror/src/pages/docs/configuration.astro create mode 100644 Divers/gitea-mirror/src/pages/docs/index.astro create mode 100644 Divers/gitea-mirror/src/pages/docs/quickstart.astro create mode 100644 Divers/gitea-mirror/src/pages/index.astro create mode 100644 Divers/gitea-mirror/src/pages/login.astro create mode 100644 Divers/gitea-mirror/src/pages/oauth/consent.astro create mode 100644 Divers/gitea-mirror/src/pages/organizations.astro create mode 100644 Divers/gitea-mirror/src/pages/repositories.astro create mode 100644 Divers/gitea-mirror/src/pages/signup.astro create mode 100644 Divers/gitea-mirror/src/styles/docs.css create mode 100644 Divers/gitea-mirror/src/styles/global.css create mode 100644 Divers/gitea-mirror/src/tests/mock-fetch.ts create mode 100644 Divers/gitea-mirror/src/tests/setup.bun.ts create mode 100644 Divers/gitea-mirror/src/tests/test-gitea-auth.ts create mode 100644 Divers/gitea-mirror/src/tests/test-metadata-mirroring.ts create mode 100644 Divers/gitea-mirror/src/types/Repository.ts create mode 100644 Divers/gitea-mirror/src/types/Sidebar.ts create mode 100644 Divers/gitea-mirror/src/types/activities.ts create mode 100644 Divers/gitea-mirror/src/types/config.ts create mode 100644 Divers/gitea-mirror/src/types/dashboard.ts create mode 100644 Divers/gitea-mirror/src/types/filter.ts create mode 100644 Divers/gitea-mirror/src/types/mirror.ts create mode 100644 Divers/gitea-mirror/src/types/organizations.ts create mode 100644 Divers/gitea-mirror/src/types/repository-status.test.ts create mode 100644 Divers/gitea-mirror/src/types/retry.ts create mode 100644 Divers/gitea-mirror/src/types/sync.ts create mode 100644 Divers/gitea-mirror/src/types/user.ts create mode 100644 Divers/gitea-mirror/tsconfig.json create mode 100644 Divers/gitea-mirror/www/.gitignore create mode 100644 Divers/gitea-mirror/www/.npmrc create mode 100644 Divers/gitea-mirror/www/CLAUDE.md create mode 100644 Divers/gitea-mirror/www/README.md create mode 100644 Divers/gitea-mirror/www/astro.config.mjs create mode 100644 Divers/gitea-mirror/www/bun.lock create mode 100644 Divers/gitea-mirror/www/components.json create mode 100644 Divers/gitea-mirror/www/docs/SEO_KEYWORDS.md create mode 100644 Divers/gitea-mirror/www/package.json create mode 100644 Divers/gitea-mirror/www/pnpm-lock.yaml create mode 100644 Divers/gitea-mirror/www/public/apple-touch-icon.png create mode 100644 Divers/gitea-mirror/www/public/assets/activity.png create mode 100644 Divers/gitea-mirror/www/public/assets/activity_mobile.png create mode 100644 Divers/gitea-mirror/www/public/assets/configuration-2.png create mode 100644 Divers/gitea-mirror/www/public/assets/configuration.png create mode 100644 Divers/gitea-mirror/www/public/assets/configuration_mobile.png create mode 100644 Divers/gitea-mirror/www/public/assets/dashboard.png create mode 100644 Divers/gitea-mirror/www/public/assets/dashboard_mobile.png create mode 100644 Divers/gitea-mirror/www/public/assets/hero_logo.webp create mode 100644 Divers/gitea-mirror/www/public/assets/logo-new.png create mode 100644 Divers/gitea-mirror/www/public/assets/logo.png create mode 100644 Divers/gitea-mirror/www/public/assets/organisation.png create mode 100644 Divers/gitea-mirror/www/public/assets/organisation_mobile.png create mode 100644 Divers/gitea-mirror/www/public/assets/repositories.png create mode 100644 Divers/gitea-mirror/www/public/assets/repositories_mobile.png create mode 100644 Divers/gitea-mirror/www/public/favicon.ico create mode 100644 Divers/gitea-mirror/www/public/favicon.svg create mode 100644 Divers/gitea-mirror/www/public/og-image.png create mode 100644 Divers/gitea-mirror/www/public/robots.txt create mode 100644 Divers/gitea-mirror/www/public/sitemap.xml create mode 100644 Divers/gitea-mirror/www/src/components/Button.astro create mode 100644 Divers/gitea-mirror/www/src/components/CTA.tsx create mode 100644 Divers/gitea-mirror/www/src/components/FAQ.astro create mode 100644 Divers/gitea-mirror/www/src/components/Features.astro create mode 100644 Divers/gitea-mirror/www/src/components/Footer.astro create mode 100644 Divers/gitea-mirror/www/src/components/GitHubButton.tsx create mode 100644 Divers/gitea-mirror/www/src/components/GitHubStats.tsx create mode 100644 Divers/gitea-mirror/www/src/components/Header.tsx create mode 100644 Divers/gitea-mirror/www/src/components/Hero.tsx create mode 100644 Divers/gitea-mirror/www/src/components/Installation.tsx create mode 100644 Divers/gitea-mirror/www/src/components/PromoBanner.tsx create mode 100644 Divers/gitea-mirror/www/src/components/Screenshots.astro create mode 100644 Divers/gitea-mirror/www/src/components/ShaderBackground.astro create mode 100644 Divers/gitea-mirror/www/src/components/ThemeToggle.tsx create mode 100644 Divers/gitea-mirror/www/src/components/UseCases.astro create mode 100644 Divers/gitea-mirror/www/src/components/UseCasesList.tsx create mode 100644 Divers/gitea-mirror/www/src/components/ui/button.tsx create mode 100644 Divers/gitea-mirror/www/src/layouts/UseCaseIndexLayout.astro create mode 100644 Divers/gitea-mirror/www/src/layouts/UseCaseLayout.astro create mode 100644 Divers/gitea-mirror/www/src/layouts/main.astro create mode 100644 Divers/gitea-mirror/www/src/lib/use-cases.ts create mode 100644 Divers/gitea-mirror/www/src/lib/utils.ts create mode 100644 Divers/gitea-mirror/www/src/pages/comparison/github-backup-tools.mdx create mode 100644 Divers/gitea-mirror/www/src/pages/index.astro create mode 100644 Divers/gitea-mirror/www/src/pages/use-cases/backup-github-repositories.mdx create mode 100644 Divers/gitea-mirror/www/src/pages/use-cases/deploy-with-helm-chart.mdx create mode 100644 Divers/gitea-mirror/www/src/pages/use-cases/github-backup-automation.mdx create mode 100644 Divers/gitea-mirror/www/src/pages/use-cases/index.mdx create mode 100644 Divers/gitea-mirror/www/src/pages/use-cases/preserve-github-history.mdx create mode 100644 Divers/gitea-mirror/www/src/pages/use-cases/proxmox-lxc-homelab.mdx create mode 100644 Divers/gitea-mirror/www/src/pages/use-cases/starred-repos-collection.mdx create mode 100644 Divers/gitea-mirror/www/src/pages/use-cases/sync-github-to-self-hosted-gitea.mdx create mode 100644 Divers/gitea-mirror/www/src/pages/use-cases/vendor-lock-in-prevention.mdx create mode 100644 Divers/gitea-mirror/www/src/styles/global.css create mode 100644 Divers/gitea-mirror/www/tsconfig.json diff --git a/Divers/gitea-mirror/.dockerignore b/Divers/gitea-mirror/.dockerignore new file mode 100644 index 0000000..02f48e0 --- /dev/null +++ b/Divers/gitea-mirror/.dockerignore @@ -0,0 +1,64 @@ +# Version control +.git +.gitignore +.github + +# Node.js +node_modules +# We don't exclude bun.lock* as it's needed for the build +npm-debug.log +yarn-debug.log +yarn-error.log + +# Build outputs +dist +build +.next +out + +# Environment variables +.env +.env.local +.env.development +.env.test +.env.production + +# IDE and editor files +.idea +.vscode +*.swp +*.swo +*~ + +# OS files +.DS_Store +Thumbs.db + +# Test coverage +coverage +.nyc_output + +# Docker +Dockerfile +.dockerignore +docker-compose.yml +docker-compose.*.yml + +# Documentation +README.md +LICENSE +docs + +# Temporary files +tmp +temp +*.tmp +*.temp + +# Logs +logs +*.log + +# Cache +.cache +.npm diff --git a/Divers/gitea-mirror/.env.example b/Divers/gitea-mirror/.env.example new file mode 100644 index 0000000..b6924ff --- /dev/null +++ b/Divers/gitea-mirror/.env.example @@ -0,0 +1,186 @@ +# Gitea Mirror Configuration +# Copy this to .env and update with your values + +# =========================================== +# CORE CONFIGURATION +# =========================================== + +# Application Configuration +NODE_ENV=production +HOST=0.0.0.0 +PORT=4321 + +# Database Configuration +# For self-hosted, SQLite is used by default +DATABASE_URL=sqlite://data/gitea-mirror.db + +# Security +# Generate with: openssl rand -base64 32 +BETTER_AUTH_SECRET=change-this-to-a-secure-random-string-in-production +BETTER_AUTH_URL=http://localhost:4321 +# PUBLIC_BETTER_AUTH_URL=https://your-domain.com # Optional: Set this if accessing from different origins (e.g., IP and domain) +# ENCRYPTION_SECRET=optional-encryption-key-for-token-encryption # Generate with: openssl rand -base64 48 + +# =========================================== +# DOCKER CONFIGURATION (Optional) +# =========================================== + +# Docker Registry Configuration +DOCKER_REGISTRY=ghcr.io +DOCKER_IMAGE=raylabshq/gitea-mirror: +DOCKER_TAG=latest + +# =========================================== +# GITHUB CONFIGURATION +# All settings can also be configured via web UI +# =========================================== + +# Basic GitHub Settings +# GITHUB_USERNAME=your-github-username +# GITHUB_TOKEN=your-github-personal-access-token +# GITHUB_TYPE=personal # Options: personal, organization + +# Repository Selection +# PRIVATE_REPOSITORIES=false +# PUBLIC_REPOSITORIES=true +# INCLUDE_ARCHIVED=false +# SKIP_FORKS=false +# MIRROR_STARRED=false +# STARRED_REPOS_ORG=starred # Organization name for starred repos + +# Organization Settings +# MIRROR_ORGANIZATIONS=false +# PRESERVE_ORG_STRUCTURE=false +# ONLY_MIRROR_ORGS=false + +# Mirror Strategy +# MIRROR_STRATEGY=preserve # Options: preserve, single-org, flat-user, mixed + +# Advanced GitHub Settings +# SKIP_STARRED_ISSUES=false # Enable lightweight mode for starred repos + +# =========================================== +# GITEA CONFIGURATION +# All settings can also be configured via web UI +# =========================================== + +# Basic Gitea Settings +# GITEA_URL=http://gitea:3000 +# GITEA_TOKEN=your-local-gitea-token +# GITEA_USERNAME=your-local-gitea-username +# GITEA_ORGANIZATION=github-mirrors # Default organization for single-org strategy + +# Repository Settings +# GITEA_ORG_VISIBILITY=public # Options: public, private, limited, default +# GITEA_MIRROR_INTERVAL=8h # Mirror sync interval (e.g., 30m, 1h, 8h, 24h) - automatically enables scheduler +# GITEA_LFS=false # Enable LFS support +# GITEA_CREATE_ORG=true # Auto-create organizations +# GITEA_PRESERVE_VISIBILITY=false # Preserve GitHub repo visibility in Gitea + +# Template Settings (for using repository templates) +# GITEA_TEMPLATE_OWNER=template-owner +# GITEA_TEMPLATE_REPO=template-repo + +# Topic Settings +# GITEA_ADD_TOPICS=true # Add topics to repositories +# GITEA_TOPIC_PREFIX=gh- # Prefix for topics + +# Fork Handling +# GITEA_FORK_STRATEGY=reference # Options: skip, reference, full-copy + +# =========================================== +# MIRROR OPTIONS +# Control what gets mirrored from GitHub +# =========================================== + +# Release and Metadata +# MIRROR_RELEASES=false # Mirror GitHub releases +# RELEASE_LIMIT=10 # Maximum number of releases to mirror per repository +# MIRROR_WIKI=false # Mirror wiki content + +# Issue Tracking (requires MIRROR_METADATA=true) +# MIRROR_METADATA=false # Master toggle for metadata mirroring +# MIRROR_ISSUES=false # Mirror issues +# MIRROR_PULL_REQUESTS=false # Mirror pull requests +# MIRROR_LABELS=false # Mirror labels +# MIRROR_MILESTONES=false # Mirror milestones + +# =========================================== +# AUTOMATION CONFIGURATION +# Schedule automatic mirroring +# =========================================== + +# Basic Schedule Settings +# SCHEDULE_ENABLED=false # When true, auto-imports and mirrors all repos on startup (v3.5.3+) +# SCHEDULE_INTERVAL=3600 # Interval in seconds or cron expression (e.g., "0 2 * * *") +# GITEA_MIRROR_INTERVAL=8h # Mirror sync interval (5m, 30m, 1h, 8h, 24h, 1d, 7d) - also triggers auto-start +# AUTO_IMPORT_REPOS=true # Automatically discover and import new GitHub repositories during syncs +# DELAY=3600 # Legacy: same as SCHEDULE_INTERVAL, kept for backward compatibility + +# Execution Settings +# SCHEDULE_CONCURRENT=false # Allow concurrent mirror operations +# SCHEDULE_BATCH_SIZE=10 # Number of repos to process in parallel +# SCHEDULE_PAUSE_BETWEEN_BATCHES=5000 # Pause between batches (ms) + +# Retry Configuration +# SCHEDULE_RETRY_ATTEMPTS=3 +# SCHEDULE_RETRY_DELAY=60000 # Delay between retries (ms) +# SCHEDULE_TIMEOUT=3600000 # Max time for a mirror operation (ms) +# SCHEDULE_AUTO_RETRY=true + +# Update Detection +# SCHEDULE_ONLY_MIRROR_UPDATED=false # Only mirror repos with updates +# SCHEDULE_UPDATE_INTERVAL=86400000 # Check for updates interval (ms) +# SCHEDULE_SKIP_RECENTLY_MIRRORED=true +# SCHEDULE_RECENT_THRESHOLD=3600000 # Skip if mirrored within this time (ms) + +# Maintenance +# SCHEDULE_CLEANUP_BEFORE_MIRROR=false # Run cleanup before mirroring + +# Notifications +# SCHEDULE_NOTIFY_ON_FAILURE=true +# SCHEDULE_NOTIFY_ON_SUCCESS=false +# SCHEDULE_LOG_LEVEL=info # Options: error, warn, info, debug +# SCHEDULE_TIMEZONE=UTC + +# =========================================== +# DATABASE CLEANUP CONFIGURATION +# Automatic cleanup of old events and data +# =========================================== + +# Basic Cleanup Settings +# CLEANUP_ENABLED=false +# CLEANUP_RETENTION_DAYS=7 # Days to keep events + +# Repository Cleanup (v3.4.0+) +# CLEANUP_DELETE_FROM_GITEA=false # Delete repos from Gitea +# CLEANUP_DELETE_IF_NOT_IN_GITHUB=false # Auto-remove repos that no longer exist in GitHub +# CLEANUP_ORPHANED_REPO_ACTION=archive # Options: skip, archive, delete +# CLEANUP_DRY_RUN=true # Test mode without actual deletion (set to false for production) + +# Protected Repositories (comma-separated) +# CLEANUP_PROTECTED_REPOS=important-repo,critical-project + +# Cleanup Execution +# CLEANUP_BATCH_SIZE=10 +# CLEANUP_PAUSE_BETWEEN_DELETES=2000 # Pause between deletions (ms) + +# =========================================== +# AUTHENTICATION CONFIGURATION +# =========================================== + +# Header Authentication (for Reverse Proxy SSO) +# Enable automatic authentication via reverse proxy headers +# HEADER_AUTH_ENABLED=false +# HEADER_AUTH_USER_HEADER=X-Authentik-Username +# HEADER_AUTH_EMAIL_HEADER=X-Authentik-Email +# HEADER_AUTH_NAME_HEADER=X-Authentik-Name +# HEADER_AUTH_AUTO_PROVISION=false +# HEADER_AUTH_ALLOWED_DOMAINS=example.com,company.org + +# =========================================== +# OPTIONAL FEATURES +# =========================================== + +# TLS/SSL Configuration +# GITEA_SKIP_TLS_VERIFY=false # WARNING: Only use for testing \ No newline at end of file diff --git a/Divers/gitea-mirror/.envrc b/Divers/gitea-mirror/.envrc new file mode 100644 index 0000000..3550a30 --- /dev/null +++ b/Divers/gitea-mirror/.envrc @@ -0,0 +1 @@ +use flake diff --git a/Divers/gitea-mirror/.gitignore b/Divers/gitea-mirror/.gitignore new file mode 100644 index 0000000..f0b1116 --- /dev/null +++ b/Divers/gitea-mirror/.gitignore @@ -0,0 +1,39 @@ +# build output +dist/ +# generated types +.astro/ + +# dependencies +node_modules/ + +# logs +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* + + +# environment variables +.env +.env.production + +# database files +data/gitea-mirror.db + +# macOS-specific files +.DS_Store + +# jetbrains setting folder +.idea/ + +# Custom CA certificates (exclude actual certs but keep README) +certs/*.crt +certs/*.pem +certs/*.cer +!certs/README.md + +# Nix build artifacts +result +result-* +.direnv/ + diff --git a/Divers/gitea-mirror/AGENTS.md b/Divers/gitea-mirror/AGENTS.md new file mode 100644 index 0000000..6c051e7 --- /dev/null +++ b/Divers/gitea-mirror/AGENTS.md @@ -0,0 +1,46 @@ +# Repository Guidelines + +## Project Structure & Module Organization +- `src/` – app code + - `components/` (React, PascalCase files), `pages/` (Astro/API routes), `lib/` (domain + utilities, kebab-case), `hooks/`, `layouts/`, `styles/`, `tests/`, `types/`, `data/`, `content/`. +- `scripts/` – operational TS scripts (DB init, recovery): e.g., `scripts/manage-db.ts`. +- `drizzle/` – SQL migrations; `data/` – runtime SQLite (`gitea-mirror.db`). +- `public/` – static assets; `dist/` – build output. +- Key config: `astro.config.mjs`, `tsconfig.json` (alias `@/* → src/*`), `bunfig.toml` (test preload), `.env(.example)`. + +## Build, Test, and Development Commands +- Prereq: Bun `>= 1.2.9` (see `package.json`). +- Setup: `bun run setup` – install deps and init DB. +- Dev: `bun run dev` – start Astro dev server. +- Build: `bun run build` – produce `dist/`. +- Preview/Start: `bun run preview` (static preview) or `bun run start` (SSR entry). +- Database: `bun run db:generate|migrate|push|studio` and `bun run manage-db init|check|fix|reset-users`. +- Tests: `bun test` | `bun run test:watch` | `bun run test:coverage`. +- Docker: see `docker-compose.yml` and variants in repo root. + +## Coding Style & Naming Conventions +- Language: TypeScript, Astro, React. +- Indentation: 2 spaces; keep existing semicolon/quote style in touched files. +- Components: PascalCase `.tsx` in `src/components/` (e.g., `MainLayout.tsx`). +- Modules/utils: kebab-case in `src/lib/` (e.g., `gitea-enhanced.ts`). +- Imports: prefer alias `@/…` (configured in `tsconfig.json`). +- Do not introduce new lint/format configs; follow current patterns. + +## Testing Guidelines +- Runner: Bun test (`bun:test`) with preload `src/tests/setup.bun.ts` (see `bunfig.toml`). +- Location/Names: `**/*.test.ts(x)` under `src/**` (examples in `src/lib/**`). +- Scope: add unit tests for new logic and API route tests for handlers. +- Aim for meaningful coverage on DB, auth, and mirroring paths. + +## Commit & Pull Request Guidelines +- Commits: short, imperative, scoped when helpful (e.g., `lib: fix token parsing`, `ui: align buttons`). +- PRs must include: + - Summary, rationale, and testing steps/commands. + - Linked issues (e.g., `Closes #123`). + - Screenshots/gifs for UI changes. + - Notes on DB/migration or .env impacts; update `docs/`/CHANGELOG if applicable. + +## Security & Configuration Tips +- Never commit secrets. Copy `.env.example` → `.env` and fill values; prefer `bun run startup-env-config` to validate. +- SQLite files live in `data/`; avoid committing generated DBs. +- Certificates (if used) reside in `certs/`; manage locally or via Docker secrets. diff --git a/Divers/gitea-mirror/CHANGELOG.md b/Divers/gitea-mirror/CHANGELOG.md new file mode 100644 index 0000000..22cfb9e --- /dev/null +++ b/Divers/gitea-mirror/CHANGELOG.md @@ -0,0 +1,474 @@ +# Changelog + +All notable changes to the Gitea Mirror project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## [Unreleased] + +### Added +- Git LFS (Large File Storage) support for mirroring (#74) + - New UI checkbox "Mirror LFS" in Mirror Options + - Automatic LFS object transfer when enabled + - Documentation for Gitea server LFS requirements +- Repository "ignored" status to skip specific repos from mirroring (#75) + - Repositories can be marked as ignored to exclude from all operations + - Scheduler automatically skips ignored repositories +- Enhanced error handling for all metadata mirroring operations + - Individual try-catch blocks for issues, PRs, labels, milestones + - Operations continue even if individual components fail +- Support for BETTER_AUTH_TRUSTED_ORIGINS environment variable (#63) + - Enables access via multiple URLs (local IP + domain) + - Comma-separated trusted origins configuration + - Proper documentation for multi-URL access patterns +- Comprehensive fix report documentation + +### Fixed +- Fixed metadata mirroring authentication errors (#68) + - Changed field checking from `username` to `defaultOwner` in metadata functions + - Added proper field validation for all metadata operations +- Fixed automatic mirroring scheduler issues (#72) + - Improved interval parsing and error handling +- Fixed OIDC authentication 500 errors with Authentik (#73) + - Added URL validation in Better Auth configuration + - Prevented undefined URL errors in auth callback +- Fixed SSL certificate handling in Docker (#48) + - NODE_EXTRA_CA_CERTS no longer gets overridden + - Proper preservation of custom CA certificates +- Fixed reverse proxy base domain issues (#63) + - Better handling of custom subdomains + - Support for trusted origins configuration +- Fixed configuration persistence bugs (#49) + - Config merging now preserves all fields + - Retention period settings no longer reset +- Fixed sync failures with improved error handling (#51) + - Comprehensive error wrapping for all operations + - Better error messages and logging + +### Improved +- Enhanced logging throughout metadata mirroring operations + - Detailed success/failure messages for each component + - Configuration details logged for debugging +- Better configuration state management + - Proper merging of loaded configs with defaults + - Preservation of user settings on refresh +- Updated documentation + - Added LFS feature documentation + - Updated README with new features + - Enhanced CLAUDE.md with repository status definitions + +## [3.7.1] - 2025-09-14 + +### Fixed +- Cleanup archiving for mirror repositories now works reliably (refs #84; awaiting user confirmation). + - Gitea rejects names violating the AlphaDashDot rule; archiving a mirror now uses a sanitized rename strategy (`archived-`), with a timestamped fallback on conflicts or validation errors. + - Owner resolution during cleanup no longer uses the GitHub owner by mistake. It prefers `mirroredLocation`, falls back to computed Gitea owner via configuration, and verifies location with a presence check to avoid `GetUserByName` 404s. +- Repositories UI crash resolved when cleanup marked repos as archived. + - Added `"archived"` to repository/job status enums, fixing Zod validation errors on the Repositories page. + +### Changed +- Archiving logic for mirror repos is non-destructive by design: data is preserved, repo is renamed with an archive marker, and mirror interval is reduced (best‑effort) to minimize sync attempts. +- Cleanup service updates DB to `status: "archived"` and `isArchived: true` on successful archive path. + +### Notes +- This release addresses the scenario where a GitHub source disappears (deleted/banned), ensuring Gitea backups are preserved even when using `CLEANUP_DELETE_IF_NOT_IN_GITHUB=true` with `CLEANUP_ORPHANED_REPO_ACTION=archive`. +- No database migration required. + +## [3.2.6] - 2025-08-09 + +### Fixed +- Added missing release asset mirroring functionality (APK, ZIP, Binary files) +- Release assets (attachments) are now properly downloaded from GitHub and uploaded to Gitea +- Fixed missing metadata component configuration checks + +### Added +- Full support for mirroring release assets/attachments +- Debug logging for metadata component configuration to help troubleshoot mirroring issues +- Download and upload progress logging for release assets + +### Improved +- Enhanced release mirroring to include all associated binary files and attachments +- Better visibility into which metadata components are enabled/disabled +- More detailed logging during the release asset transfer process + +### Notes +This patch adds the missing functionality to mirror release assets (APK, ZIP, Binary files, etc.) that was reported in Issue #68. Previously only release metadata was being mirrored, now all attachments are properly transferred to Gitea. + +## [3.2.5] - 2025-08-09 + +### Fixed +- Fixed critical authentication issue in releases mirroring that was still using encrypted tokens +- Added missing repository existence check for releases mirroring function +- Fixed "user does not exist [uid: 0]" error specifically affecting GitHub releases synchronization + +### Improved +- Enhanced releases mirroring with duplicate detection to avoid errors on re-runs +- Better error handling and logging for release operations with [Releases] prefix +- Added individual release error handling to continue mirroring even if some releases fail + +### Notes +This patch completes the authentication fixes started in v3.2.4, specifically addressing the releases mirroring function that was accidentally missed in the previous update. + +## [3.2.4] - 2025-08-09 + +### Fixed +- Fixed critical authentication issue causing "user does not exist [uid: 0]" errors during metadata mirroring (Issue #68) +- Fixed inconsistent token handling across Gitea API calls +- Fixed metadata mirroring functions attempting to operate on non-existent repositories +- Fixed organization creation failing silently without proper error messages + +### Added +- Pre-flight authentication validation for all Gitea operations +- Repository existence verification before metadata mirroring +- Graceful fallback to user account when organization creation fails due to permissions +- Authentication validation utilities for debugging configuration issues +- Diagnostic test scripts for troubleshooting authentication problems + +### Improved +- Enhanced error messages with specific guidance for authentication failures +- Better identification and logging of permission-related errors +- More robust organization creation with retry logic and better error handling +- Consistent token decryption across all API operations +- Clearer error reporting for metadata mirroring failures + +### Security +- Fixed potential exposure of encrypted tokens in API calls +- Improved token handling to ensure proper decryption before use + +## [3.2.0] - 2025-07-31 + +### Fixed +- Fixed Zod validation error in activity logs by correcting invalid "success" status values to "synced" +- Resolved activity fetch API errors that occurred after mirroring operations + +### Changed +- Improved error handling and validation for mirror job status tracking +- Enhanced reliability of organization creation and mirroring processes + +### Internal +- Consolidated Gitea integration modules for better maintainability +- Improved test coverage for mirror operations + +## [3.1.1] - 2025-07-30 + +### Fixed +- Various bug fixes and stability improvements + +## [3.1.0] - 2025-07-21 + +### Added +- Support for GITHUB_EXCLUDED_ORGS environment variable to filter out specific organizations during discovery +- New textarea UI component for improved form inputs in configuration + +### Fixed +- Fixed test failures related to mirror strategy configuration location +- Corrected organization repository routing logic for different mirror strategies +- Fixed starred repositories organization routing bug +- Resolved SSO and OIDC authentication issues + +### Improved +- Enhanced organization configuration for better repository routing control +- Better handling of mirror strategies in test suite +- Improved error handling in authentication flows + +## [3.0.0] - 2025-07-17 + +### 🔴 Breaking Changes +- **Authentication System Overhaul**: Migrated from JWT to Better Auth session-based authentication +- **Login Method Changed**: Users now log in with email instead of username +- **Environment Variables**: `JWT_SECRET` renamed to `BETTER_AUTH_SECRET`, new `BETTER_AUTH_URL` required +- **API Endpoints**: Authentication endpoints moved from `/api/auth/login` to `/api/auth/[...all]` + +### Added +- **Token Encryption**: All GitHub and Gitea tokens now encrypted with AES-256-GCM +- **SSO/OIDC Support**: Enterprise authentication with OAuth providers (Google, Azure AD, Okta, Authentik, etc.) +- **Header Authentication**: Support for reverse proxy authentication headers (Authentik, Authelia, Traefik Forward Auth) +- **OAuth Provider**: Gitea Mirror can act as an OIDC provider for other applications +- **Automated Migration**: Docker containers auto-migrate from v2 to v3 +- **Session Management**: Improved security with session-based authentication +- **Database Migration System**: Drizzle Kit for better schema management +- **Zod v4 Compatibility**: Updated to Zod v4 for schema validation + +### Improved +- **Security**: Enhanced error handling and security practices throughout +- **Documentation**: Comprehensive migration guide for v2 to v3 upgrade +- **User Management**: Better Auth provides improved user lifecycle management +- **Database Schema**: Optimized with proper indexes and relationships +- **Password Hashing**: Using bcrypt via Better Auth for secure password storage + +### Fixed +- Mirroring issues for starred repositories +- Various security vulnerabilities in authentication system +- Improved error handling across all API endpoints + +### Migration Required +- All users must re-authenticate after upgrade +- Existing tokens will be automatically encrypted +- Database schema updates applied automatically +- See [Migration Guide](MIGRATION_GUIDE.md) for detailed instructions + +## [2.22.0] - 2025-07-07 + +### Added +- Comprehensive mobile and responsive design support across the entire application +- New drawer UI component for enhanced mobile navigation +- Mobile-specific layouts for major components (ActivityLog, Header, Organization, Repository) +- Mobile screenshots in documentation showcasing responsive design + +### Improved +- Enhanced mobile user experience with optimized layouts for smaller screens +- Updated organization list cards with better mobile responsiveness +- Better touch interaction support throughout the application + +### Fixed +- Type definition issues resolved +- Removed unnecessary console.log statements + +### Documentation +- Updated README with mobile usage instructions and screenshots +- Added mobile-specific documentation sections + +## [2.20.1] - 2025-07-07 + +### Fixed +- Fixed mixed mode organization strategy not persisting after page refresh + - Added missing "mixed" case handler in GiteaConfigForm component + - Enhanced getMirrorStrategy function to properly detect mixed mode configuration +- Updated dependencies to latest versions + +## [2.20.0] - 2025-07-07 + +### Changed +- **BREAKING**: Repository moved from `arunavo4/gitea-mirror` to `RayLabsHQ/gitea-mirror` +- Docker images now hosted at `ghcr.io/raylabshq/gitea-mirror` +- Updated all repository references and links to new organization +- License changed from MIT to GNU General Public License v3.0 + +### Fixed +- Updated GitHub API endpoint for version checking to use new repository location +- Corrected all documentation references to point to RayLabsHQ organization + +### Security +- Removed test security script after confirming vulnerability resolution +- Updated base Docker image to version 1.2.18-alpine + +### Documentation +- Added repository migration notice in README +- Updated quickstart guide with new repository URLs +- Updated LXC deployment documentation with new repository location + +## [2.18.0] - 2025-06-24 + +### Added +- Fourth organization strategy "Mixed Mode" that combines aspects of existing strategies + - Personal repositories go to a single configurable organization + - Organization repositories preserve their GitHub organization structure +- "Override Options" info button in Organization Strategy component explaining customization features + - Organization overrides via edit buttons on organization cards + - Repository overrides via inline destination editor + - Starred repositories behavior and priority hierarchy + +### Improved +- Simplified mixed strategy implementation to reuse existing database fields +- Enhanced organization strategy UI with comprehensive override documentation +- Better visual indicators for the new mixed strategy with orange color theme + +## [2.17.0] - 2025-06-24 + +### Added +- Custom destination control for individual repositories with inline editing +- Organization-level destination overrides with visual destination editor +- Personal repositories organization override configuration option +- Visual indicators for starred repositories (⭐ icon) in repository list +- Repository-level destination override API endpoint +- Destination customization priority hierarchy system +- "View on Gitea" buttons for organizations with smart tooltip states + +### Changed +- Enhanced repository table with destination column showing both GitHub org and Gitea destination +- Updated organization cards to display custom destinations with visual indicators +- Improved getGiteaRepoOwnerAsync to support repository-level destination overrides + +### Improved +- Better visual feedback for custom destinations with badges and inline editing +- Enhanced user experience with hover-based edit buttons +- Comprehensive destination customization documentation in README + +## [2.16.3] - 2025-06-20 + +### Added +- Custom 404 error page with helpful navigation links +- HoverCard components for better UX in configuration forms + +### Improved +- Replaced popover components with hover cards for information tooltips +- Enhanced user experience with responsive hover interactions + +## [2.16.2] - 2025-06-17 + +### Added +- Bulk actions for repository management with selection support + +### Improved +- Enhanced organization card display with status badges and improved layout + +## [2.16.1] - 2025-06-17 + +### Improved +- Improved repository owner handling and mirror strategy in Gitea integration +- Updated label for starred repositories organization for consistency + +## [2.16.0] - 2025-06-17 + +### Added +- Enhanced OrganizationConfiguration component with improved layout and metadata options +- New GitHubMirrorSettings component with better organization and flexibility +- Enhanced starred repositories content selection and improved layout + +### Improved +- Enhanced configuration interface layout and spacing across multiple components +- Streamlined OrganizationStrategy component with cleaner imports and better organization +- Improved responsive layout for larger screens in configuration forms +- Better icon usage and clarity in configuration components +- Enhanced tooltip descriptions and component organization +- Improved version comparison logic in health API +- Enhanced issue mirroring logic for starred repositories + +### Fixed +- Fixed mirror to single organization functionality +- Resolved organization strategy layout issues +- Cleaned up unused imports across multiple components + +### Refactored +- Simplified component structures by removing unused imports and dependencies +- Enhanced layout flexibility in GitHubConfigForm and GiteaConfigForm components +- Improved component organization and code clarity +- Removed ConnectionsForm and useMirror hook for better code organization + +## [2.14.0] - 2025-06-17 + +### Added +- Enhanced UI components with @radix-ui/react-accordion dependency for improved configuration interface + +### Fixed +- Mirror strategies now properly route repositories based on selected strategy +- Starred repositories now correctly go to the designated starred repos organization +- Organization routing for single-org and flat-user strategies + +### Improved +- Documentation now explains all three mirror strategies (preserve, single-org, flat-user) +- Added detailed mirror strategy configuration guide +- Updated CLAUDE.md with mirror strategy architecture information +- Enhanced Docker Compose development configuration + +## [2.13.2] - 2025-06-15 + +### Improved +- Enhanced documentation design and layout +- Updated README with improved formatting and content + +## [2.13.1] - 2025-06-15 + +### Added +- Docker Hub authentication for Docker Scout security scanning +- Comprehensive Docker workflow consolidation with build, push & security scan + +### Improved +- Enhanced CI/CD pipeline reliability with better error handling +- Updated Bun base image to latest version for improved security +- Migrated from Trivy to Docker Scout for more comprehensive security scanning +- Enhanced Docker workflow with wait steps for image availability + +### Fixed +- Docker Scout action integration issues and image reference problems +- Workflow reliability improvements with proper error handling +- Security scanning workflow now continues on security issues without failing the build + +### Changed +- Updated package dependencies to latest versions +- Consolidated multiple Docker workflows into single comprehensive workflow +- Enhanced security scanning with Docker Scout integration + +## [2.13.0] - 2025-06-15 + +### Added +- Enhanced Configuration Interface with collapsible components and improved organization strategy UI +- Wiki Mirroring Support in configuration settings +- Auto-Save Functionality for all config forms, eliminating manual save buttons +- Live Refresh functionality with configuration status hooks and enhanced UI components +- Enhanced API Config Handling with mapping functions for UI and database structures +- Secure Error Responses with createSecureErrorResponse for consistent error handling +- Automatic Database Cleanup feature with configuration options and API support +- Enhanced Job Recovery with improved database schema and recovery mechanisms +- Fork tags to repository UI and enhanced organization cards with repository breakdown +- Skeleton loaders and better loading state management across the application + +### Improved +- Navigation context and component loading states across the application +- Card components alignment and styling consistency +- Error logging and structured error message parsing +- HTTP client standardization across the application +- Database initialization and management processes +- Visual consistency with updated icons and custom logo integration + +### Fixed +- Repository mirroring status inconsistencies +- Organizations getting stuck on mirroring status when empty +- JSON parsing errors and improved error handling +- Broken documentation links in README +- Various UI contrast and alignment issues + +### Changed +- Migrated testing framework to Bun and updated test configurations +- Implemented graceful shutdown and enhanced job recovery capabilities +- Replaced SiGitea icons with custom logo +- Updated various dependencies for improved stability and performance + +## [2.12.0] - 2025-01-27 + +### Fixed +- Fixed SQLite "no such table: mirror_jobs" error during application startup +- Implemented automatic database table creation during database initialization +- Resolved database schema inconsistencies between development and production environments + +### Improved +- Enhanced database initialization process with automatic table creation and indexing +- Added comprehensive error handling for database table creation +- Integrated database repair functionality into application startup for better reliability + +## [2.5.3] - 2025-05-22 + +### Added +- Enhanced JWT_SECRET handling with auto-generation and persistence for improved security +- Updated Proxmox LXC deployment instructions and replaced deprecated script + +## [2.5.2] - 2024-11-22 + +### Fixed +- Fixed version information in health API for Docker deployments by setting npm_package_version environment variable in entrypoint script + +## [2.5.1] - 2024-10-01 + +### Fixed +- Fixed Docker entrypoint script to prevent unnecessary `bun install` on container startup +- Removed redundant dependency installation in Docker containers for pre-built images +- Fixed "PathAlreadyExists" errors during container initialization + +### Changed +- Improved database initialization in Docker entrypoint script +- Added additional checks for TypeScript versions of database management scripts + +## [2.5.0] - 2024-09-15 + +Initial public release with core functionality: + +### Added +- GitHub to Gitea repository mirroring +- User authentication and management +- Dashboard with mirroring statistics +- Configuration management for mirroring settings +- Support for organization mirroring +- Automated mirroring with configurable schedules +- Docker multi-architecture support (amd64, arm64) +- LXC container deployment scripts diff --git a/Divers/gitea-mirror/CLAUDE.md b/Divers/gitea-mirror/CLAUDE.md new file mode 100644 index 0000000..65dc35a --- /dev/null +++ b/Divers/gitea-mirror/CLAUDE.md @@ -0,0 +1,317 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +## Project Overview + +Gitea Mirror is a self-hosted web application that automatically mirrors repositories from GitHub to Gitea instances. It's built with Astro (SSR mode), React, and runs on the Bun runtime with SQLite for data persistence. + +**Key capabilities:** +- Mirrors public, private, and starred GitHub repos to Gitea +- Supports metadata mirroring (issues, PRs as issues, labels, milestones, releases, wiki) +- Git LFS support +- Multiple authentication methods (email/password, OIDC/SSO, header auth) +- Scheduled automatic syncing with configurable intervals +- Auto-discovery of new repos and cleanup of deleted repos +- Multi-user support with encrypted token storage (AES-256-GCM) + +## Development Commands + +### Setup and Installation +```bash +# Install dependencies +bun install + +# Initialize database (first time setup) +bun run setup + +# Clean start (reset database) +bun run dev:clean +``` + +### Development +```bash +# Start development server (http://localhost:4321) +bun run dev + +# Build for production +bun run build + +# Preview production build +bun run preview + +# Start production server +bun run start +``` + +### Testing +```bash +# Run all tests +bun test + +# Run tests in watch mode +bun test:watch + +# Run tests with coverage +bun test:coverage +``` + +**Test configuration:** +- Test runner: Bun's built-in test runner (configured in `bunfig.toml`) +- Setup file: `src/tests/setup.bun.ts` (auto-loaded via bunfig.toml) +- Timeout: 5000ms default +- Tests are colocated with source files using `*.test.ts` pattern + +### Database Management +```bash +# Database operations via Drizzle +bun run db:generate # Generate migrations from schema +bun run db:migrate # Run migrations +bun run db:push # Push schema changes directly +bun run db:studio # Open Drizzle Studio (database GUI) +bun run db:check # Check schema consistency + +# Database utilities via custom scripts +bun run manage-db init # Initialize database +bun run manage-db check # Check database health +bun run manage-db fix # Fix database issues +bun run manage-db reset-users # Reset all users +bun run cleanup-db # Delete database file +``` + +### Utility Scripts +```bash +# Recovery and diagnostic scripts +bun run startup-recovery # Recover from crashes +bun run startup-recovery-force # Force recovery +bun run test-recovery # Test recovery mechanism +bun run test-shutdown # Test graceful shutdown + +# Environment configuration +bun run startup-env-config # Load config from env vars +``` + +## Architecture + +### Tech Stack +- **Frontend:** Astro v5 (SSR mode) + React v19 + Shadcn UI + Tailwind CSS v4 +- **Backend:** Astro API routes (Node adapter, standalone mode) +- **Runtime:** Bun (>=1.2.9) +- **Database:** SQLite via Drizzle ORM +- **Authentication:** Better Auth (session-based) +- **APIs:** GitHub (Octokit with throttling plugin), Gitea REST API + +### Directory Structure + +``` +src/ +├── components/ # React components (UI, features) +│ ├── ui/ # Shadcn UI components +│ ├── repositories/ # Repository management components +│ ├── organizations/ # Organization management components +│ └── ... +├── pages/ # Astro pages and API routes +│ ├── api/ # API endpoints (Better Auth integration) +│ │ ├── auth/ # Authentication endpoints +│ │ ├── github/ # GitHub operations +│ │ ├── gitea/ # Gitea operations +│ │ ├── sync/ # Mirror sync operations +│ │ ├── job/ # Job management +│ │ └── ... +│ └── *.astro # Page components +├── lib/ # Core business logic +│ ├── db/ # Database (Drizzle ORM) +│ │ ├── schema.ts # Database schema with Zod validation +│ │ ├── index.ts # Database instance and table exports +│ │ └── adapter.ts # Better Auth SQLite adapter +│ ├── github.ts # GitHub API client (Octokit) +│ ├── gitea.ts # Gitea API client +│ ├── gitea-enhanced.ts # Enhanced Gitea operations (metadata) +│ ├── scheduler-service.ts # Automatic mirroring scheduler +│ ├── cleanup-service.ts # Activity log cleanup +│ ├── repository-cleanup-service.ts # Orphaned repo cleanup +│ ├── auth.ts # Better Auth configuration +│ ├── config.ts # Configuration management +│ ├── helpers.ts # Mirror job creation +│ ├── utils/ # Utility functions +│ │ ├── encryption.ts # AES-256-GCM token encryption +│ │ ├── config-encryption.ts # Config token encryption +│ │ ├── duration-parser.ts # Parse intervals (e.g., "8h", "30m") +│ │ ├── concurrency.ts # Concurrency control utilities +│ │ └── mirror-strategies.ts # Mirror strategy logic +│ └── ... +├── types/ # TypeScript type definitions +├── tests/ # Test utilities and setup +└── middleware.ts # Astro middleware (auth, session) + +scripts/ # Utility scripts +├── manage-db.ts # Database management CLI +├── startup-recovery.ts # Crash recovery +└── ... +``` + +### Key Architectural Patterns + +#### 1. Database Schema and Validation +- **Location:** `src/lib/db/schema.ts` +- **Pattern:** Drizzle ORM tables + Zod schemas for validation +- **Key tables:** + - `configs` - User configuration (GitHub/Gitea settings, mirror options) + - `repositories` - Tracked repositories with metadata + - `organizations` - GitHub organizations with destination overrides + - `mirrorJobs` - Mirror job queue and history + - `activities` - Activity log for dashboard + - `user`, `session`, `account` - Better Auth tables + +**Important:** All config tokens (GitHub/Gitea) are encrypted at rest using AES-256-GCM. Use helper functions from `src/lib/utils/config-encryption.ts` to decrypt. + +#### 2. Mirror Job System +- **Location:** `src/lib/helpers.ts` (createMirrorJob) +- **Flow:** + 1. User triggers mirror via API endpoint + 2. `createMirrorJob()` creates job record with status "pending" + 3. Job processor (in API routes) performs GitHub → Gitea operations + 4. Job status updated throughout: "mirroring" → "success"/"failed" + 5. Events published via SSE for real-time UI updates + +#### 3. GitHub ↔ Gitea Mirroring +- **GitHub Client:** `src/lib/github.ts` - Octokit with rate limit tracking +- **Gitea Client:** `src/lib/gitea.ts` - Basic repo operations +- **Enhanced Gitea:** `src/lib/gitea-enhanced.ts` - Metadata mirroring (issues, PRs, releases) + +**Mirror strategies (configured per user):** +- `preserve` - Maintain GitHub org structure in Gitea +- `single-org` - All repos into one Gitea org +- `flat-user` - All repos under user account +- `mixed` - Personal repos in one org, org repos preserve structure + +**Metadata mirroring:** +- Issues transferred with comments, labels, assignees +- PRs converted to issues (Gitea API limitation - cannot create PRs) + - Tagged with "pull-request" label + - Title prefixed with `[PR #number] [STATUS]` + - Body includes commit history, file changes, merge status +- Releases mirrored with assets +- Labels and milestones preserved +- Wiki content cloned if enabled +- **Sequential processing:** Issues/PRs mirrored one at a time to prevent out-of-order creation (see `src/lib/gitea-enhanced.ts`) + +#### 4. Scheduler Service +- **Location:** `src/lib/scheduler-service.ts` +- **Features:** + - Cron-based or interval-based scheduling (uses `duration-parser.ts`) + - Auto-start on boot when `SCHEDULE_ENABLED=true` or `GITEA_MIRROR_INTERVAL` is set + - Auto-import new GitHub repos + - Auto-cleanup orphaned repos (archive or delete) + - Respects per-repo mirror intervals (not Gitea's default 24h) +- **Concurrency control:** Uses `src/lib/utils/concurrency.ts` for batch processing + +#### 5. Authentication System +- **Location:** `src/lib/auth.ts`, `src/lib/auth-client.ts` +- **Better Auth integration:** + - Email/password (always enabled) + - OIDC/SSO providers (configurable via UI) + - Header authentication for reverse proxies (Authentik, Authelia) +- **Session management:** Cookie-based, validated in Astro middleware +- **User helpers:** `src/lib/utils/auth-helpers.ts` + +#### 6. Environment Configuration +- **Startup:** `src/lib/env-config-loader.ts` + `scripts/startup-env-config.ts` +- **Pattern:** Environment variables can pre-configure settings, but users can override via web UI +- **Encryption:** `ENCRYPTION_SECRET` for tokens, `BETTER_AUTH_SECRET` for sessions + +#### 7. Real-time Updates +- **Events:** `src/lib/events.ts` + `src/lib/events/realtime.ts` +- **Pattern:** Server-Sent Events (SSE) for live dashboard updates +- **Endpoints:** `/api/sse` - client subscribes to job/repo events + +### Testing Patterns + +**Unit tests:** +- Colocated with source: `filename.test.ts` alongside `filename.ts` +- Use Bun's built-in assertions and mocking +- Mock external APIs (GitHub, Gitea) using `src/tests/mock-fetch.ts` + +**Integration tests:** +- Located in `src/tests/` +- Test database operations with in-memory SQLite +- Example: `src/lib/db/index.test.ts` + +**Test utilities:** +- `src/tests/setup.bun.ts` - Global test setup (loaded via bunfig.toml) +- `src/tests/mock-fetch.ts` - Fetch mocking utilities + +### Important Development Notes + +1. **Path Aliases:** Use `@/` for imports (configured in `tsconfig.json`) + ```typescript + import { db } from '@/lib/db'; + ``` + +2. **Token Encryption:** Always use encryption helpers when dealing with tokens: + ```typescript + import { getDecryptedGitHubToken, getDecryptedGiteaToken } from '@/lib/utils/config-encryption'; + ``` + +3. **API Route Pattern:** Astro API routes in `src/pages/api/` should: + - Check authentication via Better Auth + - Validate input with Zod schemas + - Handle errors gracefully + - Return JSON responses + +4. **Database Migrations:** + - Schema changes: Update `src/lib/db/schema.ts` + - Generate migration: `bun run db:generate` + - Review generated SQL in `drizzle/` directory + - Apply: `bun run db:migrate` (or `db:push` for dev) + +5. **Concurrency Control:** + - Use utilities from `src/lib/utils/concurrency.ts` for batch operations + - Respect rate limits (GitHub: 5000 req/hr authenticated, Gitea: varies) + - Issue/PR mirroring is sequential to maintain chronological order + +6. **Duration Parsing:** + - Use `parseInterval()` from `src/lib/utils/duration-parser.ts` + - Supports: "30m", "8h", "24h", "7d", cron expressions, or milliseconds + +7. **Graceful Shutdown:** + - Services implement cleanup handlers (see `src/lib/shutdown-manager.ts`) + - Recovery system in `src/lib/recovery.ts` handles interrupted jobs + +## Common Development Workflows + +### Adding a new mirror option +1. Update Zod schema in `src/lib/db/schema.ts` (e.g., `giteaConfigSchema`) +2. Update TypeScript types in `src/types/config.ts` +3. Add UI control in settings page component +4. Update API handler in `src/pages/api/config/` +5. Implement logic in `src/lib/gitea.ts` or `src/lib/gitea-enhanced.ts` + +### Debugging mirror failures +1. Check mirror jobs: `bun run db:studio` → `mirrorJobs` table +2. Review activity logs: Dashboard → Activity tab +3. Check console logs for API errors (GitHub/Gitea rate limits, auth issues) +4. Use diagnostic scripts: `bun run test-recovery` + +### Adding authentication provider +1. Update Better Auth config in `src/lib/auth.ts` +2. Add provider configuration UI in settings +3. Test with `src/tests/test-gitea-auth.ts` patterns +4. Update documentation in `docs/SSO-OIDC-SETUP.md` + +## Docker Deployment + +- **Dockerfile:** Multi-stage build (bun base → build → production) +- **Entrypoint:** `docker-entrypoint.sh` - handles CA certs, user permissions, database init +- **Compose files:** + - `docker-compose.alt.yml` - Quick start (pre-built image, minimal config) + - `docker-compose.yml` - Full setup (build from source, all env vars) + - `docker-compose.dev.yml` - Development with hot reload + +## Additional Resources + +- **Environment Variables:** See `docs/ENVIRONMENT_VARIABLES.md` for complete list +- **Development Workflow:** See `docs/DEVELOPMENT_WORKFLOW.md` +- **SSO Setup:** See `docs/SSO-OIDC-SETUP.md` +- **Contributing:** See `CONTRIBUTING.md` for code guidelines and scope +- **Graceful Shutdown:** See `docs/GRACEFUL_SHUTDOWN.md` for crash recovery details diff --git a/Divers/gitea-mirror/CONTRIBUTING.md b/Divers/gitea-mirror/CONTRIBUTING.md new file mode 100644 index 0000000..cac2a3e --- /dev/null +++ b/Divers/gitea-mirror/CONTRIBUTING.md @@ -0,0 +1,182 @@ +# Contributing to Gitea Mirror + +Thank you for your interest in contributing to Gitea Mirror! This document provides guidelines and instructions for contributing to the open-source version of the project. + +## 🎯 Project Overview + +Gitea Mirror is an open-source, self-hosted solution for mirroring GitHub repositories to Gitea instances. This guide provides everything you need to know about contributing to the project. + +## 🚀 Getting Started + +1. Fork the repository +2. Clone your fork: + ```bash + git clone https://github.com/yourusername/gitea-mirror.git + cd gitea-mirror + ``` + +3. Install dependencies: + ```bash + bun install + ``` + +4. Set up your environment: + ```bash + cp .env.example .env + # Edit .env with your configuration + ``` + +5. Start development: + ```bash + bun run dev + ``` + +## 🛠 Development Workflow + +### Running the Application + +```bash +# Development mode +bun run dev + +# Build for production +bun run build + +# Run tests +bun test +``` + +### Database Management + +```bash +# Initialize database +bun run init-db + +# Reset database +bun run cleanup-db && bun run init-db +``` + +## 📝 Code Guidelines + +### General Principles + +1. **Keep it Simple**: Gitea Mirror should remain easy to self-host +2. **Focus on Core Features**: Prioritize repository mirroring and synchronization +3. **Database**: Use SQLite for simplicity and portability +4. **Dependencies**: Minimize external dependencies for easier deployment + +### Code Style + +- Use TypeScript for all new code +- Follow the existing code formatting (Prettier is configured) +- Write meaningful commit messages +- Add tests for new features + +### Scope of Contributions + +This project focuses on personal/small team use cases. Please keep contributions aligned with: +- Core mirroring functionality +- Self-hosted simplicity +- Minimal external dependencies +- SQLite as the database +- Single-instance deployments + +## 🐛 Reporting Issues + +1. Check existing issues first +2. Use issue templates when available +3. Provide clear reproduction steps +4. Include relevant logs and screenshots + +## 🎯 Pull Request Process + +1. Create a feature branch: + ```bash + git checkout -b feature/your-feature-name + ``` + +2. Make your changes following the code guidelines + +3. Test your changes: + ```bash + # Run tests + bun test + + # Build and check + bun run build:oss + ``` + +4. Commit your changes: + ```bash + git commit -m "feat: add new feature" + ``` + +5. Push to your fork and create a Pull Request + +### PR Requirements + +- Clear description of changes +- Tests for new functionality +- Documentation updates if needed +- No breaking changes without discussion +- Passes all CI checks + +## 🏗 Architecture Overview + +``` +src/ +├── components/ # React components +├── lib/ # Core utilities +│ ├── db/ # Database queries (SQLite only) +│ ├── github/ # GitHub API integration +│ ├── gitea/ # Gitea API integration +│ └── utils/ # Helper functions +├── pages/ # Astro pages +│ └── api/ # API endpoints +└── types/ # TypeScript types +``` + +## 🧪 Testing + +```bash +# Run all tests +bun test + +# Run tests in watch mode +bun test:watch + +# Run with coverage +bun test:coverage +``` + +## 📚 Documentation + +- Update README.md for user-facing changes +- Add JSDoc comments for new functions +- Update .env.example for new environment variables + +## 💡 Feature Requests + +We welcome feature requests! When proposing new features, please consider: +- Does it enhance the core mirroring functionality? +- Will it benefit self-hosted users? +- Can it be implemented without complex external dependencies? +- Does it maintain the project's simplicity? + +## 🤝 Community + +- Be respectful and constructive +- Help others in issues and discussions +- Share your use cases and feedback + +## 📄 License + +By contributing, you agree that your contributions will be licensed under the same license as the project (MIT). + +## Questions? + +Feel free to open an issue for any questions about contributing! + +--- + +Thank you for helping make Gitea Mirror better! 🎉 \ No newline at end of file diff --git a/Divers/gitea-mirror/DISTRIBUTION_SUMMARY.md b/Divers/gitea-mirror/DISTRIBUTION_SUMMARY.md new file mode 100644 index 0000000..6031abe --- /dev/null +++ b/Divers/gitea-mirror/DISTRIBUTION_SUMMARY.md @@ -0,0 +1,169 @@ +# Nix Distribution - Ready to Use! + +## Current Status: WORKS NOW + +Your Nix package is **already distributable**! Users can run it directly from GitHub without any additional setup on your end. + +## How Users Will Use It + +### Simple: Just Run From GitHub + +```bash +nix run --extra-experimental-features 'nix-command flakes' github:RayLabsHQ/gitea-mirror +``` + +That's it! No releases, no CI, no infrastructure needed. It works right now. + +--- + +## What Happens When They Run This? + +1. **Nix fetches** your repo from GitHub +2. **Nix reads** `flake.nix` and `flake.lock` +3. **Nix builds** the package on their machine +4. **Nix runs** the application +5. **Result cached** in `/nix/store` for reuse + +--- + +## Do You Need CI or Releases? + +### For Basic Usage: **NO** +Users can already use it from GitHub. No CI or releases required. + +### For CI Validation: **Already Set Up** +GitHub Actions validates builds on every push with Magic Nix Cache (free, no setup). + +--- + +## Next Steps (Optional) + +### Option 1: Release Versioning (2 minutes) + +**Why:** Users can pin to specific versions + +**How:** +```bash +# When ready to release +git tag v3.8.11 +git push origin v3.8.11 + +# Users can then pin to this version +nix run github:RayLabsHQ/gitea-mirror/v3.8.11 +``` + +No additional CI needed - tags work automatically with flakes! + +### Option 2: Submit to nixpkgs (Long Term) + +**Why:** Maximum discoverability and trust + +**When:** After package is stable and well-tested + +**How:** Submit PR to https://github.com/NixOS/nixpkgs + +--- + +## Files Created + +### Essential (Already Working) +- `flake.nix` - Package definition +- `flake.lock` - Dependency lock file +- `.envrc` - direnv integration + +### Documentation +- `NIX.md` - Quick reference for users +- `docs/NIX_DEPLOYMENT.md` - Complete deployment guide +- `docs/NIX_DISTRIBUTION.md` - Distribution guide for you (maintainer) +- `README.md` - Updated with Nix instructions + +### CI (Already Set Up) +- `.github/workflows/nix-build.yml` - Builds and validates on Linux + macOS + +### Updated +- `.gitignore` - Added Nix artifacts + +--- + +## Comparison: Your Distribution Options + +| Setup | Time | User Experience | What You Need | +|-------|------|----------------|---------------| +| **Direct GitHub** | 0 min | Slow (build from source) | Nothing! Works now | +| **+ Git Tags** | 2 min | Versionable | Just push tags | +| **+ nixpkgs** | Hours | Official/Trusted | PR review process | + +**Recommendation:** Direct GitHub works now. Add git tags for versioning. Consider nixpkgs submission once stable. + +--- + +## Testing Your Distribution + +You can test it right now: + +```bash +# Test direct GitHub usage +nix run --extra-experimental-features 'nix-command flakes' github:RayLabsHQ/gitea-mirror + +# Test with specific commit +nix run github:RayLabsHQ/gitea-mirror/$(git rev-parse HEAD) + +# Validate flake +nix flake check +``` + +--- + +## User Documentation Locations + +Users will find instructions in: +1. **README.md** - Installation section (already updated) +2. **NIX.md** - Quick reference +3. **docs/NIX_DEPLOYMENT.md** - Detailed guide + +All docs include the correct commands with experimental features flags. + +--- + +## When to Release New Versions + +### For Git Tag Releases: +```bash +# 1. Update version in package.json +vim package.json + +# 2. Update version in flake.nix (line 17) +vim flake.nix # version = "3.8.12"; + +# 3. Commit and tag +git add package.json flake.nix +git commit -m "chore: bump version to v3.8.12" +git tag v3.8.12 +git push origin main +git push origin v3.8.12 +``` + +Users can then use: `nix run github:RayLabsHQ/gitea-mirror/v3.8.12` + +### No Release Needed For: +- Bug fixes +- Small changes +- Continuous updates + +Users can always use latest from main: `nix run github:RayLabsHQ/gitea-mirror` + +--- + +## Summary + +**Ready to distribute RIGHT NOW** +- Just commit and push your `flake.nix` +- Users can run directly from GitHub +- CI validates builds automatically + +**Optional: Submit to nixpkgs** +- Maximum discoverability +- Official Nix repository +- Do this once package is stable + +See `docs/NIX_DISTRIBUTION.md` for complete details! diff --git a/Divers/gitea-mirror/Dockerfile b/Divers/gitea-mirror/Dockerfile new file mode 100644 index 0000000..0193e9e --- /dev/null +++ b/Divers/gitea-mirror/Dockerfile @@ -0,0 +1,60 @@ +# syntax=docker/dockerfile:1.4 + +FROM oven/bun:1.3.3-debian AS base +WORKDIR /app +RUN apt-get update && apt-get install -y --no-install-recommends \ + python3 make g++ gcc wget sqlite3 openssl ca-certificates \ + && rm -rf /var/lib/apt/lists/* + +# ---------------------------- +FROM base AS deps +COPY package.json ./ +COPY bun.lock* ./ +RUN bun install --frozen-lockfile + +# ---------------------------- +FROM deps AS builder +COPY . . +RUN bun run build +RUN mkdir -p dist/scripts && \ + for script in scripts/*.ts; do \ + bun build "$script" --target=bun --outfile=dist/scripts/$(basename "${script%.ts}.js"); \ + done + +# ---------------------------- +FROM deps AS pruner +RUN bun install --production --frozen-lockfile + +# ---------------------------- +FROM base AS runner +WORKDIR /app +COPY --from=pruner /app/node_modules ./node_modules +COPY --from=builder /app/dist ./dist +COPY --from=builder /app/package.json ./package.json +COPY --from=builder /app/docker-entrypoint.sh ./docker-entrypoint.sh +COPY --from=builder /app/scripts ./scripts +COPY --from=builder /app/drizzle ./drizzle + +ENV NODE_ENV=production +ENV HOST=0.0.0.0 +ENV PORT=4321 +ENV DATABASE_URL=file:data/gitea-mirror.db + +# Create directories and setup permissions +RUN mkdir -p /app/certs && \ + chmod +x ./docker-entrypoint.sh && \ + mkdir -p /app/data && \ + addgroup --system --gid 1001 nodejs && \ + adduser --system --uid 1001 gitea-mirror && \ + chown -R gitea-mirror:nodejs /app/data && \ + chown -R gitea-mirror:nodejs /app/certs + +USER gitea-mirror + +VOLUME /app/data +EXPOSE 4321 + +HEALTHCHECK --interval=30s --timeout=5s --start-period=5s --retries=3 \ + CMD wget --no-verbose --tries=1 --spider http://localhost:4321/api/health || exit 1 + +ENTRYPOINT ["./docker-entrypoint.sh"] \ No newline at end of file diff --git a/Divers/gitea-mirror/LICENSE b/Divers/gitea-mirror/LICENSE new file mode 100644 index 0000000..ca9b055 --- /dev/null +++ b/Divers/gitea-mirror/LICENSE @@ -0,0 +1,619 @@ + GNU AFFERO GENERAL PUBLIC LICENSE + Version 3, 19 November 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU Affero General Public License is a free, copyleft license for +software and other kinds of works, specifically designed to ensure +cooperation with the community in the case of network server software. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +our General Public Licenses are intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + Developers that use our General Public Licenses protect your rights +with two steps: (1) assert copyright on the software, and (2) offer +you this License which gives you legal permission to copy, distribute +and/or modify the software. + + A secondary benefit of defending all users' freedom is that +improvements made in alternate versions of the program, if they +receive widespread use, become available for other developers to +incorporate. Many developers of free software are heartened and +encouraged by the resulting cooperation. However, in the case of +software used on network servers, this result may fail to come about. +The GNU General Public License permits making a modified version and +letting the public access it on a server without ever releasing its +source code to the public. + + The GNU Affero General Public License is designed specifically to +ensure that, in such cases, the modified source code becomes available +to the community. It requires the operator of a network server to +provide the source code of the modified version running there to the +users of that server. Therefore, public use of a modified version, on +a publicly accessible server, gives the public access to the source +code of the modified version. + + An older license, called the Affero General Public License and +published by Affero, was designed to accomplish similar goals. This is +a different license, not a version of the Affero GPL, but Affero has +released a new version of the Affero GPL which permits relicensing under +this license. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU Affero General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Remote Network Interaction; Use with the GNU General Public License. + + Notwithstanding any other provision of this License, if you modify the +Program, your modified version must prominently offer all users +interacting with it remotely through a computer network (if your version +supports such interaction) an opportunity to receive the Corresponding +Source of your version by providing access to the Corresponding Source +from a network server at no charge, through some standard or customary +means of facilitating copying of software. This Corresponding Source +shall include the Corresponding Source for any work covered by version 3 +of the GNU General Public License that is incorporated pursuant to the +following paragraph. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the work with which it is combined will remain governed by version +3 of the GNU General Public License. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU Affero General Public License from time to time. Such new versions +will be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU Affero General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU Affero General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU Affero General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS diff --git a/Divers/gitea-mirror/NIX.md b/Divers/gitea-mirror/NIX.md new file mode 100644 index 0000000..4026f2d --- /dev/null +++ b/Divers/gitea-mirror/NIX.md @@ -0,0 +1,189 @@ +# Nix Deployment Quick Reference + +## TL;DR + +```bash +# From GitHub (no clone needed!) +nix run --extra-experimental-features 'nix-command flakes' github:RayLabsHQ/gitea-mirror + +# Or from local clone +nix run --extra-experimental-features 'nix-command flakes' .#gitea-mirror +``` + +Secrets auto-generate, database auto-initializes, and the web UI starts at http://localhost:4321. + +**Note:** If you have flakes enabled in your nix config, you can omit `--extra-experimental-features 'nix-command flakes'` + +--- + +## Installation Options + +### 1. Run Without Installing (from GitHub) +```bash +# Latest version from main branch +nix run --extra-experimental-features 'nix-command flakes' github:RayLabsHQ/gitea-mirror + +# Pin to specific version +nix run github:RayLabsHQ/gitea-mirror/v3.8.11 +``` + +### 2. Install to Profile +```bash +# Install from GitHub +nix profile install --extra-experimental-features 'nix-command flakes' github:RayLabsHQ/gitea-mirror + +# Run the installed binary +gitea-mirror +``` + +### 3. Use Local Clone +```bash +# Clone and run +git clone https://github.com/RayLabsHQ/gitea-mirror.git +cd gitea-mirror +nix run --extra-experimental-features 'nix-command flakes' .#gitea-mirror +``` + +### 4. NixOS System Service +```nix +# configuration.nix +{ + inputs.gitea-mirror.url = "github:RayLabsHQ/gitea-mirror"; + + services.gitea-mirror = { + enable = true; + betterAuthUrl = "https://mirror.example.com"; # For production + openFirewall = true; + }; +} +``` + +### 5. Development (Local Clone) +```bash +nix develop --extra-experimental-features 'nix-command flakes' +# or +direnv allow # Handles experimental features automatically +``` + +--- + +## Enable Flakes Permanently (Recommended) + +To avoid typing `--extra-experimental-features` every time, add to `~/.config/nix/nix.conf`: +``` +experimental-features = nix-command flakes +``` + +--- + +## What Gets Auto-Generated? + +On first run, the wrapper automatically: + +1. Creates `~/.local/share/gitea-mirror/` (or `$DATA_DIR`) +2. Generates `BETTER_AUTH_SECRET` → `.better_auth_secret` +3. Generates `ENCRYPTION_SECRET` → `.encryption_secret` +4. Initializes SQLite database +5. Runs startup recovery and repair scripts +6. Starts the application + +--- + +## Key Commands + +```bash +# Database management +gitea-mirror-db init # Initialize database +gitea-mirror-db check # Health check +gitea-mirror-db fix # Fix issues + +# Development (add --extra-experimental-features 'nix-command flakes' if needed) +nix develop # Enter dev shell +nix build # Build package +nix flake check # Validate flake +nix flake update # Update dependencies +``` + +--- + +## Environment Variables + +All vars from `docker-compose.alt.yml` are supported: + +```bash +DATA_DIR="$HOME/.local/share/gitea-mirror" +PORT=4321 +HOST="0.0.0.0" +BETTER_AUTH_URL="http://localhost:4321" + +# Secrets (auto-generated if not set) +BETTER_AUTH_SECRET=auto-generated +ENCRYPTION_SECRET=auto-generated + +# Concurrency (for perfect ordering, set both to 1) +MIRROR_ISSUE_CONCURRENCY=3 +MIRROR_PULL_REQUEST_CONCURRENCY=5 +``` + +--- + +## NixOS Module Options + +```nix +services.gitea-mirror = { + enable = true; + package = ...; # Override package + dataDir = "/var/lib/gitea-mirror"; # Data location + user = "gitea-mirror"; # Service user + group = "gitea-mirror"; # Service group + host = "0.0.0.0"; # Bind address + port = 4321; # Listen port + betterAuthUrl = "http://..."; # External URL + betterAuthTrustedOrigins = "..."; # CORS origins + mirrorIssueConcurrency = 3; # Concurrency + mirrorPullRequestConcurrency = 5; # Concurrency + environmentFile = null; # Optional secrets file + openFirewall = true; # Open firewall +}; +``` + +--- + +## Comparison: Docker vs Nix + +| Feature | Docker | Nix | +|---------|--------|-----| +| **Config Required** | BETTER_AUTH_SECRET | None (auto-generated) | +| **Startup** | `docker-compose up` | `nix run .#gitea-mirror` | +| **Service** | Docker daemon | systemd (NixOS) | +| **Updates** | `docker pull` | `nix flake update` | +| **Reproducible** | Image-based | Hash-based | + +--- + +## Full Documentation + +- **[docs/NIX_DEPLOYMENT.md](docs/NIX_DEPLOYMENT.md)** - Complete deployment guide + - NixOS module configuration + - Home Manager integration + - Production deployment examples + - Migration from Docker + - Troubleshooting guide + +- **[docs/NIX_DISTRIBUTION.md](docs/NIX_DISTRIBUTION.md)** - Distribution guide for maintainers + - How users consume the package + - CI build caching + - Releasing new versions + - Submitting to nixpkgs + +--- + +## Key Features + +- **Zero-config deployment** - Runs immediately without setup +- **Auto-secret generation** - Secure secrets created and persisted +- **Startup recovery** - Handles interrupted jobs automatically +- **Graceful shutdown** - Proper signal handling +- **Health checks** - Built-in monitoring support +- **Security hardening** - NixOS module includes systemd protections +- **Docker parity** - Same behavior as `docker-compose.alt.yml` diff --git a/Divers/gitea-mirror/README.md b/Divers/gitea-mirror/README.md new file mode 100644 index 0000000..515bb87 --- /dev/null +++ b/Divers/gitea-mirror/README.md @@ -0,0 +1,454 @@ +

+ Gitea Mirror Logo +

Gitea Mirror

+

Automatically mirror repositories from GitHub to your self-hosted Gitea instance.

+

+ release + build + container + license +

+

+ +## 🚀 Quick Start + +```bash +# Fastest way - using the simplified Docker setup +docker compose -f docker-compose.alt.yml up -d + +# Access at http://localhost:4321 +``` + +First user signup becomes admin. Configure GitHub and Gitea through the web interface! + +

+ Dashboard + Dashboard Mobile +

+ +## ✨ Features + +- 🔁 Mirror public, private, and starred GitHub repos to Gitea +- 🏢 Mirror entire organizations with flexible strategies +- 🎯 Custom destination control for repos and organizations +- 📦 **Git LFS support** - Mirror large files with Git LFS +- 📝 **Metadata mirroring** - Issues, pull requests (as issues), labels, milestones, wiki +- 🚫 **Repository ignore** - Mark specific repos to skip +- 🔐 Secure authentication with Better Auth (email/password, SSO, OIDC) +- 📊 Real-time dashboard with activity logs +- ⏱️ Scheduled automatic mirroring with configurable intervals +- 🔄 **Auto-discovery** - Automatically import new GitHub repositories (v3.4.0+) +- 🧹 **Repository cleanup** - Auto-remove repos deleted from GitHub (v3.4.0+) +- 🎯 **Proper mirror intervals** - Respects configured sync intervals (v3.4.0+) +- 🗑️ Automatic database cleanup with configurable retention +- 🐳 Dockerized with multi-arch support (AMD64/ARM64) + +## 📸 Screenshots + +
+ Repositories + Rrepositories Mobile +
+ +
+ Organisations + Organisations Mobile +
+ +## Installation + +### Docker (Recommended) + +We provide two Docker Compose options: + +#### Option 1: Quick Start (docker-compose.alt.yml) +Perfect for trying out Gitea Mirror or simple deployments: + +```bash +# Clone repository +git clone https://github.com/RayLabsHQ/gitea-mirror.git +cd gitea-mirror + +# Start with simplified setup +docker compose -f docker-compose.alt.yml up -d + +# Access at http://localhost:4321 +``` + +**Features:** +- ✅ Pre-built image - no building required +- ✅ Minimal configuration needed +- ✅ Data stored in `./data` directory +- ✅ Configure everything through web UI +- ✅ Automatic user/group ID mapping + +**Best for:** +- First-time users +- Testing and evaluation +- Simple deployments +- When you prefer web-based configuration + +#### Option 2: Full Setup (docker-compose.yml) +For production deployments with environment-based configuration: + +```bash +# Start with full configuration options +docker compose up -d +``` + +**Features:** +- ✅ Build from source or use pre-built image +- ✅ Complete environment variable configuration +- ✅ Support for custom CA certificates +- ✅ Advanced mirror settings (forks, wiki, issues) +- ✅ Multi-registry support + +**Best for:** +- Production deployments +- Automated/scripted setups +- Advanced mirror configurations +- When using self-signed certificates + +#### Using Pre-built Image Directly + +```bash +docker pull ghcr.io/raylabshq/gitea-mirror:v3.1.1 +``` + +### Configuration Options + +#### Quick Start Configuration (docker-compose.alt.yml) + +Minimal `.env` file (optional - has sensible defaults): + +```bash +# Custom port (default: 4321) +PORT=4321 + +# User/Group IDs for file permissions (default: 1000) +PUID=1000 +PGID=1000 + +# Session secret (auto-generated if not set) +BETTER_AUTH_SECRET=your-secret-key-change-this-in-production +``` + +All other settings are configured through the web interface after starting. + +#### Full Setup Configuration (docker-compose.yml) + +Supports extensive environment variables for automated deployment. See the full [docker-compose.yml](docker-compose.yml) for all available options including GitHub tokens, Gitea URLs, mirror settings, and more. + +📚 **For a complete list of all supported environment variables, see the [Environment Variables Documentation](docs/ENVIRONMENT_VARIABLES.md).** + +### LXC Container (Proxmox) + +```bash +# One-line install on Proxmox VE +bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/ct/gitea-mirror.sh)" +``` + +See the [Proxmox VE Community Scripts](https://community-scripts.github.io/ProxmoxVE/scripts?id=gitea-mirror) for more details. + +### Nix/NixOS + +Zero-configuration deployment with Nix: + +```bash +# Run immediately - no setup needed! +nix run --extra-experimental-features 'nix-command flakes' github:RayLabsHQ/gitea-mirror + +# Or build and run locally +nix build --extra-experimental-features 'nix-command flakes' +./result/bin/gitea-mirror + +# Or install to profile +nix profile install --extra-experimental-features 'nix-command flakes' github:RayLabsHQ/gitea-mirror +gitea-mirror +``` + +**NixOS users** - add to your configuration: +```nix +{ + inputs.gitea-mirror.url = "github:RayLabsHQ/gitea-mirror"; + + services.gitea-mirror = { + enable = true; + betterAuthUrl = "https://mirror.example.com"; + openFirewall = true; + }; +} +``` + +Secrets auto-generate, database auto-initializes. See [NIX.md](NIX.md) for quick reference or [docs/NIX_DEPLOYMENT.md](docs/NIX_DEPLOYMENT.md) for full documentation. + +### Manual Installation + +```bash +# Install Bun +curl -fsSL https://bun.sh/install | bash + +# Setup and run +bun run setup +bun run dev +``` + +## Usage + +1. **First Time Setup** + - Navigate to http://localhost:4321 + - Create admin account (first user signup) + - Configure GitHub and Gitea connections + +2. **Mirror Strategies** + - **Preserve Structure**: Maintains GitHub organization structure + - **Single Organization**: All repos go to one Gitea organization + - **Flat User**: All repos under your Gitea user account + - **Mixed Mode**: Personal repos in one org, organization repos preserve structure + +3. **Customization** + - Click edit buttons on organization cards to set custom destinations + - Override individual repository destinations in the table view + - Starred repositories automatically go to a dedicated organization + +## Advanced Features + +### Git LFS (Large File Storage) +Mirror Git LFS objects along with your repositories: +- Enable "Mirror LFS" option in Settings → Mirror Options +- Requires Gitea server with LFS enabled (`LFS_START_SERVER = true`) +- Requires Git v2.1.2+ on the server + +### Metadata Mirroring +Transfer complete repository metadata from GitHub to Gitea: +- **Issues** - Mirror all issues with comments and labels +- **Pull Requests** - Transfer PR discussions to Gitea +- **Labels** - Preserve repository labels +- **Milestones** - Keep project milestones +- **Wiki** - Mirror wiki content +- **Releases** - Transfer GitHub releases with assets + +Enable in Settings → Mirror Options → Mirror metadata + +### Repository Management +- **Ignore Status** - Mark repositories to skip from mirroring +- **Automatic Cleanup** - Configure retention period for activity logs +- **Scheduled Sync** - Set custom intervals for automatic mirroring + +### Automatic Syncing & Synchronization + +Gitea Mirror provides powerful automatic synchronization features: + +#### Features (v3.4.0+) +- **Auto-discovery**: Automatically discovers and imports new GitHub repositories +- **Repository cleanup**: Removes repositories that no longer exist in GitHub +- **Proper intervals**: Mirrors respect your configured sync intervals (not Gitea's default 24h) +- **Smart scheduling**: Only syncs repositories that need updating +- **Auto-start on boot** (v3.5.3+): Automatically imports and mirrors all repositories when `SCHEDULE_ENABLED=true` or `GITEA_MIRROR_INTERVAL` is set - no manual clicks required! + +#### Configuration via Web Interface (Recommended) +Navigate to the Configuration page and enable "Automatic Syncing" with your preferred interval. + +#### Configuration via Environment Variables + +**🚀 Set it and forget it!** With these environment variables, Gitea Mirror will automatically: +1. **Import** all your GitHub repositories on startup (no manual import needed!) +2. **Mirror** them to Gitea immediately +3. **Keep them synchronized** based on your interval +4. **Auto-discover** new repos you create/star on GitHub +5. **Clean up** repos you delete from GitHub + +```bash +# Option 1: Enable automatic scheduling (triggers auto-start) +SCHEDULE_ENABLED=true +SCHEDULE_INTERVAL=3600 # Check every hour (or use cron: "0 * * * *") + +# Option 2: Set mirror interval (also triggers auto-start) +GITEA_MIRROR_INTERVAL=8h # Every 8 hours +# Other examples: 5m, 30m, 1h, 24h, 1d, 7d + +# Advanced: Use cron expressions for specific times +SCHEDULE_INTERVAL="0 2 * * *" # Daily at 2 AM (optimize bandwidth usage) + +# Auto-import new repositories (default: true) +AUTO_IMPORT_REPOS=true + +# Auto-cleanup orphaned repositories +CLEANUP_DELETE_IF_NOT_IN_GITHUB=true +CLEANUP_ORPHANED_REPO_ACTION=archive # 'archive' (recommended) or 'delete' +CLEANUP_DRY_RUN=false # Set to true to test without changes +``` + +**Important Notes**: +- **Auto-Start**: When `SCHEDULE_ENABLED=true` or `GITEA_MIRROR_INTERVAL` is set, the service automatically imports all GitHub repositories and mirrors them on startup. No manual "Import" or "Mirror" button clicks required! +- The scheduler checks every minute for tasks to run. The `GITEA_MIRROR_INTERVAL` determines how often each repository is actually synced. For example, with `8h`, each repo syncs every 8 hours from its last successful sync. + +**🛡️ Backup Protection Features**: +- **No Accidental Deletions**: Repository cleanup is automatically skipped if GitHub is inaccessible (account deleted, banned, or API errors) +- **Archive Never Deletes Data**: The `archive` action preserves all repository data: + - Regular repositories: Made read-only using Gitea's archive feature + - Mirror repositories: Renamed with `archived-` prefix (Gitea API limitation prevents archiving mirrors) + - Failed operations: Repository remains fully accessible even if marking as archived fails +- **Manual Sync on Demand**: Archived mirrors stay in Gitea with automatic syncs disabled; trigger `Manual Sync` from the Repositories page whenever you need fresh data. +- **The Whole Point of Backups**: Your Gitea mirrors are preserved even when GitHub sources disappear - that's why you have backups! +- **Strongly Recommended**: Always use `CLEANUP_ORPHANED_REPO_ACTION=archive` (default) instead of `delete` + +## Troubleshooting + +### Reverse Proxy Configuration + +If using a reverse proxy (e.g., nginx proxy manager) and experiencing issues with JavaScript files not loading properly, try enabling HTTP/2 support in your proxy configuration. While not required by the application, some proxy configurations may have better compatibility with HTTP/2 enabled. See [issue #43](https://github.com/RayLabsHQ/gitea-mirror/issues/43) for reference. + +## Development + +```bash +# Install dependencies +bun install + +# Run development server +bun run dev + +# Run tests +bun test + +# Build for production +bun run build +``` + +## Technologies + +- **Frontend**: Astro, React, Shadcn UI, Tailwind CSS v4 +- **Backend**: Bun runtime, SQLite, Drizzle ORM +- **APIs**: GitHub (Octokit), Gitea REST API +- **Auth**: Better Auth with session-based authentication + +## Security + +### Token Encryption +- All GitHub and Gitea API tokens are encrypted at rest using AES-256-GCM +- Encryption is automatic and transparent to users +- Set `ENCRYPTION_SECRET` environment variable for production deployments +- Falls back to `BETTER_AUTH_SECRET` if not set + +### Password Security +- User passwords are securely hashed by Better Auth +- Never stored in plaintext +- Secure cookie-based session management + +## Authentication + +Gitea Mirror supports multiple authentication methods. **Email/password authentication is the default and always enabled.** + +### 1. Email & Password (Default) +The standard authentication method. First user to sign up becomes the admin. + +### 2. Single Sign-On (SSO) with OIDC +Enable users to sign in with external identity providers like Google, Azure AD, Okta, Authentik, or any OIDC-compliant service. + +**Configuration:** +1. Navigate to Settings → Authentication & SSO +2. Click "Add Provider" +3. Enter your OIDC provider details: + - Issuer URL (e.g., `https://accounts.google.com`) + - Client ID and Secret from your provider + - Use the "Discover" button to auto-fill endpoints + +**Redirect URL for your provider:** +``` +https://your-domain.com/api/auth/sso/callback/{provider-id} +``` + +Need help? The [SSO & OIDC guide](docs/SSO-OIDC-SETUP.md) now includes a working Authentik walkthrough plus troubleshooting tips. If you upgraded from a version earlier than v3.8.10 and see `TypeError … url.startsWith` after the callback, delete the old provider and add it again using the Discover button (see [#73](https://github.com/RayLabsHQ/gitea-mirror/issues/73) and [#122](https://github.com/RayLabsHQ/gitea-mirror/issues/122)). + +### 3. Header Authentication (Reverse Proxy) +Perfect for automatic authentication when using reverse proxies like Authentik, Authelia, or Traefik Forward Auth. + +**Environment Variables:** +```bash +# Enable header authentication +HEADER_AUTH_ENABLED=true + +# Header names (customize based on your proxy) +HEADER_AUTH_USER_HEADER=X-Authentik-Username +HEADER_AUTH_EMAIL_HEADER=X-Authentik-Email +HEADER_AUTH_NAME_HEADER=X-Authentik-Name + +# Auto-provision new users +HEADER_AUTH_AUTO_PROVISION=true + +# Restrict to specific email domains (optional) +HEADER_AUTH_ALLOWED_DOMAINS=example.com,company.org +``` + +**How it works:** +- Users authenticated by your reverse proxy are automatically logged in +- No additional login step required +- New users can be auto-provisioned if enabled +- Falls back to regular authentication if headers are missing + +**Example Authentik Configuration:** +```nginx +# In your reverse proxy configuration +proxy_set_header X-Authentik-Username $authentik_username; +proxy_set_header X-Authentik-Email $authentik_email; +proxy_set_header X-Authentik-Name $authentik_name; +``` + +### 4. OAuth Applications (Act as Identity Provider) +Gitea Mirror can also act as an OIDC provider for other applications. Register OAuth applications in Settings → Authentication & SSO → OAuth Applications tab. + +**Use cases:** +- Allow other services to authenticate using Gitea Mirror accounts +- Create service-to-service authentication +- Build integrations with your Gitea Mirror instance + +## Known Limitations + +### Pull Request Mirroring Implementation +Pull requests **cannot be created as actual PRs** in Gitea due to API limitations. Instead, they are mirrored as **enriched issues** with comprehensive metadata. + +**Why real PR mirroring isn't possible:** +- Gitea's API doesn't support creating pull requests from external sources +- Real PRs require actual Git branches with commits to exist in the repository +- Would require complex branch synchronization and commit replication +- The mirror relationship is one-way (GitHub → Gitea) for repository content + +**How we handle Pull Requests:** +PRs are mirrored as issues with rich metadata including: +- 🏷️ Special "pull-request" label for identification +- 📌 [PR #number] prefix in title with status indicators ([MERGED], [CLOSED]) +- 👤 Original author and creation date +- 📝 Complete commit history (up to 10 commits with links) +- 📊 File changes summary with additions/deletions +- 📁 List of modified files (up to 20 files) +- 💬 Original PR description and comments +- 🔀 Base and head branch information +- ✅ Merge status tracking + +This approach preserves all important PR information while working within Gitea's API constraints. The PRs appear in Gitea's issue tracker with clear visual distinction and comprehensive details. + +## Contributing + +Contributions are welcome! Please read our [Contributing Guidelines](CONTRIBUTING.md) for details on our code of conduct and the process for submitting pull requests. + +## License + +GNU General Public License v3.0 - see [LICENSE](LICENSE) file for details. + +## Star History + + + + + + Star History Chart + + + +## Support + +- 📖 [Documentation](https://github.com/RayLabsHQ/gitea-mirror/tree/main/docs) +- 🔐 [Custom CA Certificates](docs/CA_CERTIFICATES.md) +- 🐛 [Report Issues](https://github.com/RayLabsHQ/gitea-mirror/issues) +- 💬 [Discussions](https://github.com/RayLabsHQ/gitea-mirror/discussions) +- 🔧 [Proxmox VE Script](https://community-scripts.github.io/ProxmoxVE/scripts?id=gitea-mirror) diff --git a/Divers/gitea-mirror/astro.config.mjs b/Divers/gitea-mirror/astro.config.mjs new file mode 100644 index 0000000..293315f --- /dev/null +++ b/Divers/gitea-mirror/astro.config.mjs @@ -0,0 +1,22 @@ +// @ts-check +import { defineConfig } from 'astro/config'; +import tailwindcss from '@tailwindcss/vite'; +import react from '@astrojs/react'; +import node from '@astrojs/node'; + +// https://astro.build/config +export default defineConfig({ + output: 'server', + adapter: node({ + mode: 'standalone', + }), + vite: { + plugins: [tailwindcss()], + build: { + rollupOptions: { + external: ['bun', 'bun:*'], + }, + }, + }, + integrations: [react()] +}); diff --git a/Divers/gitea-mirror/bun.lock b/Divers/gitea-mirror/bun.lock new file mode 100644 index 0000000..3f739b2 --- /dev/null +++ b/Divers/gitea-mirror/bun.lock @@ -0,0 +1,2086 @@ +{ + "lockfileVersion": 1, + "configVersion": 0, + "workspaces": { + "": { + "name": "gitea-mirror", + "dependencies": { + "@astrojs/check": "^0.9.6", + "@astrojs/mdx": "4.3.12", + "@astrojs/node": "9.5.1", + "@astrojs/react": "^4.4.2", + "@better-auth/sso": "1.4.5", + "@octokit/plugin-throttling": "^11.0.3", + "@octokit/rest": "^22.0.1", + "@radix-ui/react-accordion": "^1.2.12", + "@radix-ui/react-avatar": "^1.1.11", + "@radix-ui/react-checkbox": "^1.3.3", + "@radix-ui/react-collapsible": "^1.1.12", + "@radix-ui/react-dialog": "^1.1.15", + "@radix-ui/react-dropdown-menu": "^2.1.16", + "@radix-ui/react-hover-card": "^1.1.15", + "@radix-ui/react-label": "^2.1.8", + "@radix-ui/react-popover": "^1.1.15", + "@radix-ui/react-progress": "^1.1.8", + "@radix-ui/react-radio-group": "^1.3.8", + "@radix-ui/react-scroll-area": "^1.2.10", + "@radix-ui/react-select": "^2.2.6", + "@radix-ui/react-separator": "^1.1.8", + "@radix-ui/react-slot": "^1.2.4", + "@radix-ui/react-switch": "^1.2.6", + "@radix-ui/react-tabs": "^1.1.13", + "@radix-ui/react-tooltip": "^1.2.8", + "@tailwindcss/vite": "^4.1.17", + "@tanstack/react-virtual": "^3.13.12", + "@types/canvas-confetti": "^1.9.0", + "@types/react": "^19.2.7", + "@types/react-dom": "^19.2.3", + "astro": "^5.16.4", + "bcryptjs": "^3.0.3", + "better-auth": "1.4.5", + "buffer": "^6.0.3", + "canvas-confetti": "^1.9.4", + "class-variance-authority": "^0.7.1", + "clsx": "^2.1.1", + "cmdk": "^1.1.1", + "dotenv": "^17.2.3", + "drizzle-orm": "^0.44.7", + "fuse.js": "^7.1.0", + "jsonwebtoken": "^9.0.3", + "lucide-react": "^0.555.0", + "next-themes": "^0.4.6", + "react": "^19.2.1", + "react-dom": "^19.2.1", + "react-icons": "^5.5.0", + "sonner": "^2.0.7", + "tailwind-merge": "^3.4.0", + "tailwindcss": "^4.1.17", + "tw-animate-css": "^1.4.0", + "typescript": "^5.9.3", + "uuid": "^13.0.0", + "vaul": "^1.1.2", + "zod": "^4.1.13", + }, + "devDependencies": { + "@testing-library/jest-dom": "^6.9.1", + "@testing-library/react": "^16.3.0", + "@types/bcryptjs": "^3.0.0", + "@types/bun": "^1.3.3", + "@types/jsonwebtoken": "^9.0.10", + "@types/uuid": "^10.0.0", + "@vitejs/plugin-react": "^5.1.1", + "drizzle-kit": "^0.31.7", + "jsdom": "^27.2.0", + "tsx": "^4.21.0", + "vitest": "^4.0.15", + }, + }, + }, + "overrides": { + "@esbuild-kit/esm-loader": "npm:tsx@^4.21.0", + "devalue": "^5.5.0", + }, + "packages": { + "@acemir/cssom": ["@acemir/cssom@0.9.24", "", {}, "sha512-5YjgMmAiT2rjJZU7XK1SNI7iqTy92DpaYVgG6x63FxkJ11UpYfLndHJATtinWJClAXiOlW9XWaUyAQf8pMrQPg=="], + + "@adobe/css-tools": ["@adobe/css-tools@4.4.3", "", {}, "sha512-VQKMkwriZbaOgVCby1UDY/LDk5fIjhQicCvVPFqfe+69fWaPWydbWJ3wRt59/YzIwda1I81loas3oCoHxnqvdA=="], + + "@ampproject/remapping": ["@ampproject/remapping@2.3.0", "", { "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw=="], + + "@asamuzakjp/css-color": ["@asamuzakjp/css-color@4.1.0", "", { "dependencies": { "@csstools/css-calc": "^2.1.4", "@csstools/css-color-parser": "^3.1.0", "@csstools/css-parser-algorithms": "^3.0.5", "@csstools/css-tokenizer": "^3.0.4", "lru-cache": "^11.2.2" } }, "sha512-9xiBAtLn4aNsa4mDnpovJvBn72tNEIACyvlqaNJ+ADemR+yeMJWnBudOi2qGDviJa7SwcDOU/TRh5dnET7qk0w=="], + + "@asamuzakjp/dom-selector": ["@asamuzakjp/dom-selector@6.7.5", "", { "dependencies": { "@asamuzakjp/nwsapi": "^2.3.9", "bidi-js": "^1.0.3", "css-tree": "^3.1.0", "is-potential-custom-element-name": "^1.0.1", "lru-cache": "^11.2.2" } }, "sha512-Eks6dY8zau4m4wNRQjRVaKQRTalNcPcBvU1ZQ35w5kKRk1gUeNCkVLsRiATurjASTp3TKM4H10wsI50nx3NZdw=="], + + "@asamuzakjp/nwsapi": ["@asamuzakjp/nwsapi@2.3.9", "", {}, "sha512-n8GuYSrI9bF7FFZ/SjhwevlHc8xaVlb/7HmHelnc/PZXBD2ZR49NnN9sMMuDdEGPeeRQ5d0hqlSlEpgCX3Wl0Q=="], + + "@astrojs/check": ["@astrojs/check@0.9.6", "", { "dependencies": { "@astrojs/language-server": "^2.16.1", "chokidar": "^4.0.1", "kleur": "^4.1.5", "yargs": "^17.7.2" }, "peerDependencies": { "typescript": "^5.0.0" }, "bin": { "astro-check": "bin/astro-check.js" } }, "sha512-jlaEu5SxvSgmfGIFfNgcn5/f+29H61NJzEMfAZ82Xopr4XBchXB1GVlcJsE+elUlsYSbXlptZLX+JMG3b/wZEA=="], + + "@astrojs/compiler": ["@astrojs/compiler@2.13.0", "", {}, "sha512-mqVORhUJViA28fwHYaWmsXSzLO9osbdZ5ImUfxBarqsYdMlPbqAqGJCxsNzvppp1BEzc1mJNjOVvQqeDN8Vspw=="], + + "@astrojs/internal-helpers": ["@astrojs/internal-helpers@0.7.5", "", {}, "sha512-vreGnYSSKhAjFJCWAwe/CNhONvoc5lokxtRoZims+0wa3KbHBdPHSSthJsKxPd8d/aic6lWKpRTYGY/hsgK6EA=="], + + "@astrojs/language-server": ["@astrojs/language-server@2.16.2", "", { "dependencies": { "@astrojs/compiler": "^2.10.3", "@astrojs/yaml2ts": "^0.2.2", "@jridgewell/sourcemap-codec": "^1.4.15", "@volar/kit": "~2.4.23", "@volar/language-core": "~2.4.23", "@volar/language-server": "~2.4.23", "@volar/language-service": "~2.4.23", "fast-glob": "^3.2.12", "muggle-string": "^0.4.1", "volar-service-css": "0.0.67", "volar-service-emmet": "0.0.67", "volar-service-html": "0.0.67", "volar-service-prettier": "0.0.67", "volar-service-typescript": "0.0.67", "volar-service-typescript-twoslash-queries": "0.0.67", "volar-service-yaml": "0.0.67", "vscode-html-languageservice": "^5.5.2", "vscode-uri": "^3.1.0" }, "peerDependencies": { "prettier": "^3.0.0", "prettier-plugin-astro": ">=0.11.0" }, "optionalPeers": ["prettier", "prettier-plugin-astro"], "bin": { "astro-ls": "bin/nodeServer.js" } }, "sha512-J3hVx/mFi3FwEzKf8ExYXQNERogD6RXswtbU+TyrxoXRBiQoBO5ooo7/lRWJ+rlUKUd7+rziMPI9jYB7TRlh0w=="], + + "@astrojs/markdown-remark": ["@astrojs/markdown-remark@6.3.9", "", { "dependencies": { "@astrojs/internal-helpers": "0.7.5", "@astrojs/prism": "3.3.0", "github-slugger": "^2.0.0", "hast-util-from-html": "^2.0.3", "hast-util-to-text": "^4.0.2", "import-meta-resolve": "^4.2.0", "js-yaml": "^4.1.0", "mdast-util-definitions": "^6.0.0", "rehype-raw": "^7.0.0", "rehype-stringify": "^10.0.1", "remark-gfm": "^4.0.1", "remark-parse": "^11.0.0", "remark-rehype": "^11.1.2", "remark-smartypants": "^3.0.2", "shiki": "^3.13.0", "smol-toml": "^1.4.2", "unified": "^11.0.5", "unist-util-remove-position": "^5.0.0", "unist-util-visit": "^5.0.0", "unist-util-visit-parents": "^6.0.2", "vfile": "^6.0.3" } }, "sha512-hX2cLC/KW74Io1zIbn92kI482j9J7LleBLGCVU9EP3BeH5MVrnFawOnqD0t/q6D1Z+ZNeQG2gNKMslCcO36wng=="], + + "@astrojs/mdx": ["@astrojs/mdx@4.3.12", "", { "dependencies": { "@astrojs/markdown-remark": "6.3.9", "@mdx-js/mdx": "^3.1.1", "acorn": "^8.15.0", "es-module-lexer": "^1.7.0", "estree-util-visit": "^2.0.0", "hast-util-to-html": "^9.0.5", "piccolore": "^0.1.3", "rehype-raw": "^7.0.0", "remark-gfm": "^4.0.1", "remark-smartypants": "^3.0.2", "source-map": "^0.7.6", "unist-util-visit": "^5.0.0", "vfile": "^6.0.3" }, "peerDependencies": { "astro": "^5.0.0" } }, "sha512-pL3CVPtuQrPnDhWjy7zqbOibNyPaxP4VpQS8T8spwKqKzauJ4yoKyNkVTD8jrP7EAJHmBhZ7PTmUGZqOpKKp8g=="], + + "@astrojs/node": ["@astrojs/node@9.5.1", "", { "dependencies": { "@astrojs/internal-helpers": "0.7.5", "send": "^1.2.0", "server-destroy": "^1.0.1" }, "peerDependencies": { "astro": "^5.14.3" } }, "sha512-7k+SU877OUQylPr0mFcWrGvNuC78Lp9w+GInY8Rwc+LkHyDP9xls+nZAioK0WDWd+fyeQnlHbpDGURO3ZHuDVg=="], + + "@astrojs/prism": ["@astrojs/prism@3.3.0", "", { "dependencies": { "prismjs": "^1.30.0" } }, "sha512-q8VwfU/fDZNoDOf+r7jUnMC2//H2l0TuQ6FkGJL8vD8nw/q5KiL3DS1KKBI3QhI9UQhpJ5dc7AtqfbXWuOgLCQ=="], + + "@astrojs/react": ["@astrojs/react@4.4.2", "", { "dependencies": { "@vitejs/plugin-react": "^4.7.0", "ultrahtml": "^1.6.0", "vite": "^6.4.1" }, "peerDependencies": { "@types/react": "^17.0.50 || ^18.0.21 || ^19.0.0", "@types/react-dom": "^17.0.17 || ^18.0.6 || ^19.0.0", "react": "^17.0.2 || ^18.0.0 || ^19.0.0", "react-dom": "^17.0.2 || ^18.0.0 || ^19.0.0" } }, "sha512-1tl95bpGfuaDMDn8O3x/5Dxii1HPvzjvpL2YTuqOOrQehs60I2DKiDgh1jrKc7G8lv+LQT5H15V6QONQ+9waeQ=="], + + "@astrojs/telemetry": ["@astrojs/telemetry@3.3.0", "", { "dependencies": { "ci-info": "^4.2.0", "debug": "^4.4.0", "dlv": "^1.1.3", "dset": "^3.1.4", "is-docker": "^3.0.0", "is-wsl": "^3.1.0", "which-pm-runs": "^1.1.0" } }, "sha512-UFBgfeldP06qu6khs/yY+q1cDAaArM2/7AEIqQ9Cuvf7B1hNLq0xDrZkct+QoIGyjq56y8IaE2I3CTvG99mlhQ=="], + + "@astrojs/yaml2ts": ["@astrojs/yaml2ts@0.2.2", "", { "dependencies": { "yaml": "^2.5.0" } }, "sha512-GOfvSr5Nqy2z5XiwqTouBBpy5FyI6DEe+/g/Mk5am9SjILN1S5fOEvYK0GuWHg98yS/dobP4m8qyqw/URW35fQ=="], + + "@authenio/xml-encryption": ["@authenio/xml-encryption@2.0.2", "", { "dependencies": { "@xmldom/xmldom": "^0.8.6", "escape-html": "^1.0.3", "xpath": "0.0.32" } }, "sha512-cTlrKttbrRHEw3W+0/I609A2Matj5JQaRvfLtEIGZvlN0RaPi+3ANsMeqAyCAVlH/lUIW2tmtBlSMni74lcXeg=="], + + "@babel/code-frame": ["@babel/code-frame@7.27.1", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.27.1", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" } }, "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg=="], + + "@babel/compat-data": ["@babel/compat-data@7.27.3", "", {}, "sha512-V42wFfx1ymFte+ecf6iXghnnP8kWTO+ZLXIyZq+1LAXHHvTZdVxicn4yiVYdYMGaCO3tmqub11AorKkv+iodqw=="], + + "@babel/core": ["@babel/core@7.28.5", "", { "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/generator": "^7.28.5", "@babel/helper-compilation-targets": "^7.27.2", "@babel/helper-module-transforms": "^7.28.3", "@babel/helpers": "^7.28.4", "@babel/parser": "^7.28.5", "@babel/template": "^7.27.2", "@babel/traverse": "^7.28.5", "@babel/types": "^7.28.5", "@jridgewell/remapping": "^2.3.5", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", "json5": "^2.2.3", "semver": "^6.3.1" } }, "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw=="], + + "@babel/generator": ["@babel/generator@7.28.5", "", { "dependencies": { "@babel/parser": "^7.28.5", "@babel/types": "^7.28.5", "@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" } }, "sha512-3EwLFhZ38J4VyIP6WNtt2kUdW9dokXA9Cr4IVIFHuCpZ3H8/YFOl5JjZHisrn1fATPBmKKqXzDFvh9fUwHz6CQ=="], + + "@babel/helper-compilation-targets": ["@babel/helper-compilation-targets@7.27.2", "", { "dependencies": { "@babel/compat-data": "^7.27.2", "@babel/helper-validator-option": "^7.27.1", "browserslist": "^4.24.0", "lru-cache": "^5.1.1", "semver": "^6.3.1" } }, "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ=="], + + "@babel/helper-globals": ["@babel/helper-globals@7.28.0", "", {}, "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw=="], + + "@babel/helper-module-imports": ["@babel/helper-module-imports@7.27.1", "", { "dependencies": { "@babel/traverse": "^7.27.1", "@babel/types": "^7.27.1" } }, "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w=="], + + "@babel/helper-module-transforms": ["@babel/helper-module-transforms@7.28.3", "", { "dependencies": { "@babel/helper-module-imports": "^7.27.1", "@babel/helper-validator-identifier": "^7.27.1", "@babel/traverse": "^7.28.3" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw=="], + + "@babel/helper-plugin-utils": ["@babel/helper-plugin-utils@7.27.1", "", {}, "sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw=="], + + "@babel/helper-string-parser": ["@babel/helper-string-parser@7.27.1", "", {}, "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA=="], + + "@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.27.1", "", {}, "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow=="], + + "@babel/helper-validator-option": ["@babel/helper-validator-option@7.27.1", "", {}, "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg=="], + + "@babel/helpers": ["@babel/helpers@7.28.4", "", { "dependencies": { "@babel/template": "^7.27.2", "@babel/types": "^7.28.4" } }, "sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w=="], + + "@babel/parser": ["@babel/parser@7.28.5", "", { "dependencies": { "@babel/types": "^7.28.5" }, "bin": "./bin/babel-parser.js" }, "sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ=="], + + "@babel/plugin-transform-react-jsx-self": ["@babel/plugin-transform-react-jsx-self@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-6UzkCs+ejGdZ5mFFC/OCUrv028ab2fp1znZmCZjAOBKiBK2jXD1O+BPSfX8X2qjJ75fZBMSnQn3Rq2mrBJK2mw=="], + + "@babel/plugin-transform-react-jsx-source": ["@babel/plugin-transform-react-jsx-source@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-zbwoTsBruTeKB9hSq73ha66iFeJHuaFkUbwvqElnygoNbj/jHRsSeokowZFN3CZ64IvEqcmmkVe89OPXc7ldAw=="], + + "@babel/runtime": ["@babel/runtime@7.27.3", "", {}, "sha512-7EYtGezsdiDMyY80+65EzwiGmcJqpmcZCojSXaRgdrBaGtWTgDZKq69cPIVped6MkIM78cTQ2GOiEYjwOlG4xw=="], + + "@babel/template": ["@babel/template@7.27.2", "", { "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/parser": "^7.27.2", "@babel/types": "^7.27.1" } }, "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw=="], + + "@babel/traverse": ["@babel/traverse@7.28.5", "", { "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/generator": "^7.28.5", "@babel/helper-globals": "^7.28.0", "@babel/parser": "^7.28.5", "@babel/template": "^7.27.2", "@babel/types": "^7.28.5", "debug": "^4.3.1" } }, "sha512-TCCj4t55U90khlYkVV/0TfkJkAkUg3jZFA3Neb7unZT8CPok7iiRfaX0F+WnqWqt7OxhOn0uBKXCw4lbL8W0aQ=="], + + "@babel/types": ["@babel/types@7.28.5", "", { "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.28.5" } }, "sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA=="], + + "@better-auth/core": ["@better-auth/core@1.4.5", "", { "dependencies": { "@standard-schema/spec": "^1.0.0", "zod": "^4.1.12" }, "peerDependencies": { "@better-auth/utils": "0.3.0", "@better-fetch/fetch": "1.1.18", "better-call": "1.1.4", "jose": "^6.1.0", "kysely": "^0.28.5", "nanostores": "^1.0.1" } }, "sha512-dQ3hZOkUJzeBXfVEPTm2LVbzmWwka1nqd9KyWmB2OMlMfjr7IdUeBX4T7qJctF67d7QDhlX95jMoxu6JG0Eucw=="], + + "@better-auth/sso": ["@better-auth/sso@1.4.5", "", { "dependencies": { "@better-fetch/fetch": "1.1.18", "fast-xml-parser": "^5.2.5", "jose": "^6.1.0", "samlify": "^2.10.1", "zod": "^4.1.12" }, "peerDependencies": { "better-auth": "1.4.5" } }, "sha512-nYD46cgT2R8FNZlvL2DR1UpE1TuV1rekMSlioGIooV8Fr1nHO5FpcJkAVZMqGbl2stVK9yS51umyTyd7OWNYgg=="], + + "@better-auth/telemetry": ["@better-auth/telemetry@1.4.5", "", { "dependencies": { "@better-auth/utils": "0.3.0", "@better-fetch/fetch": "1.1.18" }, "peerDependencies": { "@better-auth/core": "1.4.5" } }, "sha512-r3NyksbaBYA10SC86JA6QwmZfHwFutkUGcphgWGfu6MVx1zutYmZehIeC8LxTjOWZqqF9FI8vLjglWBHvPQeTg=="], + + "@better-auth/utils": ["@better-auth/utils@0.3.0", "", {}, "sha512-W+Adw6ZA6mgvnSnhOki270rwJ42t4XzSK6YWGF//BbVXL6SwCLWfyzBc1lN2m/4RM28KubdBKQ4X5VMoLRNPQw=="], + + "@better-fetch/fetch": ["@better-fetch/fetch@1.1.18", "", {}, "sha512-rEFOE1MYIsBmoMJtQbl32PGHHXuG2hDxvEd7rUHE0vCBoFQVSDqaVs9hkZEtHCxRoY+CljXKFCOuJ8uxqw1LcA=="], + + "@capsizecss/unpack": ["@capsizecss/unpack@3.0.1", "", { "dependencies": { "fontkit": "^2.0.2" } }, "sha512-8XqW8xGn++Eqqbz3e9wKuK7mxryeRjs4LOHLxbh2lwKeSbuNR4NFifDZT4KzvjU6HMOPbiNTsWpniK5EJfTWkg=="], + + "@csstools/color-helpers": ["@csstools/color-helpers@5.1.0", "", {}, "sha512-S11EXWJyy0Mz5SYvRmY8nJYTFFd1LCNV+7cXyAgQtOOuzb4EsgfqDufL+9esx72/eLhsRdGZwaldu/h+E4t4BA=="], + + "@csstools/css-calc": ["@csstools/css-calc@2.1.4", "", { "peerDependencies": { "@csstools/css-parser-algorithms": "^3.0.5", "@csstools/css-tokenizer": "^3.0.4" } }, "sha512-3N8oaj+0juUw/1H3YwmDDJXCgTB1gKU6Hc/bB502u9zR0q2vd786XJH9QfrKIEgFlZmhZiq6epXl4rHqhzsIgQ=="], + + "@csstools/css-color-parser": ["@csstools/css-color-parser@3.1.0", "", { "dependencies": { "@csstools/color-helpers": "^5.1.0", "@csstools/css-calc": "^2.1.4" }, "peerDependencies": { "@csstools/css-parser-algorithms": "^3.0.5", "@csstools/css-tokenizer": "^3.0.4" } }, "sha512-nbtKwh3a6xNVIp/VRuXV64yTKnb1IjTAEEh3irzS+HkKjAOYLTGNb9pmVNntZ8iVBHcWDA2Dof0QtPgFI1BaTA=="], + + "@csstools/css-parser-algorithms": ["@csstools/css-parser-algorithms@3.0.5", "", { "peerDependencies": { "@csstools/css-tokenizer": "^3.0.4" } }, "sha512-DaDeUkXZKjdGhgYaHNJTV9pV7Y9B3b644jCLs9Upc3VeNGg6LWARAT6O+Q+/COo+2gg/bM5rhpMAtf70WqfBdQ=="], + + "@csstools/css-syntax-patches-for-csstree": ["@csstools/css-syntax-patches-for-csstree@1.0.20", "", {}, "sha512-8BHsjXfSciZxjmHQOuVdW2b8WLUPts9a+mfL13/PzEviufUEW2xnvQuOlKs9dRBHgRqJ53SF/DUoK9+MZk72oQ=="], + + "@csstools/css-tokenizer": ["@csstools/css-tokenizer@3.0.4", "", {}, "sha512-Vd/9EVDiu6PPJt9yAh6roZP6El1xHrdvIVGjyBsHR0RYwNHgL7FJPyIIW4fANJNG6FtyZfvlRPpFI4ZM/lubvw=="], + + "@drizzle-team/brocli": ["@drizzle-team/brocli@0.10.2", "", {}, "sha512-z33Il7l5dKjUgGULTqBsQBQwckHh5AbIuxhdsIxDDiZAzBOrZO6q9ogcWC65kU382AfynTfgNumVcNIjuIua6w=="], + + "@emmetio/abbreviation": ["@emmetio/abbreviation@2.3.3", "", { "dependencies": { "@emmetio/scanner": "^1.0.4" } }, "sha512-mgv58UrU3rh4YgbE/TzgLQwJ3pFsHHhCLqY20aJq+9comytTXUDNGG/SMtSeMJdkpxgXSXunBGLD8Boka3JyVA=="], + + "@emmetio/css-abbreviation": ["@emmetio/css-abbreviation@2.1.8", "", { "dependencies": { "@emmetio/scanner": "^1.0.4" } }, "sha512-s9yjhJ6saOO/uk1V74eifykk2CBYi01STTK3WlXWGOepyKa23ymJ053+DNQjpFcy1ingpaO7AxCcwLvHFY9tuw=="], + + "@emmetio/css-parser": ["@emmetio/css-parser@0.4.1", "", { "dependencies": { "@emmetio/stream-reader": "^2.2.0", "@emmetio/stream-reader-utils": "^0.1.0" } }, "sha512-2bC6m0MV/voF4CTZiAbG5MWKbq5EBmDPKu9Sb7s7nVcEzNQlrZP6mFFFlIaISM8X6514H9shWMme1fCm8cWAfQ=="], + + "@emmetio/html-matcher": ["@emmetio/html-matcher@1.3.0", "", { "dependencies": { "@emmetio/scanner": "^1.0.0" } }, "sha512-NTbsvppE5eVyBMuyGfVu2CRrLvo7J4YHb6t9sBFLyY03WYhXET37qA4zOYUjBWFCRHO7pS1B9khERtY0f5JXPQ=="], + + "@emmetio/scanner": ["@emmetio/scanner@1.0.4", "", {}, "sha512-IqRuJtQff7YHHBk4G8YZ45uB9BaAGcwQeVzgj/zj8/UdOhtQpEIupUhSk8dys6spFIWVZVeK20CzGEnqR5SbqA=="], + + "@emmetio/stream-reader": ["@emmetio/stream-reader@2.2.0", "", {}, "sha512-fXVXEyFA5Yv3M3n8sUGT7+fvecGrZP4k6FnWWMSZVQf69kAq0LLpaBQLGcPR30m3zMmKYhECP4k/ZkzvhEW5kw=="], + + "@emmetio/stream-reader-utils": ["@emmetio/stream-reader-utils@0.1.0", "", {}, "sha512-ZsZ2I9Vzso3Ho/pjZFsmmZ++FWeEd/txqybHTm4OgaZzdS8V9V/YYWQwg5TC38Z7uLWUV1vavpLLbjJtKubR1A=="], + + "@emnapi/runtime": ["@emnapi/runtime@1.4.5", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-++LApOtY0pEEz1zrd9vy1/zXVaVJJ/EbAF3u0fXIzPJEDtnITsBGbbK0EkM72amhl/R5b+5xx0Y/QhcVOpuulg=="], + + "@esbuild-kit/esm-loader": ["tsx@4.21.0", "", { "dependencies": { "esbuild": "~0.27.0", "get-tsconfig": "^4.7.5" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "bin": { "tsx": "dist/cli.mjs" } }, "sha512-5C1sg4USs1lfG0GFb2RLXsdpXqBSEhAaA/0kPL01wxzpMqLILNxIxIOKiILz+cdg/pLnOUxFYOR5yhHU666wbw=="], + + "@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.25.9", "", { "os": "aix", "cpu": "ppc64" }, "sha512-OaGtL73Jck6pBKjNIe24BnFE6agGl+6KxDtTfHhy1HmhthfKouEcOhqpSL64K4/0WCtbKFLOdzD/44cJ4k9opA=="], + + "@esbuild/android-arm": ["@esbuild/android-arm@0.25.9", "", { "os": "android", "cpu": "arm" }, "sha512-5WNI1DaMtxQ7t7B6xa572XMXpHAaI/9Hnhk8lcxF4zVN4xstUgTlvuGDorBguKEnZO70qwEcLpfifMLoxiPqHQ=="], + + "@esbuild/android-arm64": ["@esbuild/android-arm64@0.25.9", "", { "os": "android", "cpu": "arm64" }, "sha512-IDrddSmpSv51ftWslJMvl3Q2ZT98fUSL2/rlUXuVqRXHCs5EUF1/f+jbjF5+NG9UffUDMCiTyh8iec7u8RlTLg=="], + + "@esbuild/android-x64": ["@esbuild/android-x64@0.25.9", "", { "os": "android", "cpu": "x64" }, "sha512-I853iMZ1hWZdNllhVZKm34f4wErd4lMyeV7BLzEExGEIZYsOzqDWDf+y082izYUE8gtJnYHdeDpN/6tUdwvfiw=="], + + "@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.25.9", "", { "os": "darwin", "cpu": "arm64" }, "sha512-XIpIDMAjOELi/9PB30vEbVMs3GV1v2zkkPnuyRRURbhqjyzIINwj+nbQATh4H9GxUgH1kFsEyQMxwiLFKUS6Rg=="], + + "@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.25.9", "", { "os": "darwin", "cpu": "x64" }, "sha512-jhHfBzjYTA1IQu8VyrjCX4ApJDnH+ez+IYVEoJHeqJm9VhG9Dh2BYaJritkYK3vMaXrf7Ogr/0MQ8/MeIefsPQ=="], + + "@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.25.9", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-z93DmbnY6fX9+KdD4Ue/H6sYs+bhFQJNCPZsi4XWJoYblUqT06MQUdBCpcSfuiN72AbqeBFu5LVQTjfXDE2A6Q=="], + + "@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.25.9", "", { "os": "freebsd", "cpu": "x64" }, "sha512-mrKX6H/vOyo5v71YfXWJxLVxgy1kyt1MQaD8wZJgJfG4gq4DpQGpgTB74e5yBeQdyMTbgxp0YtNj7NuHN0PoZg=="], + + "@esbuild/linux-arm": ["@esbuild/linux-arm@0.25.9", "", { "os": "linux", "cpu": "arm" }, "sha512-HBU2Xv78SMgaydBmdor38lg8YDnFKSARg1Q6AT0/y2ezUAKiZvc211RDFHlEZRFNRVhcMamiToo7bDx3VEOYQw=="], + + "@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.25.9", "", { "os": "linux", "cpu": "arm64" }, "sha512-BlB7bIcLT3G26urh5Dmse7fiLmLXnRlopw4s8DalgZ8ef79Jj4aUcYbk90g8iCa2467HX8SAIidbL7gsqXHdRw=="], + + "@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.25.9", "", { "os": "linux", "cpu": "ia32" }, "sha512-e7S3MOJPZGp2QW6AK6+Ly81rC7oOSerQ+P8L0ta4FhVi+/j/v2yZzx5CqqDaWjtPFfYz21Vi1S0auHrap3Ma3A=="], + + "@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.25.9", "", { "os": "linux", "cpu": "none" }, "sha512-Sbe10Bnn0oUAB2AalYztvGcK+o6YFFA/9829PhOCUS9vkJElXGdphz0A3DbMdP8gmKkqPmPcMJmJOrI3VYB1JQ=="], + + "@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.25.9", "", { "os": "linux", "cpu": "none" }, "sha512-YcM5br0mVyZw2jcQeLIkhWtKPeVfAerES5PvOzaDxVtIyZ2NUBZKNLjC5z3/fUlDgT6w89VsxP2qzNipOaaDyA=="], + + "@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.25.9", "", { "os": "linux", "cpu": "ppc64" }, "sha512-++0HQvasdo20JytyDpFvQtNrEsAgNG2CY1CLMwGXfFTKGBGQT3bOeLSYE2l1fYdvML5KUuwn9Z8L1EWe2tzs1w=="], + + "@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.25.9", "", { "os": "linux", "cpu": "none" }, "sha512-uNIBa279Y3fkjV+2cUjx36xkx7eSjb8IvnL01eXUKXez/CBHNRw5ekCGMPM0BcmqBxBcdgUWuUXmVWwm4CH9kg=="], + + "@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.25.9", "", { "os": "linux", "cpu": "s390x" }, "sha512-Mfiphvp3MjC/lctb+7D287Xw1DGzqJPb/J2aHHcHxflUo+8tmN/6d4k6I2yFR7BVo5/g7x2Monq4+Yew0EHRIA=="], + + "@esbuild/linux-x64": ["@esbuild/linux-x64@0.25.9", "", { "os": "linux", "cpu": "x64" }, "sha512-iSwByxzRe48YVkmpbgoxVzn76BXjlYFXC7NvLYq+b+kDjyyk30J0JY47DIn8z1MO3K0oSl9fZoRmZPQI4Hklzg=="], + + "@esbuild/netbsd-arm64": ["@esbuild/netbsd-arm64@0.25.9", "", { "os": "none", "cpu": "arm64" }, "sha512-9jNJl6FqaUG+COdQMjSCGW4QiMHH88xWbvZ+kRVblZsWrkXlABuGdFJ1E9L7HK+T0Yqd4akKNa/lO0+jDxQD4Q=="], + + "@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.25.9", "", { "os": "none", "cpu": "x64" }, "sha512-RLLdkflmqRG8KanPGOU7Rpg829ZHu8nFy5Pqdi9U01VYtG9Y0zOG6Vr2z4/S+/3zIyOxiK6cCeYNWOFR9QP87g=="], + + "@esbuild/openbsd-arm64": ["@esbuild/openbsd-arm64@0.25.9", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-YaFBlPGeDasft5IIM+CQAhJAqS3St3nJzDEgsgFixcfZeyGPCd6eJBWzke5piZuZ7CtL656eOSYKk4Ls2C0FRQ=="], + + "@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.25.9", "", { "os": "openbsd", "cpu": "x64" }, "sha512-1MkgTCuvMGWuqVtAvkpkXFmtL8XhWy+j4jaSO2wxfJtilVCi0ZE37b8uOdMItIHz4I6z1bWWtEX4CJwcKYLcuA=="], + + "@esbuild/openharmony-arm64": ["@esbuild/openharmony-arm64@0.25.9", "", { "os": "none", "cpu": "arm64" }, "sha512-4Xd0xNiMVXKh6Fa7HEJQbrpP3m3DDn43jKxMjxLLRjWnRsfxjORYJlXPO4JNcXtOyfajXorRKY9NkOpTHptErg=="], + + "@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.25.9", "", { "os": "sunos", "cpu": "x64" }, "sha512-WjH4s6hzo00nNezhp3wFIAfmGZ8U7KtrJNlFMRKxiI9mxEK1scOMAaa9i4crUtu+tBr+0IN6JCuAcSBJZfnphw=="], + + "@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.25.9", "", { "os": "win32", "cpu": "arm64" }, "sha512-mGFrVJHmZiRqmP8xFOc6b84/7xa5y5YvR1x8djzXpJBSv/UsNK6aqec+6JDjConTgvvQefdGhFDAs2DLAds6gQ=="], + + "@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.25.9", "", { "os": "win32", "cpu": "ia32" }, "sha512-b33gLVU2k11nVx1OhX3C8QQP6UHQK4ZtN56oFWvVXvz2VkDoe6fbG8TOgHFxEvqeqohmRnIHe5A1+HADk4OQww=="], + + "@esbuild/win32-x64": ["@esbuild/win32-x64@0.25.9", "", { "os": "win32", "cpu": "x64" }, "sha512-PPOl1mi6lpLNQxnGoyAfschAodRFYXJ+9fs6WHXz7CSWKbOqiMZsubC+BQsVKuul+3vKLuwTHsS2c2y9EoKwxQ=="], + + "@floating-ui/core": ["@floating-ui/core@1.7.0", "", { "dependencies": { "@floating-ui/utils": "^0.2.9" } }, "sha512-FRdBLykrPPA6P76GGGqlex/e7fbe0F1ykgxHYNXQsH/iTEtjMj/f9bpY5oQqbjt5VgZvgz/uKXbGuROijh3VLA=="], + + "@floating-ui/dom": ["@floating-ui/dom@1.7.0", "", { "dependencies": { "@floating-ui/core": "^1.7.0", "@floating-ui/utils": "^0.2.9" } }, "sha512-lGTor4VlXcesUMh1cupTUTDoCxMb0V6bm3CnxHzQcw8Eaf1jQbgQX4i02fYgT0vJ82tb5MZ4CZk1LRGkktJCzg=="], + + "@floating-ui/react-dom": ["@floating-ui/react-dom@2.1.2", "", { "dependencies": { "@floating-ui/dom": "^1.0.0" }, "peerDependencies": { "react": ">=16.8.0", "react-dom": ">=16.8.0" } }, "sha512-06okr5cgPzMNBy+Ycse2A6udMi4bqwW/zgBF/rwjcNqWkyr82Mcg8b0vjX8OJpZFy/FKjJmw6wV7t44kK6kW7A=="], + + "@floating-ui/utils": ["@floating-ui/utils@0.2.9", "", {}, "sha512-MDWhGtE+eHw5JW7lq4qhc5yRLS11ERl1c7Z6Xd0a58DozHES6EnNNwUWbMiG4J9Cgj053Bhk8zvlhFYKVhULwg=="], + + "@img/sharp-darwin-arm64": ["@img/sharp-darwin-arm64@0.34.3", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-arm64": "1.2.0" }, "os": "darwin", "cpu": "arm64" }, "sha512-ryFMfvxxpQRsgZJqBd4wsttYQbCxsJksrv9Lw/v798JcQ8+w84mBWuXwl+TT0WJ/WrYOLaYpwQXi3sA9nTIaIg=="], + + "@img/sharp-darwin-x64": ["@img/sharp-darwin-x64@0.34.3", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-x64": "1.2.0" }, "os": "darwin", "cpu": "x64" }, "sha512-yHpJYynROAj12TA6qil58hmPmAwxKKC7reUqtGLzsOHfP7/rniNGTL8tjWX6L3CTV4+5P4ypcS7Pp+7OB+8ihA=="], + + "@img/sharp-libvips-darwin-arm64": ["@img/sharp-libvips-darwin-arm64@1.2.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-sBZmpwmxqwlqG9ueWFXtockhsxefaV6O84BMOrhtg/YqbTaRdqDE7hxraVE3y6gVM4eExmfzW4a8el9ArLeEiQ=="], + + "@img/sharp-libvips-darwin-x64": ["@img/sharp-libvips-darwin-x64@1.2.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-M64XVuL94OgiNHa5/m2YvEQI5q2cl9d/wk0qFTDVXcYzi43lxuiFTftMR1tOnFQovVXNZJ5TURSDK2pNe9Yzqg=="], + + "@img/sharp-libvips-linux-arm": ["@img/sharp-libvips-linux-arm@1.2.0", "", { "os": "linux", "cpu": "arm" }, "sha512-mWd2uWvDtL/nvIzThLq3fr2nnGfyr/XMXlq8ZJ9WMR6PXijHlC3ksp0IpuhK6bougvQrchUAfzRLnbsen0Cqvw=="], + + "@img/sharp-libvips-linux-arm64": ["@img/sharp-libvips-linux-arm64@1.2.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-RXwd0CgG+uPRX5YYrkzKyalt2OJYRiJQ8ED/fi1tq9WQW2jsQIn0tqrlR5l5dr/rjqq6AHAxURhj2DVjyQWSOA=="], + + "@img/sharp-libvips-linux-ppc64": ["@img/sharp-libvips-linux-ppc64@1.2.0", "", { "os": "linux", "cpu": "ppc64" }, "sha512-Xod/7KaDDHkYu2phxxfeEPXfVXFKx70EAFZ0qyUdOjCcxbjqyJOEUpDe6RIyaunGxT34Anf9ue/wuWOqBW2WcQ=="], + + "@img/sharp-libvips-linux-s390x": ["@img/sharp-libvips-linux-s390x@1.2.0", "", { "os": "linux", "cpu": "s390x" }, "sha512-eMKfzDxLGT8mnmPJTNMcjfO33fLiTDsrMlUVcp6b96ETbnJmd4uvZxVJSKPQfS+odwfVaGifhsB07J1LynFehw=="], + + "@img/sharp-libvips-linux-x64": ["@img/sharp-libvips-linux-x64@1.2.0", "", { "os": "linux", "cpu": "x64" }, "sha512-ZW3FPWIc7K1sH9E3nxIGB3y3dZkpJlMnkk7z5tu1nSkBoCgw2nSRTFHI5pB/3CQaJM0pdzMF3paf9ckKMSE9Tg=="], + + "@img/sharp-libvips-linuxmusl-arm64": ["@img/sharp-libvips-linuxmusl-arm64@1.2.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-UG+LqQJbf5VJ8NWJ5Z3tdIe/HXjuIdo4JeVNADXBFuG7z9zjoegpzzGIyV5zQKi4zaJjnAd2+g2nna8TZvuW9Q=="], + + "@img/sharp-libvips-linuxmusl-x64": ["@img/sharp-libvips-linuxmusl-x64@1.2.0", "", { "os": "linux", "cpu": "x64" }, "sha512-SRYOLR7CXPgNze8akZwjoGBoN1ThNZoqpOgfnOxmWsklTGVfJiGJoC/Lod7aNMGA1jSsKWM1+HRX43OP6p9+6Q=="], + + "@img/sharp-linux-arm": ["@img/sharp-linux-arm@0.34.3", "", { "optionalDependencies": { "@img/sharp-libvips-linux-arm": "1.2.0" }, "os": "linux", "cpu": "arm" }, "sha512-oBK9l+h6KBN0i3dC8rYntLiVfW8D8wH+NPNT3O/WBHeW0OQWCjfWksLUaPidsrDKpJgXp3G3/hkmhptAW0I3+A=="], + + "@img/sharp-linux-arm64": ["@img/sharp-linux-arm64@0.34.3", "", { "optionalDependencies": { "@img/sharp-libvips-linux-arm64": "1.2.0" }, "os": "linux", "cpu": "arm64" }, "sha512-QdrKe3EvQrqwkDrtuTIjI0bu6YEJHTgEeqdzI3uWJOH6G1O8Nl1iEeVYRGdj1h5I21CqxSvQp1Yv7xeU3ZewbA=="], + + "@img/sharp-linux-ppc64": ["@img/sharp-linux-ppc64@0.34.3", "", { "optionalDependencies": { "@img/sharp-libvips-linux-ppc64": "1.2.0" }, "os": "linux", "cpu": "ppc64" }, "sha512-GLtbLQMCNC5nxuImPR2+RgrviwKwVql28FWZIW1zWruy6zLgA5/x2ZXk3mxj58X/tszVF69KK0Is83V8YgWhLA=="], + + "@img/sharp-linux-s390x": ["@img/sharp-linux-s390x@0.34.3", "", { "optionalDependencies": { "@img/sharp-libvips-linux-s390x": "1.2.0" }, "os": "linux", "cpu": "s390x" }, "sha512-3gahT+A6c4cdc2edhsLHmIOXMb17ltffJlxR0aC2VPZfwKoTGZec6u5GrFgdR7ciJSsHT27BD3TIuGcuRT0KmQ=="], + + "@img/sharp-linux-x64": ["@img/sharp-linux-x64@0.34.3", "", { "optionalDependencies": { "@img/sharp-libvips-linux-x64": "1.2.0" }, "os": "linux", "cpu": "x64" }, "sha512-8kYso8d806ypnSq3/Ly0QEw90V5ZoHh10yH0HnrzOCr6DKAPI6QVHvwleqMkVQ0m+fc7EH8ah0BB0QPuWY6zJQ=="], + + "@img/sharp-linuxmusl-arm64": ["@img/sharp-linuxmusl-arm64@0.34.3", "", { "optionalDependencies": { "@img/sharp-libvips-linuxmusl-arm64": "1.2.0" }, "os": "linux", "cpu": "arm64" }, "sha512-vAjbHDlr4izEiXM1OTggpCcPg9tn4YriK5vAjowJsHwdBIdx0fYRsURkxLG2RLm9gyBq66gwtWI8Gx0/ov+JKQ=="], + + "@img/sharp-linuxmusl-x64": ["@img/sharp-linuxmusl-x64@0.34.3", "", { "optionalDependencies": { "@img/sharp-libvips-linuxmusl-x64": "1.2.0" }, "os": "linux", "cpu": "x64" }, "sha512-gCWUn9547K5bwvOn9l5XGAEjVTTRji4aPTqLzGXHvIr6bIDZKNTA34seMPgM0WmSf+RYBH411VavCejp3PkOeQ=="], + + "@img/sharp-wasm32": ["@img/sharp-wasm32@0.34.3", "", { "dependencies": { "@emnapi/runtime": "^1.4.4" }, "cpu": "none" }, "sha512-+CyRcpagHMGteySaWos8IbnXcHgfDn7pO2fiC2slJxvNq9gDipYBN42/RagzctVRKgxATmfqOSulgZv5e1RdMg=="], + + "@img/sharp-win32-arm64": ["@img/sharp-win32-arm64@0.34.3", "", { "os": "win32", "cpu": "arm64" }, "sha512-MjnHPnbqMXNC2UgeLJtX4XqoVHHlZNd+nPt1kRPmj63wURegwBhZlApELdtxM2OIZDRv/DFtLcNhVbd1z8GYXQ=="], + + "@img/sharp-win32-ia32": ["@img/sharp-win32-ia32@0.34.3", "", { "os": "win32", "cpu": "ia32" }, "sha512-xuCdhH44WxuXgOM714hn4amodJMZl3OEvf0GVTm0BEyMeA2to+8HEdRPShH0SLYptJY1uBw+SCFP9WVQi1Q/cw=="], + + "@img/sharp-win32-x64": ["@img/sharp-win32-x64@0.34.3", "", { "os": "win32", "cpu": "x64" }, "sha512-OWwz05d++TxzLEv4VnsTz5CmZ6mI6S05sfQGEMrNrQcOEERbX46332IvE7pO/EUiw7jUrrS40z/M7kPyjfl04g=="], + + "@jridgewell/gen-mapping": ["@jridgewell/gen-mapping@0.3.12", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-OuLGC46TjB5BbN1dH8JULVVZY4WTdkF7tV9Ys6wLL1rubZnCMstOhNHueU5bLCrnRuDhKPDM4g6sw4Bel5Gzqg=="], + + "@jridgewell/remapping": ["@jridgewell/remapping@2.3.5", "", { "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ=="], + + "@jridgewell/resolve-uri": ["@jridgewell/resolve-uri@3.1.2", "", {}, "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw=="], + + "@jridgewell/set-array": ["@jridgewell/set-array@1.2.1", "", {}, "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A=="], + + "@jridgewell/sourcemap-codec": ["@jridgewell/sourcemap-codec@1.5.5", "", {}, "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og=="], + + "@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.29", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-uw6guiW/gcAGPDhLmd77/6lW8QLeiV5RUTsAX46Db6oLhGaVj4lhnPwb184s1bkc8kdVg/+h988dro8GRDpmYQ=="], + + "@mdx-js/mdx": ["@mdx-js/mdx@3.1.1", "", { "dependencies": { "@types/estree": "^1.0.0", "@types/estree-jsx": "^1.0.0", "@types/hast": "^3.0.0", "@types/mdx": "^2.0.0", "acorn": "^8.0.0", "collapse-white-space": "^2.0.0", "devlop": "^1.0.0", "estree-util-is-identifier-name": "^3.0.0", "estree-util-scope": "^1.0.0", "estree-walker": "^3.0.0", "hast-util-to-jsx-runtime": "^2.0.0", "markdown-extensions": "^2.0.0", "recma-build-jsx": "^1.0.0", "recma-jsx": "^1.0.0", "recma-stringify": "^1.0.0", "rehype-recma": "^1.0.0", "remark-mdx": "^3.0.0", "remark-parse": "^11.0.0", "remark-rehype": "^11.0.0", "source-map": "^0.7.0", "unified": "^11.0.0", "unist-util-position-from-estree": "^2.0.0", "unist-util-stringify-position": "^4.0.0", "unist-util-visit": "^5.0.0", "vfile": "^6.0.0" } }, "sha512-f6ZO2ifpwAQIpzGWaBQT2TXxPv6z3RBzQKpVftEWN78Vl/YweF1uwussDx8ECAXVtr3Rs89fKyG9YlzUs9DyGQ=="], + + "@noble/ciphers": ["@noble/ciphers@2.0.0", "", {}, "sha512-j/l6jpnpaIBM87cAYPJzi/6TgqmBv9spkqPyCXvRYsu5uxqh6tPJZDnD85yo8VWqzTuTQPgfv7NgT63u7kbwAQ=="], + + "@noble/hashes": ["@noble/hashes@2.0.0", "", {}, "sha512-h8VUBlE8R42+XIDO229cgisD287im3kdY6nbNZJFjc6ZvKIXPYXe6Vc/t+kyjFdMFyt5JpapzTsEg8n63w5/lw=="], + + "@nodelib/fs.scandir": ["@nodelib/fs.scandir@2.1.5", "", { "dependencies": { "@nodelib/fs.stat": "2.0.5", "run-parallel": "^1.1.9" } }, "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g=="], + + "@nodelib/fs.stat": ["@nodelib/fs.stat@2.0.5", "", {}, "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A=="], + + "@nodelib/fs.walk": ["@nodelib/fs.walk@1.2.8", "", { "dependencies": { "@nodelib/fs.scandir": "2.1.5", "fastq": "^1.6.0" } }, "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg=="], + + "@octokit/auth-token": ["@octokit/auth-token@6.0.0", "", {}, "sha512-P4YJBPdPSpWTQ1NU4XYdvHvXJJDxM6YwpS0FZHRgP7YFkdVxsWcpWGy/NVqlAA7PcPCnMacXlRm1y2PFZRWL/w=="], + + "@octokit/core": ["@octokit/core@7.0.6", "", { "dependencies": { "@octokit/auth-token": "^6.0.0", "@octokit/graphql": "^9.0.3", "@octokit/request": "^10.0.6", "@octokit/request-error": "^7.0.2", "@octokit/types": "^16.0.0", "before-after-hook": "^4.0.0", "universal-user-agent": "^7.0.0" } }, "sha512-DhGl4xMVFGVIyMwswXeyzdL4uXD5OGILGX5N8Y+f6W7LhC1Ze2poSNrkF/fedpVDHEEZ+PHFW0vL14I+mm8K3Q=="], + + "@octokit/endpoint": ["@octokit/endpoint@11.0.2", "", { "dependencies": { "@octokit/types": "^16.0.0", "universal-user-agent": "^7.0.2" } }, "sha512-4zCpzP1fWc7QlqunZ5bSEjxc6yLAlRTnDwKtgXfcI/FxxGoqedDG8V2+xJ60bV2kODqcGB+nATdtap/XYq2NZQ=="], + + "@octokit/graphql": ["@octokit/graphql@9.0.3", "", { "dependencies": { "@octokit/request": "^10.0.6", "@octokit/types": "^16.0.0", "universal-user-agent": "^7.0.0" } }, "sha512-grAEuupr/C1rALFnXTv6ZQhFuL1D8G5y8CN04RgrO4FIPMrtm+mcZzFG7dcBm+nq+1ppNixu+Jd78aeJOYxlGA=="], + + "@octokit/openapi-types": ["@octokit/openapi-types@27.0.0", "", {}, "sha512-whrdktVs1h6gtR+09+QsNk2+FO+49j6ga1c55YZudfEG+oKJVvJLQi3zkOm5JjiUXAagWK2tI2kTGKJ2Ys7MGA=="], + + "@octokit/plugin-paginate-rest": ["@octokit/plugin-paginate-rest@14.0.0", "", { "dependencies": { "@octokit/types": "^16.0.0" }, "peerDependencies": { "@octokit/core": ">=6" } }, "sha512-fNVRE7ufJiAA3XUrha2omTA39M6IXIc6GIZLvlbsm8QOQCYvpq/LkMNGyFlB1d8hTDzsAXa3OKtybdMAYsV/fw=="], + + "@octokit/plugin-request-log": ["@octokit/plugin-request-log@6.0.0", "", { "peerDependencies": { "@octokit/core": ">=6" } }, "sha512-UkOzeEN3W91/eBq9sPZNQ7sUBvYCqYbrrD8gTbBuGtHEuycE4/awMXcYvx6sVYo7LypPhmQwwpUe4Yyu4QZN5Q=="], + + "@octokit/plugin-rest-endpoint-methods": ["@octokit/plugin-rest-endpoint-methods@17.0.0", "", { "dependencies": { "@octokit/types": "^16.0.0" }, "peerDependencies": { "@octokit/core": ">=6" } }, "sha512-B5yCyIlOJFPqUUeiD0cnBJwWJO8lkJs5d8+ze9QDP6SvfiXSz1BF+91+0MeI1d2yxgOhU/O+CvtiZ9jSkHhFAw=="], + + "@octokit/plugin-throttling": ["@octokit/plugin-throttling@11.0.3", "", { "dependencies": { "@octokit/types": "^16.0.0", "bottleneck": "^2.15.3" }, "peerDependencies": { "@octokit/core": "^7.0.0" } }, "sha512-34eE0RkFCKycLl2D2kq7W+LovheM/ex3AwZCYN8udpi6bxsyjZidb2McXs69hZhLmJlDqTSP8cH+jSRpiaijBg=="], + + "@octokit/request": ["@octokit/request@10.0.6", "", { "dependencies": { "@octokit/endpoint": "^11.0.2", "@octokit/request-error": "^7.0.2", "@octokit/types": "^16.0.0", "fast-content-type-parse": "^3.0.0", "universal-user-agent": "^7.0.2" } }, "sha512-FO+UgZCUu+pPnZAR+iKdUt64kPE7QW7ciqpldaMXaNzixz5Jld8dJ31LAUewk0cfSRkNSRKyqG438ba9c/qDlQ=="], + + "@octokit/request-error": ["@octokit/request-error@7.0.2", "", { "dependencies": { "@octokit/types": "^16.0.0" } }, "sha512-U8piOROoQQUyExw5c6dTkU3GKxts5/ERRThIauNL7yaRoeXW0q/5bgHWT7JfWBw1UyrbK8ERId2wVkcB32n0uQ=="], + + "@octokit/rest": ["@octokit/rest@22.0.1", "", { "dependencies": { "@octokit/core": "^7.0.6", "@octokit/plugin-paginate-rest": "^14.0.0", "@octokit/plugin-request-log": "^6.0.0", "@octokit/plugin-rest-endpoint-methods": "^17.0.0" } }, "sha512-Jzbhzl3CEexhnivb1iQ0KJ7s5vvjMWcmRtq5aUsKmKDrRW6z3r84ngmiFKFvpZjpiU/9/S6ITPFRpn5s/3uQJw=="], + + "@octokit/types": ["@octokit/types@16.0.0", "", { "dependencies": { "@octokit/openapi-types": "^27.0.0" } }, "sha512-sKq+9r1Mm4efXW1FCk7hFSeJo4QKreL/tTbR0rz/qx/r1Oa2VV83LTA/H/MuCOX7uCIJmQVRKBcbmWoySjAnSg=="], + + "@oslojs/encoding": ["@oslojs/encoding@1.1.0", "", {}, "sha512-70wQhgYmndg4GCPxPPxPGevRKqTIJ2Nh4OkiMWmDAVYsTQ+Ta7Sq+rPevXyXGdzr30/qZBnyOalCszoMxlyldQ=="], + + "@radix-ui/number": ["@radix-ui/number@1.1.1", "", {}, "sha512-MkKCwxlXTgz6CFoJx3pCwn07GKp36+aZyu/u2Ln2VrA5DcdyCZkASEDBTd8x5whTQQL5CiYf4prXKLcgQdv29g=="], + + "@radix-ui/primitive": ["@radix-ui/primitive@1.1.3", "", {}, "sha512-JTF99U/6XIjCBo0wqkU5sK10glYe27MRRsfwoiq5zzOEZLHU3A3KCMa5X/azekYRCJ0HlwI0crAXS/5dEHTzDg=="], + + "@radix-ui/react-accordion": ["@radix-ui/react-accordion@1.2.12", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-collapsible": "1.1.12", "@radix-ui/react-collection": "1.1.7", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-controllable-state": "1.2.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-T4nygeh9YE9dLRPhAHSeOZi7HBXo+0kYIPJXayZfvWOWA0+n3dESrZbjfDPUABkUNym6Hd+f2IR113To8D2GPA=="], + + "@radix-ui/react-arrow": ["@radix-ui/react-arrow@1.1.7", "", { "dependencies": { "@radix-ui/react-primitive": "2.1.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-F+M1tLhO+mlQaOWspE8Wstg+z6PwxwRd8oQ8IXceWz92kfAmalTRf0EjrouQeo7QssEPfCn05B4Ihs1K9WQ/7w=="], + + "@radix-ui/react-avatar": ["@radix-ui/react-avatar@1.1.11", "", { "dependencies": { "@radix-ui/react-context": "1.1.3", "@radix-ui/react-primitive": "2.1.4", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-is-hydrated": "0.1.0", "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-0Qk603AHGV28BOBO34p7IgD5m+V5Sg/YovfayABkoDDBM5d3NCx0Mp4gGrjzLGes1jV5eNOE1r3itqOR33VC6Q=="], + + "@radix-ui/react-checkbox": ["@radix-ui/react-checkbox@1.3.3", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-presence": "1.1.5", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-controllable-state": "1.2.2", "@radix-ui/react-use-previous": "1.1.1", "@radix-ui/react-use-size": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-wBbpv+NQftHDdG86Qc0pIyXk5IR3tM8Vd0nWLKDcX8nNn4nXFOFwsKuqw2okA/1D/mpaAkmuyndrPJTYDNZtFw=="], + + "@radix-ui/react-collapsible": ["@radix-ui/react-collapsible@1.1.12", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-presence": "1.1.5", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-controllable-state": "1.2.2", "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-Uu+mSh4agx2ib1uIGPP4/CKNULyajb3p92LsVXmH2EHVMTfZWpll88XJ0j4W0z3f8NK1eYl1+Mf/szHPmcHzyA=="], + + "@radix-ui/react-collection": ["@radix-ui/react-collection@1.1.7", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-Fh9rGN0MoI4ZFUNyfFVNU4y9LUz93u9/0K+yLgA2bwRojxM8JU1DyvvMBabnZPBgMWREAJvU2jjVzq+LrFUglw=="], + + "@radix-ui/react-compose-refs": ["@radix-ui/react-compose-refs@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg=="], + + "@radix-ui/react-context": ["@radix-ui/react-context@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA=="], + + "@radix-ui/react-dialog": ["@radix-ui/react-dialog@1.1.15", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-dismissable-layer": "1.1.11", "@radix-ui/react-focus-guards": "1.1.3", "@radix-ui/react-focus-scope": "1.1.7", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-portal": "1.1.9", "@radix-ui/react-presence": "1.1.5", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-slot": "1.2.3", "@radix-ui/react-use-controllable-state": "1.2.2", "aria-hidden": "^1.2.4", "react-remove-scroll": "^2.6.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-TCglVRtzlffRNxRMEyR36DGBLJpeusFcgMVD9PZEzAKnUs1lKCgX5u9BmC2Yg+LL9MgZDugFFs1Vl+Jp4t/PGw=="], + + "@radix-ui/react-direction": ["@radix-ui/react-direction@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-1UEWRX6jnOA2y4H5WczZ44gOOjTEmlqv1uNW4GAJEO5+bauCBhv8snY65Iw5/VOS/ghKN9gr2KjnLKxrsvoMVw=="], + + "@radix-ui/react-dismissable-layer": ["@radix-ui/react-dismissable-layer@1.1.11", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-escape-keydown": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-Nqcp+t5cTB8BinFkZgXiMJniQH0PsUt2k51FUhbdfeKvc4ACcG2uQniY/8+h1Yv6Kza4Q7lD7PQV0z0oicE0Mg=="], + + "@radix-ui/react-dropdown-menu": ["@radix-ui/react-dropdown-menu@2.1.16", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-menu": "2.1.16", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-controllable-state": "1.2.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-1PLGQEynI/3OX/ftV54COn+3Sud/Mn8vALg2rWnBLnRaGtJDduNW/22XjlGgPdpcIbiQxjKtb7BkcjP00nqfJw=="], + + "@radix-ui/react-focus-guards": ["@radix-ui/react-focus-guards@1.1.3", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-0rFg/Rj2Q62NCm62jZw0QX7a3sz6QCQU0LpZdNrJX8byRGaGVTqbrW9jAoIAHyMQqsNpeZ81YgSizOt5WXq0Pw=="], + + "@radix-ui/react-focus-scope": ["@radix-ui/react-focus-scope@1.1.7", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-callback-ref": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-t2ODlkXBQyn7jkl6TNaw/MtVEVvIGelJDCG41Okq/KwUsJBwQ4XVZsHAVUkK4mBv3ewiAS3PGuUWuY2BoK4ZUw=="], + + "@radix-ui/react-hover-card": ["@radix-ui/react-hover-card@1.1.15", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-dismissable-layer": "1.1.11", "@radix-ui/react-popper": "1.2.8", "@radix-ui/react-portal": "1.1.9", "@radix-ui/react-presence": "1.1.5", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-controllable-state": "1.2.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-qgTkjNT1CfKMoP0rcasmlH2r1DAiYicWsDsufxl940sT2wHNEWWv6FMWIQXWhVdmC1d/HYfbhQx60KYyAtKxjg=="], + + "@radix-ui/react-id": ["@radix-ui/react-id@1.1.1", "", { "dependencies": { "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-kGkGegYIdQsOb4XjsfM97rXsiHaBwco+hFI66oO4s9LU+PLAC5oJ7khdOVFxkhsmlbpUqDAvXw11CluXP+jkHg=="], + + "@radix-ui/react-label": ["@radix-ui/react-label@2.1.8", "", { "dependencies": { "@radix-ui/react-primitive": "2.1.4" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-FmXs37I6hSBVDlO4y764TNz1rLgKwjJMQ0EGte6F3Cb3f4bIuHB/iLa/8I9VKkmOy+gNHq8rql3j686ACVV21A=="], + + "@radix-ui/react-menu": ["@radix-ui/react-menu@2.1.16", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-collection": "1.1.7", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", "@radix-ui/react-dismissable-layer": "1.1.11", "@radix-ui/react-focus-guards": "1.1.3", "@radix-ui/react-focus-scope": "1.1.7", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-popper": "1.2.8", "@radix-ui/react-portal": "1.1.9", "@radix-ui/react-presence": "1.1.5", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-roving-focus": "1.1.11", "@radix-ui/react-slot": "1.2.3", "@radix-ui/react-use-callback-ref": "1.1.1", "aria-hidden": "^1.2.4", "react-remove-scroll": "^2.6.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-72F2T+PLlphrqLcAotYPp0uJMr5SjP5SL01wfEspJbru5Zs5vQaSHb4VB3ZMJPimgHHCHG7gMOeOB9H3Hdmtxg=="], + + "@radix-ui/react-popover": ["@radix-ui/react-popover@1.1.15", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-dismissable-layer": "1.1.11", "@radix-ui/react-focus-guards": "1.1.3", "@radix-ui/react-focus-scope": "1.1.7", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-popper": "1.2.8", "@radix-ui/react-portal": "1.1.9", "@radix-ui/react-presence": "1.1.5", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-slot": "1.2.3", "@radix-ui/react-use-controllable-state": "1.2.2", "aria-hidden": "^1.2.4", "react-remove-scroll": "^2.6.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-kr0X2+6Yy/vJzLYJUPCZEc8SfQcf+1COFoAqauJm74umQhta9M7lNJHP7QQS3vkvcGLQUbWpMzwrXYwrYztHKA=="], + + "@radix-ui/react-popper": ["@radix-ui/react-popper@1.2.8", "", { "dependencies": { "@floating-ui/react-dom": "^2.0.0", "@radix-ui/react-arrow": "1.1.7", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-layout-effect": "1.1.1", "@radix-ui/react-use-rect": "1.1.1", "@radix-ui/react-use-size": "1.1.1", "@radix-ui/rect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-0NJQ4LFFUuWkE7Oxf0htBKS6zLkkjBH+hM1uk7Ng705ReR8m/uelduy1DBo0PyBXPKVnBA6YBlU94MBGXrSBCw=="], + + "@radix-ui/react-portal": ["@radix-ui/react-portal@1.1.9", "", { "dependencies": { "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-bpIxvq03if6UNwXZ+HTK71JLh4APvnXntDc6XOX8UVq4XQOVl7lwok0AvIl+b8zgCw3fSaVTZMpAPPagXbKmHQ=="], + + "@radix-ui/react-presence": ["@radix-ui/react-presence@1.1.5", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-/jfEwNDdQVBCNvjkGit4h6pMOzq8bHkopq458dPt2lMjx+eBQUohZNG9A7DtO/O5ukSbxuaNGXMjHicgwy6rQQ=="], + + "@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.3", "", { "dependencies": { "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ=="], + + "@radix-ui/react-progress": ["@radix-ui/react-progress@1.1.8", "", { "dependencies": { "@radix-ui/react-context": "1.1.3", "@radix-ui/react-primitive": "2.1.4" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-+gISHcSPUJ7ktBy9RnTqbdKW78bcGke3t6taawyZ71pio1JewwGSJizycs7rLhGTvMJYCQB1DBK4KQsxs7U8dA=="], + + "@radix-ui/react-radio-group": ["@radix-ui/react-radio-group@1.3.8", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", "@radix-ui/react-presence": "1.1.5", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-roving-focus": "1.1.11", "@radix-ui/react-use-controllable-state": "1.2.2", "@radix-ui/react-use-previous": "1.1.1", "@radix-ui/react-use-size": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-VBKYIYImA5zsxACdisNQ3BjCBfmbGH3kQlnFVqlWU4tXwjy7cGX8ta80BcrO+WJXIn5iBylEH3K6ZTlee//lgQ=="], + + "@radix-ui/react-roving-focus": ["@radix-ui/react-roving-focus@1.1.11", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-collection": "1.1.7", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-controllable-state": "1.2.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-7A6S9jSgm/S+7MdtNDSb+IU859vQqJ/QAtcYQcfFC6W8RS4IxIZDldLR0xqCFZ6DCyrQLjLPsxtTNch5jVA4lA=="], + + "@radix-ui/react-scroll-area": ["@radix-ui/react-scroll-area@1.2.10", "", { "dependencies": { "@radix-ui/number": "1.1.1", "@radix-ui/primitive": "1.1.3", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", "@radix-ui/react-presence": "1.1.5", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-tAXIa1g3sM5CGpVT0uIbUx/U3Gs5N8T52IICuCtObaos1S8fzsrPXG5WObkQN3S6NVl6wKgPhAIiBGbWnvc97A=="], + + "@radix-ui/react-select": ["@radix-ui/react-select@2.2.6", "", { "dependencies": { "@radix-ui/number": "1.1.1", "@radix-ui/primitive": "1.1.3", "@radix-ui/react-collection": "1.1.7", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", "@radix-ui/react-dismissable-layer": "1.1.11", "@radix-ui/react-focus-guards": "1.1.3", "@radix-ui/react-focus-scope": "1.1.7", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-popper": "1.2.8", "@radix-ui/react-portal": "1.1.9", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-slot": "1.2.3", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-controllable-state": "1.2.2", "@radix-ui/react-use-layout-effect": "1.1.1", "@radix-ui/react-use-previous": "1.1.1", "@radix-ui/react-visually-hidden": "1.2.3", "aria-hidden": "^1.2.4", "react-remove-scroll": "^2.6.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-I30RydO+bnn2PQztvo25tswPH+wFBjehVGtmagkU78yMdwTwVf12wnAOF+AeP8S2N8xD+5UPbGhkUfPyvT+mwQ=="], + + "@radix-ui/react-separator": ["@radix-ui/react-separator@1.1.8", "", { "dependencies": { "@radix-ui/react-primitive": "2.1.4" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-sDvqVY4itsKwwSMEe0jtKgfTh+72Sy3gPmQpjqcQneqQ4PFmr/1I0YA+2/puilhggCe2gJcx5EBAYFkWkdpa5g=="], + + "@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.4", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-Jl+bCv8HxKnlTLVrcDE8zTMJ09R9/ukw4qBs/oZClOfoQk/cOTbDn+NceXfV7j09YPVQUryJPHurafcSg6EVKA=="], + + "@radix-ui/react-switch": ["@radix-ui/react-switch@1.2.6", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-controllable-state": "1.2.2", "@radix-ui/react-use-previous": "1.1.1", "@radix-ui/react-use-size": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-bByzr1+ep1zk4VubeEVViV592vu2lHE2BZY5OnzehZqOOgogN80+mNtCqPkhn2gklJqOpxWgPoYTSnhBCqpOXQ=="], + + "@radix-ui/react-tabs": ["@radix-ui/react-tabs@1.1.13", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-presence": "1.1.5", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-roving-focus": "1.1.11", "@radix-ui/react-use-controllable-state": "1.2.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-7xdcatg7/U+7+Udyoj2zodtI9H/IIopqo+YOIcZOq1nJwXWBZ9p8xiu5llXlekDbZkca79a/fozEYQXIA4sW6A=="], + + "@radix-ui/react-tooltip": ["@radix-ui/react-tooltip@1.2.8", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-dismissable-layer": "1.1.11", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-popper": "1.2.8", "@radix-ui/react-portal": "1.1.9", "@radix-ui/react-presence": "1.1.5", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-slot": "1.2.3", "@radix-ui/react-use-controllable-state": "1.2.2", "@radix-ui/react-visually-hidden": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-tY7sVt1yL9ozIxvmbtN5qtmH2krXcBCfjEiCgKGLqunJHvgvZG2Pcl2oQ3kbcZARb1BGEHdkLzcYGO8ynVlieg=="], + + "@radix-ui/react-use-callback-ref": ["@radix-ui/react-use-callback-ref@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-FkBMwD+qbGQeMu1cOHnuGB6x4yzPjho8ap5WtbEJ26umhgqVXbhekKUQO+hZEL1vU92a3wHwdp0HAcqAUF5iDg=="], + + "@radix-ui/react-use-controllable-state": ["@radix-ui/react-use-controllable-state@1.2.2", "", { "dependencies": { "@radix-ui/react-use-effect-event": "0.0.2", "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-BjasUjixPFdS+NKkypcyyN5Pmg83Olst0+c6vGov0diwTEo6mgdqVR6hxcEgFuh4QrAs7Rc+9KuGJ9TVCj0Zzg=="], + + "@radix-ui/react-use-effect-event": ["@radix-ui/react-use-effect-event@0.0.2", "", { "dependencies": { "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-Qp8WbZOBe+blgpuUT+lw2xheLP8q0oatc9UpmiemEICxGvFLYmHm9QowVZGHtJlGbS6A6yJ3iViad/2cVjnOiA=="], + + "@radix-ui/react-use-escape-keydown": ["@radix-ui/react-use-escape-keydown@1.1.1", "", { "dependencies": { "@radix-ui/react-use-callback-ref": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-Il0+boE7w/XebUHyBjroE+DbByORGR9KKmITzbR7MyQ4akpORYP/ZmbhAr0DG7RmmBqoOnZdy2QlvajJ2QA59g=="], + + "@radix-ui/react-use-is-hydrated": ["@radix-ui/react-use-is-hydrated@0.1.0", "", { "dependencies": { "use-sync-external-store": "^1.5.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-U+UORVEq+cTnRIaostJv9AGdV3G6Y+zbVd+12e18jQ5A3c0xL03IhnHuiU4UV69wolOQp5GfR58NW/EgdQhwOA=="], + + "@radix-ui/react-use-layout-effect": ["@radix-ui/react-use-layout-effect@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ=="], + + "@radix-ui/react-use-previous": ["@radix-ui/react-use-previous@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-2dHfToCj/pzca2Ck724OZ5L0EVrr3eHRNsG/b3xQJLA2hZpVCS99bLAX+hm1IHXDEnzU6by5z/5MIY794/a8NQ=="], + + "@radix-ui/react-use-rect": ["@radix-ui/react-use-rect@1.1.1", "", { "dependencies": { "@radix-ui/rect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-QTYuDesS0VtuHNNvMh+CjlKJ4LJickCMUAqjlE3+j8w+RlRpwyX3apEQKGFzbZGdo7XNG1tXa+bQqIE7HIXT2w=="], + + "@radix-ui/react-use-size": ["@radix-ui/react-use-size@1.1.1", "", { "dependencies": { "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-ewrXRDTAqAXlkl6t/fkXWNAhFX9I+CkKlw6zjEwk86RSPKwZr3xpBRso655aqYafwtnbpHLj6toFzmd6xdVptQ=="], + + "@radix-ui/react-visually-hidden": ["@radix-ui/react-visually-hidden@1.2.3", "", { "dependencies": { "@radix-ui/react-primitive": "2.1.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-pzJq12tEaaIhqjbzpCuv/OypJY/BPavOofm+dbab+MHLajy277+1lLm6JFcGgF5eskJ6mquGirhXY2GD/8u8Ug=="], + + "@radix-ui/rect": ["@radix-ui/rect@1.1.1", "", {}, "sha512-HPwpGIzkl28mWyZqG52jiqDJ12waP11Pa1lGoiyUkIEuMLBP0oeK/C89esbXrxsky5we7dfd8U58nm0SgAWpVw=="], + + "@rolldown/pluginutils": ["@rolldown/pluginutils@1.0.0-beta.47", "", {}, "sha512-8QagwMH3kNCuzD8EWL8R2YPW5e4OrHNSAHRFDdmFqEwEaD/KcNKjVoumo+gP2vW5eKB2UPbM6vTYiGZX0ixLnw=="], + + "@rollup/pluginutils": ["@rollup/pluginutils@5.3.0", "", { "dependencies": { "@types/estree": "^1.0.0", "estree-walker": "^2.0.2", "picomatch": "^4.0.2" }, "peerDependencies": { "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" }, "optionalPeers": ["rollup"] }, "sha512-5EdhGZtnu3V88ces7s53hhfK5KSASnJZv8Lulpc04cWO3REESroJXg73DFsOmgbU2BhwV0E20bu2IDZb3VKW4Q=="], + + "@rollup/rollup-android-arm-eabi": ["@rollup/rollup-android-arm-eabi@4.41.1", "", { "os": "android", "cpu": "arm" }, "sha512-NELNvyEWZ6R9QMkiytB4/L4zSEaBC03KIXEghptLGLZWJ6VPrL63ooZQCOnlx36aQPGhzuOMwDerC1Eb2VmrLw=="], + + "@rollup/rollup-android-arm64": ["@rollup/rollup-android-arm64@4.41.1", "", { "os": "android", "cpu": "arm64" }, "sha512-DXdQe1BJ6TK47ukAoZLehRHhfKnKg9BjnQYUu9gzhI8Mwa1d2fzxA1aw2JixHVl403bwp1+/o/NhhHtxWJBgEA=="], + + "@rollup/rollup-darwin-arm64": ["@rollup/rollup-darwin-arm64@4.41.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-5afxvwszzdulsU2w8JKWwY8/sJOLPzf0e1bFuvcW5h9zsEg+RQAojdW0ux2zyYAz7R8HvvzKCjLNJhVq965U7w=="], + + "@rollup/rollup-darwin-x64": ["@rollup/rollup-darwin-x64@4.41.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-egpJACny8QOdHNNMZKf8xY0Is6gIMz+tuqXlusxquWu3F833DcMwmGM7WlvCO9sB3OsPjdC4U0wHw5FabzCGZg=="], + + "@rollup/rollup-freebsd-arm64": ["@rollup/rollup-freebsd-arm64@4.41.1", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-DBVMZH5vbjgRk3r0OzgjS38z+atlupJ7xfKIDJdZZL6sM6wjfDNo64aowcLPKIx7LMQi8vybB56uh1Ftck/Atg=="], + + "@rollup/rollup-freebsd-x64": ["@rollup/rollup-freebsd-x64@4.41.1", "", { "os": "freebsd", "cpu": "x64" }, "sha512-3FkydeohozEskBxNWEIbPfOE0aqQgB6ttTkJ159uWOFn42VLyfAiyD9UK5mhu+ItWzft60DycIN1Xdgiy8o/SA=="], + + "@rollup/rollup-linux-arm-gnueabihf": ["@rollup/rollup-linux-arm-gnueabihf@4.41.1", "", { "os": "linux", "cpu": "arm" }, "sha512-wC53ZNDgt0pqx5xCAgNunkTzFE8GTgdZ9EwYGVcg+jEjJdZGtq9xPjDnFgfFozQI/Xm1mh+D9YlYtl+ueswNEg=="], + + "@rollup/rollup-linux-arm-musleabihf": ["@rollup/rollup-linux-arm-musleabihf@4.41.1", "", { "os": "linux", "cpu": "arm" }, "sha512-jwKCca1gbZkZLhLRtsrka5N8sFAaxrGz/7wRJ8Wwvq3jug7toO21vWlViihG85ei7uJTpzbXZRcORotE+xyrLA=="], + + "@rollup/rollup-linux-arm64-gnu": ["@rollup/rollup-linux-arm64-gnu@4.41.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-g0UBcNknsmmNQ8V2d/zD2P7WWfJKU0F1nu0k5pW4rvdb+BIqMm8ToluW/eeRmxCared5dD76lS04uL4UaNgpNA=="], + + "@rollup/rollup-linux-arm64-musl": ["@rollup/rollup-linux-arm64-musl@4.41.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-XZpeGB5TKEZWzIrj7sXr+BEaSgo/ma/kCgrZgL0oo5qdB1JlTzIYQKel/RmhT6vMAvOdM2teYlAaOGJpJ9lahg=="], + + "@rollup/rollup-linux-loongarch64-gnu": ["@rollup/rollup-linux-loongarch64-gnu@4.41.1", "", { "os": "linux", "cpu": "none" }, "sha512-bkCfDJ4qzWfFRCNt5RVV4DOw6KEgFTUZi2r2RuYhGWC8WhCA8lCAJhDeAmrM/fdiAH54m0mA0Vk2FGRPyzI+tw=="], + + "@rollup/rollup-linux-powerpc64le-gnu": ["@rollup/rollup-linux-powerpc64le-gnu@4.41.1", "", { "os": "linux", "cpu": "ppc64" }, "sha512-3mr3Xm+gvMX+/8EKogIZSIEF0WUu0HL9di+YWlJpO8CQBnoLAEL/roTCxuLncEdgcfJcvA4UMOf+2dnjl4Ut1A=="], + + "@rollup/rollup-linux-riscv64-gnu": ["@rollup/rollup-linux-riscv64-gnu@4.41.1", "", { "os": "linux", "cpu": "none" }, "sha512-3rwCIh6MQ1LGrvKJitQjZFuQnT2wxfU+ivhNBzmxXTXPllewOF7JR1s2vMX/tWtUYFgphygxjqMl76q4aMotGw=="], + + "@rollup/rollup-linux-riscv64-musl": ["@rollup/rollup-linux-riscv64-musl@4.41.1", "", { "os": "linux", "cpu": "none" }, "sha512-LdIUOb3gvfmpkgFZuccNa2uYiqtgZAz3PTzjuM5bH3nvuy9ty6RGc/Q0+HDFrHrizJGVpjnTZ1yS5TNNjFlklw=="], + + "@rollup/rollup-linux-s390x-gnu": ["@rollup/rollup-linux-s390x-gnu@4.41.1", "", { "os": "linux", "cpu": "s390x" }, "sha512-oIE6M8WC9ma6xYqjvPhzZYk6NbobIURvP/lEbh7FWplcMO6gn7MM2yHKA1eC/GvYwzNKK/1LYgqzdkZ8YFxR8g=="], + + "@rollup/rollup-linux-x64-gnu": ["@rollup/rollup-linux-x64-gnu@4.41.1", "", { "os": "linux", "cpu": "x64" }, "sha512-cWBOvayNvA+SyeQMp79BHPK8ws6sHSsYnK5zDcsC3Hsxr1dgTABKjMnMslPq1DvZIp6uO7kIWhiGwaTdR4Og9A=="], + + "@rollup/rollup-linux-x64-musl": ["@rollup/rollup-linux-x64-musl@4.41.1", "", { "os": "linux", "cpu": "x64" }, "sha512-y5CbN44M+pUCdGDlZFzGGBSKCA4A/J2ZH4edTYSSxFg7ce1Xt3GtydbVKWLlzL+INfFIZAEg1ZV6hh9+QQf9YQ=="], + + "@rollup/rollup-win32-arm64-msvc": ["@rollup/rollup-win32-arm64-msvc@4.41.1", "", { "os": "win32", "cpu": "arm64" }, "sha512-lZkCxIrjlJlMt1dLO/FbpZbzt6J/A8p4DnqzSa4PWqPEUUUnzXLeki/iyPLfV0BmHItlYgHUqJe+3KiyydmiNQ=="], + + "@rollup/rollup-win32-ia32-msvc": ["@rollup/rollup-win32-ia32-msvc@4.41.1", "", { "os": "win32", "cpu": "ia32" }, "sha512-+psFT9+pIh2iuGsxFYYa/LhS5MFKmuivRsx9iPJWNSGbh2XVEjk90fmpUEjCnILPEPJnikAU6SFDiEUyOv90Pg=="], + + "@rollup/rollup-win32-x64-msvc": ["@rollup/rollup-win32-x64-msvc@4.41.1", "", { "os": "win32", "cpu": "x64" }, "sha512-Wq2zpapRYLfi4aKxf2Xff0tN+7slj2d4R87WEzqw7ZLsVvO5zwYCIuEGSZYiK41+GlwUo1HiR+GdkLEJnCKTCw=="], + + "@shikijs/core": ["@shikijs/core@3.19.0", "", { "dependencies": { "@shikijs/types": "3.19.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4", "hast-util-to-html": "^9.0.5" } }, "sha512-L7SrRibU7ZoYi1/TrZsJOFAnnHyLTE1SwHG1yNWjZIVCqjOEmCSuK2ZO9thnRbJG6TOkPp+Z963JmpCNw5nzvA=="], + + "@shikijs/engine-javascript": ["@shikijs/engine-javascript@3.19.0", "", { "dependencies": { "@shikijs/types": "3.19.0", "@shikijs/vscode-textmate": "^10.0.2", "oniguruma-to-es": "^4.3.4" } }, "sha512-ZfWJNm2VMhKkQIKT9qXbs76RRcT0SF/CAvEz0+RkpUDAoDaCx0uFdCGzSRiD9gSlhm6AHkjdieOBJMaO2eC1rQ=="], + + "@shikijs/engine-oniguruma": ["@shikijs/engine-oniguruma@3.19.0", "", { "dependencies": { "@shikijs/types": "3.19.0", "@shikijs/vscode-textmate": "^10.0.2" } }, "sha512-1hRxtYIJfJSZeM5ivbUXv9hcJP3PWRo5prG/V2sWwiubUKTa+7P62d2qxCW8jiVFX4pgRHhnHNp+qeR7Xl+6kg=="], + + "@shikijs/langs": ["@shikijs/langs@3.19.0", "", { "dependencies": { "@shikijs/types": "3.19.0" } }, "sha512-dBMFzzg1QiXqCVQ5ONc0z2ebyoi5BKz+MtfByLm0o5/nbUu3Iz8uaTCa5uzGiscQKm7lVShfZHU1+OG3t5hgwg=="], + + "@shikijs/themes": ["@shikijs/themes@3.19.0", "", { "dependencies": { "@shikijs/types": "3.19.0" } }, "sha512-H36qw+oh91Y0s6OlFfdSuQ0Ld+5CgB/VE6gNPK+Hk4VRbVG/XQgkjnt4KzfnnoO6tZPtKJKHPjwebOCfjd6F8A=="], + + "@shikijs/types": ["@shikijs/types@3.19.0", "", { "dependencies": { "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-Z2hdeEQlzuntf/BZpFG8a+Fsw9UVXdML7w0o3TgSXV3yNESGon+bs9ITkQb3Ki7zxoXOOu5oJWqZ2uto06V9iQ=="], + + "@shikijs/vscode-textmate": ["@shikijs/vscode-textmate@10.0.2", "", {}, "sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg=="], + + "@standard-schema/spec": ["@standard-schema/spec@1.0.0", "", {}, "sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA=="], + + "@swc/helpers": ["@swc/helpers@0.5.17", "", { "dependencies": { "tslib": "^2.8.0" } }, "sha512-5IKx/Y13RsYd+sauPb2x+U/xZikHjolzfuDgTAl/Tdf3Q8rslRvC19NKDLgAJQ6wsqADk10ntlv08nPFw/gO/A=="], + + "@tailwindcss/node": ["@tailwindcss/node@4.1.17", "", { "dependencies": { "@jridgewell/remapping": "^2.3.4", "enhanced-resolve": "^5.18.3", "jiti": "^2.6.1", "lightningcss": "1.30.2", "magic-string": "^0.30.21", "source-map-js": "^1.2.1", "tailwindcss": "4.1.17" } }, "sha512-csIkHIgLb3JisEFQ0vxr2Y57GUNYh447C8xzwj89U/8fdW8LhProdxvnVH6U8M2Y73QKiTIH+LWbK3V2BBZsAg=="], + + "@tailwindcss/oxide": ["@tailwindcss/oxide@4.1.17", "", { "optionalDependencies": { "@tailwindcss/oxide-android-arm64": "4.1.17", "@tailwindcss/oxide-darwin-arm64": "4.1.17", "@tailwindcss/oxide-darwin-x64": "4.1.17", "@tailwindcss/oxide-freebsd-x64": "4.1.17", "@tailwindcss/oxide-linux-arm-gnueabihf": "4.1.17", "@tailwindcss/oxide-linux-arm64-gnu": "4.1.17", "@tailwindcss/oxide-linux-arm64-musl": "4.1.17", "@tailwindcss/oxide-linux-x64-gnu": "4.1.17", "@tailwindcss/oxide-linux-x64-musl": "4.1.17", "@tailwindcss/oxide-wasm32-wasi": "4.1.17", "@tailwindcss/oxide-win32-arm64-msvc": "4.1.17", "@tailwindcss/oxide-win32-x64-msvc": "4.1.17" } }, "sha512-F0F7d01fmkQhsTjXezGBLdrl1KresJTcI3DB8EkScCldyKp3Msz4hub4uyYaVnk88BAS1g5DQjjF6F5qczheLA=="], + + "@tailwindcss/oxide-android-arm64": ["@tailwindcss/oxide-android-arm64@4.1.17", "", { "os": "android", "cpu": "arm64" }, "sha512-BMqpkJHgOZ5z78qqiGE6ZIRExyaHyuxjgrJ6eBO5+hfrfGkuya0lYfw8fRHG77gdTjWkNWEEm+qeG2cDMxArLQ=="], + + "@tailwindcss/oxide-darwin-arm64": ["@tailwindcss/oxide-darwin-arm64@4.1.17", "", { "os": "darwin", "cpu": "arm64" }, "sha512-EquyumkQweUBNk1zGEU/wfZo2qkp/nQKRZM8bUYO0J+Lums5+wl2CcG1f9BgAjn/u9pJzdYddHWBiFXJTcxmOg=="], + + "@tailwindcss/oxide-darwin-x64": ["@tailwindcss/oxide-darwin-x64@4.1.17", "", { "os": "darwin", "cpu": "x64" }, "sha512-gdhEPLzke2Pog8s12oADwYu0IAw04Y2tlmgVzIN0+046ytcgx8uZmCzEg4VcQh+AHKiS7xaL8kGo/QTiNEGRog=="], + + "@tailwindcss/oxide-freebsd-x64": ["@tailwindcss/oxide-freebsd-x64@4.1.17", "", { "os": "freebsd", "cpu": "x64" }, "sha512-hxGS81KskMxML9DXsaXT1H0DyA+ZBIbyG/sSAjWNe2EDl7TkPOBI42GBV3u38itzGUOmFfCzk1iAjDXds8Oh0g=="], + + "@tailwindcss/oxide-linux-arm-gnueabihf": ["@tailwindcss/oxide-linux-arm-gnueabihf@4.1.17", "", { "os": "linux", "cpu": "arm" }, "sha512-k7jWk5E3ldAdw0cNglhjSgv501u7yrMf8oeZ0cElhxU6Y2o7f8yqelOp3fhf7evjIS6ujTI3U8pKUXV2I4iXHQ=="], + + "@tailwindcss/oxide-linux-arm64-gnu": ["@tailwindcss/oxide-linux-arm64-gnu@4.1.17", "", { "os": "linux", "cpu": "arm64" }, "sha512-HVDOm/mxK6+TbARwdW17WrgDYEGzmoYayrCgmLEw7FxTPLcp/glBisuyWkFz/jb7ZfiAXAXUACfyItn+nTgsdQ=="], + + "@tailwindcss/oxide-linux-arm64-musl": ["@tailwindcss/oxide-linux-arm64-musl@4.1.17", "", { "os": "linux", "cpu": "arm64" }, "sha512-HvZLfGr42i5anKtIeQzxdkw/wPqIbpeZqe7vd3V9vI3RQxe3xU1fLjss0TjyhxWcBaipk7NYwSrwTwK1hJARMg=="], + + "@tailwindcss/oxide-linux-x64-gnu": ["@tailwindcss/oxide-linux-x64-gnu@4.1.17", "", { "os": "linux", "cpu": "x64" }, "sha512-M3XZuORCGB7VPOEDH+nzpJ21XPvK5PyjlkSFkFziNHGLc5d6g3di2McAAblmaSUNl8IOmzYwLx9NsE7bplNkwQ=="], + + "@tailwindcss/oxide-linux-x64-musl": ["@tailwindcss/oxide-linux-x64-musl@4.1.17", "", { "os": "linux", "cpu": "x64" }, "sha512-k7f+pf9eXLEey4pBlw+8dgfJHY4PZ5qOUFDyNf7SI6lHjQ9Zt7+NcscjpwdCEbYi6FI5c2KDTDWyf2iHcCSyyQ=="], + + "@tailwindcss/oxide-wasm32-wasi": ["@tailwindcss/oxide-wasm32-wasi@4.1.17", "", { "dependencies": { "@emnapi/core": "^1.6.0", "@emnapi/runtime": "^1.6.0", "@emnapi/wasi-threads": "^1.1.0", "@napi-rs/wasm-runtime": "^1.0.7", "@tybys/wasm-util": "^0.10.1", "tslib": "^2.4.0" }, "cpu": "none" }, "sha512-cEytGqSSoy7zK4JRWiTCx43FsKP/zGr0CsuMawhH67ONlH+T79VteQeJQRO/X7L0juEUA8ZyuYikcRBf0vsxhg=="], + + "@tailwindcss/oxide-win32-arm64-msvc": ["@tailwindcss/oxide-win32-arm64-msvc@4.1.17", "", { "os": "win32", "cpu": "arm64" }, "sha512-JU5AHr7gKbZlOGvMdb4722/0aYbU+tN6lv1kONx0JK2cGsh7g148zVWLM0IKR3NeKLv+L90chBVYcJ8uJWbC9A=="], + + "@tailwindcss/oxide-win32-x64-msvc": ["@tailwindcss/oxide-win32-x64-msvc@4.1.17", "", { "os": "win32", "cpu": "x64" }, "sha512-SKWM4waLuqx0IH+FMDUw6R66Hu4OuTALFgnleKbqhgGU30DY20NORZMZUKgLRjQXNN2TLzKvh48QXTig4h4bGw=="], + + "@tailwindcss/vite": ["@tailwindcss/vite@4.1.17", "", { "dependencies": { "@tailwindcss/node": "4.1.17", "@tailwindcss/oxide": "4.1.17", "tailwindcss": "4.1.17" }, "peerDependencies": { "vite": "^5.2.0 || ^6 || ^7" } }, "sha512-4+9w8ZHOiGnpcGI6z1TVVfWaX/koK7fKeSYF3qlYg2xpBtbteP2ddBxiarL+HVgfSJGeK5RIxRQmKm4rTJJAwA=="], + + "@tanstack/react-virtual": ["@tanstack/react-virtual@3.13.12", "", { "dependencies": { "@tanstack/virtual-core": "3.13.12" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-Gd13QdxPSukP8ZrkbgS2RwoZseTTbQPLnQEn7HY/rqtM+8Zt95f7xKC7N0EsKs7aoz0WzZ+fditZux+F8EzYxA=="], + + "@tanstack/virtual-core": ["@tanstack/virtual-core@3.13.12", "", {}, "sha512-1YBOJfRHV4sXUmWsFSf5rQor4Ss82G8dQWLRbnk3GA4jeP8hQt1hxXh0tmflpC0dz3VgEv/1+qwPyLeWkQuPFA=="], + + "@testing-library/dom": ["@testing-library/dom@10.4.0", "", { "dependencies": { "@babel/code-frame": "^7.10.4", "@babel/runtime": "^7.12.5", "@types/aria-query": "^5.0.1", "aria-query": "5.3.0", "chalk": "^4.1.0", "dom-accessibility-api": "^0.5.9", "lz-string": "^1.5.0", "pretty-format": "^27.0.2" } }, "sha512-pemlzrSESWbdAloYml3bAJMEfNh1Z7EduzqPKprCH5S341frlpYnUEW0H72dLxa6IsYr+mPno20GiSm+h9dEdQ=="], + + "@testing-library/jest-dom": ["@testing-library/jest-dom@6.9.1", "", { "dependencies": { "@adobe/css-tools": "^4.4.0", "aria-query": "^5.0.0", "css.escape": "^1.5.1", "dom-accessibility-api": "^0.6.3", "picocolors": "^1.1.1", "redent": "^3.0.0" } }, "sha512-zIcONa+hVtVSSep9UT3jZ5rizo2BsxgyDYU7WFD5eICBE7no3881HGeb/QkGfsJs6JTkY1aQhT7rIPC7e+0nnA=="], + + "@testing-library/react": ["@testing-library/react@16.3.0", "", { "dependencies": { "@babel/runtime": "^7.12.5" }, "peerDependencies": { "@testing-library/dom": "^10.0.0", "@types/react": "^18.0.0 || ^19.0.0", "@types/react-dom": "^18.0.0 || ^19.0.0", "react": "^18.0.0 || ^19.0.0", "react-dom": "^18.0.0 || ^19.0.0" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-kFSyxiEDwv1WLl2fgsq6pPBbw5aWKrsY2/noi1Id0TK0UParSF62oFQFGHXIyaG4pp2tEub/Zlel+fjjZILDsw=="], + + "@types/aria-query": ["@types/aria-query@5.0.4", "", {}, "sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw=="], + + "@types/babel__core": ["@types/babel__core@7.20.5", "", { "dependencies": { "@babel/parser": "^7.20.7", "@babel/types": "^7.20.7", "@types/babel__generator": "*", "@types/babel__template": "*", "@types/babel__traverse": "*" } }, "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA=="], + + "@types/babel__generator": ["@types/babel__generator@7.27.0", "", { "dependencies": { "@babel/types": "^7.0.0" } }, "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg=="], + + "@types/babel__template": ["@types/babel__template@7.4.4", "", { "dependencies": { "@babel/parser": "^7.1.0", "@babel/types": "^7.0.0" } }, "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A=="], + + "@types/babel__traverse": ["@types/babel__traverse@7.20.7", "", { "dependencies": { "@babel/types": "^7.20.7" } }, "sha512-dkO5fhS7+/oos4ciWxyEyjWe48zmG6wbCheo/G2ZnHx4fs3EU6YC6UM8rk56gAjNJ9P3MTH2jo5jb92/K6wbng=="], + + "@types/bcryptjs": ["@types/bcryptjs@3.0.0", "", { "dependencies": { "bcryptjs": "*" } }, "sha512-WRZOuCuaz8UcZZE4R5HXTco2goQSI2XxjGY3hbM/xDvwmqFWd4ivooImsMx65OKM6CtNKbnZ5YL+YwAwK7c1dg=="], + + "@types/bun": ["@types/bun@1.3.3", "", { "dependencies": { "bun-types": "1.3.3" } }, "sha512-ogrKbJ2X5N0kWLLFKeytG0eHDleBYtngtlbu9cyBKFtNL3cnpDZkNdQj8flVf6WTZUX5ulI9AY1oa7ljhSrp+g=="], + + "@types/canvas-confetti": ["@types/canvas-confetti@1.9.0", "", {}, "sha512-aBGj/dULrimR1XDZLtG9JwxX1b4HPRF6CX9Yfwh3NvstZEm1ZL7RBnel4keCPSqs1ANRu1u2Aoz9R+VmtjYuTg=="], + + "@types/chai": ["@types/chai@5.2.2", "", { "dependencies": { "@types/deep-eql": "*" } }, "sha512-8kB30R7Hwqf40JPiKhVzodJs2Qc1ZJ5zuT3uzw5Hq/dhNCl3G3l83jfpdI1e20BP348+fV7VIL/+FxaXkqBmWg=="], + + "@types/debug": ["@types/debug@4.1.12", "", { "dependencies": { "@types/ms": "*" } }, "sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ=="], + + "@types/deep-eql": ["@types/deep-eql@4.0.2", "", {}, "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw=="], + + "@types/estree": ["@types/estree@1.0.7", "", {}, "sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ=="], + + "@types/estree-jsx": ["@types/estree-jsx@1.0.5", "", { "dependencies": { "@types/estree": "*" } }, "sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg=="], + + "@types/fontkit": ["@types/fontkit@2.0.8", "", { "dependencies": { "@types/node": "*" } }, "sha512-wN+8bYxIpJf+5oZdrdtaX04qUuWHcKxcDEgRS9Qm9ZClSHjzEn13SxUC+5eRM+4yXIeTYk8mTzLAWGF64847ew=="], + + "@types/hast": ["@types/hast@3.0.4", "", { "dependencies": { "@types/unist": "*" } }, "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ=="], + + "@types/jsonwebtoken": ["@types/jsonwebtoken@9.0.10", "", { "dependencies": { "@types/ms": "*", "@types/node": "*" } }, "sha512-asx5hIG9Qmf/1oStypjanR7iKTv0gXQ1Ov/jfrX6kS/EO0OFni8orbmGCn0672NHR3kXHwpAwR+B368ZGN/2rA=="], + + "@types/mdast": ["@types/mdast@4.0.4", "", { "dependencies": { "@types/unist": "*" } }, "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA=="], + + "@types/mdx": ["@types/mdx@2.0.13", "", {}, "sha512-+OWZQfAYyio6YkJb3HLxDrvnx6SWWDbC0zVPfBRzUk0/nqoDyf6dNxQi3eArPe8rJ473nobTMQ/8Zk+LxJ+Yuw=="], + + "@types/ms": ["@types/ms@2.1.0", "", {}, "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA=="], + + "@types/nlcst": ["@types/nlcst@2.0.3", "", { "dependencies": { "@types/unist": "*" } }, "sha512-vSYNSDe6Ix3q+6Z7ri9lyWqgGhJTmzRjZRqyq15N0Z/1/UnVsno9G/N40NBijoYx2seFDIl0+B2mgAb9mezUCA=="], + + "@types/node": ["@types/node@22.15.23", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-7Ec1zaFPF4RJ0eXu1YT/xgiebqwqoJz8rYPDi/O2BcZ++Wpt0Kq9cl0eg6NN6bYbPnR67ZLo7St5Q3UK0SnARw=="], + + "@types/react": ["@types/react@19.2.7", "", { "dependencies": { "csstype": "^3.2.2" } }, "sha512-MWtvHrGZLFttgeEj28VXHxpmwYbor/ATPYbBfSFZEIRK0ecCFLl2Qo55z52Hss+UV9CRN7trSeq1zbgx7YDWWg=="], + + "@types/react-dom": ["@types/react-dom@19.2.3", "", { "peerDependencies": { "@types/react": "^19.2.0" } }, "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ=="], + + "@types/unist": ["@types/unist@3.0.3", "", {}, "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q=="], + + "@types/uuid": ["@types/uuid@10.0.0", "", {}, "sha512-7gqG38EyHgyP1S+7+xomFtL+ZNHcKv6DwNaCZmJmo1vgMugyF3TCnXVg4t1uk89mLNwnLtnY3TpOpCOyp1/xHQ=="], + + "@ungap/structured-clone": ["@ungap/structured-clone@1.3.0", "", {}, "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g=="], + + "@vitejs/plugin-react": ["@vitejs/plugin-react@5.1.1", "", { "dependencies": { "@babel/core": "^7.28.5", "@babel/plugin-transform-react-jsx-self": "^7.27.1", "@babel/plugin-transform-react-jsx-source": "^7.27.1", "@rolldown/pluginutils": "1.0.0-beta.47", "@types/babel__core": "^7.20.5", "react-refresh": "^0.18.0" }, "peerDependencies": { "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0" } }, "sha512-WQfkSw0QbQ5aJ2CHYw23ZGkqnRwqKHD/KYsMeTkZzPT4Jcf0DcBxBtwMJxnu6E7oxw5+JC6ZAiePgh28uJ1HBA=="], + + "@vitest/expect": ["@vitest/expect@4.0.15", "", { "dependencies": { "@standard-schema/spec": "^1.0.0", "@types/chai": "^5.2.2", "@vitest/spy": "4.0.15", "@vitest/utils": "4.0.15", "chai": "^6.2.1", "tinyrainbow": "^3.0.3" } }, "sha512-Gfyva9/GxPAWXIWjyGDli9O+waHDC0Q0jaLdFP1qPAUUfo1FEXPXUfUkp3eZA0sSq340vPycSyOlYUeM15Ft1w=="], + + "@vitest/mocker": ["@vitest/mocker@4.0.15", "", { "dependencies": { "@vitest/spy": "4.0.15", "estree-walker": "^3.0.3", "magic-string": "^0.30.21" }, "peerDependencies": { "msw": "^2.4.9", "vite": "^6.0.0 || ^7.0.0-0" }, "optionalPeers": ["msw", "vite"] }, "sha512-CZ28GLfOEIFkvCFngN8Sfx5h+Se0zN+h4B7yOsPVCcgtiO7t5jt9xQh2E1UkFep+eb9fjyMfuC5gBypwb07fvQ=="], + + "@vitest/pretty-format": ["@vitest/pretty-format@4.0.15", "", { "dependencies": { "tinyrainbow": "^3.0.3" } }, "sha512-SWdqR8vEv83WtZcrfLNqlqeQXlQLh2iilO1Wk1gv4eiHKjEzvgHb2OVc3mIPyhZE6F+CtfYjNlDJwP5MN6Km7A=="], + + "@vitest/runner": ["@vitest/runner@4.0.15", "", { "dependencies": { "@vitest/utils": "4.0.15", "pathe": "^2.0.3" } }, "sha512-+A+yMY8dGixUhHmNdPUxOh0la6uVzun86vAbuMT3hIDxMrAOmn5ILBHm8ajrqHE0t8R9T1dGnde1A5DTnmi3qw=="], + + "@vitest/snapshot": ["@vitest/snapshot@4.0.15", "", { "dependencies": { "@vitest/pretty-format": "4.0.15", "magic-string": "^0.30.21", "pathe": "^2.0.3" } }, "sha512-A7Ob8EdFZJIBjLjeO0DZF4lqR6U7Ydi5/5LIZ0xcI+23lYlsYJAfGn8PrIWTYdZQRNnSRlzhg0zyGu37mVdy5g=="], + + "@vitest/spy": ["@vitest/spy@4.0.15", "", {}, "sha512-+EIjOJmnY6mIfdXtE/bnozKEvTC4Uczg19yeZ2vtCz5Yyb0QQ31QWVQ8hswJ3Ysx/K2EqaNsVanjr//2+P3FHw=="], + + "@vitest/utils": ["@vitest/utils@4.0.15", "", { "dependencies": { "@vitest/pretty-format": "4.0.15", "tinyrainbow": "^3.0.3" } }, "sha512-HXjPW2w5dxhTD0dLwtYHDnelK3j8sR8cWIaLxr22evTyY6q8pRCjZSmhRWVjBaOVXChQd6AwMzi9pucorXCPZA=="], + + "@volar/kit": ["@volar/kit@2.4.26", "", { "dependencies": { "@volar/language-service": "2.4.26", "@volar/typescript": "2.4.26", "typesafe-path": "^0.2.2", "vscode-languageserver-textdocument": "^1.0.11", "vscode-uri": "^3.0.8" }, "peerDependencies": { "typescript": "*" } }, "sha512-shgNg7PbV8SIxxQLOQh5zMr8KV0JvdG9If0MwJb5L1HMrBU91jBxR0ANi2OJPMMme6/l1vIYm4hCaO6W2JaEcQ=="], + + "@volar/language-core": ["@volar/language-core@2.4.26", "", { "dependencies": { "@volar/source-map": "2.4.26" } }, "sha512-hH0SMitMxnB43OZpyF1IFPS9bgb2I3bpCh76m2WEK7BE0A0EzpYsRp0CCH2xNKshr7kacU5TQBLYn4zj7CG60A=="], + + "@volar/language-server": ["@volar/language-server@2.4.26", "", { "dependencies": { "@volar/language-core": "2.4.26", "@volar/language-service": "2.4.26", "@volar/typescript": "2.4.26", "path-browserify": "^1.0.1", "request-light": "^0.7.0", "vscode-languageserver": "^9.0.1", "vscode-languageserver-protocol": "^3.17.5", "vscode-languageserver-textdocument": "^1.0.11", "vscode-uri": "^3.0.8" } }, "sha512-Xsyu+VDgM8TyVkQfBz2aIViSEOgH2un0gIJlp0M8rssDDLCqr4ssQzwHOyPf7sT7UIjrlAMnJvRkC/u0mmgtYw=="], + + "@volar/language-service": ["@volar/language-service@2.4.26", "", { "dependencies": { "@volar/language-core": "2.4.26", "vscode-languageserver-protocol": "^3.17.5", "vscode-languageserver-textdocument": "^1.0.11", "vscode-uri": "^3.0.8" } }, "sha512-ZBPRR1ytXttSV5X4VPvEQR/glxs+7/4IOJIBCOW3/EJk4z77R4mF2y4wM3fNgOXXZT5h16j3sC5w+LGNkz2VlA=="], + + "@volar/source-map": ["@volar/source-map@2.4.26", "", {}, "sha512-JJw0Tt/kSFsIRmgTQF4JSt81AUSI1aEye5Zl65EeZ8H35JHnTvFGmpDOBn5iOxd48fyGE+ZvZBp5FcgAy/1Qhw=="], + + "@volar/typescript": ["@volar/typescript@2.4.26", "", { "dependencies": { "@volar/language-core": "2.4.26", "path-browserify": "^1.0.1", "vscode-uri": "^3.0.8" } }, "sha512-N87ecLD48Sp6zV9zID/5yuS1+5foj0DfuYGdQ6KHj/IbKvyKv1zNX6VCmnKYwtmHadEO6mFc2EKISiu3RDPAvA=="], + + "@vscode/emmet-helper": ["@vscode/emmet-helper@2.11.0", "", { "dependencies": { "emmet": "^2.4.3", "jsonc-parser": "^2.3.0", "vscode-languageserver-textdocument": "^1.0.1", "vscode-languageserver-types": "^3.15.1", "vscode-uri": "^3.0.8" } }, "sha512-QLxjQR3imPZPQltfbWRnHU6JecWTF1QSWhx3GAKQpslx7y3Dp6sIIXhKjiUJ/BR9FX8PVthjr9PD6pNwOJfAzw=="], + + "@vscode/l10n": ["@vscode/l10n@0.0.18", "", {}, "sha512-KYSIHVmslkaCDyw013pphY+d7x1qV8IZupYfeIfzNA+nsaWHbn5uPuQRvdRFsa9zFzGeudPuoGoZ1Op4jrJXIQ=="], + + "@xmldom/is-dom-node": ["@xmldom/is-dom-node@1.0.1", "", {}, "sha512-CJDxIgE5I0FH+ttq/Fxy6nRpxP70+e2O048EPe85J2use3XKdatVM7dDVvFNjQudd9B49NPoZ+8PG49zj4Er8Q=="], + + "@xmldom/xmldom": ["@xmldom/xmldom@0.8.10", "", {}, "sha512-2WALfTl4xo2SkGCYRt6rDTFfk9R1czmBvUQy12gK2KuRKIpWEhcbbzy8EZXtz/jkRqHX8bFEc6FC1HjX4TUWYw=="], + + "acorn": ["acorn@8.15.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg=="], + + "acorn-jsx": ["acorn-jsx@5.3.2", "", { "peerDependencies": { "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ=="], + + "agent-base": ["agent-base@7.1.3", "", {}, "sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw=="], + + "ajv": ["ajv@8.17.1", "", { "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2" } }, "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g=="], + + "ajv-draft-04": ["ajv-draft-04@1.0.0", "", { "peerDependencies": { "ajv": "^8.5.0" }, "optionalPeers": ["ajv"] }, "sha512-mv00Te6nmYbRp5DCwclxtt7yV/joXJPGS7nM+97GdxvuttCOfgI3K4U25zboyeX0O+myI8ERluxQe5wljMmVIw=="], + + "ansi-align": ["ansi-align@3.0.1", "", { "dependencies": { "string-width": "^4.1.0" } }, "sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w=="], + + "ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], + + "ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="], + + "anymatch": ["anymatch@3.1.3", "", { "dependencies": { "normalize-path": "^3.0.0", "picomatch": "^2.0.4" } }, "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw=="], + + "argparse": ["argparse@2.0.1", "", {}, "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="], + + "aria-hidden": ["aria-hidden@1.2.6", "", { "dependencies": { "tslib": "^2.0.0" } }, "sha512-ik3ZgC9dY/lYVVM++OISsaYDeg1tb0VtP5uL3ouh1koGOaUMDPpbFIei4JkFimWUFPn90sbMNMXQAIVOlnYKJA=="], + + "aria-query": ["aria-query@5.3.2", "", {}, "sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw=="], + + "array-iterate": ["array-iterate@2.0.1", "", {}, "sha512-I1jXZMjAgCMmxT4qxXfPXa6SthSoE8h6gkSI9BGGNv8mP8G/v0blc+qFnZu6K42vTOiuME596QaLO0TP3Lk0xg=="], + + "asn1": ["asn1@0.2.6", "", { "dependencies": { "safer-buffer": "~2.1.0" } }, "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ=="], + + "astring": ["astring@1.9.0", "", { "bin": { "astring": "bin/astring" } }, "sha512-LElXdjswlqjWrPpJFg1Fx4wpkOCxj1TDHlSV4PlaRxHGWko024xICaa97ZkMfs6DRKlCguiAI+rbXv5GWwXIkg=="], + + "astro": ["astro@5.16.4", "", { "dependencies": { "@astrojs/compiler": "^2.13.0", "@astrojs/internal-helpers": "0.7.5", "@astrojs/markdown-remark": "6.3.9", "@astrojs/telemetry": "3.3.0", "@capsizecss/unpack": "^3.0.1", "@oslojs/encoding": "^1.1.0", "@rollup/pluginutils": "^5.3.0", "acorn": "^8.15.0", "aria-query": "^5.3.2", "axobject-query": "^4.1.0", "boxen": "8.0.1", "ci-info": "^4.3.1", "clsx": "^2.1.1", "common-ancestor-path": "^1.0.1", "cookie": "^1.0.2", "cssesc": "^3.0.0", "debug": "^4.4.3", "deterministic-object-hash": "^2.0.2", "devalue": "^5.5.0", "diff": "^5.2.0", "dlv": "^1.1.3", "dset": "^3.1.4", "es-module-lexer": "^1.7.0", "esbuild": "^0.25.0", "estree-walker": "^3.0.3", "flattie": "^1.1.1", "fontace": "~0.3.1", "github-slugger": "^2.0.0", "html-escaper": "3.0.3", "http-cache-semantics": "^4.2.0", "import-meta-resolve": "^4.2.0", "js-yaml": "^4.1.1", "magic-string": "^0.30.21", "magicast": "^0.5.1", "mrmime": "^2.0.1", "neotraverse": "^0.6.18", "p-limit": "^6.2.0", "p-queue": "^8.1.1", "package-manager-detector": "^1.5.0", "piccolore": "^0.1.3", "picomatch": "^4.0.3", "prompts": "^2.4.2", "rehype": "^13.0.2", "semver": "^7.7.3", "shiki": "^3.15.0", "smol-toml": "^1.5.2", "svgo": "^4.0.0", "tinyexec": "^1.0.2", "tinyglobby": "^0.2.15", "tsconfck": "^3.1.6", "ultrahtml": "^1.6.0", "unifont": "~0.6.0", "unist-util-visit": "^5.0.0", "unstorage": "^1.17.3", "vfile": "^6.0.3", "vite": "^6.4.1", "vitefu": "^1.1.1", "xxhash-wasm": "^1.1.0", "yargs-parser": "^21.1.1", "yocto-spinner": "^0.2.3", "zod": "^3.25.76", "zod-to-json-schema": "^3.25.0", "zod-to-ts": "^1.2.0" }, "optionalDependencies": { "sharp": "^0.34.0" }, "bin": { "astro": "astro.js" } }, "sha512-rgXI/8/tnO3Y9tfAaUyg/8beKhlIMltbiC8Q6jCoAfEidOyaue4KYKzbe0gJIb6qEdEaG3Kf3BY3EOSLkbWOLg=="], + + "axobject-query": ["axobject-query@4.1.0", "", {}, "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ=="], + + "bail": ["bail@2.0.2", "", {}, "sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw=="], + + "base-64": ["base-64@1.0.0", "", {}, "sha512-kwDPIFCGx0NZHog36dj+tHiwP4QMzsZ3AgMViUBKI0+V5n4U0ufTCUMhnQ04diaRI8EX/QcPfql7zlhZ7j4zgg=="], + + "base64-js": ["base64-js@1.5.1", "", {}, "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA=="], + + "bcryptjs": ["bcryptjs@3.0.3", "", { "bin": { "bcrypt": "bin/bcrypt" } }, "sha512-GlF5wPWnSa/X5LKM1o0wz0suXIINz1iHRLvTS+sLyi7XPbe5ycmYI3DlZqVGZZtDgl4DmasFg7gOB3JYbphV5g=="], + + "before-after-hook": ["before-after-hook@4.0.0", "", {}, "sha512-q6tR3RPqIB1pMiTRMFcZwuG5T8vwp+vUvEG0vuI6B+Rikh5BfPp2fQ82c925FOs+b0lcFQ8CFrL+KbilfZFhOQ=="], + + "better-auth": ["better-auth@1.4.5", "", { "dependencies": { "@better-auth/core": "1.4.5", "@better-auth/telemetry": "1.4.5", "@better-auth/utils": "0.3.0", "@better-fetch/fetch": "1.1.18", "@noble/ciphers": "^2.0.0", "@noble/hashes": "^2.0.0", "better-call": "1.1.4", "defu": "^6.1.4", "jose": "^6.1.0", "kysely": "^0.28.5", "ms": "4.0.0-nightly.202508271359", "nanostores": "^1.0.1", "zod": "^4.1.12" }, "peerDependencies": { "@lynx-js/react": "*", "@sveltejs/kit": "^2.0.0", "@tanstack/react-start": "^1.0.0", "next": "^14.0.0 || ^15.0.0 || ^16.0.0", "react": "^18.0.0 || ^19.0.0", "react-dom": "^18.0.0 || ^19.0.0", "solid-js": "^1.0.0", "svelte": "^4.0.0 || ^5.0.0", "vue": "^3.0.0" }, "optionalPeers": ["@lynx-js/react", "@sveltejs/kit", "@tanstack/react-start", "next", "react", "react-dom", "solid-js", "svelte", "vue"] }, "sha512-pHV2YE0OogRHvoA6pndHXCei4pcep/mjY7psSaHVrRgjBtumVI68SV1g9U9XPRZ4KkoGca9jfwuv+bB2UILiFw=="], + + "better-call": ["better-call@1.1.4", "", { "dependencies": { "@better-auth/utils": "^0.3.0", "@better-fetch/fetch": "^1.1.4", "rou3": "^0.7.10", "set-cookie-parser": "^2.7.1" }, "peerDependencies": { "zod": "^4.0.0" }, "optionalPeers": ["zod"] }, "sha512-NJouLY6IVKv0nDuFoc6FcbKDFzEnmgMNofC9F60Mwx1Ecm7X6/Ecyoe5b+JSVZ42F/0n46/M89gbYP1ZCVv8xQ=="], + + "bidi-js": ["bidi-js@1.0.3", "", { "dependencies": { "require-from-string": "^2.0.2" } }, "sha512-RKshQI1R3YQ+n9YJz2QQ147P66ELpa1FQEg20Dk8oW9t2KgLbpDLLp9aGZ7y8WHSshDknG0bknqGw5/tyCs5tw=="], + + "boolbase": ["boolbase@1.0.0", "", {}, "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww=="], + + "bottleneck": ["bottleneck@2.19.5", "", {}, "sha512-VHiNCbI1lKdl44tGrhNfU3lup0Tj/ZBMJB5/2ZbNXRCPuRCO7ed2mgcK4r17y+KB2EfuYuRaVlwNbAeaWGSpbw=="], + + "boxen": ["boxen@8.0.1", "", { "dependencies": { "ansi-align": "^3.0.1", "camelcase": "^8.0.0", "chalk": "^5.3.0", "cli-boxes": "^3.0.0", "string-width": "^7.2.0", "type-fest": "^4.21.0", "widest-line": "^5.0.0", "wrap-ansi": "^9.0.0" } }, "sha512-F3PH5k5juxom4xktynS7MoFY+NUWH5LC4CnH11YB8NPew+HLpmBLCybSAEyb2F+4pRXhuhWqFesoQd6DAyc2hw=="], + + "braces": ["braces@3.0.3", "", { "dependencies": { "fill-range": "^7.1.1" } }, "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA=="], + + "brotli": ["brotli@1.3.3", "", { "dependencies": { "base64-js": "^1.1.2" } }, "sha512-oTKjJdShmDuGW94SyyaoQvAjf30dZaHnjJ8uAF+u2/vGJkJbJPJAT1gDiOJP5v1Zb6f9KEyW/1HpuaWIXtGHPg=="], + + "browserslist": ["browserslist@4.24.5", "", { "dependencies": { "caniuse-lite": "^1.0.30001716", "electron-to-chromium": "^1.5.149", "node-releases": "^2.0.19", "update-browserslist-db": "^1.1.3" }, "bin": { "browserslist": "cli.js" } }, "sha512-FDToo4Wo82hIdgc1CQ+NQD0hEhmpPjrZ3hiUgwgOG6IuTdlpr8jdjyG24P6cNP1yJpTLzS5OcGgSw0xmDU1/Tw=="], + + "buffer": ["buffer@6.0.3", "", { "dependencies": { "base64-js": "^1.3.1", "ieee754": "^1.2.1" } }, "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA=="], + + "buffer-equal-constant-time": ["buffer-equal-constant-time@1.0.1", "", {}, "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA=="], + + "bun-types": ["bun-types@1.3.3", "", { "dependencies": { "@types/node": "*" } }, "sha512-z3Xwlg7j2l9JY27x5Qn3Wlyos8YAp0kKRlrePAOjgjMGS5IG6E7Jnlx736vH9UVI4wUICwwhC9anYL++XeOgTQ=="], + + "camelcase": ["camelcase@6.3.0", "", {}, "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA=="], + + "caniuse-lite": ["caniuse-lite@1.0.30001718", "", {}, "sha512-AflseV1ahcSunK53NfEs9gFWgOEmzr0f+kaMFA4xiLZlr9Hzt7HxcSpIFcnNCUkz6R6dWKa54rUz3HUmI3nVcw=="], + + "canvas-confetti": ["canvas-confetti@1.9.4", "", {}, "sha512-yxQbJkAVrFXWNbTUjPqjF7G+g6pDotOUHGbkZq2NELZUMDpiJ85rIEazVb8GTaAptNW2miJAXbs1BtioA251Pw=="], + + "ccount": ["ccount@2.0.1", "", {}, "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg=="], + + "chai": ["chai@6.2.1", "", {}, "sha512-p4Z49OGG5W/WBCPSS/dH3jQ73kD6tiMmUM+bckNK6Jr5JHMG3k9bg/BvKR8lKmtVBKmOiuVaV2ws8s9oSbwysg=="], + + "chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="], + + "character-entities": ["character-entities@2.0.2", "", {}, "sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ=="], + + "character-entities-html4": ["character-entities-html4@2.1.0", "", {}, "sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA=="], + + "character-entities-legacy": ["character-entities-legacy@3.0.0", "", {}, "sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ=="], + + "character-reference-invalid": ["character-reference-invalid@2.0.1", "", {}, "sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw=="], + + "chokidar": ["chokidar@4.0.3", "", { "dependencies": { "readdirp": "^4.0.1" } }, "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA=="], + + "ci-info": ["ci-info@4.3.1", "", {}, "sha512-Wdy2Igu8OcBpI2pZePZ5oWjPC38tmDVx5WKUXKwlLYkA0ozo85sLsLvkBbBn/sZaSCMFOGZJ14fvW9t5/d7kdA=="], + + "class-variance-authority": ["class-variance-authority@0.7.1", "", { "dependencies": { "clsx": "^2.1.1" } }, "sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg=="], + + "cli-boxes": ["cli-boxes@3.0.0", "", {}, "sha512-/lzGpEWL/8PfI0BmBOPRwp0c/wFNX1RdUML3jK/RcSBA9T8mZDdQpqYBKtCFTOfQbwPqWEOpjqW+Fnayc0969g=="], + + "cliui": ["cliui@8.0.1", "", { "dependencies": { "string-width": "^4.2.0", "strip-ansi": "^6.0.1", "wrap-ansi": "^7.0.0" } }, "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ=="], + + "clone": ["clone@2.1.2", "", {}, "sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w=="], + + "clsx": ["clsx@2.1.1", "", {}, "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA=="], + + "cmdk": ["cmdk@1.1.1", "", { "dependencies": { "@radix-ui/react-compose-refs": "^1.1.1", "@radix-ui/react-dialog": "^1.1.6", "@radix-ui/react-id": "^1.1.0", "@radix-ui/react-primitive": "^2.0.2" }, "peerDependencies": { "react": "^18 || ^19 || ^19.0.0-rc", "react-dom": "^18 || ^19 || ^19.0.0-rc" } }, "sha512-Vsv7kFaXm+ptHDMZ7izaRsP70GgrW9NBNGswt9OZaVBLlE0SNpDq8eu/VGXyF9r7M0azK3Wy7OlYXsuyYLFzHg=="], + + "collapse-white-space": ["collapse-white-space@2.1.0", "", {}, "sha512-loKTxY1zCOuG4j9f6EPnuyyYkf58RnhhWTvRoZEokgB+WbdXehfjFviyOVYkqzEWz1Q5kRiZdBYS5SwxbQYwzw=="], + + "color": ["color@4.2.3", "", { "dependencies": { "color-convert": "^2.0.1", "color-string": "^1.9.0" } }, "sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A=="], + + "color-convert": ["color-convert@2.0.1", "", { "dependencies": { "color-name": "~1.1.4" } }, "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ=="], + + "color-name": ["color-name@1.1.4", "", {}, "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="], + + "color-string": ["color-string@1.9.1", "", { "dependencies": { "color-name": "^1.0.0", "simple-swizzle": "^0.2.2" } }, "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg=="], + + "comma-separated-tokens": ["comma-separated-tokens@2.0.3", "", {}, "sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg=="], + + "commander": ["commander@11.1.0", "", {}, "sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ=="], + + "common-ancestor-path": ["common-ancestor-path@1.0.1", "", {}, "sha512-L3sHRo1pXXEqX8VU28kfgUY+YGsk09hPqZiZmLacNib6XNTCM8ubYeT7ryXQw8asB1sKgcU5lkB7ONug08aB8w=="], + + "convert-source-map": ["convert-source-map@2.0.0", "", {}, "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg=="], + + "cookie": ["cookie@1.0.2", "", {}, "sha512-9Kr/j4O16ISv8zBBhJoi4bXOYNTkFLOqSL3UDB0njXxCXNezjeyVrJyGOWtgfs/q2km1gwBcfH8q1yEGoMYunA=="], + + "cookie-es": ["cookie-es@1.2.2", "", {}, "sha512-+W7VmiVINB+ywl1HGXJXmrqkOhpKrIiVZV6tQuV54ZyQC7MMuBt81Vc336GMLoHBq5hV/F9eXgt5Mnx0Rha5Fg=="], + + "crossws": ["crossws@0.3.5", "", { "dependencies": { "uncrypto": "^0.1.3" } }, "sha512-ojKiDvcmByhwa8YYqbQI/hg7MEU0NC03+pSdEq4ZUnZR9xXpwk7E43SMNGkn+JxJGPFtNvQ48+vV2p+P1ml5PA=="], + + "css-select": ["css-select@5.2.2", "", { "dependencies": { "boolbase": "^1.0.0", "css-what": "^6.1.0", "domhandler": "^5.0.2", "domutils": "^3.0.1", "nth-check": "^2.0.1" } }, "sha512-TizTzUddG/xYLA3NXodFM0fSbNizXjOKhqiQQwvhlspadZokn1KDy0NZFS0wuEubIYAV5/c1/lAr0TaaFXEXzw=="], + + "css-tree": ["css-tree@3.1.0", "", { "dependencies": { "mdn-data": "2.12.2", "source-map-js": "^1.0.1" } }, "sha512-0eW44TGN5SQXU1mWSkKwFstI/22X2bG1nYzZTYMAWjylYURhse752YgbE4Cx46AC+bAvI+/dYTPRk1LqSUnu6w=="], + + "css-what": ["css-what@6.2.2", "", {}, "sha512-u/O3vwbptzhMs3L1fQE82ZSLHQQfto5gyZzwteVIEyeaY5Fc7R4dapF/BvRoSYFeqfBk4m0V1Vafq5Pjv25wvA=="], + + "css.escape": ["css.escape@1.5.1", "", {}, "sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg=="], + + "cssesc": ["cssesc@3.0.0", "", { "bin": { "cssesc": "bin/cssesc" } }, "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg=="], + + "csso": ["csso@5.0.5", "", { "dependencies": { "css-tree": "~2.2.0" } }, "sha512-0LrrStPOdJj+SPCCrGhzryycLjwcgUSHBtxNA8aIDxf0GLsRh1cKYhB00Gd1lDOS4yGH69+SNn13+TWbVHETFQ=="], + + "cssstyle": ["cssstyle@5.3.3", "", { "dependencies": { "@asamuzakjp/css-color": "^4.0.3", "@csstools/css-syntax-patches-for-csstree": "^1.0.14", "css-tree": "^3.1.0" } }, "sha512-OytmFH+13/QXONJcC75QNdMtKpceNk3u8ThBjyyYjkEcy/ekBwR1mMAuNvi3gdBPW3N5TlCzQ0WZw8H0lN/bDw=="], + + "csstype": ["csstype@3.2.3", "", {}, "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ=="], + + "data-urls": ["data-urls@6.0.0", "", { "dependencies": { "whatwg-mimetype": "^4.0.0", "whatwg-url": "^15.0.0" } }, "sha512-BnBS08aLUM+DKamupXs3w2tJJoqU+AkaE/+6vQxi/G/DPmIZFJJp9Dkb1kM03AZx8ADehDUZgsNxju3mPXZYIA=="], + + "debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="], + + "decimal.js": ["decimal.js@10.6.0", "", {}, "sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg=="], + + "decode-named-character-reference": ["decode-named-character-reference@1.1.0", "", { "dependencies": { "character-entities": "^2.0.0" } }, "sha512-Wy+JTSbFThEOXQIR2L6mxJvEs+veIzpmqD7ynWxMXGpnk3smkHQOp6forLdHsKpAMW9iJpaBBIxz285t1n1C3w=="], + + "defu": ["defu@6.1.4", "", {}, "sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg=="], + + "depd": ["depd@2.0.0", "", {}, "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw=="], + + "dequal": ["dequal@2.0.3", "", {}, "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA=="], + + "destr": ["destr@2.0.5", "", {}, "sha512-ugFTXCtDZunbzasqBxrK93Ik/DRYsO6S/fedkWEMKqt04xZ4csmnmwGDBAb07QWNaGMAmnTIemsYZCksjATwsA=="], + + "detect-libc": ["detect-libc@2.0.4", "", {}, "sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA=="], + + "detect-node-es": ["detect-node-es@1.1.0", "", {}, "sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ=="], + + "deterministic-object-hash": ["deterministic-object-hash@2.0.2", "", { "dependencies": { "base-64": "^1.0.0" } }, "sha512-KxektNH63SrbfUyDiwXqRb1rLwKt33AmMv+5Nhsw1kqZ13SJBRTgZHtGbE+hH3a1mVW1cz+4pqSWVPAtLVXTzQ=="], + + "devalue": ["devalue@5.5.0", "", {}, "sha512-69sM5yrHfFLJt0AZ9QqZXGCPfJ7fQjvpln3Rq5+PS03LD32Ost1Q9N+eEnaQwGRIriKkMImXD56ocjQmfjbV3w=="], + + "devlop": ["devlop@1.1.0", "", { "dependencies": { "dequal": "^2.0.0" } }, "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA=="], + + "dfa": ["dfa@1.2.0", "", {}, "sha512-ED3jP8saaweFTjeGX8HQPjeC1YYyZs98jGNZx6IiBvxW7JG5v492kamAQB3m2wop07CvU/RQmzcKr6bgcC5D/Q=="], + + "diff": ["diff@5.2.0", "", {}, "sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A=="], + + "dlv": ["dlv@1.1.3", "", {}, "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA=="], + + "dom-accessibility-api": ["dom-accessibility-api@0.6.3", "", {}, "sha512-7ZgogeTnjuHbo+ct10G9Ffp0mif17idi0IyWNVA/wcwcm7NPOD/WEHVP3n7n3MhXqxoIYm8d6MuZohYWIZ4T3w=="], + + "dom-serializer": ["dom-serializer@2.0.0", "", { "dependencies": { "domelementtype": "^2.3.0", "domhandler": "^5.0.2", "entities": "^4.2.0" } }, "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg=="], + + "domelementtype": ["domelementtype@2.3.0", "", {}, "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw=="], + + "domhandler": ["domhandler@5.0.3", "", { "dependencies": { "domelementtype": "^2.3.0" } }, "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w=="], + + "domutils": ["domutils@3.2.2", "", { "dependencies": { "dom-serializer": "^2.0.0", "domelementtype": "^2.3.0", "domhandler": "^5.0.3" } }, "sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw=="], + + "dotenv": ["dotenv@17.2.3", "", {}, "sha512-JVUnt+DUIzu87TABbhPmNfVdBDt18BLOWjMUFJMSi/Qqg7NTYtabbvSNJGOJ7afbRuv9D/lngizHtP7QyLQ+9w=="], + + "drizzle-kit": ["drizzle-kit@0.31.7", "", { "dependencies": { "@drizzle-team/brocli": "^0.10.2", "@esbuild-kit/esm-loader": "^2.5.5", "esbuild": "^0.25.4", "esbuild-register": "^3.5.0" }, "bin": { "drizzle-kit": "bin.cjs" } }, "sha512-hOzRGSdyKIU4FcTSFYGKdXEjFsncVwHZ43gY3WU5Bz9j5Iadp6Rh6hxLSQ1IWXpKLBKt/d5y1cpSPcV+FcoQ1A=="], + + "drizzle-orm": ["drizzle-orm@0.44.7", "", { "peerDependencies": { "@aws-sdk/client-rds-data": ">=3", "@cloudflare/workers-types": ">=4", "@electric-sql/pglite": ">=0.2.0", "@libsql/client": ">=0.10.0", "@libsql/client-wasm": ">=0.10.0", "@neondatabase/serverless": ">=0.10.0", "@op-engineering/op-sqlite": ">=2", "@opentelemetry/api": "^1.4.1", "@planetscale/database": ">=1.13", "@prisma/client": "*", "@tidbcloud/serverless": "*", "@types/better-sqlite3": "*", "@types/pg": "*", "@types/sql.js": "*", "@upstash/redis": ">=1.34.7", "@vercel/postgres": ">=0.8.0", "@xata.io/client": "*", "better-sqlite3": ">=7", "bun-types": "*", "expo-sqlite": ">=14.0.0", "gel": ">=2", "knex": "*", "kysely": "*", "mysql2": ">=2", "pg": ">=8", "postgres": ">=3", "sql.js": ">=1", "sqlite3": ">=5" }, "optionalPeers": ["@aws-sdk/client-rds-data", "@cloudflare/workers-types", "@electric-sql/pglite", "@libsql/client", "@libsql/client-wasm", "@neondatabase/serverless", "@op-engineering/op-sqlite", "@opentelemetry/api", "@planetscale/database", "@prisma/client", "@tidbcloud/serverless", "@types/better-sqlite3", "@types/pg", "@types/sql.js", "@upstash/redis", "@vercel/postgres", "@xata.io/client", "better-sqlite3", "bun-types", "expo-sqlite", "gel", "knex", "kysely", "mysql2", "pg", "postgres", "sql.js", "sqlite3"] }, "sha512-quIpnYznjU9lHshEOAYLoZ9s3jweleHlZIAWR/jX9gAWNg/JhQ1wj0KGRf7/Zm+obRrYd9GjPVJg790QY9N5AQ=="], + + "dset": ["dset@3.1.4", "", {}, "sha512-2QF/g9/zTaPDc3BjNcVTGoBbXBgYfMTTceLaYcFJ/W9kggFUkhxD/hMEeuLKbugyef9SqAx8cpgwlIP/jinUTA=="], + + "ecdsa-sig-formatter": ["ecdsa-sig-formatter@1.0.11", "", { "dependencies": { "safe-buffer": "^5.0.1" } }, "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ=="], + + "ee-first": ["ee-first@1.1.1", "", {}, "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow=="], + + "electron-to-chromium": ["electron-to-chromium@1.5.159", "", {}, "sha512-CEvHptWAMV5p6GJ0Lq8aheyvVbfzVrv5mmidu1D3pidoVNkB3tTBsTMVtPJ+rzRK5oV229mCLz9Zj/hNvU8GBA=="], + + "emmet": ["emmet@2.4.11", "", { "dependencies": { "@emmetio/abbreviation": "^2.3.3", "@emmetio/css-abbreviation": "^2.1.8" } }, "sha512-23QPJB3moh/U9sT4rQzGgeyyGIrcM+GH5uVYg2C6wZIxAIJq7Ng3QLT79tl8FUwDXhyq9SusfknOrofAKqvgyQ=="], + + "emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="], + + "encodeurl": ["encodeurl@2.0.0", "", {}, "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg=="], + + "enhanced-resolve": ["enhanced-resolve@5.18.3", "", { "dependencies": { "graceful-fs": "^4.2.4", "tapable": "^2.2.0" } }, "sha512-d4lC8xfavMeBjzGr2vECC3fsGXziXZQyJxD868h2M/mBI3PwAuODxAkLkq5HYuvrPYcUtiLzsTo8U3PgX3Ocww=="], + + "entities": ["entities@6.0.0", "", {}, "sha512-aKstq2TDOndCn4diEyp9Uq/Flu2i1GlLkc6XIDQSDMuaFE3OPW5OphLCyQ5SpSJZTb4reN+kTcYru5yIfXoRPw=="], + + "es-module-lexer": ["es-module-lexer@1.7.0", "", {}, "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA=="], + + "esast-util-from-estree": ["esast-util-from-estree@2.0.0", "", { "dependencies": { "@types/estree-jsx": "^1.0.0", "devlop": "^1.0.0", "estree-util-visit": "^2.0.0", "unist-util-position-from-estree": "^2.0.0" } }, "sha512-4CyanoAudUSBAn5K13H4JhsMH6L9ZP7XbLVe/dKybkxMO7eDyLsT8UHl9TRNrU2Gr9nz+FovfSIjuXWJ81uVwQ=="], + + "esast-util-from-js": ["esast-util-from-js@2.0.1", "", { "dependencies": { "@types/estree-jsx": "^1.0.0", "acorn": "^8.0.0", "esast-util-from-estree": "^2.0.0", "vfile-message": "^4.0.0" } }, "sha512-8Ja+rNJ0Lt56Pcf3TAmpBZjmx8ZcK5Ts4cAzIOjsjevg9oSXJnl6SUQ2EevU8tv3h6ZLWmoKL5H4fgWvdvfETw=="], + + "esbuild": ["esbuild@0.25.9", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.25.9", "@esbuild/android-arm": "0.25.9", "@esbuild/android-arm64": "0.25.9", "@esbuild/android-x64": "0.25.9", "@esbuild/darwin-arm64": "0.25.9", "@esbuild/darwin-x64": "0.25.9", "@esbuild/freebsd-arm64": "0.25.9", "@esbuild/freebsd-x64": "0.25.9", "@esbuild/linux-arm": "0.25.9", "@esbuild/linux-arm64": "0.25.9", "@esbuild/linux-ia32": "0.25.9", "@esbuild/linux-loong64": "0.25.9", "@esbuild/linux-mips64el": "0.25.9", "@esbuild/linux-ppc64": "0.25.9", "@esbuild/linux-riscv64": "0.25.9", "@esbuild/linux-s390x": "0.25.9", "@esbuild/linux-x64": "0.25.9", "@esbuild/netbsd-arm64": "0.25.9", "@esbuild/netbsd-x64": "0.25.9", "@esbuild/openbsd-arm64": "0.25.9", "@esbuild/openbsd-x64": "0.25.9", "@esbuild/openharmony-arm64": "0.25.9", "@esbuild/sunos-x64": "0.25.9", "@esbuild/win32-arm64": "0.25.9", "@esbuild/win32-ia32": "0.25.9", "@esbuild/win32-x64": "0.25.9" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-CRbODhYyQx3qp7ZEwzxOk4JBqmD/seJrzPa/cGjY1VtIn5E09Oi9/dB4JwctnfZ8Q8iT7rioVv5k/FNT/uf54g=="], + + "esbuild-register": ["esbuild-register@3.6.0", "", { "dependencies": { "debug": "^4.3.4" }, "peerDependencies": { "esbuild": ">=0.12 <1" } }, "sha512-H2/S7Pm8a9CL1uhp9OvjwrBh5Pvx0H8qVOxNu8Wed9Y7qv56MPtq+GGM8RJpq6glYJn9Wspr8uw7l55uyinNeg=="], + + "escalade": ["escalade@3.2.0", "", {}, "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA=="], + + "escape-html": ["escape-html@1.0.3", "", {}, "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow=="], + + "escape-string-regexp": ["escape-string-regexp@5.0.0", "", {}, "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw=="], + + "estree-util-attach-comments": ["estree-util-attach-comments@3.0.0", "", { "dependencies": { "@types/estree": "^1.0.0" } }, "sha512-cKUwm/HUcTDsYh/9FgnuFqpfquUbwIqwKM26BVCGDPVgvaCl/nDCCjUfiLlx6lsEZ3Z4RFxNbOQ60pkaEwFxGw=="], + + "estree-util-build-jsx": ["estree-util-build-jsx@3.0.1", "", { "dependencies": { "@types/estree-jsx": "^1.0.0", "devlop": "^1.0.0", "estree-util-is-identifier-name": "^3.0.0", "estree-walker": "^3.0.0" } }, "sha512-8U5eiL6BTrPxp/CHbs2yMgP8ftMhR5ww1eIKoWRMlqvltHF8fZn5LRDvTKuxD3DUn+shRbLGqXemcP51oFCsGQ=="], + + "estree-util-is-identifier-name": ["estree-util-is-identifier-name@3.0.0", "", {}, "sha512-hFtqIDZTIUZ9BXLb8y4pYGyk6+wekIivNVTcmvk8NoOh+VeRn5y6cEHzbURrWbfp1fIqdVipilzj+lfaadNZmg=="], + + "estree-util-scope": ["estree-util-scope@1.0.0", "", { "dependencies": { "@types/estree": "^1.0.0", "devlop": "^1.0.0" } }, "sha512-2CAASclonf+JFWBNJPndcOpA8EMJwa0Q8LUFJEKqXLW6+qBvbFZuF5gItbQOs/umBUkjviCSDCbBwU2cXbmrhQ=="], + + "estree-util-to-js": ["estree-util-to-js@2.0.0", "", { "dependencies": { "@types/estree-jsx": "^1.0.0", "astring": "^1.8.0", "source-map": "^0.7.0" } }, "sha512-WDF+xj5rRWmD5tj6bIqRi6CkLIXbbNQUcxQHzGysQzvHmdYG2G7p/Tf0J0gpxGgkeMZNTIjT/AoSvC9Xehcgdg=="], + + "estree-util-visit": ["estree-util-visit@2.0.0", "", { "dependencies": { "@types/estree-jsx": "^1.0.0", "@types/unist": "^3.0.0" } }, "sha512-m5KgiH85xAhhW8Wta0vShLcUvOsh3LLPI2YVwcbio1l7E09NTLL1EyMZFM1OyWowoH0skScNbhOPl4kcBgzTww=="], + + "estree-walker": ["estree-walker@3.0.3", "", { "dependencies": { "@types/estree": "^1.0.0" } }, "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g=="], + + "etag": ["etag@1.8.1", "", {}, "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg=="], + + "eventemitter3": ["eventemitter3@5.0.1", "", {}, "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA=="], + + "expect-type": ["expect-type@1.2.2", "", {}, "sha512-JhFGDVJ7tmDJItKhYgJCGLOWjuK9vPxiXoUFLwLDc99NlmklilbiQJwoctZtt13+xMw91MCk/REan6MWHqDjyA=="], + + "extend": ["extend@3.0.2", "", {}, "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g=="], + + "fast-content-type-parse": ["fast-content-type-parse@3.0.0", "", {}, "sha512-ZvLdcY8P+N8mGQJahJV5G4U88CSvT1rP8ApL6uETe88MBXrBHAkZlSEySdUlyztF7ccb+Znos3TFqaepHxdhBg=="], + + "fast-deep-equal": ["fast-deep-equal@3.1.3", "", {}, "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="], + + "fast-glob": ["fast-glob@3.3.3", "", { "dependencies": { "@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.walk": "^1.2.3", "glob-parent": "^5.1.2", "merge2": "^1.3.0", "micromatch": "^4.0.8" } }, "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg=="], + + "fast-uri": ["fast-uri@3.0.6", "", {}, "sha512-Atfo14OibSv5wAp4VWNsFYE1AchQRTv9cBGWET4pZWHzYshFSS9NQI6I57rdKn9croWVMbYFbLhJ+yJvmZIIHw=="], + + "fast-xml-parser": ["fast-xml-parser@5.2.5", "", { "dependencies": { "strnum": "^2.1.0" }, "bin": { "fxparser": "src/cli/cli.js" } }, "sha512-pfX9uG9Ki0yekDHx2SiuRIyFdyAr1kMIMitPvb0YBo8SUfKvia7w7FIyd/l6av85pFYRhZscS75MwMnbvY+hcQ=="], + + "fastq": ["fastq@1.19.1", "", { "dependencies": { "reusify": "^1.0.4" } }, "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ=="], + + "fdir": ["fdir@6.4.4", "", { "peerDependencies": { "picomatch": "^3 || ^4" }, "optionalPeers": ["picomatch"] }, "sha512-1NZP+GK4GfuAv3PqKvxQRDMjdSRZjnkq7KfhlNrCNNlZ0ygQFpebfrnfnq/W7fpUnAv9aGWmY1zKx7FYL3gwhg=="], + + "fill-range": ["fill-range@7.1.1", "", { "dependencies": { "to-regex-range": "^5.0.1" } }, "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg=="], + + "flattie": ["flattie@1.1.1", "", {}, "sha512-9UbaD6XdAL97+k/n+N7JwX46K/M6Zc6KcFYskrYL8wbBV/Uyk0CTAMY0VT+qiK5PM7AIc9aTWYtq65U7T+aCNQ=="], + + "fontace": ["fontace@0.3.1", "", { "dependencies": { "@types/fontkit": "^2.0.8", "fontkit": "^2.0.4" } }, "sha512-9f5g4feWT1jWT8+SbL85aLIRLIXUaDygaM2xPXRmzPYxrOMNok79Lr3FGJoKVNKibE0WCunNiEVG2mwuE+2qEg=="], + + "fontkit": ["fontkit@2.0.4", "", { "dependencies": { "@swc/helpers": "^0.5.12", "brotli": "^1.3.2", "clone": "^2.1.2", "dfa": "^1.2.0", "fast-deep-equal": "^3.1.3", "restructure": "^3.0.0", "tiny-inflate": "^1.0.3", "unicode-properties": "^1.4.0", "unicode-trie": "^2.0.0" } }, "sha512-syetQadaUEDNdxdugga9CpEYVaQIxOwk7GlwZWWZ19//qW4zE5bknOKeMBDYAASwnpaSHKJITRLMF9m1fp3s6g=="], + + "fresh": ["fresh@2.0.0", "", {}, "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A=="], + + "fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="], + + "fuse.js": ["fuse.js@7.1.0", "", {}, "sha512-trLf4SzuuUxfusZADLINj+dE8clK1frKdmqiJNb1Es75fmI5oY6X2mxLVUciLLjxqw/xr72Dhy+lER6dGd02FQ=="], + + "gensync": ["gensync@1.0.0-beta.2", "", {}, "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg=="], + + "get-caller-file": ["get-caller-file@2.0.5", "", {}, "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg=="], + + "get-east-asian-width": ["get-east-asian-width@1.3.0", "", {}, "sha512-vpeMIQKxczTD/0s2CdEWHcb0eeJe6TFjxb+J5xgX7hScxqrGuyjmv4c1D4A/gelKfyox0gJJwIHF+fLjeaM8kQ=="], + + "get-nonce": ["get-nonce@1.0.1", "", {}, "sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q=="], + + "get-tsconfig": ["get-tsconfig@4.10.1", "", { "dependencies": { "resolve-pkg-maps": "^1.0.0" } }, "sha512-auHyJ4AgMz7vgS8Hp3N6HXSmlMdUyhSUrfBF16w153rxtLIEOE+HGqaBppczZvnHLqQJfiHotCYpNhl0lUROFQ=="], + + "github-slugger": ["github-slugger@2.0.0", "", {}, "sha512-IaOQ9puYtjrkq7Y0Ygl9KDZnrf/aiUJYUpVf89y8kyaxbRG7Y1SrX/jaumrv81vc61+kiMempujsM3Yw7w5qcw=="], + + "glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="], + + "globals": ["globals@11.12.0", "", {}, "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA=="], + + "graceful-fs": ["graceful-fs@4.2.11", "", {}, "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="], + + "h3": ["h3@1.15.4", "", { "dependencies": { "cookie-es": "^1.2.2", "crossws": "^0.3.5", "defu": "^6.1.4", "destr": "^2.0.5", "iron-webcrypto": "^1.2.1", "node-mock-http": "^1.0.2", "radix3": "^1.1.2", "ufo": "^1.6.1", "uncrypto": "^0.1.3" } }, "sha512-z5cFQWDffyOe4vQ9xIqNfCZdV4p//vy6fBnr8Q1AWnVZ0teurKMG66rLj++TKwKPUP3u7iMUvrvKaEUiQw2QWQ=="], + + "has-flag": ["has-flag@4.0.0", "", {}, "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="], + + "hast-util-from-html": ["hast-util-from-html@2.0.3", "", { "dependencies": { "@types/hast": "^3.0.0", "devlop": "^1.1.0", "hast-util-from-parse5": "^8.0.0", "parse5": "^7.0.0", "vfile": "^6.0.0", "vfile-message": "^4.0.0" } }, "sha512-CUSRHXyKjzHov8yKsQjGOElXy/3EKpyX56ELnkHH34vDVw1N1XSQ1ZcAvTyAPtGqLTuKP/uxM+aLkSPqF/EtMw=="], + + "hast-util-from-parse5": ["hast-util-from-parse5@8.0.3", "", { "dependencies": { "@types/hast": "^3.0.0", "@types/unist": "^3.0.0", "devlop": "^1.0.0", "hastscript": "^9.0.0", "property-information": "^7.0.0", "vfile": "^6.0.0", "vfile-location": "^5.0.0", "web-namespaces": "^2.0.0" } }, "sha512-3kxEVkEKt0zvcZ3hCRYI8rqrgwtlIOFMWkbclACvjlDw8Li9S2hk/d51OI0nr/gIpdMHNepwgOKqZ/sy0Clpyg=="], + + "hast-util-is-element": ["hast-util-is-element@3.0.0", "", { "dependencies": { "@types/hast": "^3.0.0" } }, "sha512-Val9mnv2IWpLbNPqc/pUem+a7Ipj2aHacCwgNfTiK0vJKl0LF+4Ba4+v1oPHFpf3bLYmreq0/l3Gud9S5OH42g=="], + + "hast-util-parse-selector": ["hast-util-parse-selector@4.0.0", "", { "dependencies": { "@types/hast": "^3.0.0" } }, "sha512-wkQCkSYoOGCRKERFWcxMVMOcYE2K1AaNLU8DXS9arxnLOUEWbOXKXiJUNzEpqZ3JOKpnha3jkFrumEjVliDe7A=="], + + "hast-util-raw": ["hast-util-raw@9.1.0", "", { "dependencies": { "@types/hast": "^3.0.0", "@types/unist": "^3.0.0", "@ungap/structured-clone": "^1.0.0", "hast-util-from-parse5": "^8.0.0", "hast-util-to-parse5": "^8.0.0", "html-void-elements": "^3.0.0", "mdast-util-to-hast": "^13.0.0", "parse5": "^7.0.0", "unist-util-position": "^5.0.0", "unist-util-visit": "^5.0.0", "vfile": "^6.0.0", "web-namespaces": "^2.0.0", "zwitch": "^2.0.0" } }, "sha512-Y8/SBAHkZGoNkpzqqfCldijcuUKh7/su31kEBp67cFY09Wy0mTRgtsLYsiIxMJxlu0f6AA5SUTbDR8K0rxnbUw=="], + + "hast-util-to-estree": ["hast-util-to-estree@3.1.3", "", { "dependencies": { "@types/estree": "^1.0.0", "@types/estree-jsx": "^1.0.0", "@types/hast": "^3.0.0", "comma-separated-tokens": "^2.0.0", "devlop": "^1.0.0", "estree-util-attach-comments": "^3.0.0", "estree-util-is-identifier-name": "^3.0.0", "hast-util-whitespace": "^3.0.0", "mdast-util-mdx-expression": "^2.0.0", "mdast-util-mdx-jsx": "^3.0.0", "mdast-util-mdxjs-esm": "^2.0.0", "property-information": "^7.0.0", "space-separated-tokens": "^2.0.0", "style-to-js": "^1.0.0", "unist-util-position": "^5.0.0", "zwitch": "^2.0.0" } }, "sha512-48+B/rJWAp0jamNbAAf9M7Uf//UVqAoMmgXhBdxTDJLGKY+LRnZ99qcG+Qjl5HfMpYNzS5v4EAwVEF34LeAj7w=="], + + "hast-util-to-html": ["hast-util-to-html@9.0.5", "", { "dependencies": { "@types/hast": "^3.0.0", "@types/unist": "^3.0.0", "ccount": "^2.0.0", "comma-separated-tokens": "^2.0.0", "hast-util-whitespace": "^3.0.0", "html-void-elements": "^3.0.0", "mdast-util-to-hast": "^13.0.0", "property-information": "^7.0.0", "space-separated-tokens": "^2.0.0", "stringify-entities": "^4.0.0", "zwitch": "^2.0.4" } }, "sha512-OguPdidb+fbHQSU4Q4ZiLKnzWo8Wwsf5bZfbvu7//a9oTYoqD/fWpe96NuHkoS9h0ccGOTe0C4NGXdtS0iObOw=="], + + "hast-util-to-jsx-runtime": ["hast-util-to-jsx-runtime@2.3.6", "", { "dependencies": { "@types/estree": "^1.0.0", "@types/hast": "^3.0.0", "@types/unist": "^3.0.0", "comma-separated-tokens": "^2.0.0", "devlop": "^1.0.0", "estree-util-is-identifier-name": "^3.0.0", "hast-util-whitespace": "^3.0.0", "mdast-util-mdx-expression": "^2.0.0", "mdast-util-mdx-jsx": "^3.0.0", "mdast-util-mdxjs-esm": "^2.0.0", "property-information": "^7.0.0", "space-separated-tokens": "^2.0.0", "style-to-js": "^1.0.0", "unist-util-position": "^5.0.0", "vfile-message": "^4.0.0" } }, "sha512-zl6s8LwNyo1P9uw+XJGvZtdFF1GdAkOg8ujOw+4Pyb76874fLps4ueHXDhXWdk6YHQ6OgUtinliG7RsYvCbbBg=="], + + "hast-util-to-parse5": ["hast-util-to-parse5@8.0.0", "", { "dependencies": { "@types/hast": "^3.0.0", "comma-separated-tokens": "^2.0.0", "devlop": "^1.0.0", "property-information": "^6.0.0", "space-separated-tokens": "^2.0.0", "web-namespaces": "^2.0.0", "zwitch": "^2.0.0" } }, "sha512-3KKrV5ZVI8if87DVSi1vDeByYrkGzg4mEfeu4alwgmmIeARiBLKCZS2uw5Gb6nU9x9Yufyj3iudm6i7nl52PFw=="], + + "hast-util-to-text": ["hast-util-to-text@4.0.2", "", { "dependencies": { "@types/hast": "^3.0.0", "@types/unist": "^3.0.0", "hast-util-is-element": "^3.0.0", "unist-util-find-after": "^5.0.0" } }, "sha512-KK6y/BN8lbaq654j7JgBydev7wuNMcID54lkRav1P0CaE1e47P72AWWPiGKXTJU271ooYzcvTAn/Zt0REnvc7A=="], + + "hast-util-whitespace": ["hast-util-whitespace@3.0.0", "", { "dependencies": { "@types/hast": "^3.0.0" } }, "sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw=="], + + "hastscript": ["hastscript@9.0.1", "", { "dependencies": { "@types/hast": "^3.0.0", "comma-separated-tokens": "^2.0.0", "hast-util-parse-selector": "^4.0.0", "property-information": "^7.0.0", "space-separated-tokens": "^2.0.0" } }, "sha512-g7df9rMFX/SPi34tyGCyUBREQoKkapwdY/T04Qn9TDWfHhAYt4/I0gMVirzK5wEzeUqIjEB+LXC/ypb7Aqno5w=="], + + "html-encoding-sniffer": ["html-encoding-sniffer@4.0.0", "", { "dependencies": { "whatwg-encoding": "^3.1.1" } }, "sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ=="], + + "html-escaper": ["html-escaper@3.0.3", "", {}, "sha512-RuMffC89BOWQoY0WKGpIhn5gX3iI54O6nRA0yC124NYVtzjmFWBIiFd8M0x+ZdX0P9R4lADg1mgP8C7PxGOWuQ=="], + + "html-void-elements": ["html-void-elements@3.0.0", "", {}, "sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg=="], + + "http-cache-semantics": ["http-cache-semantics@4.2.0", "", {}, "sha512-dTxcvPXqPvXBQpq5dUr6mEMJX4oIEFv6bwom3FDwKRDsuIjjJGANqhBuoAn9c1RQJIdAKav33ED65E2ys+87QQ=="], + + "http-errors": ["http-errors@2.0.0", "", { "dependencies": { "depd": "2.0.0", "inherits": "2.0.4", "setprototypeof": "1.2.0", "statuses": "2.0.1", "toidentifier": "1.0.1" } }, "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ=="], + + "http-proxy-agent": ["http-proxy-agent@7.0.2", "", { "dependencies": { "agent-base": "^7.1.0", "debug": "^4.3.4" } }, "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig=="], + + "https-proxy-agent": ["https-proxy-agent@7.0.6", "", { "dependencies": { "agent-base": "^7.1.2", "debug": "4" } }, "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw=="], + + "iconv-lite": ["iconv-lite@0.6.3", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw=="], + + "ieee754": ["ieee754@1.2.1", "", {}, "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA=="], + + "import-meta-resolve": ["import-meta-resolve@4.2.0", "", {}, "sha512-Iqv2fzaTQN28s/FwZAoFq0ZSs/7hMAHJVX+w8PZl3cY19Pxk6jFFalxQoIfW2826i/fDLXv8IiEZRIT0lDuWcg=="], + + "indent-string": ["indent-string@4.0.0", "", {}, "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg=="], + + "inherits": ["inherits@2.0.4", "", {}, "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="], + + "inline-style-parser": ["inline-style-parser@0.2.4", "", {}, "sha512-0aO8FkhNZlj/ZIbNi7Lxxr12obT7cL1moPfE4tg1LkX7LlLfC6DeX4l2ZEud1ukP9jNQyNnfzQVqwbwmAATY4Q=="], + + "iron-webcrypto": ["iron-webcrypto@1.2.1", "", {}, "sha512-feOM6FaSr6rEABp/eDfVseKyTMDt+KGpeB35SkVn9Tyn0CqvVsY3EwI0v5i8nMHyJnzCIQf7nsy3p41TPkJZhg=="], + + "is-alphabetical": ["is-alphabetical@2.0.1", "", {}, "sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ=="], + + "is-alphanumerical": ["is-alphanumerical@2.0.1", "", { "dependencies": { "is-alphabetical": "^2.0.0", "is-decimal": "^2.0.0" } }, "sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw=="], + + "is-arrayish": ["is-arrayish@0.3.2", "", {}, "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ=="], + + "is-decimal": ["is-decimal@2.0.1", "", {}, "sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A=="], + + "is-docker": ["is-docker@3.0.0", "", { "bin": { "is-docker": "cli.js" } }, "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ=="], + + "is-extglob": ["is-extglob@2.1.1", "", {}, "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ=="], + + "is-fullwidth-code-point": ["is-fullwidth-code-point@3.0.0", "", {}, "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg=="], + + "is-glob": ["is-glob@4.0.3", "", { "dependencies": { "is-extglob": "^2.1.1" } }, "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg=="], + + "is-hexadecimal": ["is-hexadecimal@2.0.1", "", {}, "sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg=="], + + "is-inside-container": ["is-inside-container@1.0.0", "", { "dependencies": { "is-docker": "^3.0.0" }, "bin": { "is-inside-container": "cli.js" } }, "sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA=="], + + "is-number": ["is-number@7.0.0", "", {}, "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng=="], + + "is-plain-obj": ["is-plain-obj@4.1.0", "", {}, "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg=="], + + "is-potential-custom-element-name": ["is-potential-custom-element-name@1.0.1", "", {}, "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ=="], + + "is-wsl": ["is-wsl@3.1.0", "", { "dependencies": { "is-inside-container": "^1.0.0" } }, "sha512-UcVfVfaK4Sc4m7X3dUSoHoozQGBEFeDC+zVo06t98xe8CzHSZZBekNXH+tu0NalHolcJ/QAGqS46Hef7QXBIMw=="], + + "jiti": ["jiti@2.4.2", "", { "bin": { "jiti": "lib/jiti-cli.mjs" } }, "sha512-rg9zJN+G4n2nfJl5MW3BMygZX56zKPNVEYYqq7adpmMh4Jn2QNEwhvQlFy6jPVdcod7txZtKHWnyZiA3a0zP7A=="], + + "jose": ["jose@6.1.0", "", {}, "sha512-TTQJyoEoKcC1lscpVDCSsVgYzUDg/0Bt3WE//WiTPK6uOCQC2KZS4MpugbMWt/zyjkopgZoXhZuCi00gLudfUA=="], + + "js-tokens": ["js-tokens@4.0.0", "", {}, "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="], + + "js-yaml": ["js-yaml@4.1.1", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA=="], + + "jsdom": ["jsdom@27.2.0", "", { "dependencies": { "@acemir/cssom": "^0.9.23", "@asamuzakjp/dom-selector": "^6.7.4", "cssstyle": "^5.3.3", "data-urls": "^6.0.0", "decimal.js": "^10.6.0", "html-encoding-sniffer": "^4.0.0", "http-proxy-agent": "^7.0.2", "https-proxy-agent": "^7.0.6", "is-potential-custom-element-name": "^1.0.1", "parse5": "^8.0.0", "saxes": "^6.0.0", "symbol-tree": "^3.2.4", "tough-cookie": "^6.0.0", "w3c-xmlserializer": "^5.0.0", "webidl-conversions": "^8.0.0", "whatwg-encoding": "^3.1.1", "whatwg-mimetype": "^4.0.0", "whatwg-url": "^15.1.0", "ws": "^8.18.3", "xml-name-validator": "^5.0.0" }, "peerDependencies": { "canvas": "^3.0.0" }, "optionalPeers": ["canvas"] }, "sha512-454TI39PeRDW1LgpyLPyURtB4Zx1tklSr6+OFOipsxGUH1WMTvk6C65JQdrj455+DP2uJ1+veBEHTGFKWVLFoA=="], + + "jsesc": ["jsesc@3.1.0", "", { "bin": { "jsesc": "bin/jsesc" } }, "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA=="], + + "json-schema-traverse": ["json-schema-traverse@1.0.0", "", {}, "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="], + + "json5": ["json5@2.2.3", "", { "bin": { "json5": "lib/cli.js" } }, "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg=="], + + "jsonc-parser": ["jsonc-parser@2.3.1", "", {}, "sha512-H8jvkz1O50L3dMZCsLqiuB2tA7muqbSg1AtGEkN0leAqGjsUzDJir3Zwr02BhqdcITPg3ei3mZ+HjMocAknhhg=="], + + "jsonwebtoken": ["jsonwebtoken@9.0.3", "", { "dependencies": { "jws": "^4.0.1", "lodash.includes": "^4.3.0", "lodash.isboolean": "^3.0.3", "lodash.isinteger": "^4.0.4", "lodash.isnumber": "^3.0.3", "lodash.isplainobject": "^4.0.6", "lodash.isstring": "^4.0.1", "lodash.once": "^4.0.0", "ms": "^2.1.1", "semver": "^7.5.4" } }, "sha512-MT/xP0CrubFRNLNKvxJ2BYfy53Zkm++5bX9dtuPbqAeQpTVe0MQTFhao8+Cp//EmJp244xt6Drw/GVEGCUj40g=="], + + "jwa": ["jwa@2.0.1", "", { "dependencies": { "buffer-equal-constant-time": "^1.0.1", "ecdsa-sig-formatter": "1.0.11", "safe-buffer": "^5.0.1" } }, "sha512-hRF04fqJIP8Abbkq5NKGN0Bbr3JxlQ+qhZufXVr0DvujKy93ZCbXZMHDL4EOtodSbCWxOqR8MS1tXA5hwqCXDg=="], + + "jws": ["jws@4.0.1", "", { "dependencies": { "jwa": "^2.0.1", "safe-buffer": "^5.0.1" } }, "sha512-EKI/M/yqPncGUUh44xz0PxSidXFr/+r0pA70+gIYhjv+et7yxM+s29Y+VGDkovRofQem0fs7Uvf4+YmAdyRduA=="], + + "kleur": ["kleur@4.1.5", "", {}, "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ=="], + + "kysely": ["kysely@0.28.5", "", {}, "sha512-rlB0I/c6FBDWPcQoDtkxi9zIvpmnV5xoIalfCMSMCa7nuA6VGA3F54TW9mEgX4DVf10sXAWCF5fDbamI/5ZpKA=="], + + "lightningcss": ["lightningcss@1.30.1", "", { "dependencies": { "detect-libc": "^2.0.3" }, "optionalDependencies": { "lightningcss-darwin-arm64": "1.30.1", "lightningcss-darwin-x64": "1.30.1", "lightningcss-freebsd-x64": "1.30.1", "lightningcss-linux-arm-gnueabihf": "1.30.1", "lightningcss-linux-arm64-gnu": "1.30.1", "lightningcss-linux-arm64-musl": "1.30.1", "lightningcss-linux-x64-gnu": "1.30.1", "lightningcss-linux-x64-musl": "1.30.1", "lightningcss-win32-arm64-msvc": "1.30.1", "lightningcss-win32-x64-msvc": "1.30.1" } }, "sha512-xi6IyHML+c9+Q3W0S4fCQJOym42pyurFiJUHEcEyHS0CeKzia4yZDEsLlqOFykxOdHpNy0NmvVO31vcSqAxJCg=="], + + "lightningcss-android-arm64": ["lightningcss-android-arm64@1.30.2", "", { "os": "android", "cpu": "arm64" }, "sha512-BH9sEdOCahSgmkVhBLeU7Hc9DWeZ1Eb6wNS6Da8igvUwAe0sqROHddIlvU06q3WyXVEOYDZ6ykBZQnjTbmo4+A=="], + + "lightningcss-darwin-arm64": ["lightningcss-darwin-arm64@1.30.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-c8JK7hyE65X1MHMN+Viq9n11RRC7hgin3HhYKhrMyaXflk5GVplZ60IxyoVtzILeKr+xAJwg6zK6sjTBJ0FKYQ=="], + + "lightningcss-darwin-x64": ["lightningcss-darwin-x64@1.30.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-k1EvjakfumAQoTfcXUcHQZhSpLlkAuEkdMBsI/ivWw9hL+7FtilQc0Cy3hrx0AAQrVtQAbMI7YjCgYgvn37PzA=="], + + "lightningcss-freebsd-x64": ["lightningcss-freebsd-x64@1.30.1", "", { "os": "freebsd", "cpu": "x64" }, "sha512-kmW6UGCGg2PcyUE59K5r0kWfKPAVy4SltVeut+umLCFoJ53RdCUWxcRDzO1eTaxf/7Q2H7LTquFHPL5R+Gjyig=="], + + "lightningcss-linux-arm-gnueabihf": ["lightningcss-linux-arm-gnueabihf@1.30.1", "", { "os": "linux", "cpu": "arm" }, "sha512-MjxUShl1v8pit+6D/zSPq9S9dQ2NPFSQwGvxBCYaBYLPlCWuPh9/t1MRS8iUaR8i+a6w7aps+B4N0S1TYP/R+Q=="], + + "lightningcss-linux-arm64-gnu": ["lightningcss-linux-arm64-gnu@1.30.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-gB72maP8rmrKsnKYy8XUuXi/4OctJiuQjcuqWNlJQ6jZiWqtPvqFziskH3hnajfvKB27ynbVCucKSm2rkQp4Bw=="], + + "lightningcss-linux-arm64-musl": ["lightningcss-linux-arm64-musl@1.30.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-jmUQVx4331m6LIX+0wUhBbmMX7TCfjF5FoOH6SD1CttzuYlGNVpA7QnrmLxrsub43ClTINfGSYyHe2HWeLl5CQ=="], + + "lightningcss-linux-x64-gnu": ["lightningcss-linux-x64-gnu@1.30.1", "", { "os": "linux", "cpu": "x64" }, "sha512-piWx3z4wN8J8z3+O5kO74+yr6ze/dKmPnI7vLqfSqI8bccaTGY5xiSGVIJBDd5K5BHlvVLpUB3S2YCfelyJ1bw=="], + + "lightningcss-linux-x64-musl": ["lightningcss-linux-x64-musl@1.30.1", "", { "os": "linux", "cpu": "x64" }, "sha512-rRomAK7eIkL+tHY0YPxbc5Dra2gXlI63HL+v1Pdi1a3sC+tJTcFrHX+E86sulgAXeI7rSzDYhPSeHHjqFhqfeQ=="], + + "lightningcss-win32-arm64-msvc": ["lightningcss-win32-arm64-msvc@1.30.1", "", { "os": "win32", "cpu": "arm64" }, "sha512-mSL4rqPi4iXq5YVqzSsJgMVFENoa4nGTT/GjO2c0Yl9OuQfPsIfncvLrEW6RbbB24WtZ3xP/2CCmI3tNkNV4oA=="], + + "lightningcss-win32-x64-msvc": ["lightningcss-win32-x64-msvc@1.30.1", "", { "os": "win32", "cpu": "x64" }, "sha512-PVqXh48wh4T53F/1CCu8PIPCxLzWyCnn/9T5W1Jpmdy5h9Cwd+0YQS6/LwhHXSafuc61/xg9Lv5OrCby6a++jg=="], + + "lodash": ["lodash@4.17.21", "", {}, "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="], + + "lodash.includes": ["lodash.includes@4.3.0", "", {}, "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w=="], + + "lodash.isboolean": ["lodash.isboolean@3.0.3", "", {}, "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg=="], + + "lodash.isinteger": ["lodash.isinteger@4.0.4", "", {}, "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA=="], + + "lodash.isnumber": ["lodash.isnumber@3.0.3", "", {}, "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw=="], + + "lodash.isplainobject": ["lodash.isplainobject@4.0.6", "", {}, "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA=="], + + "lodash.isstring": ["lodash.isstring@4.0.1", "", {}, "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw=="], + + "lodash.once": ["lodash.once@4.1.1", "", {}, "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg=="], + + "longest-streak": ["longest-streak@3.1.0", "", {}, "sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g=="], + + "lru-cache": ["lru-cache@11.2.4", "", {}, "sha512-B5Y16Jr9LB9dHVkh6ZevG+vAbOsNOYCX+sXvFWFu7B3Iz5mijW3zdbMyhsh8ANd2mSWBYdJgnqi+mL7/LrOPYg=="], + + "lucide-react": ["lucide-react@0.555.0", "", { "peerDependencies": { "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-D8FvHUGbxWBRQM90NZeIyhAvkFfsh3u9ekrMvJ30Z6gnpBHS6HC6ldLg7tL45hwiIz/u66eKDtdA23gwwGsAHA=="], + + "lz-string": ["lz-string@1.5.0", "", { "bin": { "lz-string": "bin/bin.js" } }, "sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ=="], + + "magic-string": ["magic-string@0.30.21", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.5" } }, "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ=="], + + "magicast": ["magicast@0.5.1", "", { "dependencies": { "@babel/parser": "^7.28.5", "@babel/types": "^7.28.5", "source-map-js": "^1.2.1" } }, "sha512-xrHS24IxaLrvuo613F719wvOIv9xPHFWQHuvGUBmPnCA/3MQxKI3b+r7n1jAoDHmsbC5bRhTZYR77invLAxVnw=="], + + "markdown-extensions": ["markdown-extensions@2.0.0", "", {}, "sha512-o5vL7aDWatOTX8LzaS1WMoaoxIiLRQJuIKKe2wAw6IeULDHaqbiqiggmx+pKvZDb1Sj+pE46Sn1T7lCqfFtg1Q=="], + + "markdown-table": ["markdown-table@3.0.4", "", {}, "sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw=="], + + "mdast-util-definitions": ["mdast-util-definitions@6.0.0", "", { "dependencies": { "@types/mdast": "^4.0.0", "@types/unist": "^3.0.0", "unist-util-visit": "^5.0.0" } }, "sha512-scTllyX6pnYNZH/AIp/0ePz6s4cZtARxImwoPJ7kS42n+MnVsI4XbnG6d4ibehRIldYMWM2LD7ImQblVhUejVQ=="], + + "mdast-util-find-and-replace": ["mdast-util-find-and-replace@3.0.2", "", { "dependencies": { "@types/mdast": "^4.0.0", "escape-string-regexp": "^5.0.0", "unist-util-is": "^6.0.0", "unist-util-visit-parents": "^6.0.0" } }, "sha512-Tmd1Vg/m3Xz43afeNxDIhWRtFZgM2VLyaf4vSTYwudTyeuTneoL3qtWMA5jeLyz/O1vDJmmV4QuScFCA2tBPwg=="], + + "mdast-util-from-markdown": ["mdast-util-from-markdown@2.0.2", "", { "dependencies": { "@types/mdast": "^4.0.0", "@types/unist": "^3.0.0", "decode-named-character-reference": "^1.0.0", "devlop": "^1.0.0", "mdast-util-to-string": "^4.0.0", "micromark": "^4.0.0", "micromark-util-decode-numeric-character-reference": "^2.0.0", "micromark-util-decode-string": "^2.0.0", "micromark-util-normalize-identifier": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0", "unist-util-stringify-position": "^4.0.0" } }, "sha512-uZhTV/8NBuw0WHkPTrCqDOl0zVe1BIng5ZtHoDk49ME1qqcjYmmLmOf0gELgcRMxN4w2iuIeVso5/6QymSrgmA=="], + + "mdast-util-gfm": ["mdast-util-gfm@3.1.0", "", { "dependencies": { "mdast-util-from-markdown": "^2.0.0", "mdast-util-gfm-autolink-literal": "^2.0.0", "mdast-util-gfm-footnote": "^2.0.0", "mdast-util-gfm-strikethrough": "^2.0.0", "mdast-util-gfm-table": "^2.0.0", "mdast-util-gfm-task-list-item": "^2.0.0", "mdast-util-to-markdown": "^2.0.0" } }, "sha512-0ulfdQOM3ysHhCJ1p06l0b0VKlhU0wuQs3thxZQagjcjPrlFRqY215uZGHHJan9GEAXd9MbfPjFJz+qMkVR6zQ=="], + + "mdast-util-gfm-autolink-literal": ["mdast-util-gfm-autolink-literal@2.0.1", "", { "dependencies": { "@types/mdast": "^4.0.0", "ccount": "^2.0.0", "devlop": "^1.0.0", "mdast-util-find-and-replace": "^3.0.0", "micromark-util-character": "^2.0.0" } }, "sha512-5HVP2MKaP6L+G6YaxPNjuL0BPrq9orG3TsrZ9YXbA3vDw/ACI4MEsnoDpn6ZNm7GnZgtAcONJyPhOP8tNJQavQ=="], + + "mdast-util-gfm-footnote": ["mdast-util-gfm-footnote@2.1.0", "", { "dependencies": { "@types/mdast": "^4.0.0", "devlop": "^1.1.0", "mdast-util-from-markdown": "^2.0.0", "mdast-util-to-markdown": "^2.0.0", "micromark-util-normalize-identifier": "^2.0.0" } }, "sha512-sqpDWlsHn7Ac9GNZQMeUzPQSMzR6Wv0WKRNvQRg0KqHh02fpTz69Qc1QSseNX29bhz1ROIyNyxExfawVKTm1GQ=="], + + "mdast-util-gfm-strikethrough": ["mdast-util-gfm-strikethrough@2.0.0", "", { "dependencies": { "@types/mdast": "^4.0.0", "mdast-util-from-markdown": "^2.0.0", "mdast-util-to-markdown": "^2.0.0" } }, "sha512-mKKb915TF+OC5ptj5bJ7WFRPdYtuHv0yTRxK2tJvi+BDqbkiG7h7u/9SI89nRAYcmap2xHQL9D+QG/6wSrTtXg=="], + + "mdast-util-gfm-table": ["mdast-util-gfm-table@2.0.0", "", { "dependencies": { "@types/mdast": "^4.0.0", "devlop": "^1.0.0", "markdown-table": "^3.0.0", "mdast-util-from-markdown": "^2.0.0", "mdast-util-to-markdown": "^2.0.0" } }, "sha512-78UEvebzz/rJIxLvE7ZtDd/vIQ0RHv+3Mh5DR96p7cS7HsBhYIICDBCu8csTNWNO6tBWfqXPWekRuj2FNOGOZg=="], + + "mdast-util-gfm-task-list-item": ["mdast-util-gfm-task-list-item@2.0.0", "", { "dependencies": { "@types/mdast": "^4.0.0", "devlop": "^1.0.0", "mdast-util-from-markdown": "^2.0.0", "mdast-util-to-markdown": "^2.0.0" } }, "sha512-IrtvNvjxC1o06taBAVJznEnkiHxLFTzgonUdy8hzFVeDun0uTjxxrRGVaNFqkU1wJR3RBPEfsxmU6jDWPofrTQ=="], + + "mdast-util-mdx": ["mdast-util-mdx@3.0.0", "", { "dependencies": { "mdast-util-from-markdown": "^2.0.0", "mdast-util-mdx-expression": "^2.0.0", "mdast-util-mdx-jsx": "^3.0.0", "mdast-util-mdxjs-esm": "^2.0.0", "mdast-util-to-markdown": "^2.0.0" } }, "sha512-JfbYLAW7XnYTTbUsmpu0kdBUVe+yKVJZBItEjwyYJiDJuZ9w4eeaqks4HQO+R7objWgS2ymV60GYpI14Ug554w=="], + + "mdast-util-mdx-expression": ["mdast-util-mdx-expression@2.0.1", "", { "dependencies": { "@types/estree-jsx": "^1.0.0", "@types/hast": "^3.0.0", "@types/mdast": "^4.0.0", "devlop": "^1.0.0", "mdast-util-from-markdown": "^2.0.0", "mdast-util-to-markdown": "^2.0.0" } }, "sha512-J6f+9hUp+ldTZqKRSg7Vw5V6MqjATc+3E4gf3CFNcuZNWD8XdyI6zQ8GqH7f8169MM6P7hMBRDVGnn7oHB9kXQ=="], + + "mdast-util-mdx-jsx": ["mdast-util-mdx-jsx@3.2.0", "", { "dependencies": { "@types/estree-jsx": "^1.0.0", "@types/hast": "^3.0.0", "@types/mdast": "^4.0.0", "@types/unist": "^3.0.0", "ccount": "^2.0.0", "devlop": "^1.1.0", "mdast-util-from-markdown": "^2.0.0", "mdast-util-to-markdown": "^2.0.0", "parse-entities": "^4.0.0", "stringify-entities": "^4.0.0", "unist-util-stringify-position": "^4.0.0", "vfile-message": "^4.0.0" } }, "sha512-lj/z8v0r6ZtsN/cGNNtemmmfoLAFZnjMbNyLzBafjzikOM+glrjNHPlf6lQDOTccj9n5b0PPihEBbhneMyGs1Q=="], + + "mdast-util-mdxjs-esm": ["mdast-util-mdxjs-esm@2.0.1", "", { "dependencies": { "@types/estree-jsx": "^1.0.0", "@types/hast": "^3.0.0", "@types/mdast": "^4.0.0", "devlop": "^1.0.0", "mdast-util-from-markdown": "^2.0.0", "mdast-util-to-markdown": "^2.0.0" } }, "sha512-EcmOpxsZ96CvlP03NghtH1EsLtr0n9Tm4lPUJUBccV9RwUOneqSycg19n5HGzCf+10LozMRSObtVr3ee1WoHtg=="], + + "mdast-util-phrasing": ["mdast-util-phrasing@4.1.0", "", { "dependencies": { "@types/mdast": "^4.0.0", "unist-util-is": "^6.0.0" } }, "sha512-TqICwyvJJpBwvGAMZjj4J2n0X8QWp21b9l0o7eXyVJ25YNWYbJDVIyD1bZXE6WtV6RmKJVYmQAKWa0zWOABz2w=="], + + "mdast-util-to-hast": ["mdast-util-to-hast@13.2.0", "", { "dependencies": { "@types/hast": "^3.0.0", "@types/mdast": "^4.0.0", "@ungap/structured-clone": "^1.0.0", "devlop": "^1.0.0", "micromark-util-sanitize-uri": "^2.0.0", "trim-lines": "^3.0.0", "unist-util-position": "^5.0.0", "unist-util-visit": "^5.0.0", "vfile": "^6.0.0" } }, "sha512-QGYKEuUsYT9ykKBCMOEDLsU5JRObWQusAolFMeko/tYPufNkRffBAQjIE+99jbA87xv6FgmjLtwjh9wBWajwAA=="], + + "mdast-util-to-markdown": ["mdast-util-to-markdown@2.1.2", "", { "dependencies": { "@types/mdast": "^4.0.0", "@types/unist": "^3.0.0", "longest-streak": "^3.0.0", "mdast-util-phrasing": "^4.0.0", "mdast-util-to-string": "^4.0.0", "micromark-util-classify-character": "^2.0.0", "micromark-util-decode-string": "^2.0.0", "unist-util-visit": "^5.0.0", "zwitch": "^2.0.0" } }, "sha512-xj68wMTvGXVOKonmog6LwyJKrYXZPvlwabaryTjLh9LuvovB/KAH+kvi8Gjj+7rJjsFi23nkUxRQv1KqSroMqA=="], + + "mdast-util-to-string": ["mdast-util-to-string@4.0.0", "", { "dependencies": { "@types/mdast": "^4.0.0" } }, "sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg=="], + + "mdn-data": ["mdn-data@2.12.2", "", {}, "sha512-IEn+pegP1aManZuckezWCO+XZQDplx1366JoVhTpMpBB1sPey/SbveZQUosKiKiGYjg1wH4pMlNgXbCiYgihQA=="], + + "merge2": ["merge2@1.4.1", "", {}, "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg=="], + + "micromark": ["micromark@4.0.2", "", { "dependencies": { "@types/debug": "^4.0.0", "debug": "^4.0.0", "decode-named-character-reference": "^1.0.0", "devlop": "^1.0.0", "micromark-core-commonmark": "^2.0.0", "micromark-factory-space": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-chunked": "^2.0.0", "micromark-util-combine-extensions": "^2.0.0", "micromark-util-decode-numeric-character-reference": "^2.0.0", "micromark-util-encode": "^2.0.0", "micromark-util-normalize-identifier": "^2.0.0", "micromark-util-resolve-all": "^2.0.0", "micromark-util-sanitize-uri": "^2.0.0", "micromark-util-subtokenize": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-zpe98Q6kvavpCr1NPVSCMebCKfD7CA2NqZ+rykeNhONIJBpc1tFKt9hucLGwha3jNTNI8lHpctWJWoimVF4PfA=="], + + "micromark-core-commonmark": ["micromark-core-commonmark@2.0.3", "", { "dependencies": { "decode-named-character-reference": "^1.0.0", "devlop": "^1.0.0", "micromark-factory-destination": "^2.0.0", "micromark-factory-label": "^2.0.0", "micromark-factory-space": "^2.0.0", "micromark-factory-title": "^2.0.0", "micromark-factory-whitespace": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-chunked": "^2.0.0", "micromark-util-classify-character": "^2.0.0", "micromark-util-html-tag-name": "^2.0.0", "micromark-util-normalize-identifier": "^2.0.0", "micromark-util-resolve-all": "^2.0.0", "micromark-util-subtokenize": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg=="], + + "micromark-extension-gfm": ["micromark-extension-gfm@3.0.0", "", { "dependencies": { "micromark-extension-gfm-autolink-literal": "^2.0.0", "micromark-extension-gfm-footnote": "^2.0.0", "micromark-extension-gfm-strikethrough": "^2.0.0", "micromark-extension-gfm-table": "^2.0.0", "micromark-extension-gfm-tagfilter": "^2.0.0", "micromark-extension-gfm-task-list-item": "^2.0.0", "micromark-util-combine-extensions": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-vsKArQsicm7t0z2GugkCKtZehqUm31oeGBV/KVSorWSy8ZlNAv7ytjFhvaryUiCUJYqs+NoE6AFhpQvBTM6Q4w=="], + + "micromark-extension-gfm-autolink-literal": ["micromark-extension-gfm-autolink-literal@2.1.0", "", { "dependencies": { "micromark-util-character": "^2.0.0", "micromark-util-sanitize-uri": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-oOg7knzhicgQ3t4QCjCWgTmfNhvQbDDnJeVu9v81r7NltNCVmhPy1fJRX27pISafdjL+SVc4d3l48Gb6pbRypw=="], + + "micromark-extension-gfm-footnote": ["micromark-extension-gfm-footnote@2.1.0", "", { "dependencies": { "devlop": "^1.0.0", "micromark-core-commonmark": "^2.0.0", "micromark-factory-space": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-normalize-identifier": "^2.0.0", "micromark-util-sanitize-uri": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-/yPhxI1ntnDNsiHtzLKYnE3vf9JZ6cAisqVDauhp4CEHxlb4uoOTxOCJ+9s51bIB8U1N1FJ1RXOKTIlD5B/gqw=="], + + "micromark-extension-gfm-strikethrough": ["micromark-extension-gfm-strikethrough@2.1.0", "", { "dependencies": { "devlop": "^1.0.0", "micromark-util-chunked": "^2.0.0", "micromark-util-classify-character": "^2.0.0", "micromark-util-resolve-all": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-ADVjpOOkjz1hhkZLlBiYA9cR2Anf8F4HqZUO6e5eDcPQd0Txw5fxLzzxnEkSkfnD0wziSGiv7sYhk/ktvbf1uw=="], + + "micromark-extension-gfm-table": ["micromark-extension-gfm-table@2.1.1", "", { "dependencies": { "devlop": "^1.0.0", "micromark-factory-space": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-t2OU/dXXioARrC6yWfJ4hqB7rct14e8f7m0cbI5hUmDyyIlwv5vEtooptH8INkbLzOatzKuVbQmAYcbWoyz6Dg=="], + + "micromark-extension-gfm-tagfilter": ["micromark-extension-gfm-tagfilter@2.0.0", "", { "dependencies": { "micromark-util-types": "^2.0.0" } }, "sha512-xHlTOmuCSotIA8TW1mDIM6X2O1SiX5P9IuDtqGonFhEK0qgRI4yeC6vMxEV2dgyr2TiD+2PQ10o+cOhdVAcwfg=="], + + "micromark-extension-gfm-task-list-item": ["micromark-extension-gfm-task-list-item@2.1.0", "", { "dependencies": { "devlop": "^1.0.0", "micromark-factory-space": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-qIBZhqxqI6fjLDYFTBIa4eivDMnP+OZqsNwmQ3xNLE4Cxwc+zfQEfbs6tzAo2Hjq+bh6q5F+Z8/cksrLFYWQQw=="], + + "micromark-extension-mdx-expression": ["micromark-extension-mdx-expression@3.0.1", "", { "dependencies": { "@types/estree": "^1.0.0", "devlop": "^1.0.0", "micromark-factory-mdx-expression": "^2.0.0", "micromark-factory-space": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-events-to-acorn": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-dD/ADLJ1AeMvSAKBwO22zG22N4ybhe7kFIZ3LsDI0GlsNr2A3KYxb0LdC1u5rj4Nw+CHKY0RVdnHX8vj8ejm4Q=="], + + "micromark-extension-mdx-jsx": ["micromark-extension-mdx-jsx@3.0.2", "", { "dependencies": { "@types/estree": "^1.0.0", "devlop": "^1.0.0", "estree-util-is-identifier-name": "^3.0.0", "micromark-factory-mdx-expression": "^2.0.0", "micromark-factory-space": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-events-to-acorn": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0", "vfile-message": "^4.0.0" } }, "sha512-e5+q1DjMh62LZAJOnDraSSbDMvGJ8x3cbjygy2qFEi7HCeUT4BDKCvMozPozcD6WmOt6sVvYDNBKhFSz3kjOVQ=="], + + "micromark-extension-mdx-md": ["micromark-extension-mdx-md@2.0.0", "", { "dependencies": { "micromark-util-types": "^2.0.0" } }, "sha512-EpAiszsB3blw4Rpba7xTOUptcFeBFi+6PY8VnJ2hhimH+vCQDirWgsMpz7w1XcZE7LVrSAUGb9VJpG9ghlYvYQ=="], + + "micromark-extension-mdxjs": ["micromark-extension-mdxjs@3.0.0", "", { "dependencies": { "acorn": "^8.0.0", "acorn-jsx": "^5.0.0", "micromark-extension-mdx-expression": "^3.0.0", "micromark-extension-mdx-jsx": "^3.0.0", "micromark-extension-mdx-md": "^2.0.0", "micromark-extension-mdxjs-esm": "^3.0.0", "micromark-util-combine-extensions": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-A873fJfhnJ2siZyUrJ31l34Uqwy4xIFmvPY1oj+Ean5PHcPBYzEsvqvWGaWcfEIr11O5Dlw3p2y0tZWpKHDejQ=="], + + "micromark-extension-mdxjs-esm": ["micromark-extension-mdxjs-esm@3.0.0", "", { "dependencies": { "@types/estree": "^1.0.0", "devlop": "^1.0.0", "micromark-core-commonmark": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-events-to-acorn": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0", "unist-util-position-from-estree": "^2.0.0", "vfile-message": "^4.0.0" } }, "sha512-DJFl4ZqkErRpq/dAPyeWp15tGrcrrJho1hKK5uBS70BCtfrIFg81sqcTVu3Ta+KD1Tk5vAtBNElWxtAa+m8K9A=="], + + "micromark-factory-destination": ["micromark-factory-destination@2.0.1", "", { "dependencies": { "micromark-util-character": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-Xe6rDdJlkmbFRExpTOmRj9N3MaWmbAgdpSrBQvCFqhezUn4AHqJHbaEnfbVYYiexVSs//tqOdY/DxhjdCiJnIA=="], + + "micromark-factory-label": ["micromark-factory-label@2.0.1", "", { "dependencies": { "devlop": "^1.0.0", "micromark-util-character": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-VFMekyQExqIW7xIChcXn4ok29YE3rnuyveW3wZQWWqF4Nv9Wk5rgJ99KzPvHjkmPXF93FXIbBp6YdW3t71/7Vg=="], + + "micromark-factory-mdx-expression": ["micromark-factory-mdx-expression@2.0.3", "", { "dependencies": { "@types/estree": "^1.0.0", "devlop": "^1.0.0", "micromark-factory-space": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-events-to-acorn": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0", "unist-util-position-from-estree": "^2.0.0", "vfile-message": "^4.0.0" } }, "sha512-kQnEtA3vzucU2BkrIa8/VaSAsP+EJ3CKOvhMuJgOEGg9KDC6OAY6nSnNDVRiVNRqj7Y4SlSzcStaH/5jge8JdQ=="], + + "micromark-factory-space": ["micromark-factory-space@2.0.1", "", { "dependencies": { "micromark-util-character": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg=="], + + "micromark-factory-title": ["micromark-factory-title@2.0.1", "", { "dependencies": { "micromark-factory-space": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-5bZ+3CjhAd9eChYTHsjy6TGxpOFSKgKKJPJxr293jTbfry2KDoWkhBb6TcPVB4NmzaPhMs1Frm9AZH7OD4Cjzw=="], + + "micromark-factory-whitespace": ["micromark-factory-whitespace@2.0.1", "", { "dependencies": { "micromark-factory-space": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-Ob0nuZ3PKt/n0hORHyvoD9uZhr+Za8sFoP+OnMcnWK5lngSzALgQYKMr9RJVOWLqQYuyn6ulqGWSXdwf6F80lQ=="], + + "micromark-util-character": ["micromark-util-character@2.1.1", "", { "dependencies": { "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q=="], + + "micromark-util-chunked": ["micromark-util-chunked@2.0.1", "", { "dependencies": { "micromark-util-symbol": "^2.0.0" } }, "sha512-QUNFEOPELfmvv+4xiNg2sRYeS/P84pTW0TCgP5zc9FpXetHY0ab7SxKyAQCNCc1eK0459uoLI1y5oO5Vc1dbhA=="], + + "micromark-util-classify-character": ["micromark-util-classify-character@2.0.1", "", { "dependencies": { "micromark-util-character": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-K0kHzM6afW/MbeWYWLjoHQv1sgg2Q9EccHEDzSkxiP/EaagNzCm7T/WMKZ3rjMbvIpvBiZgwR3dKMygtA4mG1Q=="], + + "micromark-util-combine-extensions": ["micromark-util-combine-extensions@2.0.1", "", { "dependencies": { "micromark-util-chunked": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-OnAnH8Ujmy59JcyZw8JSbK9cGpdVY44NKgSM7E9Eh7DiLS2E9RNQf0dONaGDzEG9yjEl5hcqeIsj4hfRkLH/Bg=="], + + "micromark-util-decode-numeric-character-reference": ["micromark-util-decode-numeric-character-reference@2.0.2", "", { "dependencies": { "micromark-util-symbol": "^2.0.0" } }, "sha512-ccUbYk6CwVdkmCQMyr64dXz42EfHGkPQlBj5p7YVGzq8I7CtjXZJrubAYezf7Rp+bjPseiROqe7G6foFd+lEuw=="], + + "micromark-util-decode-string": ["micromark-util-decode-string@2.0.1", "", { "dependencies": { "decode-named-character-reference": "^1.0.0", "micromark-util-character": "^2.0.0", "micromark-util-decode-numeric-character-reference": "^2.0.0", "micromark-util-symbol": "^2.0.0" } }, "sha512-nDV/77Fj6eH1ynwscYTOsbK7rR//Uj0bZXBwJZRfaLEJ1iGBR6kIfNmlNqaqJf649EP0F3NWNdeJi03elllNUQ=="], + + "micromark-util-encode": ["micromark-util-encode@2.0.1", "", {}, "sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw=="], + + "micromark-util-events-to-acorn": ["micromark-util-events-to-acorn@2.0.3", "", { "dependencies": { "@types/estree": "^1.0.0", "@types/unist": "^3.0.0", "devlop": "^1.0.0", "estree-util-visit": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0", "vfile-message": "^4.0.0" } }, "sha512-jmsiEIiZ1n7X1Rr5k8wVExBQCg5jy4UXVADItHmNk1zkwEVhBuIUKRu3fqv+hs4nxLISi2DQGlqIOGiFxgbfHg=="], + + "micromark-util-html-tag-name": ["micromark-util-html-tag-name@2.0.1", "", {}, "sha512-2cNEiYDhCWKI+Gs9T0Tiysk136SnR13hhO8yW6BGNyhOC4qYFnwF1nKfD3HFAIXA5c45RrIG1ub11GiXeYd1xA=="], + + "micromark-util-normalize-identifier": ["micromark-util-normalize-identifier@2.0.1", "", { "dependencies": { "micromark-util-symbol": "^2.0.0" } }, "sha512-sxPqmo70LyARJs0w2UclACPUUEqltCkJ6PhKdMIDuJ3gSf/Q+/GIe3WKl0Ijb/GyH9lOpUkRAO2wp0GVkLvS9Q=="], + + "micromark-util-resolve-all": ["micromark-util-resolve-all@2.0.1", "", { "dependencies": { "micromark-util-types": "^2.0.0" } }, "sha512-VdQyxFWFT2/FGJgwQnJYbe1jjQoNTS4RjglmSjTUlpUMa95Htx9NHeYW4rGDJzbjvCsl9eLjMQwGeElsqmzcHg=="], + + "micromark-util-sanitize-uri": ["micromark-util-sanitize-uri@2.0.1", "", { "dependencies": { "micromark-util-character": "^2.0.0", "micromark-util-encode": "^2.0.0", "micromark-util-symbol": "^2.0.0" } }, "sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ=="], + + "micromark-util-subtokenize": ["micromark-util-subtokenize@2.1.0", "", { "dependencies": { "devlop": "^1.0.0", "micromark-util-chunked": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-XQLu552iSctvnEcgXw6+Sx75GflAPNED1qx7eBJ+wydBb2KCbRZe+NwvIEEMM83uml1+2WSXpBAcp9IUCgCYWA=="], + + "micromark-util-symbol": ["micromark-util-symbol@2.0.1", "", {}, "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q=="], + + "micromark-util-types": ["micromark-util-types@2.0.2", "", {}, "sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA=="], + + "micromatch": ["micromatch@4.0.8", "", { "dependencies": { "braces": "^3.0.3", "picomatch": "^2.3.1" } }, "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA=="], + + "mime-db": ["mime-db@1.54.0", "", {}, "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ=="], + + "mime-types": ["mime-types@3.0.1", "", { "dependencies": { "mime-db": "^1.54.0" } }, "sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA=="], + + "min-indent": ["min-indent@1.0.1", "", {}, "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg=="], + + "mrmime": ["mrmime@2.0.1", "", {}, "sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ=="], + + "ms": ["ms@4.0.0-nightly.202508271359", "", {}, "sha512-WC/Eo7NzFrOV/RRrTaI0fxKVbNCzEy76j2VqNV8SxDf9D69gSE2Lh0QwYvDlhiYmheBYExAvEAxVf5NoN0cj2A=="], + + "muggle-string": ["muggle-string@0.4.1", "", {}, "sha512-VNTrAak/KhO2i8dqqnqnAHOa3cYBwXEZe9h+D5h/1ZqFSTEFHdM65lR7RoIqq3tBBYavsOXV84NoHXZ0AkPyqQ=="], + + "nanoid": ["nanoid@3.3.11", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w=="], + + "nanostores": ["nanostores@1.0.1", "", {}, "sha512-kNZ9xnoJYKg/AfxjrVL4SS0fKX++4awQReGqWnwTRHxeHGZ1FJFVgTqr/eMrNQdp0Tz7M7tG/TDaX8QfHDwVCw=="], + + "neotraverse": ["neotraverse@0.6.18", "", {}, "sha512-Z4SmBUweYa09+o6pG+eASabEpP6QkQ70yHj351pQoEXIs8uHbaU2DWVmzBANKgflPa47A50PtB2+NgRpQvr7vA=="], + + "next-themes": ["next-themes@0.4.6", "", { "peerDependencies": { "react": "^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc", "react-dom": "^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc" } }, "sha512-pZvgD5L0IEvX5/9GWyHMf3m8BKiVQwsCMHfoFosXtXBMnaS0ZnIJ9ST4b4NqLVKDEm8QBxoNNGNaBv2JNF6XNA=="], + + "nlcst-to-string": ["nlcst-to-string@4.0.0", "", { "dependencies": { "@types/nlcst": "^2.0.0" } }, "sha512-YKLBCcUYKAg0FNlOBT6aI91qFmSiFKiluk655WzPF+DDMA02qIyy8uiRqI8QXtcFpEvll12LpL5MXqEmAZ+dcA=="], + + "node-fetch-native": ["node-fetch-native@1.6.7", "", {}, "sha512-g9yhqoedzIUm0nTnTqAQvueMPVOuIY16bqgAJJC8XOOubYFNwz6IER9qs0Gq2Xd0+CecCKFjtdDTMA4u4xG06Q=="], + + "node-forge": ["node-forge@1.3.1", "", {}, "sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA=="], + + "node-mock-http": ["node-mock-http@1.0.3", "", {}, "sha512-jN8dK25fsfnMrVsEhluUTPkBFY+6ybu7jSB1n+ri/vOGjJxU8J9CZhpSGkHXSkFjtUhbmoncG/YG9ta5Ludqog=="], + + "node-releases": ["node-releases@2.0.19", "", {}, "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw=="], + + "node-rsa": ["node-rsa@1.1.1", "", { "dependencies": { "asn1": "^0.2.4" } }, "sha512-Jd4cvbJMryN21r5HgxQOpMEqv+ooke/korixNNK3mGqfGJmy0M77WDDzo/05969+OkMy3XW1UuZsSmW9KQm7Fw=="], + + "normalize-path": ["normalize-path@3.0.0", "", {}, "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA=="], + + "nth-check": ["nth-check@2.1.1", "", { "dependencies": { "boolbase": "^1.0.0" } }, "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w=="], + + "obug": ["obug@2.1.1", "", {}, "sha512-uTqF9MuPraAQ+IsnPf366RG4cP9RtUi7MLO1N3KEc+wb0a6yKpeL0lmk2IB1jY5KHPAlTc6T/JRdC/YqxHNwkQ=="], + + "ofetch": ["ofetch@1.4.1", "", { "dependencies": { "destr": "^2.0.3", "node-fetch-native": "^1.6.4", "ufo": "^1.5.4" } }, "sha512-QZj2DfGplQAr2oj9KzceK9Hwz6Whxazmn85yYeVuS3u9XTMOGMRx0kO95MQ+vLsj/S/NwBDMMLU5hpxvI6Tklw=="], + + "ohash": ["ohash@2.0.11", "", {}, "sha512-RdR9FQrFwNBNXAr4GixM8YaRZRJ5PUWbKYbE5eOsrwAjJW0q2REGcf79oYPsLyskQCZG1PLN+S/K1V00joZAoQ=="], + + "on-finished": ["on-finished@2.4.1", "", { "dependencies": { "ee-first": "1.1.1" } }, "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg=="], + + "oniguruma-parser": ["oniguruma-parser@0.12.1", "", {}, "sha512-8Unqkvk1RYc6yq2WBYRj4hdnsAxVze8i7iPfQr8e4uSP3tRv0rpZcbGUDvxfQQcdwHt/e9PrMvGCsa8OqG9X3w=="], + + "oniguruma-to-es": ["oniguruma-to-es@4.3.4", "", { "dependencies": { "oniguruma-parser": "^0.12.1", "regex": "^6.0.1", "regex-recursion": "^6.0.2" } }, "sha512-3VhUGN3w2eYxnTzHn+ikMI+fp/96KoRSVK9/kMTcFqj1NRDh2IhQCKvYxDnWePKRXY/AqH+Fuiyb7VHSzBjHfA=="], + + "p-limit": ["p-limit@6.2.0", "", { "dependencies": { "yocto-queue": "^1.1.1" } }, "sha512-kuUqqHNUqoIWp/c467RI4X6mmyuojY5jGutNU0wVTmEOOfcuwLqyMVoAi9MKi2Ak+5i9+nhmrK4ufZE8069kHA=="], + + "p-queue": ["p-queue@8.1.1", "", { "dependencies": { "eventemitter3": "^5.0.1", "p-timeout": "^6.1.2" } }, "sha512-aNZ+VfjobsWryoiPnEApGGmf5WmNsCo9xu8dfaYamG5qaLP7ClhLN6NgsFe6SwJ2UbLEBK5dv9x8Mn5+RVhMWQ=="], + + "p-timeout": ["p-timeout@6.1.4", "", {}, "sha512-MyIV3ZA/PmyBN/ud8vV9XzwTrNtR4jFrObymZYnZqMmW0zA8Z17vnT0rBgFE/TlohB+YCHqXMgZzb3Csp49vqg=="], + + "package-manager-detector": ["package-manager-detector@1.6.0", "", {}, "sha512-61A5ThoTiDG/C8s8UMZwSorAGwMJ0ERVGj2OjoW5pAalsNOg15+iQiPzrLJ4jhZ1HJzmC2PIHT2oEiH3R5fzNA=="], + + "pako": ["pako@1.0.11", "", {}, "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw=="], + + "parse-entities": ["parse-entities@4.0.2", "", { "dependencies": { "@types/unist": "^2.0.0", "character-entities-legacy": "^3.0.0", "character-reference-invalid": "^2.0.0", "decode-named-character-reference": "^1.0.0", "is-alphanumerical": "^2.0.0", "is-decimal": "^2.0.0", "is-hexadecimal": "^2.0.0" } }, "sha512-GG2AQYWoLgL877gQIKeRPGO1xF9+eG1ujIb5soS5gPvLQ1y2o8FL90w2QWNdf9I361Mpp7726c+lj3U0qK1uGw=="], + + "parse-latin": ["parse-latin@7.0.0", "", { "dependencies": { "@types/nlcst": "^2.0.0", "@types/unist": "^3.0.0", "nlcst-to-string": "^4.0.0", "unist-util-modify-children": "^4.0.0", "unist-util-visit-children": "^3.0.0", "vfile": "^6.0.0" } }, "sha512-mhHgobPPua5kZ98EF4HWiH167JWBfl4pvAIXXdbaVohtK7a6YBOy56kvhCqduqyo/f3yrHFWmqmiMg/BkBkYYQ=="], + + "parse5": ["parse5@8.0.0", "", { "dependencies": { "entities": "^6.0.0" } }, "sha512-9m4m5GSgXjL4AjumKzq1Fgfp3Z8rsvjRNbnkVwfu2ImRqE5D0LnY2QfDen18FSY9C573YU5XxSapdHZTZ2WolA=="], + + "path-browserify": ["path-browserify@1.0.1", "", {}, "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g=="], + + "pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="], + + "piccolore": ["piccolore@0.1.3", "", {}, "sha512-o8bTeDWjE086iwKrROaDf31K0qC/BENdm15/uH9usSC/uZjJOKb2YGiVHfLY4GhwsERiPI1jmwI2XrA7ACOxVw=="], + + "picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="], + + "picomatch": ["picomatch@4.0.3", "", {}, "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q=="], + + "postcss": ["postcss@8.5.3", "", { "dependencies": { "nanoid": "^3.3.8", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A=="], + + "prettier": ["prettier@3.7.4", "", { "bin": { "prettier": "bin/prettier.cjs" } }, "sha512-v6UNi1+3hSlVvv8fSaoUbggEM5VErKmmpGA7Pl3HF8V6uKY7rvClBOJlH6yNwQtfTueNkGVpOv/mtWL9L4bgRA=="], + + "pretty-format": ["pretty-format@27.5.1", "", { "dependencies": { "ansi-regex": "^5.0.1", "ansi-styles": "^5.0.0", "react-is": "^17.0.1" } }, "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ=="], + + "prismjs": ["prismjs@1.30.0", "", {}, "sha512-DEvV2ZF2r2/63V+tK8hQvrR2ZGn10srHbXviTlcv7Kpzw8jWiNTqbVgjO3IY8RxrrOUF8VPMQQFysYYYv0YZxw=="], + + "prompts": ["prompts@2.4.2", "", { "dependencies": { "kleur": "^3.0.3", "sisteransi": "^1.0.5" } }, "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q=="], + + "property-information": ["property-information@7.1.0", "", {}, "sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ=="], + + "punycode": ["punycode@2.3.1", "", {}, "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg=="], + + "queue-microtask": ["queue-microtask@1.2.3", "", {}, "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A=="], + + "radix3": ["radix3@1.1.2", "", {}, "sha512-b484I/7b8rDEdSDKckSSBA8knMpcdsXudlE/LNL639wFoHKwLbEkQFZHWEYwDC0wa0FKUcCY+GAF73Z7wxNVFA=="], + + "range-parser": ["range-parser@1.2.1", "", {}, "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg=="], + + "react": ["react@19.2.1", "", {}, "sha512-DGrYcCWK7tvYMnWh79yrPHt+vdx9tY+1gPZa7nJQtO/p8bLTDaHp4dzwEhQB7pZ4Xe3ok4XKuEPrVuc+wlpkmw=="], + + "react-dom": ["react-dom@19.2.1", "", { "dependencies": { "scheduler": "^0.27.0" }, "peerDependencies": { "react": "^19.2.1" } }, "sha512-ibrK8llX2a4eOskq1mXKu/TGZj9qzomO+sNfO98M6d9zIPOEhlBkMkBUBLd1vgS0gQsLDBzA+8jJBVXDnfHmJg=="], + + "react-icons": ["react-icons@5.5.0", "", { "peerDependencies": { "react": "*" } }, "sha512-MEFcXdkP3dLo8uumGI5xN3lDFNsRtrjbOEKDLD7yv76v4wpnEq2Lt2qeHaQOr34I/wPN3s3+N08WkQ+CW37Xiw=="], + + "react-is": ["react-is@17.0.2", "", {}, "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w=="], + + "react-refresh": ["react-refresh@0.18.0", "", {}, "sha512-QgT5//D3jfjJb6Gsjxv0Slpj23ip+HtOpnNgnb2S5zU3CB26G/IDPGoy4RJB42wzFE46DRsstbW6tKHoKbhAxw=="], + + "react-remove-scroll": ["react-remove-scroll@2.7.0", "", { "dependencies": { "react-remove-scroll-bar": "^2.3.7", "react-style-singleton": "^2.2.3", "tslib": "^2.1.0", "use-callback-ref": "^1.3.3", "use-sidecar": "^1.1.3" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-sGsQtcjMqdQyijAHytfGEELB8FufGbfXIsvUTe+NLx1GDRJCXtCFLBLUI1eyZCKXXvbEU2C6gai0PZKoIE9Vbg=="], + + "react-remove-scroll-bar": ["react-remove-scroll-bar@2.3.8", "", { "dependencies": { "react-style-singleton": "^2.2.2", "tslib": "^2.0.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" }, "optionalPeers": ["@types/react"] }, "sha512-9r+yi9+mgU33AKcj6IbT9oRCO78WriSj6t/cF8DWBZJ9aOGPOTEDvdUDz1FwKim7QXWwmHqtdHnRJfhAxEG46Q=="], + + "react-style-singleton": ["react-style-singleton@2.2.3", "", { "dependencies": { "get-nonce": "^1.0.0", "tslib": "^2.0.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-b6jSvxvVnyptAiLjbkWLE/lOnR4lfTtDAl+eUC7RZy+QQWc6wRzIV2CE6xBuMmDxc2qIihtDCZD5NPOFl7fRBQ=="], + + "readdirp": ["readdirp@4.1.2", "", {}, "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg=="], + + "recma-build-jsx": ["recma-build-jsx@1.0.0", "", { "dependencies": { "@types/estree": "^1.0.0", "estree-util-build-jsx": "^3.0.0", "vfile": "^6.0.0" } }, "sha512-8GtdyqaBcDfva+GUKDr3nev3VpKAhup1+RvkMvUxURHpW7QyIvk9F5wz7Vzo06CEMSilw6uArgRqhpiUcWp8ew=="], + + "recma-jsx": ["recma-jsx@1.0.0", "", { "dependencies": { "acorn-jsx": "^5.0.0", "estree-util-to-js": "^2.0.0", "recma-parse": "^1.0.0", "recma-stringify": "^1.0.0", "unified": "^11.0.0" } }, "sha512-5vwkv65qWwYxg+Atz95acp8DMu1JDSqdGkA2Of1j6rCreyFUE/gp15fC8MnGEuG1W68UKjM6x6+YTWIh7hZM/Q=="], + + "recma-parse": ["recma-parse@1.0.0", "", { "dependencies": { "@types/estree": "^1.0.0", "esast-util-from-js": "^2.0.0", "unified": "^11.0.0", "vfile": "^6.0.0" } }, "sha512-OYLsIGBB5Y5wjnSnQW6t3Xg7q3fQ7FWbw/vcXtORTnyaSFscOtABg+7Pnz6YZ6c27fG1/aN8CjfwoUEUIdwqWQ=="], + + "recma-stringify": ["recma-stringify@1.0.0", "", { "dependencies": { "@types/estree": "^1.0.0", "estree-util-to-js": "^2.0.0", "unified": "^11.0.0", "vfile": "^6.0.0" } }, "sha512-cjwII1MdIIVloKvC9ErQ+OgAtwHBmcZ0Bg4ciz78FtbT8In39aAYbaA7zvxQ61xVMSPE8WxhLwLbhif4Js2C+g=="], + + "redent": ["redent@3.0.0", "", { "dependencies": { "indent-string": "^4.0.0", "strip-indent": "^3.0.0" } }, "sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg=="], + + "regex": ["regex@6.0.1", "", { "dependencies": { "regex-utilities": "^2.3.0" } }, "sha512-uorlqlzAKjKQZ5P+kTJr3eeJGSVroLKoHmquUj4zHWuR+hEyNqlXsSKlYYF5F4NI6nl7tWCs0apKJ0lmfsXAPA=="], + + "regex-recursion": ["regex-recursion@6.0.2", "", { "dependencies": { "regex-utilities": "^2.3.0" } }, "sha512-0YCaSCq2VRIebiaUviZNs0cBz1kg5kVS2UKUfNIx8YVs1cN3AV7NTctO5FOKBA+UT2BPJIWZauYHPqJODG50cg=="], + + "regex-utilities": ["regex-utilities@2.3.0", "", {}, "sha512-8VhliFJAWRaUiVvREIiW2NXXTmHs4vMNnSzuJVhscgmGav3g9VDxLrQndI3dZZVVdp0ZO/5v0xmX516/7M9cng=="], + + "rehype": ["rehype@13.0.2", "", { "dependencies": { "@types/hast": "^3.0.0", "rehype-parse": "^9.0.0", "rehype-stringify": "^10.0.0", "unified": "^11.0.0" } }, "sha512-j31mdaRFrwFRUIlxGeuPXXKWQxet52RBQRvCmzl5eCefn/KGbomK5GMHNMsOJf55fgo3qw5tST5neDuarDYR2A=="], + + "rehype-parse": ["rehype-parse@9.0.1", "", { "dependencies": { "@types/hast": "^3.0.0", "hast-util-from-html": "^2.0.0", "unified": "^11.0.0" } }, "sha512-ksCzCD0Fgfh7trPDxr2rSylbwq9iYDkSn8TCDmEJ49ljEUBxDVCzCHv7QNzZOfODanX4+bWQ4WZqLCRWYLfhag=="], + + "rehype-raw": ["rehype-raw@7.0.0", "", { "dependencies": { "@types/hast": "^3.0.0", "hast-util-raw": "^9.0.0", "vfile": "^6.0.0" } }, "sha512-/aE8hCfKlQeA8LmyeyQvQF3eBiLRGNlfBJEvWH7ivp9sBqs7TNqBL5X3v157rM4IFETqDnIOO+z5M/biZbo9Ww=="], + + "rehype-recma": ["rehype-recma@1.0.0", "", { "dependencies": { "@types/estree": "^1.0.0", "@types/hast": "^3.0.0", "hast-util-to-estree": "^3.0.0" } }, "sha512-lqA4rGUf1JmacCNWWZx0Wv1dHqMwxzsDWYMTowuplHF3xH0N/MmrZ/G3BDZnzAkRmxDadujCjaKM2hqYdCBOGw=="], + + "rehype-stringify": ["rehype-stringify@10.0.1", "", { "dependencies": { "@types/hast": "^3.0.0", "hast-util-to-html": "^9.0.0", "unified": "^11.0.0" } }, "sha512-k9ecfXHmIPuFVI61B9DeLPN0qFHfawM6RsuX48hoqlaKSF61RskNjSm1lI8PhBEM0MRdLxVVm4WmTqJQccH9mA=="], + + "remark-gfm": ["remark-gfm@4.0.1", "", { "dependencies": { "@types/mdast": "^4.0.0", "mdast-util-gfm": "^3.0.0", "micromark-extension-gfm": "^3.0.0", "remark-parse": "^11.0.0", "remark-stringify": "^11.0.0", "unified": "^11.0.0" } }, "sha512-1quofZ2RQ9EWdeN34S79+KExV1764+wCUGop5CPL1WGdD0ocPpu91lzPGbwWMECpEpd42kJGQwzRfyov9j4yNg=="], + + "remark-mdx": ["remark-mdx@3.1.0", "", { "dependencies": { "mdast-util-mdx": "^3.0.0", "micromark-extension-mdxjs": "^3.0.0" } }, "sha512-Ngl/H3YXyBV9RcRNdlYsZujAmhsxwzxpDzpDEhFBVAGthS4GDgnctpDjgFl/ULx5UEDzqtW1cyBSNKqYYrqLBA=="], + + "remark-parse": ["remark-parse@11.0.0", "", { "dependencies": { "@types/mdast": "^4.0.0", "mdast-util-from-markdown": "^2.0.0", "micromark-util-types": "^2.0.0", "unified": "^11.0.0" } }, "sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA=="], + + "remark-rehype": ["remark-rehype@11.1.2", "", { "dependencies": { "@types/hast": "^3.0.0", "@types/mdast": "^4.0.0", "mdast-util-to-hast": "^13.0.0", "unified": "^11.0.0", "vfile": "^6.0.0" } }, "sha512-Dh7l57ianaEoIpzbp0PC9UKAdCSVklD8E5Rpw7ETfbTl3FqcOOgq5q2LVDhgGCkaBv7p24JXikPdvhhmHvKMsw=="], + + "remark-smartypants": ["remark-smartypants@3.0.2", "", { "dependencies": { "retext": "^9.0.0", "retext-smartypants": "^6.0.0", "unified": "^11.0.4", "unist-util-visit": "^5.0.0" } }, "sha512-ILTWeOriIluwEvPjv67v7Blgrcx+LZOkAUVtKI3putuhlZm84FnqDORNXPPm+HY3NdZOMhyDwZ1E+eZB/Df5dA=="], + + "remark-stringify": ["remark-stringify@11.0.0", "", { "dependencies": { "@types/mdast": "^4.0.0", "mdast-util-to-markdown": "^2.0.0", "unified": "^11.0.0" } }, "sha512-1OSmLd3awB/t8qdoEOMazZkNsfVTeY4fTsgzcQFdXNq8ToTN4ZGwrMnlda4K6smTFKD+GRV6O48i6Z4iKgPPpw=="], + + "request-light": ["request-light@0.7.0", "", {}, "sha512-lMbBMrDoxgsyO+yB3sDcrDuX85yYt7sS8BfQd11jtbW/z5ZWgLZRcEGLsLoYw7I0WSUGQBs8CC8ScIxkTX1+6Q=="], + + "require-directory": ["require-directory@2.1.1", "", {}, "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q=="], + + "require-from-string": ["require-from-string@2.0.2", "", {}, "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw=="], + + "resolve-pkg-maps": ["resolve-pkg-maps@1.0.0", "", {}, "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw=="], + + "restructure": ["restructure@3.0.2", "", {}, "sha512-gSfoiOEA0VPE6Tukkrr7I0RBdE0s7H1eFCDBk05l1KIQT1UIKNc5JZy6jdyW6eYH3aR3g5b3PuL77rq0hvwtAw=="], + + "retext": ["retext@9.0.0", "", { "dependencies": { "@types/nlcst": "^2.0.0", "retext-latin": "^4.0.0", "retext-stringify": "^4.0.0", "unified": "^11.0.0" } }, "sha512-sbMDcpHCNjvlheSgMfEcVrZko3cDzdbe1x/e7G66dFp0Ff7Mldvi2uv6JkJQzdRcvLYE8CA8Oe8siQx8ZOgTcA=="], + + "retext-latin": ["retext-latin@4.0.0", "", { "dependencies": { "@types/nlcst": "^2.0.0", "parse-latin": "^7.0.0", "unified": "^11.0.0" } }, "sha512-hv9woG7Fy0M9IlRQloq/N6atV82NxLGveq+3H2WOi79dtIYWN8OaxogDm77f8YnVXJL2VD3bbqowu5E3EMhBYA=="], + + "retext-smartypants": ["retext-smartypants@6.2.0", "", { "dependencies": { "@types/nlcst": "^2.0.0", "nlcst-to-string": "^4.0.0", "unist-util-visit": "^5.0.0" } }, "sha512-kk0jOU7+zGv//kfjXEBjdIryL1Acl4i9XNkHxtM7Tm5lFiCog576fjNC9hjoR7LTKQ0DsPWy09JummSsH1uqfQ=="], + + "retext-stringify": ["retext-stringify@4.0.0", "", { "dependencies": { "@types/nlcst": "^2.0.0", "nlcst-to-string": "^4.0.0", "unified": "^11.0.0" } }, "sha512-rtfN/0o8kL1e+78+uxPTqu1Klt0yPzKuQ2BfWwwfgIUSayyzxpM1PJzkKt4V8803uB9qSy32MvI7Xep9khTpiA=="], + + "reusify": ["reusify@1.1.0", "", {}, "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw=="], + + "rollup": ["rollup@4.41.1", "", { "dependencies": { "@types/estree": "1.0.7" }, "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.41.1", "@rollup/rollup-android-arm64": "4.41.1", "@rollup/rollup-darwin-arm64": "4.41.1", "@rollup/rollup-darwin-x64": "4.41.1", "@rollup/rollup-freebsd-arm64": "4.41.1", "@rollup/rollup-freebsd-x64": "4.41.1", "@rollup/rollup-linux-arm-gnueabihf": "4.41.1", "@rollup/rollup-linux-arm-musleabihf": "4.41.1", "@rollup/rollup-linux-arm64-gnu": "4.41.1", "@rollup/rollup-linux-arm64-musl": "4.41.1", "@rollup/rollup-linux-loongarch64-gnu": "4.41.1", "@rollup/rollup-linux-powerpc64le-gnu": "4.41.1", "@rollup/rollup-linux-riscv64-gnu": "4.41.1", "@rollup/rollup-linux-riscv64-musl": "4.41.1", "@rollup/rollup-linux-s390x-gnu": "4.41.1", "@rollup/rollup-linux-x64-gnu": "4.41.1", "@rollup/rollup-linux-x64-musl": "4.41.1", "@rollup/rollup-win32-arm64-msvc": "4.41.1", "@rollup/rollup-win32-ia32-msvc": "4.41.1", "@rollup/rollup-win32-x64-msvc": "4.41.1", "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-cPmwD3FnFv8rKMBc1MxWCwVQFxwf1JEmSX3iQXrRVVG15zerAIXRjMFVWnd5Q5QvgKF7Aj+5ykXFhUl+QGnyOw=="], + + "rou3": ["rou3@0.7.10", "", {}, "sha512-aoFj6f7MJZ5muJ+Of79nrhs9N3oLGqi2VEMe94Zbkjb6Wupha46EuoYgpWSOZlXww3bbd8ojgXTAA2mzimX5Ww=="], + + "run-parallel": ["run-parallel@1.2.0", "", { "dependencies": { "queue-microtask": "^1.2.2" } }, "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA=="], + + "safe-buffer": ["safe-buffer@5.2.1", "", {}, "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="], + + "safer-buffer": ["safer-buffer@2.1.2", "", {}, "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="], + + "samlify": ["samlify@2.10.1", "", { "dependencies": { "@authenio/xml-encryption": "^2.0.2", "@xmldom/xmldom": "^0.8.6", "camelcase": "^6.2.0", "node-forge": "^1.3.0", "node-rsa": "^1.1.1", "pako": "^1.0.10", "uuid": "^8.3.2", "xml": "^1.0.1", "xml-crypto": "^6.1.2", "xml-escape": "^1.1.0", "xpath": "^0.0.32" } }, "sha512-4zHbKKTvPnnqfGu4tks26K4fJjsY99ylsP7TPMobW5rggwcsxNlyhLE9ucxW3JFCsUcoKXb77QjQjwQo1TtRgw=="], + + "sax": ["sax@1.4.3", "", {}, "sha512-yqYn1JhPczigF94DMS+shiDMjDowYO6y9+wB/4WgO0Y19jWYk0lQ4tuG5KI7kj4FTp1wxPj5IFfcrz/s1c3jjQ=="], + + "saxes": ["saxes@6.0.0", "", { "dependencies": { "xmlchars": "^2.2.0" } }, "sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA=="], + + "scheduler": ["scheduler@0.27.0", "", {}, "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q=="], + + "semver": ["semver@7.7.3", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q=="], + + "send": ["send@1.2.0", "", { "dependencies": { "debug": "^4.3.5", "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "etag": "^1.8.1", "fresh": "^2.0.0", "http-errors": "^2.0.0", "mime-types": "^3.0.1", "ms": "^2.1.3", "on-finished": "^2.4.1", "range-parser": "^1.2.1", "statuses": "^2.0.1" } }, "sha512-uaW0WwXKpL9blXE2o0bRhoL2EGXIrZxQ2ZQ4mgcfoBxdFmQold+qWsD2jLrfZ0trjKL6vOw0j//eAwcALFjKSw=="], + + "server-destroy": ["server-destroy@1.0.1", "", {}, "sha512-rb+9B5YBIEzYcD6x2VKidaa+cqYBJQKnU4oe4E3ANwRRN56yk/ua1YCJT1n21NTS8w6CcOclAKNP3PhdCXKYtQ=="], + + "set-cookie-parser": ["set-cookie-parser@2.7.1", "", {}, "sha512-IOc8uWeOZgnb3ptbCURJWNjWUPcO3ZnTTdzsurqERrP6nPyv+paC55vJM0LpOlT2ne+Ix+9+CRG1MNLlyZ4GjQ=="], + + "setprototypeof": ["setprototypeof@1.2.0", "", {}, "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw=="], + + "sharp": ["sharp@0.34.3", "", { "dependencies": { "color": "^4.2.3", "detect-libc": "^2.0.4", "semver": "^7.7.2" }, "optionalDependencies": { "@img/sharp-darwin-arm64": "0.34.3", "@img/sharp-darwin-x64": "0.34.3", "@img/sharp-libvips-darwin-arm64": "1.2.0", "@img/sharp-libvips-darwin-x64": "1.2.0", "@img/sharp-libvips-linux-arm": "1.2.0", "@img/sharp-libvips-linux-arm64": "1.2.0", "@img/sharp-libvips-linux-ppc64": "1.2.0", "@img/sharp-libvips-linux-s390x": "1.2.0", "@img/sharp-libvips-linux-x64": "1.2.0", "@img/sharp-libvips-linuxmusl-arm64": "1.2.0", "@img/sharp-libvips-linuxmusl-x64": "1.2.0", "@img/sharp-linux-arm": "0.34.3", "@img/sharp-linux-arm64": "0.34.3", "@img/sharp-linux-ppc64": "0.34.3", "@img/sharp-linux-s390x": "0.34.3", "@img/sharp-linux-x64": "0.34.3", "@img/sharp-linuxmusl-arm64": "0.34.3", "@img/sharp-linuxmusl-x64": "0.34.3", "@img/sharp-wasm32": "0.34.3", "@img/sharp-win32-arm64": "0.34.3", "@img/sharp-win32-ia32": "0.34.3", "@img/sharp-win32-x64": "0.34.3" } }, "sha512-eX2IQ6nFohW4DbvHIOLRB3MHFpYqaqvXd3Tp5e/T/dSH83fxaNJQRvDMhASmkNTsNTVF2/OOopzRCt7xokgPfg=="], + + "shiki": ["shiki@3.19.0", "", { "dependencies": { "@shikijs/core": "3.19.0", "@shikijs/engine-javascript": "3.19.0", "@shikijs/engine-oniguruma": "3.19.0", "@shikijs/langs": "3.19.0", "@shikijs/themes": "3.19.0", "@shikijs/types": "3.19.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-77VJr3OR/VUZzPiStyRhADmO2jApMM0V2b1qf0RpfWya8Zr1PeZev5AEpPGAAKWdiYUtcZGBE4F5QvJml1PvWA=="], + + "siginfo": ["siginfo@2.0.0", "", {}, "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g=="], + + "simple-swizzle": ["simple-swizzle@0.2.2", "", { "dependencies": { "is-arrayish": "^0.3.1" } }, "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg=="], + + "sisteransi": ["sisteransi@1.0.5", "", {}, "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg=="], + + "smol-toml": ["smol-toml@1.5.2", "", {}, "sha512-QlaZEqcAH3/RtNyet1IPIYPsEWAaYyXXv1Krsi+1L/QHppjX4Ifm8MQsBISz9vE8cHicIq3clogsheili5vhaQ=="], + + "sonner": ["sonner@2.0.7", "", { "peerDependencies": { "react": "^18.0.0 || ^19.0.0 || ^19.0.0-rc", "react-dom": "^18.0.0 || ^19.0.0 || ^19.0.0-rc" } }, "sha512-W6ZN4p58k8aDKA4XPcx2hpIQXBRAgyiWVkYhT7CvK6D3iAu7xjvVyhQHg2/iaKJZ1XVJ4r7XuwGL+WGEK37i9w=="], + + "source-map": ["source-map@0.7.6", "", {}, "sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ=="], + + "source-map-js": ["source-map-js@1.2.1", "", {}, "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="], + + "space-separated-tokens": ["space-separated-tokens@2.0.2", "", {}, "sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q=="], + + "stackback": ["stackback@0.0.2", "", {}, "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw=="], + + "statuses": ["statuses@2.0.1", "", {}, "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ=="], + + "std-env": ["std-env@3.10.0", "", {}, "sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg=="], + + "string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="], + + "stringify-entities": ["stringify-entities@4.0.4", "", { "dependencies": { "character-entities-html4": "^2.0.0", "character-entities-legacy": "^3.0.0" } }, "sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg=="], + + "strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="], + + "strip-indent": ["strip-indent@3.0.0", "", { "dependencies": { "min-indent": "^1.0.0" } }, "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ=="], + + "strnum": ["strnum@2.1.1", "", {}, "sha512-7ZvoFTiCnGxBtDqJ//Cu6fWtZtc7Y3x+QOirG15wztbdngGSkht27o2pyGWrVy0b4WAy3jbKmnoK6g5VlVNUUw=="], + + "style-to-js": ["style-to-js@1.1.16", "", { "dependencies": { "style-to-object": "1.0.8" } }, "sha512-/Q6ld50hKYPH3d/r6nr117TZkHR0w0kGGIVfpG9N6D8NymRPM9RqCUv4pRpJ62E5DqOYx2AFpbZMyCPnjQCnOw=="], + + "style-to-object": ["style-to-object@1.0.8", "", { "dependencies": { "inline-style-parser": "0.2.4" } }, "sha512-xT47I/Eo0rwJmaXC4oilDGDWLohVhR6o/xAQcPQN8q6QBuZVL8qMYL85kLmST5cPjAorwvqIA4qXTRQoYHaL6g=="], + + "supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="], + + "svgo": ["svgo@4.0.0", "", { "dependencies": { "commander": "^11.1.0", "css-select": "^5.1.0", "css-tree": "^3.0.1", "css-what": "^6.1.0", "csso": "^5.0.5", "picocolors": "^1.1.1", "sax": "^1.4.1" }, "bin": "./bin/svgo.js" }, "sha512-VvrHQ+9uniE+Mvx3+C9IEe/lWasXCU0nXMY2kZeLrHNICuRiC8uMPyM14UEaMOFA5mhyQqEkB02VoQ16n3DLaw=="], + + "symbol-tree": ["symbol-tree@3.2.4", "", {}, "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw=="], + + "tailwind-merge": ["tailwind-merge@3.4.0", "", {}, "sha512-uSaO4gnW+b3Y2aWoWfFpX62vn2sR3skfhbjsEnaBI81WD1wBLlHZe5sWf0AqjksNdYTbGBEd0UasQMT3SNV15g=="], + + "tailwindcss": ["tailwindcss@4.1.17", "", {}, "sha512-j9Ee2YjuQqYT9bbRTfTZht9W/ytp5H+jJpZKiYdP/bpnXARAuELt9ofP0lPnmHjbga7SNQIxdTAXCmtKVYjN+Q=="], + + "tapable": ["tapable@2.2.2", "", {}, "sha512-Re10+NauLTMCudc7T5WLFLAwDhQ0JWdrMK+9B2M8zR5hRExKmsRDCBA7/aV/pNJFltmBFO5BAMlQFi/vq3nKOg=="], + + "tiny-inflate": ["tiny-inflate@1.0.3", "", {}, "sha512-pkY1fj1cKHb2seWDy0B16HeWyczlJA9/WW3u3c4z/NiWDsO3DOU5D7nhTLE9CF0yXv/QZFY7sEJmj24dK+Rrqw=="], + + "tinybench": ["tinybench@2.9.0", "", {}, "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg=="], + + "tinyexec": ["tinyexec@1.0.2", "", {}, "sha512-W/KYk+NFhkmsYpuHq5JykngiOCnxeVL8v8dFnqxSD8qEEdRfXk1SDM6JzNqcERbcGYj9tMrDQBYV9cjgnunFIg=="], + + "tinyglobby": ["tinyglobby@0.2.15", "", { "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.3" } }, "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ=="], + + "tinyrainbow": ["tinyrainbow@3.0.3", "", {}, "sha512-PSkbLUoxOFRzJYjjxHJt9xro7D+iilgMX/C9lawzVuYiIdcihh9DXmVibBe8lmcFrRi/VzlPjBxbN7rH24q8/Q=="], + + "tldts": ["tldts@7.0.19", "", { "dependencies": { "tldts-core": "^7.0.19" }, "bin": { "tldts": "bin/cli.js" } }, "sha512-8PWx8tvC4jDB39BQw1m4x8y5MH1BcQ5xHeL2n7UVFulMPH/3Q0uiamahFJ3lXA0zO2SUyRXuVVbWSDmstlt9YA=="], + + "tldts-core": ["tldts-core@7.0.19", "", {}, "sha512-lJX2dEWx0SGH4O6p+7FPwYmJ/bu1JbcGJ8RLaG9b7liIgZ85itUVEPbMtWRVrde/0fnDPEPHW10ZsKW3kVsE9A=="], + + "to-regex-range": ["to-regex-range@5.0.1", "", { "dependencies": { "is-number": "^7.0.0" } }, "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ=="], + + "toidentifier": ["toidentifier@1.0.1", "", {}, "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA=="], + + "tough-cookie": ["tough-cookie@6.0.0", "", { "dependencies": { "tldts": "^7.0.5" } }, "sha512-kXuRi1mtaKMrsLUxz3sQYvVl37B0Ns6MzfrtV5DvJceE9bPyspOqk9xxv7XbZWcfLWbFmm997vl83qUWVJA64w=="], + + "tr46": ["tr46@6.0.0", "", { "dependencies": { "punycode": "^2.3.1" } }, "sha512-bLVMLPtstlZ4iMQHpFHTR7GAGj2jxi8Dg0s2h2MafAE4uSWF98FC/3MomU51iQAMf8/qDUbKWf5GxuvvVcXEhw=="], + + "trim-lines": ["trim-lines@3.0.1", "", {}, "sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg=="], + + "trough": ["trough@2.2.0", "", {}, "sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw=="], + + "tsconfck": ["tsconfck@3.1.6", "", { "peerDependencies": { "typescript": "^5.0.0" }, "optionalPeers": ["typescript"], "bin": { "tsconfck": "bin/tsconfck.js" } }, "sha512-ks6Vjr/jEw0P1gmOVwutM3B7fWxoWBL2KRDb1JfqGVawBmO5UsvmWOQFGHBPl5yxYz4eERr19E6L7NMv+Fej4w=="], + + "tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="], + + "tsx": ["tsx@4.21.0", "", { "dependencies": { "esbuild": "~0.27.0", "get-tsconfig": "^4.7.5" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "bin": { "tsx": "dist/cli.mjs" } }, "sha512-5C1sg4USs1lfG0GFb2RLXsdpXqBSEhAaA/0kPL01wxzpMqLILNxIxIOKiILz+cdg/pLnOUxFYOR5yhHU666wbw=="], + + "tw-animate-css": ["tw-animate-css@1.4.0", "", {}, "sha512-7bziOlRqH0hJx80h/3mbicLW7o8qLsH5+RaLR2t+OHM3D0JlWGODQKQ4cxbK7WlvmUxpcj6Kgu6EKqjrGFe3QQ=="], + + "type-fest": ["type-fest@4.41.0", "", {}, "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA=="], + + "typesafe-path": ["typesafe-path@0.2.2", "", {}, "sha512-OJabfkAg1WLZSqJAJ0Z6Sdt3utnbzr/jh+NAHoyWHJe8CMSy79Gm085094M9nvTPy22KzTVn5Zq5mbapCI/hPA=="], + + "typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="], + + "typescript-auto-import-cache": ["typescript-auto-import-cache@0.3.6", "", { "dependencies": { "semver": "^7.3.8" } }, "sha512-RpuHXrknHdVdK7wv/8ug3Fr0WNsNi5l5aB8MYYuXhq2UH5lnEB1htJ1smhtD5VeCsGr2p8mUDtd83LCQDFVgjQ=="], + + "ufo": ["ufo@1.6.1", "", {}, "sha512-9a4/uxlTWJ4+a5i0ooc1rU7C7YOw3wT+UGqdeNNHWnOF9qcMBgLRS+4IYUqbczewFx4mLEig6gawh7X6mFlEkA=="], + + "ultrahtml": ["ultrahtml@1.6.0", "", {}, "sha512-R9fBn90VTJrqqLDwyMph+HGne8eqY1iPfYhPzZrvKpIfwkWZbcYlfpsb8B9dTvBfpy1/hqAD7Wi8EKfP9e8zdw=="], + + "uncrypto": ["uncrypto@0.1.3", "", {}, "sha512-Ql87qFHB3s/De2ClA9e0gsnS6zXG27SkTiSJwjCc9MebbfapQfuPzumMIUMi38ezPZVNFcHI9sUIepeQfw8J8Q=="], + + "undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="], + + "unicode-properties": ["unicode-properties@1.4.1", "", { "dependencies": { "base64-js": "^1.3.0", "unicode-trie": "^2.0.0" } }, "sha512-CLjCCLQ6UuMxWnbIylkisbRj31qxHPAurvena/0iwSVbQ2G1VY5/HjV0IRabOEbDHlzZlRdCrD4NhB0JtU40Pg=="], + + "unicode-trie": ["unicode-trie@2.0.0", "", { "dependencies": { "pako": "^0.2.5", "tiny-inflate": "^1.0.0" } }, "sha512-x7bc76x0bm4prf1VLg79uhAzKw8DVboClSN5VxJuQ+LKDOVEW9CdH+VY7SP+vX7xCYQqzzgQpFqz15zeLvAtZQ=="], + + "unified": ["unified@11.0.5", "", { "dependencies": { "@types/unist": "^3.0.0", "bail": "^2.0.0", "devlop": "^1.0.0", "extend": "^3.0.0", "is-plain-obj": "^4.0.0", "trough": "^2.0.0", "vfile": "^6.0.0" } }, "sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA=="], + + "unifont": ["unifont@0.6.0", "", { "dependencies": { "css-tree": "^3.0.0", "ofetch": "^1.4.1", "ohash": "^2.0.0" } }, "sha512-5Fx50fFQMQL5aeHyWnZX9122sSLckcDvcfFiBf3QYeHa7a1MKJooUy52b67moi2MJYkrfo/TWY+CoLdr/w0tTA=="], + + "unist-util-find-after": ["unist-util-find-after@5.0.0", "", { "dependencies": { "@types/unist": "^3.0.0", "unist-util-is": "^6.0.0" } }, "sha512-amQa0Ep2m6hE2g72AugUItjbuM8X8cGQnFoHk0pGfrFeT9GZhzN5SW8nRsiGKK7Aif4CrACPENkA6P/Lw6fHGQ=="], + + "unist-util-is": ["unist-util-is@6.0.0", "", { "dependencies": { "@types/unist": "^3.0.0" } }, "sha512-2qCTHimwdxLfz+YzdGfkqNlH0tLi9xjTnHddPmJwtIG9MGsdbutfTc4P+haPD7l7Cjxf/WZj+we5qfVPvvxfYw=="], + + "unist-util-modify-children": ["unist-util-modify-children@4.0.0", "", { "dependencies": { "@types/unist": "^3.0.0", "array-iterate": "^2.0.0" } }, "sha512-+tdN5fGNddvsQdIzUF3Xx82CU9sMM+fA0dLgR9vOmT0oPT2jH+P1nd5lSqfCfXAw+93NhcXNY2qqvTUtE4cQkw=="], + + "unist-util-position": ["unist-util-position@5.0.0", "", { "dependencies": { "@types/unist": "^3.0.0" } }, "sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA=="], + + "unist-util-position-from-estree": ["unist-util-position-from-estree@2.0.0", "", { "dependencies": { "@types/unist": "^3.0.0" } }, "sha512-KaFVRjoqLyF6YXCbVLNad/eS4+OfPQQn2yOd7zF/h5T/CSL2v8NpN6a5TPvtbXthAGw5nG+PuTtq+DdIZr+cRQ=="], + + "unist-util-remove-position": ["unist-util-remove-position@5.0.0", "", { "dependencies": { "@types/unist": "^3.0.0", "unist-util-visit": "^5.0.0" } }, "sha512-Hp5Kh3wLxv0PHj9m2yZhhLt58KzPtEYKQQ4yxfYFEO7EvHwzyDYnduhHnY1mDxoqr7VUwVuHXk9RXKIiYS1N8Q=="], + + "unist-util-stringify-position": ["unist-util-stringify-position@4.0.0", "", { "dependencies": { "@types/unist": "^3.0.0" } }, "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ=="], + + "unist-util-visit": ["unist-util-visit@5.0.0", "", { "dependencies": { "@types/unist": "^3.0.0", "unist-util-is": "^6.0.0", "unist-util-visit-parents": "^6.0.0" } }, "sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg=="], + + "unist-util-visit-children": ["unist-util-visit-children@3.0.0", "", { "dependencies": { "@types/unist": "^3.0.0" } }, "sha512-RgmdTfSBOg04sdPcpTSD1jzoNBjt9a80/ZCzp5cI9n1qPzLZWF9YdvWGN2zmTumP1HWhXKdUWexjy/Wy/lJ7tA=="], + + "unist-util-visit-parents": ["unist-util-visit-parents@6.0.2", "", { "dependencies": { "@types/unist": "^3.0.0", "unist-util-is": "^6.0.0" } }, "sha512-goh1s1TBrqSqukSc8wrjwWhL0hiJxgA8m4kFxGlQ+8FYQ3C/m11FcTs4YYem7V664AhHVvgoQLk890Ssdsr2IQ=="], + + "universal-user-agent": ["universal-user-agent@7.0.3", "", {}, "sha512-TmnEAEAsBJVZM/AADELsK76llnwcf9vMKuPz8JflO1frO8Lchitr0fNaN9d+Ap0BjKtqWqd/J17qeDnXh8CL2A=="], + + "unstorage": ["unstorage@1.17.3", "", { "dependencies": { "anymatch": "^3.1.3", "chokidar": "^4.0.3", "destr": "^2.0.5", "h3": "^1.15.4", "lru-cache": "^10.4.3", "node-fetch-native": "^1.6.7", "ofetch": "^1.5.1", "ufo": "^1.6.1" }, "peerDependencies": { "@azure/app-configuration": "^1.8.0", "@azure/cosmos": "^4.2.0", "@azure/data-tables": "^13.3.0", "@azure/identity": "^4.6.0", "@azure/keyvault-secrets": "^4.9.0", "@azure/storage-blob": "^12.26.0", "@capacitor/preferences": "^6.0.3 || ^7.0.0", "@deno/kv": ">=0.9.0", "@netlify/blobs": "^6.5.0 || ^7.0.0 || ^8.1.0 || ^9.0.0 || ^10.0.0", "@planetscale/database": "^1.19.0", "@upstash/redis": "^1.34.3", "@vercel/blob": ">=0.27.1", "@vercel/functions": "^2.2.12 || ^3.0.0", "@vercel/kv": "^1.0.1", "aws4fetch": "^1.0.20", "db0": ">=0.2.1", "idb-keyval": "^6.2.1", "ioredis": "^5.4.2", "uploadthing": "^7.4.4" }, "optionalPeers": ["@azure/app-configuration", "@azure/cosmos", "@azure/data-tables", "@azure/identity", "@azure/keyvault-secrets", "@azure/storage-blob", "@capacitor/preferences", "@deno/kv", "@netlify/blobs", "@planetscale/database", "@upstash/redis", "@vercel/blob", "@vercel/functions", "@vercel/kv", "aws4fetch", "db0", "idb-keyval", "ioredis", "uploadthing"] }, "sha512-i+JYyy0DoKmQ3FximTHbGadmIYb8JEpq7lxUjnjeB702bCPum0vzo6oy5Mfu0lpqISw7hCyMW2yj4nWC8bqJ3Q=="], + + "update-browserslist-db": ["update-browserslist-db@1.1.3", "", { "dependencies": { "escalade": "^3.2.0", "picocolors": "^1.1.1" }, "peerDependencies": { "browserslist": ">= 4.21.0" }, "bin": { "update-browserslist-db": "cli.js" } }, "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw=="], + + "use-callback-ref": ["use-callback-ref@1.3.3", "", { "dependencies": { "tslib": "^2.0.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-jQL3lRnocaFtu3V00JToYz/4QkNWswxijDaCVNZRiRTO3HQDLsdu1ZtmIUvV4yPp+rvWm5j0y0TG/S61cuijTg=="], + + "use-sidecar": ["use-sidecar@1.1.3", "", { "dependencies": { "detect-node-es": "^1.1.0", "tslib": "^2.0.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-Fedw0aZvkhynoPYlA5WXrMCAMm+nSWdZt6lzJQ7Ok8S6Q+VsHmHpRWndVRJ8Be0ZbkfPc5LRYH+5XrzXcEeLRQ=="], + + "use-sync-external-store": ["use-sync-external-store@1.5.0", "", { "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-Rb46I4cGGVBmjamjphe8L/UnvJD+uPPtTkNvX5mZgqdbavhI4EbgIWJiIHXJ8bc/i9EQGPRh4DwEURJ552Do0A=="], + + "uuid": ["uuid@13.0.0", "", { "bin": { "uuid": "dist-node/bin/uuid" } }, "sha512-XQegIaBTVUjSHliKqcnFqYypAd4S+WCYt5NIeRs6w/UAry7z8Y9j5ZwRRL4kzq9U3sD6v+85er9FvkEaBpji2w=="], + + "vaul": ["vaul@1.1.2", "", { "dependencies": { "@radix-ui/react-dialog": "^1.1.1" }, "peerDependencies": { "react": "^16.8 || ^17.0 || ^18.0 || ^19.0.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0.0 || ^19.0.0-rc" } }, "sha512-ZFkClGpWyI2WUQjdLJ/BaGuV6AVQiJ3uELGk3OYtP+B6yCO7Cmn9vPFXVJkRaGkOJu3m8bQMgtyzNHixULceQA=="], + + "vfile": ["vfile@6.0.3", "", { "dependencies": { "@types/unist": "^3.0.0", "vfile-message": "^4.0.0" } }, "sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q=="], + + "vfile-location": ["vfile-location@5.0.3", "", { "dependencies": { "@types/unist": "^3.0.0", "vfile": "^6.0.0" } }, "sha512-5yXvWDEgqeiYiBe1lbxYF7UMAIm/IcopxMHrMQDq3nvKcjPKIhZklUKL+AE7J7uApI4kwe2snsK+eI6UTj9EHg=="], + + "vfile-message": ["vfile-message@4.0.2", "", { "dependencies": { "@types/unist": "^3.0.0", "unist-util-stringify-position": "^4.0.0" } }, "sha512-jRDZ1IMLttGj41KcZvlrYAaI3CfqpLpfpf+Mfig13viT6NKvRzWZ+lXz0Y5D60w6uJIBAOGq9mSHf0gktF0duw=="], + + "vite": ["vite@6.4.1", "", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.4.4", "picomatch": "^4.0.2", "postcss": "^8.5.3", "rollup": "^4.34.9", "tinyglobby": "^0.2.13" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", "jiti": ">=1.21.0", "less": "*", "lightningcss": "^1.21.0", "sass": "*", "sass-embedded": "*", "stylus": "*", "sugarss": "*", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-+Oxm7q9hDoLMyJOYfUYBuHQo+dkAloi33apOPP56pzj+vsdJDzr+j1NISE5pyaAuKL4A3UD34qd0lx5+kfKp2g=="], + + "vitefu": ["vitefu@1.1.1", "", { "peerDependencies": { "vite": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0-beta.0" }, "optionalPeers": ["vite"] }, "sha512-B/Fegf3i8zh0yFbpzZ21amWzHmuNlLlmJT6n7bu5e+pCHUKQIfXSYokrqOBGEMMe9UG2sostKQF9mml/vYaWJQ=="], + + "vitest": ["vitest@4.0.15", "", { "dependencies": { "@vitest/expect": "4.0.15", "@vitest/mocker": "4.0.15", "@vitest/pretty-format": "4.0.15", "@vitest/runner": "4.0.15", "@vitest/snapshot": "4.0.15", "@vitest/spy": "4.0.15", "@vitest/utils": "4.0.15", "es-module-lexer": "^1.7.0", "expect-type": "^1.2.2", "magic-string": "^0.30.21", "obug": "^2.1.1", "pathe": "^2.0.3", "picomatch": "^4.0.3", "std-env": "^3.10.0", "tinybench": "^2.9.0", "tinyexec": "^1.0.2", "tinyglobby": "^0.2.15", "tinyrainbow": "^3.0.3", "vite": "^6.0.0 || ^7.0.0", "why-is-node-running": "^2.3.0" }, "peerDependencies": { "@edge-runtime/vm": "*", "@opentelemetry/api": "^1.9.0", "@types/node": "^20.0.0 || ^22.0.0 || >=24.0.0", "@vitest/browser-playwright": "4.0.15", "@vitest/browser-preview": "4.0.15", "@vitest/browser-webdriverio": "4.0.15", "@vitest/ui": "4.0.15", "happy-dom": "*", "jsdom": "*" }, "optionalPeers": ["@edge-runtime/vm", "@opentelemetry/api", "@types/node", "@vitest/browser-playwright", "@vitest/browser-preview", "@vitest/browser-webdriverio", "@vitest/ui", "happy-dom", "jsdom"], "bin": { "vitest": "vitest.mjs" } }, "sha512-n1RxDp8UJm6N0IbJLQo+yzLZ2sQCDyl1o0LeugbPWf8+8Fttp29GghsQBjYJVmWq3gBFfe9Hs1spR44vovn2wA=="], + + "volar-service-css": ["volar-service-css@0.0.67", "", { "dependencies": { "vscode-css-languageservice": "^6.3.0", "vscode-languageserver-textdocument": "^1.0.11", "vscode-uri": "^3.0.8" }, "peerDependencies": { "@volar/language-service": "~2.4.0" }, "optionalPeers": ["@volar/language-service"] }, "sha512-zV7C6enn9T9tuvQ6iSUyYEs34iPXR69Pf9YYWpbFYPWzVs22w96BtE8p04XYXbmjU6unt5oFt+iLL77bMB5fhA=="], + + "volar-service-emmet": ["volar-service-emmet@0.0.67", "", { "dependencies": { "@emmetio/css-parser": "^0.4.1", "@emmetio/html-matcher": "^1.3.0", "@vscode/emmet-helper": "^2.9.3", "vscode-uri": "^3.0.8" }, "peerDependencies": { "@volar/language-service": "~2.4.0" }, "optionalPeers": ["@volar/language-service"] }, "sha512-UDBL5x7KptmuJZNCCXMlCndMhFult/tj+9jXq3FH1ZGS1E4M/1U5hC06pg1c6e4kn+vnR6bqmvX0vIhL4f98+A=="], + + "volar-service-html": ["volar-service-html@0.0.67", "", { "dependencies": { "vscode-html-languageservice": "^5.3.0", "vscode-languageserver-textdocument": "^1.0.11", "vscode-uri": "^3.0.8" }, "peerDependencies": { "@volar/language-service": "~2.4.0" }, "optionalPeers": ["@volar/language-service"] }, "sha512-ljREMF79JbcjNvObiv69HK2HCl5UT7WTD10zi6CRFUHMbPfiF2UZ42HGLsEGSzaHGZz6H4IFjSS/qfENRLUviQ=="], + + "volar-service-prettier": ["volar-service-prettier@0.0.67", "", { "dependencies": { "vscode-uri": "^3.0.8" }, "peerDependencies": { "@volar/language-service": "~2.4.0", "prettier": "^2.2 || ^3.0" }, "optionalPeers": ["@volar/language-service", "prettier"] }, "sha512-B4KnPJPNWFTkEDa6Fn08i5PpO6T1CecmLLTFZoXz2eI4Fxwba/3nDaaVSsEP7e/vEe+U5YqV9fBzayJT71G5xg=="], + + "volar-service-typescript": ["volar-service-typescript@0.0.67", "", { "dependencies": { "path-browserify": "^1.0.1", "semver": "^7.6.2", "typescript-auto-import-cache": "^0.3.5", "vscode-languageserver-textdocument": "^1.0.11", "vscode-nls": "^5.2.0", "vscode-uri": "^3.0.8" }, "peerDependencies": { "@volar/language-service": "~2.4.0" }, "optionalPeers": ["@volar/language-service"] }, "sha512-rfQBy36Rm1PU9vLWHk8BYJ4r2j/CI024vocJcH4Nb6K2RTc2Irmw6UOVY5DdGiPRV5r+e10wLMK5njj/EcL8sA=="], + + "volar-service-typescript-twoslash-queries": ["volar-service-typescript-twoslash-queries@0.0.67", "", { "dependencies": { "vscode-uri": "^3.0.8" }, "peerDependencies": { "@volar/language-service": "~2.4.0" }, "optionalPeers": ["@volar/language-service"] }, "sha512-LD2R7WivDYp1SPgZrxx/0222xVTitDjm36oKo5+bfYG5kEgnw+BOPVHdwmvpJKg/RfssfxDI1ouwD4XkEDEfbA=="], + + "volar-service-yaml": ["volar-service-yaml@0.0.67", "", { "dependencies": { "vscode-uri": "^3.0.8", "yaml-language-server": "~1.19.2" }, "peerDependencies": { "@volar/language-service": "~2.4.0" }, "optionalPeers": ["@volar/language-service"] }, "sha512-jkdP/RF6wPIXEE3Ktnd81oJPn7aAvnVSiaqQHThC2Hrvo6xd9pEcqtbBUI+YfqVgvcMtXAkbtNO61K2GPhAiuA=="], + + "vscode-css-languageservice": ["vscode-css-languageservice@6.3.6", "", { "dependencies": { "@vscode/l10n": "^0.0.18", "vscode-languageserver-textdocument": "^1.0.12", "vscode-languageserver-types": "3.17.5", "vscode-uri": "^3.1.0" } }, "sha512-fU4h8mT3KlvfRcbF74v/M+Gzbligav6QMx4AD/7CbclWPYOpGb9kgIswfpZVJbIcOEJJACI9iYizkNwdiAqlHw=="], + + "vscode-html-languageservice": ["vscode-html-languageservice@5.6.1", "", { "dependencies": { "@vscode/l10n": "^0.0.18", "vscode-languageserver-textdocument": "^1.0.12", "vscode-languageserver-types": "^3.17.5", "vscode-uri": "^3.1.0" } }, "sha512-5Mrqy5CLfFZUgkyhNZLA1Ye5g12Cb/v6VM7SxUzZUaRKWMDz4md+y26PrfRTSU0/eQAl3XpO9m2og+GGtDMuaA=="], + + "vscode-json-languageservice": ["vscode-json-languageservice@4.1.8", "", { "dependencies": { "jsonc-parser": "^3.0.0", "vscode-languageserver-textdocument": "^1.0.1", "vscode-languageserver-types": "^3.16.0", "vscode-nls": "^5.0.0", "vscode-uri": "^3.0.2" } }, "sha512-0vSpg6Xd9hfV+eZAaYN63xVVMOTmJ4GgHxXnkLCh+9RsQBkWKIghzLhW2B9ebfG+LQQg8uLtsQ2aUKjTgE+QOg=="], + + "vscode-jsonrpc": ["vscode-jsonrpc@8.2.0", "", {}, "sha512-C+r0eKJUIfiDIfwJhria30+TYWPtuHJXHtI7J0YlOmKAo7ogxP20T0zxB7HZQIFhIyvoBPwWskjxrvAtfjyZfA=="], + + "vscode-languageserver": ["vscode-languageserver@9.0.1", "", { "dependencies": { "vscode-languageserver-protocol": "3.17.5" }, "bin": { "installServerIntoExtension": "bin/installServerIntoExtension" } }, "sha512-woByF3PDpkHFUreUa7Hos7+pUWdeWMXRd26+ZX2A8cFx6v/JPTtd4/uN0/jB6XQHYaOlHbio03NTHCqrgG5n7g=="], + + "vscode-languageserver-protocol": ["vscode-languageserver-protocol@3.17.5", "", { "dependencies": { "vscode-jsonrpc": "8.2.0", "vscode-languageserver-types": "3.17.5" } }, "sha512-mb1bvRJN8SVznADSGWM9u/b07H7Ecg0I3OgXDuLdn307rl/J3A9YD6/eYOssqhecL27hK1IPZAsaqh00i/Jljg=="], + + "vscode-languageserver-textdocument": ["vscode-languageserver-textdocument@1.0.12", "", {}, "sha512-cxWNPesCnQCcMPeenjKKsOCKQZ/L6Tv19DTRIGuLWe32lyzWhihGVJ/rcckZXJxfdKCFvRLS3fpBIsV/ZGX4zA=="], + + "vscode-languageserver-types": ["vscode-languageserver-types@3.17.5", "", {}, "sha512-Ld1VelNuX9pdF39h2Hgaeb5hEZM2Z3jUrrMgWQAu82jMtZp7p3vJT3BzToKtZI7NgQssZje5o0zryOrhQvzQAg=="], + + "vscode-nls": ["vscode-nls@5.2.0", "", {}, "sha512-RAaHx7B14ZU04EU31pT+rKz2/zSl7xMsfIZuo8pd+KZO6PXtQmpevpq3vxvWNcrGbdmhM/rr5Uw5Mz+NBfhVng=="], + + "vscode-uri": ["vscode-uri@3.1.0", "", {}, "sha512-/BpdSx+yCQGnCvecbyXdxHDkuk55/G3xwnC0GqY4gmQ3j+A+g8kzzgB4Nk/SINjqn6+waqw3EgbVF2QKExkRxQ=="], + + "w3c-xmlserializer": ["w3c-xmlserializer@5.0.0", "", { "dependencies": { "xml-name-validator": "^5.0.0" } }, "sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA=="], + + "web-namespaces": ["web-namespaces@2.0.1", "", {}, "sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ=="], + + "webidl-conversions": ["webidl-conversions@8.0.0", "", {}, "sha512-n4W4YFyz5JzOfQeA8oN7dUYpR+MBP3PIUsn2jLjWXwK5ASUzt0Jc/A5sAUZoCYFJRGF0FBKJ+1JjN43rNdsQzA=="], + + "whatwg-encoding": ["whatwg-encoding@3.1.1", "", { "dependencies": { "iconv-lite": "0.6.3" } }, "sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ=="], + + "whatwg-mimetype": ["whatwg-mimetype@4.0.0", "", {}, "sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg=="], + + "whatwg-url": ["whatwg-url@15.1.0", "", { "dependencies": { "tr46": "^6.0.0", "webidl-conversions": "^8.0.0" } }, "sha512-2ytDk0kiEj/yu90JOAp44PVPUkO9+jVhyf+SybKlRHSDlvOOZhdPIrr7xTH64l4WixO2cP+wQIcgujkGBPPz6g=="], + + "which-pm-runs": ["which-pm-runs@1.1.0", "", {}, "sha512-n1brCuqClxfFfq/Rb0ICg9giSZqCS+pLtccdag6C2HyufBrh3fBOiy9nb6ggRMvWOVH5GrdJskj5iGTZNxd7SA=="], + + "why-is-node-running": ["why-is-node-running@2.3.0", "", { "dependencies": { "siginfo": "^2.0.0", "stackback": "0.0.2" }, "bin": { "why-is-node-running": "cli.js" } }, "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w=="], + + "widest-line": ["widest-line@5.0.0", "", { "dependencies": { "string-width": "^7.0.0" } }, "sha512-c9bZp7b5YtRj2wOe6dlj32MK+Bx/M/d+9VB2SHM1OtsUHR0aV0tdP6DWh/iMt0kWi1t5g1Iudu6hQRNd1A4PVA=="], + + "wrap-ansi": ["wrap-ansi@9.0.0", "", { "dependencies": { "ansi-styles": "^6.2.1", "string-width": "^7.0.0", "strip-ansi": "^7.1.0" } }, "sha512-G8ura3S+3Z2G+mkgNRq8dqaFZAuxfsxpBB8OCTGRTCtp+l/v9nbFNmCUP1BZMts3G1142MsZfn6eeUKrr4PD1Q=="], + + "ws": ["ws@8.18.3", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg=="], + + "xml": ["xml@1.0.1", "", {}, "sha512-huCv9IH9Tcf95zuYCsQraZtWnJvBtLVE0QHMOs8bWyZAFZNDcYjsPq1nEx8jKA9y+Beo9v+7OBPRisQTjinQMw=="], + + "xml-crypto": ["xml-crypto@6.1.2", "", { "dependencies": { "@xmldom/is-dom-node": "^1.0.1", "@xmldom/xmldom": "^0.8.10", "xpath": "^0.0.33" } }, "sha512-leBOVQdVi8FvPJrMYoum7Ici9qyxfE4kVi+AkpUoYCSXaQF4IlBm1cneTK9oAxR61LpYxTx7lNcsnBIeRpGW2w=="], + + "xml-escape": ["xml-escape@1.1.0", "", {}, "sha512-B/T4sDK8Z6aUh/qNr7mjKAwwncIljFuUP+DO/D5hloYFj+90O88z8Wf7oSucZTHxBAsC1/CTP4rtx/x1Uf72Mg=="], + + "xml-name-validator": ["xml-name-validator@5.0.0", "", {}, "sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg=="], + + "xmlchars": ["xmlchars@2.2.0", "", {}, "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw=="], + + "xpath": ["xpath@0.0.32", "", {}, "sha512-rxMJhSIoiO8vXcWvSifKqhvV96GjiD5wYb8/QHdoRyQvraTpp4IEv944nhGausZZ3u7dhQXteZuZbaqfpB7uYw=="], + + "xxhash-wasm": ["xxhash-wasm@1.1.0", "", {}, "sha512-147y/6YNh+tlp6nd/2pWq38i9h6mz/EuQ6njIrmW8D1BS5nCqs0P6DG+m6zTGnNz5I+uhZ0SHxBs9BsPrwcKDA=="], + + "y18n": ["y18n@5.0.8", "", {}, "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA=="], + + "yallist": ["yallist@3.1.1", "", {}, "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g=="], + + "yaml": ["yaml@2.8.0", "", { "bin": { "yaml": "bin.mjs" } }, "sha512-4lLa/EcQCB0cJkyts+FpIRx5G/llPxfP6VQU5KByHEhLxY3IJCH0f0Hy1MHI8sClTvsIb8qwRJ6R/ZdlDJ/leQ=="], + + "yaml-language-server": ["yaml-language-server@1.19.2", "", { "dependencies": { "@vscode/l10n": "^0.0.18", "ajv": "^8.17.1", "ajv-draft-04": "^1.0.0", "lodash": "4.17.21", "prettier": "^3.5.0", "request-light": "^0.5.7", "vscode-json-languageservice": "4.1.8", "vscode-languageserver": "^9.0.0", "vscode-languageserver-textdocument": "^1.0.1", "vscode-languageserver-types": "^3.16.0", "vscode-uri": "^3.0.2", "yaml": "2.7.1" }, "bin": { "yaml-language-server": "bin/yaml-language-server" } }, "sha512-9F3myNmJzUN/679jycdMxqtydPSDRAarSj3wPiF7pchEPnO9Dg07Oc+gIYLqXR4L+g+FSEVXXv2+mr54StLFOg=="], + + "yargs": ["yargs@17.7.2", "", { "dependencies": { "cliui": "^8.0.1", "escalade": "^3.1.1", "get-caller-file": "^2.0.5", "require-directory": "^2.1.1", "string-width": "^4.2.3", "y18n": "^5.0.5", "yargs-parser": "^21.1.1" } }, "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w=="], + + "yargs-parser": ["yargs-parser@21.1.1", "", {}, "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw=="], + + "yocto-queue": ["yocto-queue@1.2.1", "", {}, "sha512-AyeEbWOu/TAXdxlV9wmGcR0+yh2j3vYPGOECcIj2S7MkrLyC7ne+oye2BKTItt0ii2PHk4cDy+95+LshzbXnGg=="], + + "yocto-spinner": ["yocto-spinner@0.2.3", "", { "dependencies": { "yoctocolors": "^2.1.1" } }, "sha512-sqBChb33loEnkoXte1bLg45bEBsOP9N1kzQh5JZNKj/0rik4zAPTNSAVPj3uQAdc6slYJ0Ksc403G2XgxsJQFQ=="], + + "yoctocolors": ["yoctocolors@2.1.1", "", {}, "sha512-GQHQqAopRhwU8Kt1DDM8NjibDXHC8eoh1erhGAJPEyveY9qqVeXvVikNKrDz69sHowPMorbPUrH/mx8c50eiBQ=="], + + "zod": ["zod@4.1.13", "", {}, "sha512-AvvthqfqrAhNH9dnfmrfKzX5upOdjUVJYFqNSlkmGf64gRaTzlPwz99IHYnVs28qYAybvAlBV+H7pn0saFY4Ig=="], + + "zod-to-json-schema": ["zod-to-json-schema@3.25.0", "", { "peerDependencies": { "zod": "^3.25 || ^4" } }, "sha512-HvWtU2UG41LALjajJrML6uQejQhNJx+JBO9IflpSja4R03iNWfKXrj6W2h7ljuLyc1nKS+9yDyL/9tD1U/yBnQ=="], + + "zod-to-ts": ["zod-to-ts@1.2.0", "", { "peerDependencies": { "typescript": "^4.9.4 || ^5.0.2", "zod": "^3" } }, "sha512-x30XE43V+InwGpvTySRNz9kB7qFU8DlyEy7BsSTCHPH1R0QasMmHWZDCzYm6bVXtj/9NNJAZF3jW8rzFvH5OFA=="], + + "zwitch": ["zwitch@2.0.4", "", {}, "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A=="], + + "@ampproject/remapping/@jridgewell/gen-mapping": ["@jridgewell/gen-mapping@0.3.8", "", { "dependencies": { "@jridgewell/set-array": "^1.2.1", "@jridgewell/sourcemap-codec": "^1.4.10", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA=="], + + "@ampproject/remapping/@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.25", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ=="], + + "@astrojs/react/@vitejs/plugin-react": ["@vitejs/plugin-react@4.7.0", "", { "dependencies": { "@babel/core": "^7.28.0", "@babel/plugin-transform-react-jsx-self": "^7.27.1", "@babel/plugin-transform-react-jsx-source": "^7.27.1", "@rolldown/pluginutils": "1.0.0-beta.27", "@types/babel__core": "^7.20.5", "react-refresh": "^0.17.0" }, "peerDependencies": { "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0" } }, "sha512-gUu9hwfWvvEDBBmgtAowQCojwZmJ5mcLn3aufeCsitijs3+f2NsrPtlAWIR6OPiqljl96GVCUbLe0HyqIpVaoA=="], + + "@astrojs/telemetry/ci-info": ["ci-info@4.2.0", "", {}, "sha512-cYY9mypksY8NRqgDB1XD1RiJL338v/551niynFTGkZOO2LHuB2OmOYxDIe/ttN9AHwrqdum1360G3ald0W9kCg=="], + + "@astrojs/telemetry/debug": ["debug@4.4.1", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ=="], + + "@babel/core/semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="], + + "@babel/helper-compilation-targets/lru-cache": ["lru-cache@5.1.1", "", { "dependencies": { "yallist": "^3.0.2" } }, "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w=="], + + "@babel/helper-compilation-targets/semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="], + + "@babel/helper-module-imports/@babel/traverse": ["@babel/traverse@7.27.3", "", { "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/generator": "^7.27.3", "@babel/parser": "^7.27.3", "@babel/template": "^7.27.2", "@babel/types": "^7.27.3", "debug": "^4.3.1", "globals": "^11.1.0" } }, "sha512-lId/IfN/Ye1CIu8xG7oKBHXd2iNb2aW1ilPszzGcJug6M8RCKfVNcYhpI5+bMvFYjK7lXIM0R+a+6r8xhHp2FQ=="], + + "@babel/helper-module-imports/@babel/types": ["@babel/types@7.27.3", "", { "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.27.1" } }, "sha512-Y1GkI4ktrtvmawoSq+4FCVHNryea6uR+qUQy0AGxLSsjCX0nVmkYQMBLHDkXZuo5hGx7eYdnIaslsdBFm7zbUw=="], + + "@babel/helper-module-transforms/@babel/traverse": ["@babel/traverse@7.28.3", "", { "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/generator": "^7.28.3", "@babel/helper-globals": "^7.28.0", "@babel/parser": "^7.28.3", "@babel/template": "^7.27.2", "@babel/types": "^7.28.2", "debug": "^4.3.1" } }, "sha512-7w4kZYHneL3A6NP2nxzHvT3HCZ7puDZZjFMqDpBPECub79sTtSO5CGXDkKrTQq8ksAwfD/XI2MRFX23njdDaIQ=="], + + "@babel/helpers/@babel/types": ["@babel/types@7.28.4", "", { "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.27.1" } }, "sha512-bkFqkLhh3pMBUQQkpVgWDWq/lqzc2678eUyDlTBhRqhCHFguYYGM0Efga7tYk4TogG/3x0EEl66/OQ+WGbWB/Q=="], + + "@babel/template/@babel/parser": ["@babel/parser@7.27.3", "", { "dependencies": { "@babel/types": "^7.27.3" }, "bin": "./bin/babel-parser.js" }, "sha512-xyYxRj6+tLNDTWi0KCBcZ9V7yg3/lwL9DWh9Uwh/RIVlIfFidggcgxKX3GCXwCiswwcGRawBKbEg2LG/Y8eJhw=="], + + "@babel/template/@babel/types": ["@babel/types@7.27.3", "", { "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.27.1" } }, "sha512-Y1GkI4ktrtvmawoSq+4FCVHNryea6uR+qUQy0AGxLSsjCX0nVmkYQMBLHDkXZuo5hGx7eYdnIaslsdBFm7zbUw=="], + + "@babel/types/@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.28.5", "", {}, "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q=="], + + "@esbuild-kit/esm-loader/esbuild": ["esbuild@0.27.1", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.27.1", "@esbuild/android-arm": "0.27.1", "@esbuild/android-arm64": "0.27.1", "@esbuild/android-x64": "0.27.1", "@esbuild/darwin-arm64": "0.27.1", "@esbuild/darwin-x64": "0.27.1", "@esbuild/freebsd-arm64": "0.27.1", "@esbuild/freebsd-x64": "0.27.1", "@esbuild/linux-arm": "0.27.1", "@esbuild/linux-arm64": "0.27.1", "@esbuild/linux-ia32": "0.27.1", "@esbuild/linux-loong64": "0.27.1", "@esbuild/linux-mips64el": "0.27.1", "@esbuild/linux-ppc64": "0.27.1", "@esbuild/linux-riscv64": "0.27.1", "@esbuild/linux-s390x": "0.27.1", "@esbuild/linux-x64": "0.27.1", "@esbuild/netbsd-arm64": "0.27.1", "@esbuild/netbsd-x64": "0.27.1", "@esbuild/openbsd-arm64": "0.27.1", "@esbuild/openbsd-x64": "0.27.1", "@esbuild/openharmony-arm64": "0.27.1", "@esbuild/sunos-x64": "0.27.1", "@esbuild/win32-arm64": "0.27.1", "@esbuild/win32-ia32": "0.27.1", "@esbuild/win32-x64": "0.27.1" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-yY35KZckJJuVVPXpvjgxiCuVEJT67F6zDeVTv4rizyPrfGBUpZQsvmxnN+C371c2esD/hNMjj4tpBhuueLN7aA=="], + + "@jridgewell/gen-mapping/@jridgewell/sourcemap-codec": ["@jridgewell/sourcemap-codec@1.5.0", "", {}, "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ=="], + + "@jridgewell/trace-mapping/@jridgewell/sourcemap-codec": ["@jridgewell/sourcemap-codec@1.5.0", "", {}, "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ=="], + + "@radix-ui/react-avatar/@radix-ui/react-context": ["@radix-ui/react-context@1.1.3", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-ieIFACdMpYfMEjF0rEf5KLvfVyIkOz6PDGyNnP+u+4xQ6jny3VCgA4OgXOwNx2aUkxn8zx9fiVcM8CfFYv9Lxw=="], + + "@radix-ui/react-avatar/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.4", "", { "dependencies": { "@radix-ui/react-slot": "1.2.4" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-9hQc4+GNVtJAIEPEqlYqW5RiYdrr8ea5XQ0ZOnD6fgru+83kqT15mq2OCcbe8KnjRZl5vF3ks69AKz3kh1jrhg=="], + + "@radix-ui/react-collection/@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.3", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A=="], + + "@radix-ui/react-dialog/@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.3", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A=="], + + "@radix-ui/react-label/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.4", "", { "dependencies": { "@radix-ui/react-slot": "1.2.4" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-9hQc4+GNVtJAIEPEqlYqW5RiYdrr8ea5XQ0ZOnD6fgru+83kqT15mq2OCcbe8KnjRZl5vF3ks69AKz3kh1jrhg=="], + + "@radix-ui/react-menu/@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.3", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A=="], + + "@radix-ui/react-popover/@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.3", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A=="], + + "@radix-ui/react-primitive/@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.3", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A=="], + + "@radix-ui/react-progress/@radix-ui/react-context": ["@radix-ui/react-context@1.1.3", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-ieIFACdMpYfMEjF0rEf5KLvfVyIkOz6PDGyNnP+u+4xQ6jny3VCgA4OgXOwNx2aUkxn8zx9fiVcM8CfFYv9Lxw=="], + + "@radix-ui/react-progress/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.4", "", { "dependencies": { "@radix-ui/react-slot": "1.2.4" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-9hQc4+GNVtJAIEPEqlYqW5RiYdrr8ea5XQ0ZOnD6fgru+83kqT15mq2OCcbe8KnjRZl5vF3ks69AKz3kh1jrhg=="], + + "@radix-ui/react-select/@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.3", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A=="], + + "@radix-ui/react-separator/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.4", "", { "dependencies": { "@radix-ui/react-slot": "1.2.4" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-9hQc4+GNVtJAIEPEqlYqW5RiYdrr8ea5XQ0ZOnD6fgru+83kqT15mq2OCcbe8KnjRZl5vF3ks69AKz3kh1jrhg=="], + + "@radix-ui/react-tooltip/@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.3", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A=="], + + "@rollup/pluginutils/estree-walker": ["estree-walker@2.0.2", "", {}, "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="], + + "@tailwindcss/node/jiti": ["jiti@2.6.1", "", { "bin": { "jiti": "lib/jiti-cli.mjs" } }, "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ=="], + + "@tailwindcss/node/lightningcss": ["lightningcss@1.30.2", "", { "dependencies": { "detect-libc": "^2.0.3" }, "optionalDependencies": { "lightningcss-android-arm64": "1.30.2", "lightningcss-darwin-arm64": "1.30.2", "lightningcss-darwin-x64": "1.30.2", "lightningcss-freebsd-x64": "1.30.2", "lightningcss-linux-arm-gnueabihf": "1.30.2", "lightningcss-linux-arm64-gnu": "1.30.2", "lightningcss-linux-arm64-musl": "1.30.2", "lightningcss-linux-x64-gnu": "1.30.2", "lightningcss-linux-x64-musl": "1.30.2", "lightningcss-win32-arm64-msvc": "1.30.2", "lightningcss-win32-x64-msvc": "1.30.2" } }, "sha512-utfs7Pr5uJyyvDETitgsaqSyjCb2qNRAtuqUeWIAKztsOYdcACf2KtARYXg2pSvhkt+9NfoaNY7fxjl6nuMjIQ=="], + + "@tailwindcss/oxide-wasm32-wasi/@emnapi/core": ["@emnapi/core@1.6.0", "", { "dependencies": { "@emnapi/wasi-threads": "1.1.0", "tslib": "^2.4.0" }, "bundled": true }, "sha512-zq/ay+9fNIJJtJiZxdTnXS20PllcYMX3OE23ESc4HK/bdYu3cOWYVhsOhVnXALfU/uqJIxn5NBPd9z4v+SfoSg=="], + + "@tailwindcss/oxide-wasm32-wasi/@emnapi/runtime": ["@emnapi/runtime@1.6.0", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-obtUmAHTMjll499P+D9A3axeJFlhdjOWdKUNs/U6QIGT7V5RjcUW1xToAzjvmgTSQhDbYn/NwfTRoJcQ2rNBxA=="], + + "@tailwindcss/oxide-wasm32-wasi/@emnapi/wasi-threads": ["@emnapi/wasi-threads@1.1.0", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-WI0DdZ8xFSbgMjR1sFsKABJ/C5OnRrjT06JXbZKexJGrDuPTzZdDYfFlsgcCXCyf+suG5QU2e/y1Wo2V/OapLQ=="], + + "@tailwindcss/oxide-wasm32-wasi/@napi-rs/wasm-runtime": ["@napi-rs/wasm-runtime@1.0.7", "", { "dependencies": { "@emnapi/core": "^1.5.0", "@emnapi/runtime": "^1.5.0", "@tybys/wasm-util": "^0.10.1" }, "bundled": true }, "sha512-SeDnOO0Tk7Okiq6DbXmmBODgOAb9dp9gjlphokTUxmt8U3liIP1ZsozBahH69j/RJv+Rfs6IwUKHTgQYJ/HBAw=="], + + "@tailwindcss/oxide-wasm32-wasi/@tybys/wasm-util": ["@tybys/wasm-util@0.10.1", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg=="], + + "@tailwindcss/oxide-wasm32-wasi/tslib": ["tslib@2.8.1", "", { "bundled": true }, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="], + + "@testing-library/dom/aria-query": ["aria-query@5.3.0", "", { "dependencies": { "dequal": "^2.0.3" } }, "sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A=="], + + "@testing-library/dom/dom-accessibility-api": ["dom-accessibility-api@0.5.16", "", {}, "sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg=="], + + "@types/babel__core/@babel/parser": ["@babel/parser@7.27.3", "", { "dependencies": { "@babel/types": "^7.27.3" }, "bin": "./bin/babel-parser.js" }, "sha512-xyYxRj6+tLNDTWi0KCBcZ9V7yg3/lwL9DWh9Uwh/RIVlIfFidggcgxKX3GCXwCiswwcGRawBKbEg2LG/Y8eJhw=="], + + "@types/babel__core/@babel/types": ["@babel/types@7.27.3", "", { "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.27.1" } }, "sha512-Y1GkI4ktrtvmawoSq+4FCVHNryea6uR+qUQy0AGxLSsjCX0nVmkYQMBLHDkXZuo5hGx7eYdnIaslsdBFm7zbUw=="], + + "@types/babel__generator/@babel/types": ["@babel/types@7.27.3", "", { "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.27.1" } }, "sha512-Y1GkI4ktrtvmawoSq+4FCVHNryea6uR+qUQy0AGxLSsjCX0nVmkYQMBLHDkXZuo5hGx7eYdnIaslsdBFm7zbUw=="], + + "@types/babel__template/@babel/parser": ["@babel/parser@7.27.3", "", { "dependencies": { "@babel/types": "^7.27.3" }, "bin": "./bin/babel-parser.js" }, "sha512-xyYxRj6+tLNDTWi0KCBcZ9V7yg3/lwL9DWh9Uwh/RIVlIfFidggcgxKX3GCXwCiswwcGRawBKbEg2LG/Y8eJhw=="], + + "@types/babel__template/@babel/types": ["@babel/types@7.27.3", "", { "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.27.1" } }, "sha512-Y1GkI4ktrtvmawoSq+4FCVHNryea6uR+qUQy0AGxLSsjCX0nVmkYQMBLHDkXZuo5hGx7eYdnIaslsdBFm7zbUw=="], + + "@types/babel__traverse/@babel/types": ["@babel/types@7.27.3", "", { "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.27.1" } }, "sha512-Y1GkI4ktrtvmawoSq+4FCVHNryea6uR+qUQy0AGxLSsjCX0nVmkYQMBLHDkXZuo5hGx7eYdnIaslsdBFm7zbUw=="], + + "@types/bcryptjs/bcryptjs": ["bcryptjs@3.0.2", "", { "bin": { "bcrypt": "bin/bcrypt" } }, "sha512-k38b3XOZKv60C4E2hVsXTolJWfkGRMbILBIe2IBITXciy5bOsTKot5kDrf3ZfufQtQOUN5mXceUEpU1rTl9Uog=="], + + "anymatch/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="], + + "astro/zod": ["zod@3.25.76", "", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="], + + "boxen/camelcase": ["camelcase@8.0.0", "", {}, "sha512-8WB3Jcas3swSvjIeA2yvCJ+Miyz5l1ZmB6HFb9R1317dt9LCQoswg/BGrmAmkWVEszSrrg4RwmO46qIm2OEnSA=="], + + "boxen/chalk": ["chalk@5.4.1", "", {}, "sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w=="], + + "boxen/string-width": ["string-width@7.2.0", "", { "dependencies": { "emoji-regex": "^10.3.0", "get-east-asian-width": "^1.0.0", "strip-ansi": "^7.1.0" } }, "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ=="], + + "cliui/wrap-ansi": ["wrap-ansi@7.0.0", "", { "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" } }, "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q=="], + + "cmdk/@radix-ui/react-dialog": ["@radix-ui/react-dialog@1.1.14", "", { "dependencies": { "@radix-ui/primitive": "1.1.2", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-dismissable-layer": "1.1.10", "@radix-ui/react-focus-guards": "1.1.2", "@radix-ui/react-focus-scope": "1.1.7", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-portal": "1.1.9", "@radix-ui/react-presence": "1.1.4", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-slot": "1.2.3", "@radix-ui/react-use-controllable-state": "1.2.2", "aria-hidden": "^1.2.4", "react-remove-scroll": "^2.6.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-+CpweKjqpzTmwRwcYECQcNYbI8V9VSQt0SNFKeEBLgfucbsLssU6Ppq7wUdNXEGb573bMjFhVjKVll8rmV6zMw=="], + + "csso/css-tree": ["css-tree@2.2.1", "", { "dependencies": { "mdn-data": "2.0.28", "source-map-js": "^1.0.1" } }, "sha512-OA0mILzGc1kCOCSJerOeqDxDQ4HOh+G8NbOJFOTgOCzpw7fCBubk0fEyxp8AgOL/jvLgYA/uV0cMbe43ElF1JA=="], + + "debug/ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="], + + "dom-serializer/entities": ["entities@4.5.0", "", {}, "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw=="], + + "esast-util-from-js/acorn": ["acorn@8.14.1", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg=="], + + "esbuild-register/debug": ["debug@4.4.1", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ=="], + + "estree-util-to-js/source-map": ["source-map@0.7.4", "", {}, "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA=="], + + "hast-util-from-html/parse5": ["parse5@7.3.0", "", { "dependencies": { "entities": "^6.0.0" } }, "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw=="], + + "hast-util-raw/parse5": ["parse5@7.3.0", "", { "dependencies": { "entities": "^6.0.0" } }, "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw=="], + + "hast-util-to-parse5/property-information": ["property-information@6.5.0", "", {}, "sha512-PgTgs/BlvHxOu8QuEN7wi5A0OmXaBcHpmCSTehcs6Uuu9IkDIEo13Hy7n898RHfrQ49vKCoGeWZSaAK01nwVig=="], + + "http-proxy-agent/debug": ["debug@4.4.1", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ=="], + + "https-proxy-agent/debug": ["debug@4.4.1", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ=="], + + "jsonwebtoken/ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="], + + "mdast-util-find-and-replace/unist-util-visit-parents": ["unist-util-visit-parents@6.0.1", "", { "dependencies": { "@types/unist": "^3.0.0", "unist-util-is": "^6.0.0" } }, "sha512-L/PqWzfTP9lzzEa6CKs0k2nARxTdZduw3zyh8d2NVBnsyvHjSX4TWse388YrrQKbvI8w20fGjGlhgT96WwKykw=="], + + "micromark/debug": ["debug@4.4.1", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ=="], + + "micromark-extension-mdxjs/acorn": ["acorn@8.14.1", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg=="], + + "micromatch/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="], + + "ofetch/node-fetch-native": ["node-fetch-native@1.6.6", "", {}, "sha512-8Mc2HhqPdlIfedsuZoc3yioPuzp6b+L5jRCRY1QzuWZh2EGJVQrGppC6V6cF0bLdbW0+O2YpqCA25aF/1lvipQ=="], + + "parse-entities/@types/unist": ["@types/unist@2.0.11", "", {}, "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA=="], + + "pretty-format/ansi-styles": ["ansi-styles@5.2.0", "", {}, "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA=="], + + "prompts/kleur": ["kleur@3.0.3", "", {}, "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w=="], + + "samlify/uuid": ["uuid@8.3.2", "", { "bin": { "uuid": "dist/bin/uuid" } }, "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg=="], + + "send/debug": ["debug@4.4.1", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ=="], + + "send/ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="], + + "sharp/semver": ["semver@7.7.2", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA=="], + + "tinyglobby/fdir": ["fdir@6.5.0", "", { "peerDependencies": { "picomatch": "^3 || ^4" }, "optionalPeers": ["picomatch"] }, "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg=="], + + "tsx/esbuild": ["esbuild@0.27.1", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.27.1", "@esbuild/android-arm": "0.27.1", "@esbuild/android-arm64": "0.27.1", "@esbuild/android-x64": "0.27.1", "@esbuild/darwin-arm64": "0.27.1", "@esbuild/darwin-x64": "0.27.1", "@esbuild/freebsd-arm64": "0.27.1", "@esbuild/freebsd-x64": "0.27.1", "@esbuild/linux-arm": "0.27.1", "@esbuild/linux-arm64": "0.27.1", "@esbuild/linux-ia32": "0.27.1", "@esbuild/linux-loong64": "0.27.1", "@esbuild/linux-mips64el": "0.27.1", "@esbuild/linux-ppc64": "0.27.1", "@esbuild/linux-riscv64": "0.27.1", "@esbuild/linux-s390x": "0.27.1", "@esbuild/linux-x64": "0.27.1", "@esbuild/netbsd-arm64": "0.27.1", "@esbuild/netbsd-x64": "0.27.1", "@esbuild/openbsd-arm64": "0.27.1", "@esbuild/openbsd-x64": "0.27.1", "@esbuild/openharmony-arm64": "0.27.1", "@esbuild/sunos-x64": "0.27.1", "@esbuild/win32-arm64": "0.27.1", "@esbuild/win32-ia32": "0.27.1", "@esbuild/win32-x64": "0.27.1" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-yY35KZckJJuVVPXpvjgxiCuVEJT67F6zDeVTv4rizyPrfGBUpZQsvmxnN+C371c2esD/hNMjj4tpBhuueLN7aA=="], + + "typescript-auto-import-cache/semver": ["semver@7.7.2", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA=="], + + "unicode-trie/pako": ["pako@0.2.9", "", {}, "sha512-NUcwaKxUxWrZLpDG+z/xZaCgQITkA/Dv4V/T6bw7VON6l1Xz/VnrBqrYjZQ12TamKHzITTfOEIYUj48y2KXImA=="], + + "unist-util-visit/unist-util-visit-parents": ["unist-util-visit-parents@6.0.1", "", { "dependencies": { "@types/unist": "^3.0.0", "unist-util-is": "^6.0.0" } }, "sha512-L/PqWzfTP9lzzEa6CKs0k2nARxTdZduw3zyh8d2NVBnsyvHjSX4TWse388YrrQKbvI8w20fGjGlhgT96WwKykw=="], + + "unstorage/lru-cache": ["lru-cache@10.4.3", "", {}, "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ=="], + + "unstorage/ofetch": ["ofetch@1.5.1", "", { "dependencies": { "destr": "^2.0.5", "node-fetch-native": "^1.6.7", "ufo": "^1.6.1" } }, "sha512-2W4oUZlVaqAPAil6FUg/difl6YhqhUR7x2eZY4bQCko22UXg3hptq9KLQdqFClV+Wu85UX7hNtdGTngi/1BxcA=="], + + "vaul/@radix-ui/react-dialog": ["@radix-ui/react-dialog@1.1.14", "", { "dependencies": { "@radix-ui/primitive": "1.1.2", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-dismissable-layer": "1.1.10", "@radix-ui/react-focus-guards": "1.1.2", "@radix-ui/react-focus-scope": "1.1.7", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-portal": "1.1.9", "@radix-ui/react-presence": "1.1.4", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-slot": "1.2.3", "@radix-ui/react-use-controllable-state": "1.2.2", "aria-hidden": "^1.2.4", "react-remove-scroll": "^2.6.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-+CpweKjqpzTmwRwcYECQcNYbI8V9VSQt0SNFKeEBLgfucbsLssU6Ppq7wUdNXEGb573bMjFhVjKVll8rmV6zMw=="], + + "vite/tinyglobby": ["tinyglobby@0.2.14", "", { "dependencies": { "fdir": "^6.4.4", "picomatch": "^4.0.2" } }, "sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ=="], + + "vscode-json-languageservice/jsonc-parser": ["jsonc-parser@3.3.1", "", {}, "sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ=="], + + "widest-line/string-width": ["string-width@7.2.0", "", { "dependencies": { "emoji-regex": "^10.3.0", "get-east-asian-width": "^1.0.0", "strip-ansi": "^7.1.0" } }, "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ=="], + + "wrap-ansi/ansi-styles": ["ansi-styles@6.2.1", "", {}, "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug=="], + + "wrap-ansi/string-width": ["string-width@7.2.0", "", { "dependencies": { "emoji-regex": "^10.3.0", "get-east-asian-width": "^1.0.0", "strip-ansi": "^7.1.0" } }, "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ=="], + + "wrap-ansi/strip-ansi": ["strip-ansi@7.1.0", "", { "dependencies": { "ansi-regex": "^6.0.1" } }, "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ=="], + + "xml-crypto/xpath": ["xpath@0.0.33", "", {}, "sha512-NNXnzrkDrAzalLhIUc01jO2mOzXGXh1JwPgkihcLLzw98c0WgYDmmjSh1Kl3wzaxSVWMuA+fe0WTWOBDWCBmNA=="], + + "yaml-language-server/request-light": ["request-light@0.5.8", "", {}, "sha512-3Zjgh+8b5fhRJBQZoy+zbVKpAQGLyka0MPgW3zruTF4dFFJ8Fqcfu9YsAvi/rvdcaTeWG3MkbZv4WKxAn/84Lg=="], + + "yaml-language-server/yaml": ["yaml@2.7.1", "", { "bin": { "yaml": "bin.mjs" } }, "sha512-10ULxpnOCQXxJvBgxsn9ptjq6uviG/htZKk9veJGhlqn3w/DxQ631zFF+nlQXLwmImeS5amR2dl2U8sg6U9jsQ=="], + + "@ampproject/remapping/@jridgewell/gen-mapping/@jridgewell/sourcemap-codec": ["@jridgewell/sourcemap-codec@1.5.0", "", {}, "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ=="], + + "@ampproject/remapping/@jridgewell/trace-mapping/@jridgewell/sourcemap-codec": ["@jridgewell/sourcemap-codec@1.5.0", "", {}, "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ=="], + + "@astrojs/react/@vitejs/plugin-react/@babel/core": ["@babel/core@7.28.3", "", { "dependencies": { "@ampproject/remapping": "^2.2.0", "@babel/code-frame": "^7.27.1", "@babel/generator": "^7.28.3", "@babel/helper-compilation-targets": "^7.27.2", "@babel/helper-module-transforms": "^7.28.3", "@babel/helpers": "^7.28.3", "@babel/parser": "^7.28.3", "@babel/template": "^7.27.2", "@babel/traverse": "^7.28.3", "@babel/types": "^7.28.2", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", "json5": "^2.2.3", "semver": "^6.3.1" } }, "sha512-yDBHV9kQNcr2/sUr9jghVyz9C3Y5G2zUM2H2lo+9mKv4sFgbA8s8Z9t8D1jiTkGoO/NoIfKMyKWr4s6CN23ZwQ=="], + + "@astrojs/react/@vitejs/plugin-react/@rolldown/pluginutils": ["@rolldown/pluginutils@1.0.0-beta.27", "", {}, "sha512-+d0F4MKMCbeVUJwG96uQ4SgAznZNSq93I3V+9NHA4OpvqG8mRCpGdKmK8l/dl02h2CCDHwW2FqilnTyDcAnqjA=="], + + "@astrojs/react/@vitejs/plugin-react/react-refresh": ["react-refresh@0.17.0", "", {}, "sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ=="], + + "@astrojs/telemetry/debug/ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="], + + "@babel/helper-module-imports/@babel/traverse/@babel/generator": ["@babel/generator@7.27.3", "", { "dependencies": { "@babel/parser": "^7.27.3", "@babel/types": "^7.27.3", "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.25", "jsesc": "^3.0.2" } }, "sha512-xnlJYj5zepml8NXtjkG0WquFUv8RskFqyFcVgTBp5k+NaA/8uw/K+OSVf8AMGw5e9HKP2ETd5xpK5MLZQD6b4Q=="], + + "@babel/helper-module-imports/@babel/traverse/@babel/parser": ["@babel/parser@7.27.3", "", { "dependencies": { "@babel/types": "^7.27.3" }, "bin": "./bin/babel-parser.js" }, "sha512-xyYxRj6+tLNDTWi0KCBcZ9V7yg3/lwL9DWh9Uwh/RIVlIfFidggcgxKX3GCXwCiswwcGRawBKbEg2LG/Y8eJhw=="], + + "@babel/helper-module-imports/@babel/traverse/debug": ["debug@4.4.1", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ=="], + + "@babel/helper-module-transforms/@babel/traverse/@babel/generator": ["@babel/generator@7.28.3", "", { "dependencies": { "@babel/parser": "^7.28.3", "@babel/types": "^7.28.2", "@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" } }, "sha512-3lSpxGgvnmZznmBkCRnVREPUFJv2wrv9iAoFDvADJc0ypmdOxdUtcLeBgBJ6zE0PMeTKnxeQzyk0xTBq4Ep7zw=="], + + "@babel/helper-module-transforms/@babel/traverse/@babel/parser": ["@babel/parser@7.28.3", "", { "dependencies": { "@babel/types": "^7.28.2" }, "bin": "./bin/babel-parser.js" }, "sha512-7+Ey1mAgYqFAx2h0RuoxcQT5+MlG3GTV0TQrgr7/ZliKsm/MNDxVVutlWaziMq7wJNAz8MTqz55XLpWvva6StA=="], + + "@babel/helper-module-transforms/@babel/traverse/@babel/types": ["@babel/types@7.28.2", "", { "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.27.1" } }, "sha512-ruv7Ae4J5dUYULmeXw1gmb7rYRz57OWCPM57pHojnLq/3Z1CK2lNSLTCVjxVk1F/TZHwOZZrOWi0ur95BbLxNQ=="], + + "@babel/helper-module-transforms/@babel/traverse/debug": ["debug@4.4.1", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ=="], + + "@esbuild-kit/esm-loader/esbuild/@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.27.1", "", { "os": "aix", "cpu": "ppc64" }, "sha512-HHB50pdsBX6k47S4u5g/CaLjqS3qwaOVE5ILsq64jyzgMhLuCuZ8rGzM9yhsAjfjkbgUPMzZEPa7DAp7yz6vuA=="], + + "@esbuild-kit/esm-loader/esbuild/@esbuild/android-arm": ["@esbuild/android-arm@0.27.1", "", { "os": "android", "cpu": "arm" }, "sha512-kFqa6/UcaTbGm/NncN9kzVOODjhZW8e+FRdSeypWe6j33gzclHtwlANs26JrupOntlcWmB0u8+8HZo8s7thHvg=="], + + "@esbuild-kit/esm-loader/esbuild/@esbuild/android-arm64": ["@esbuild/android-arm64@0.27.1", "", { "os": "android", "cpu": "arm64" }, "sha512-45fuKmAJpxnQWixOGCrS+ro4Uvb4Re9+UTieUY2f8AEc+t7d4AaZ6eUJ3Hva7dtrxAAWHtlEFsXFMAgNnGU9uQ=="], + + "@esbuild-kit/esm-loader/esbuild/@esbuild/android-x64": ["@esbuild/android-x64@0.27.1", "", { "os": "android", "cpu": "x64" }, "sha512-LBEpOz0BsgMEeHgenf5aqmn/lLNTFXVfoWMUox8CtWWYK9X4jmQzWjoGoNb8lmAYml/tQ/Ysvm8q7szu7BoxRQ=="], + + "@esbuild-kit/esm-loader/esbuild/@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.27.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-veg7fL8eMSCVKL7IW4pxb54QERtedFDfY/ASrumK/SbFsXnRazxY4YykN/THYqFnFwJ0aVjiUrVG2PwcdAEqQQ=="], + + "@esbuild-kit/esm-loader/esbuild/@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.27.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-+3ELd+nTzhfWb07Vol7EZ+5PTbJ/u74nC6iv4/lwIU99Ip5uuY6QoIf0Hn4m2HoV0qcnRivN3KSqc+FyCHjoVQ=="], + + "@esbuild-kit/esm-loader/esbuild/@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.27.1", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-/8Rfgns4XD9XOSXlzUDepG8PX+AVWHliYlUkFI3K3GB6tqbdjYqdhcb4BKRd7C0BhZSoaCxhv8kTcBrcZWP+xg=="], + + "@esbuild-kit/esm-loader/esbuild/@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.27.1", "", { "os": "freebsd", "cpu": "x64" }, "sha512-GITpD8dK9C+r+5yRT/UKVT36h/DQLOHdwGVwwoHidlnA168oD3uxA878XloXebK4Ul3gDBBIvEdL7go9gCUFzQ=="], + + "@esbuild-kit/esm-loader/esbuild/@esbuild/linux-arm": ["@esbuild/linux-arm@0.27.1", "", { "os": "linux", "cpu": "arm" }, "sha512-ieMID0JRZY/ZeCrsFQ3Y3NlHNCqIhTprJfDgSB3/lv5jJZ8FX3hqPyXWhe+gvS5ARMBJ242PM+VNz/ctNj//eA=="], + + "@esbuild-kit/esm-loader/esbuild/@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.27.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-W9//kCrh/6in9rWIBdKaMtuTTzNj6jSeG/haWBADqLLa9P8O5YSRDzgD5y9QBok4AYlzS6ARHifAb75V6G670Q=="], + + "@esbuild-kit/esm-loader/esbuild/@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.27.1", "", { "os": "linux", "cpu": "ia32" }, "sha512-VIUV4z8GD8rtSVMfAj1aXFahsi/+tcoXXNYmXgzISL+KB381vbSTNdeZHHHIYqFyXcoEhu9n5cT+05tRv13rlw=="], + + "@esbuild-kit/esm-loader/esbuild/@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.27.1", "", { "os": "linux", "cpu": "none" }, "sha512-l4rfiiJRN7sTNI//ff65zJ9z8U+k6zcCg0LALU5iEWzY+a1mVZ8iWC1k5EsNKThZ7XCQ6YWtsZ8EWYm7r1UEsg=="], + + "@esbuild-kit/esm-loader/esbuild/@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.27.1", "", { "os": "linux", "cpu": "none" }, "sha512-U0bEuAOLvO/DWFdygTHWY8C067FXz+UbzKgxYhXC0fDieFa0kDIra1FAhsAARRJbvEyso8aAqvPdNxzWuStBnA=="], + + "@esbuild-kit/esm-loader/esbuild/@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.27.1", "", { "os": "linux", "cpu": "ppc64" }, "sha512-NzdQ/Xwu6vPSf/GkdmRNsOfIeSGnh7muundsWItmBsVpMoNPVpM61qNzAVY3pZ1glzzAxLR40UyYM23eaDDbYQ=="], + + "@esbuild-kit/esm-loader/esbuild/@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.27.1", "", { "os": "linux", "cpu": "none" }, "sha512-7zlw8p3IApcsN7mFw0O1Z1PyEk6PlKMu18roImfl3iQHTnr/yAfYv6s4hXPidbDoI2Q0pW+5xeoM4eTCC0UdrQ=="], + + "@esbuild-kit/esm-loader/esbuild/@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.27.1", "", { "os": "linux", "cpu": "s390x" }, "sha512-cGj5wli+G+nkVQdZo3+7FDKC25Uh4ZVwOAK6A06Hsvgr8WqBBuOy/1s+PUEd/6Je+vjfm6stX0kmib5b/O2Ykw=="], + + "@esbuild-kit/esm-loader/esbuild/@esbuild/linux-x64": ["@esbuild/linux-x64@0.27.1", "", { "os": "linux", "cpu": "x64" }, "sha512-z3H/HYI9MM0HTv3hQZ81f+AKb+yEoCRlUby1F80vbQ5XdzEMyY/9iNlAmhqiBKw4MJXwfgsh7ERGEOhrM1niMA=="], + + "@esbuild-kit/esm-loader/esbuild/@esbuild/netbsd-arm64": ["@esbuild/netbsd-arm64@0.27.1", "", { "os": "none", "cpu": "arm64" }, "sha512-wzC24DxAvk8Em01YmVXyjl96Mr+ecTPyOuADAvjGg+fyBpGmxmcr2E5ttf7Im8D0sXZihpxzO1isus8MdjMCXQ=="], + + "@esbuild-kit/esm-loader/esbuild/@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.27.1", "", { "os": "none", "cpu": "x64" }, "sha512-1YQ8ybGi2yIXswu6eNzJsrYIGFpnlzEWRl6iR5gMgmsrR0FcNoV1m9k9sc3PuP5rUBLshOZylc9nqSgymI+TYg=="], + + "@esbuild-kit/esm-loader/esbuild/@esbuild/openbsd-arm64": ["@esbuild/openbsd-arm64@0.27.1", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-5Z+DzLCrq5wmU7RDaMDe2DVXMRm2tTDvX2KU14JJVBN2CT/qov7XVix85QoJqHltpvAOZUAc3ndU56HSMWrv8g=="], + + "@esbuild-kit/esm-loader/esbuild/@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.27.1", "", { "os": "openbsd", "cpu": "x64" }, "sha512-Q73ENzIdPF5jap4wqLtsfh8YbYSZ8Q0wnxplOlZUOyZy7B4ZKW8DXGWgTCZmF8VWD7Tciwv5F4NsRf6vYlZtqg=="], + + "@esbuild-kit/esm-loader/esbuild/@esbuild/openharmony-arm64": ["@esbuild/openharmony-arm64@0.27.1", "", { "os": "none", "cpu": "arm64" }, "sha512-ajbHrGM/XiK+sXM0JzEbJAen+0E+JMQZ2l4RR4VFwvV9JEERx+oxtgkpoKv1SevhjavK2z2ReHk32pjzktWbGg=="], + + "@esbuild-kit/esm-loader/esbuild/@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.27.1", "", { "os": "sunos", "cpu": "x64" }, "sha512-IPUW+y4VIjuDVn+OMzHc5FV4GubIwPnsz6ubkvN8cuhEqH81NovB53IUlrlBkPMEPxvNnf79MGBoz8rZ2iW8HA=="], + + "@esbuild-kit/esm-loader/esbuild/@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.27.1", "", { "os": "win32", "cpu": "arm64" }, "sha512-RIVRWiljWA6CdVu8zkWcRmGP7iRRIIwvhDKem8UMBjPql2TXM5PkDVvvrzMtj1V+WFPB4K7zkIGM7VzRtFkjdg=="], + + "@esbuild-kit/esm-loader/esbuild/@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.27.1", "", { "os": "win32", "cpu": "ia32" }, "sha512-2BR5M8CPbptC1AK5JbJT1fWrHLvejwZidKx3UMSF0ecHMa+smhi16drIrCEggkgviBwLYd5nwrFLSl5Kho96RQ=="], + + "@esbuild-kit/esm-loader/esbuild/@esbuild/win32-x64": ["@esbuild/win32-x64@0.27.1", "", { "os": "win32", "cpu": "x64" }, "sha512-d5X6RMYv6taIymSk8JBP+nxv8DQAMY6A51GPgusqLdK9wBz5wWIXy1KjTck6HnjE9hqJzJRdk+1p/t5soSbCtw=="], + + "@tailwindcss/node/lightningcss/lightningcss-darwin-arm64": ["lightningcss-darwin-arm64@1.30.2", "", { "os": "darwin", "cpu": "arm64" }, "sha512-ylTcDJBN3Hp21TdhRT5zBOIi73P6/W0qwvlFEk22fkdXchtNTOU4Qc37SkzV+EKYxLouZ6M4LG9NfZ1qkhhBWA=="], + + "@tailwindcss/node/lightningcss/lightningcss-darwin-x64": ["lightningcss-darwin-x64@1.30.2", "", { "os": "darwin", "cpu": "x64" }, "sha512-oBZgKchomuDYxr7ilwLcyms6BCyLn0z8J0+ZZmfpjwg9fRVZIR5/GMXd7r9RH94iDhld3UmSjBM6nXWM2TfZTQ=="], + + "@tailwindcss/node/lightningcss/lightningcss-freebsd-x64": ["lightningcss-freebsd-x64@1.30.2", "", { "os": "freebsd", "cpu": "x64" }, "sha512-c2bH6xTrf4BDpK8MoGG4Bd6zAMZDAXS569UxCAGcA7IKbHNMlhGQ89eRmvpIUGfKWNVdbhSbkQaWhEoMGmGslA=="], + + "@tailwindcss/node/lightningcss/lightningcss-linux-arm-gnueabihf": ["lightningcss-linux-arm-gnueabihf@1.30.2", "", { "os": "linux", "cpu": "arm" }, "sha512-eVdpxh4wYcm0PofJIZVuYuLiqBIakQ9uFZmipf6LF/HRj5Bgm0eb3qL/mr1smyXIS1twwOxNWndd8z0E374hiA=="], + + "@tailwindcss/node/lightningcss/lightningcss-linux-arm64-gnu": ["lightningcss-linux-arm64-gnu@1.30.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-UK65WJAbwIJbiBFXpxrbTNArtfuznvxAJw4Q2ZGlU8kPeDIWEX1dg3rn2veBVUylA2Ezg89ktszWbaQnxD/e3A=="], + + "@tailwindcss/node/lightningcss/lightningcss-linux-arm64-musl": ["lightningcss-linux-arm64-musl@1.30.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-5Vh9dGeblpTxWHpOx8iauV02popZDsCYMPIgiuw97OJ5uaDsL86cnqSFs5LZkG3ghHoX5isLgWzMs+eD1YzrnA=="], + + "@tailwindcss/node/lightningcss/lightningcss-linux-x64-gnu": ["lightningcss-linux-x64-gnu@1.30.2", "", { "os": "linux", "cpu": "x64" }, "sha512-Cfd46gdmj1vQ+lR6VRTTadNHu6ALuw2pKR9lYq4FnhvgBc4zWY1EtZcAc6EffShbb1MFrIPfLDXD6Xprbnni4w=="], + + "@tailwindcss/node/lightningcss/lightningcss-linux-x64-musl": ["lightningcss-linux-x64-musl@1.30.2", "", { "os": "linux", "cpu": "x64" }, "sha512-XJaLUUFXb6/QG2lGIW6aIk6jKdtjtcffUT0NKvIqhSBY3hh9Ch+1LCeH80dR9q9LBjG3ewbDjnumefsLsP6aiA=="], + + "@tailwindcss/node/lightningcss/lightningcss-win32-arm64-msvc": ["lightningcss-win32-arm64-msvc@1.30.2", "", { "os": "win32", "cpu": "arm64" }, "sha512-FZn+vaj7zLv//D/192WFFVA0RgHawIcHqLX9xuWiQt7P0PtdFEVaxgF9rjM/IRYHQXNnk61/H/gb2Ei+kUQ4xQ=="], + + "@tailwindcss/node/lightningcss/lightningcss-win32-x64-msvc": ["lightningcss-win32-x64-msvc@1.30.2", "", { "os": "win32", "cpu": "x64" }, "sha512-5g1yc73p+iAkid5phb4oVFMB45417DkRevRbt/El/gKXJk4jid+vPFF/AXbxn05Aky8PapwzZrdJShv5C0avjw=="], + + "boxen/string-width/emoji-regex": ["emoji-regex@10.4.0", "", {}, "sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw=="], + + "boxen/string-width/strip-ansi": ["strip-ansi@7.1.0", "", { "dependencies": { "ansi-regex": "^6.0.1" } }, "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ=="], + + "cmdk/@radix-ui/react-dialog/@radix-ui/primitive": ["@radix-ui/primitive@1.1.2", "", {}, "sha512-XnbHrrprsNqZKQhStrSwgRUQzoCI1glLzdw79xiZPoofhGICeZRSQ3dIxAKH1gb3OHfNf4d6f+vAv3kil2eggA=="], + + "cmdk/@radix-ui/react-dialog/@radix-ui/react-dismissable-layer": ["@radix-ui/react-dismissable-layer@1.1.10", "", { "dependencies": { "@radix-ui/primitive": "1.1.2", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-escape-keydown": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-IM1zzRV4W3HtVgftdQiiOmA0AdJlCtMLe00FXaHwgt3rAnNsIyDqshvkIW3hj/iu5hu8ERP7KIYki6NkqDxAwQ=="], + + "cmdk/@radix-ui/react-dialog/@radix-ui/react-focus-guards": ["@radix-ui/react-focus-guards@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-fyjAACV62oPV925xFCrH8DR5xWhg9KYtJT4s3u54jxp+L/hbpTY2kIeEFFbFe+a/HCE94zGQMZLIpVTPVZDhaA=="], + + "cmdk/@radix-ui/react-dialog/@radix-ui/react-presence": ["@radix-ui/react-presence@1.1.4", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-ueDqRbdc4/bkaQT3GIpLQssRlFgWaL/U2z/S31qRwwLWoxHLgry3SIfCwhxeQNbirEUXFa+lq3RL3oBYXtcmIA=="], + + "cmdk/@radix-ui/react-dialog/@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.3", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A=="], + + "csso/css-tree/mdn-data": ["mdn-data@2.0.28", "", {}, "sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g=="], + + "esbuild-register/debug/ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="], + + "http-proxy-agent/debug/ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="], + + "https-proxy-agent/debug/ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="], + + "micromark/debug/ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="], + + "tsx/esbuild/@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.27.1", "", { "os": "aix", "cpu": "ppc64" }, "sha512-HHB50pdsBX6k47S4u5g/CaLjqS3qwaOVE5ILsq64jyzgMhLuCuZ8rGzM9yhsAjfjkbgUPMzZEPa7DAp7yz6vuA=="], + + "tsx/esbuild/@esbuild/android-arm": ["@esbuild/android-arm@0.27.1", "", { "os": "android", "cpu": "arm" }, "sha512-kFqa6/UcaTbGm/NncN9kzVOODjhZW8e+FRdSeypWe6j33gzclHtwlANs26JrupOntlcWmB0u8+8HZo8s7thHvg=="], + + "tsx/esbuild/@esbuild/android-arm64": ["@esbuild/android-arm64@0.27.1", "", { "os": "android", "cpu": "arm64" }, "sha512-45fuKmAJpxnQWixOGCrS+ro4Uvb4Re9+UTieUY2f8AEc+t7d4AaZ6eUJ3Hva7dtrxAAWHtlEFsXFMAgNnGU9uQ=="], + + "tsx/esbuild/@esbuild/android-x64": ["@esbuild/android-x64@0.27.1", "", { "os": "android", "cpu": "x64" }, "sha512-LBEpOz0BsgMEeHgenf5aqmn/lLNTFXVfoWMUox8CtWWYK9X4jmQzWjoGoNb8lmAYml/tQ/Ysvm8q7szu7BoxRQ=="], + + "tsx/esbuild/@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.27.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-veg7fL8eMSCVKL7IW4pxb54QERtedFDfY/ASrumK/SbFsXnRazxY4YykN/THYqFnFwJ0aVjiUrVG2PwcdAEqQQ=="], + + "tsx/esbuild/@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.27.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-+3ELd+nTzhfWb07Vol7EZ+5PTbJ/u74nC6iv4/lwIU99Ip5uuY6QoIf0Hn4m2HoV0qcnRivN3KSqc+FyCHjoVQ=="], + + "tsx/esbuild/@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.27.1", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-/8Rfgns4XD9XOSXlzUDepG8PX+AVWHliYlUkFI3K3GB6tqbdjYqdhcb4BKRd7C0BhZSoaCxhv8kTcBrcZWP+xg=="], + + "tsx/esbuild/@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.27.1", "", { "os": "freebsd", "cpu": "x64" }, "sha512-GITpD8dK9C+r+5yRT/UKVT36h/DQLOHdwGVwwoHidlnA168oD3uxA878XloXebK4Ul3gDBBIvEdL7go9gCUFzQ=="], + + "tsx/esbuild/@esbuild/linux-arm": ["@esbuild/linux-arm@0.27.1", "", { "os": "linux", "cpu": "arm" }, "sha512-ieMID0JRZY/ZeCrsFQ3Y3NlHNCqIhTprJfDgSB3/lv5jJZ8FX3hqPyXWhe+gvS5ARMBJ242PM+VNz/ctNj//eA=="], + + "tsx/esbuild/@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.27.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-W9//kCrh/6in9rWIBdKaMtuTTzNj6jSeG/haWBADqLLa9P8O5YSRDzgD5y9QBok4AYlzS6ARHifAb75V6G670Q=="], + + "tsx/esbuild/@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.27.1", "", { "os": "linux", "cpu": "ia32" }, "sha512-VIUV4z8GD8rtSVMfAj1aXFahsi/+tcoXXNYmXgzISL+KB381vbSTNdeZHHHIYqFyXcoEhu9n5cT+05tRv13rlw=="], + + "tsx/esbuild/@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.27.1", "", { "os": "linux", "cpu": "none" }, "sha512-l4rfiiJRN7sTNI//ff65zJ9z8U+k6zcCg0LALU5iEWzY+a1mVZ8iWC1k5EsNKThZ7XCQ6YWtsZ8EWYm7r1UEsg=="], + + "tsx/esbuild/@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.27.1", "", { "os": "linux", "cpu": "none" }, "sha512-U0bEuAOLvO/DWFdygTHWY8C067FXz+UbzKgxYhXC0fDieFa0kDIra1FAhsAARRJbvEyso8aAqvPdNxzWuStBnA=="], + + "tsx/esbuild/@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.27.1", "", { "os": "linux", "cpu": "ppc64" }, "sha512-NzdQ/Xwu6vPSf/GkdmRNsOfIeSGnh7muundsWItmBsVpMoNPVpM61qNzAVY3pZ1glzzAxLR40UyYM23eaDDbYQ=="], + + "tsx/esbuild/@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.27.1", "", { "os": "linux", "cpu": "none" }, "sha512-7zlw8p3IApcsN7mFw0O1Z1PyEk6PlKMu18roImfl3iQHTnr/yAfYv6s4hXPidbDoI2Q0pW+5xeoM4eTCC0UdrQ=="], + + "tsx/esbuild/@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.27.1", "", { "os": "linux", "cpu": "s390x" }, "sha512-cGj5wli+G+nkVQdZo3+7FDKC25Uh4ZVwOAK6A06Hsvgr8WqBBuOy/1s+PUEd/6Je+vjfm6stX0kmib5b/O2Ykw=="], + + "tsx/esbuild/@esbuild/linux-x64": ["@esbuild/linux-x64@0.27.1", "", { "os": "linux", "cpu": "x64" }, "sha512-z3H/HYI9MM0HTv3hQZ81f+AKb+yEoCRlUby1F80vbQ5XdzEMyY/9iNlAmhqiBKw4MJXwfgsh7ERGEOhrM1niMA=="], + + "tsx/esbuild/@esbuild/netbsd-arm64": ["@esbuild/netbsd-arm64@0.27.1", "", { "os": "none", "cpu": "arm64" }, "sha512-wzC24DxAvk8Em01YmVXyjl96Mr+ecTPyOuADAvjGg+fyBpGmxmcr2E5ttf7Im8D0sXZihpxzO1isus8MdjMCXQ=="], + + "tsx/esbuild/@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.27.1", "", { "os": "none", "cpu": "x64" }, "sha512-1YQ8ybGi2yIXswu6eNzJsrYIGFpnlzEWRl6iR5gMgmsrR0FcNoV1m9k9sc3PuP5rUBLshOZylc9nqSgymI+TYg=="], + + "tsx/esbuild/@esbuild/openbsd-arm64": ["@esbuild/openbsd-arm64@0.27.1", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-5Z+DzLCrq5wmU7RDaMDe2DVXMRm2tTDvX2KU14JJVBN2CT/qov7XVix85QoJqHltpvAOZUAc3ndU56HSMWrv8g=="], + + "tsx/esbuild/@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.27.1", "", { "os": "openbsd", "cpu": "x64" }, "sha512-Q73ENzIdPF5jap4wqLtsfh8YbYSZ8Q0wnxplOlZUOyZy7B4ZKW8DXGWgTCZmF8VWD7Tciwv5F4NsRf6vYlZtqg=="], + + "tsx/esbuild/@esbuild/openharmony-arm64": ["@esbuild/openharmony-arm64@0.27.1", "", { "os": "none", "cpu": "arm64" }, "sha512-ajbHrGM/XiK+sXM0JzEbJAen+0E+JMQZ2l4RR4VFwvV9JEERx+oxtgkpoKv1SevhjavK2z2ReHk32pjzktWbGg=="], + + "tsx/esbuild/@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.27.1", "", { "os": "sunos", "cpu": "x64" }, "sha512-IPUW+y4VIjuDVn+OMzHc5FV4GubIwPnsz6ubkvN8cuhEqH81NovB53IUlrlBkPMEPxvNnf79MGBoz8rZ2iW8HA=="], + + "tsx/esbuild/@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.27.1", "", { "os": "win32", "cpu": "arm64" }, "sha512-RIVRWiljWA6CdVu8zkWcRmGP7iRRIIwvhDKem8UMBjPql2TXM5PkDVvvrzMtj1V+WFPB4K7zkIGM7VzRtFkjdg=="], + + "tsx/esbuild/@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.27.1", "", { "os": "win32", "cpu": "ia32" }, "sha512-2BR5M8CPbptC1AK5JbJT1fWrHLvejwZidKx3UMSF0ecHMa+smhi16drIrCEggkgviBwLYd5nwrFLSl5Kho96RQ=="], + + "tsx/esbuild/@esbuild/win32-x64": ["@esbuild/win32-x64@0.27.1", "", { "os": "win32", "cpu": "x64" }, "sha512-d5X6RMYv6taIymSk8JBP+nxv8DQAMY6A51GPgusqLdK9wBz5wWIXy1KjTck6HnjE9hqJzJRdk+1p/t5soSbCtw=="], + + "vaul/@radix-ui/react-dialog/@radix-ui/primitive": ["@radix-ui/primitive@1.1.2", "", {}, "sha512-XnbHrrprsNqZKQhStrSwgRUQzoCI1glLzdw79xiZPoofhGICeZRSQ3dIxAKH1gb3OHfNf4d6f+vAv3kil2eggA=="], + + "vaul/@radix-ui/react-dialog/@radix-ui/react-dismissable-layer": ["@radix-ui/react-dismissable-layer@1.1.10", "", { "dependencies": { "@radix-ui/primitive": "1.1.2", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-escape-keydown": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-IM1zzRV4W3HtVgftdQiiOmA0AdJlCtMLe00FXaHwgt3rAnNsIyDqshvkIW3hj/iu5hu8ERP7KIYki6NkqDxAwQ=="], + + "vaul/@radix-ui/react-dialog/@radix-ui/react-focus-guards": ["@radix-ui/react-focus-guards@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-fyjAACV62oPV925xFCrH8DR5xWhg9KYtJT4s3u54jxp+L/hbpTY2kIeEFFbFe+a/HCE94zGQMZLIpVTPVZDhaA=="], + + "vaul/@radix-ui/react-dialog/@radix-ui/react-presence": ["@radix-ui/react-presence@1.1.4", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-ueDqRbdc4/bkaQT3GIpLQssRlFgWaL/U2z/S31qRwwLWoxHLgry3SIfCwhxeQNbirEUXFa+lq3RL3oBYXtcmIA=="], + + "vaul/@radix-ui/react-dialog/@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.3", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A=="], + + "vite/tinyglobby/picomatch": ["picomatch@4.0.2", "", {}, "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg=="], + + "widest-line/string-width/emoji-regex": ["emoji-regex@10.4.0", "", {}, "sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw=="], + + "widest-line/string-width/strip-ansi": ["strip-ansi@7.1.0", "", { "dependencies": { "ansi-regex": "^6.0.1" } }, "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ=="], + + "wrap-ansi/string-width/emoji-regex": ["emoji-regex@10.4.0", "", {}, "sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw=="], + + "wrap-ansi/strip-ansi/ansi-regex": ["ansi-regex@6.1.0", "", {}, "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA=="], + + "@astrojs/react/@vitejs/plugin-react/@babel/core/@babel/generator": ["@babel/generator@7.28.3", "", { "dependencies": { "@babel/parser": "^7.28.3", "@babel/types": "^7.28.2", "@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" } }, "sha512-3lSpxGgvnmZznmBkCRnVREPUFJv2wrv9iAoFDvADJc0ypmdOxdUtcLeBgBJ6zE0PMeTKnxeQzyk0xTBq4Ep7zw=="], + + "@astrojs/react/@vitejs/plugin-react/@babel/core/@babel/helpers": ["@babel/helpers@7.28.3", "", { "dependencies": { "@babel/template": "^7.27.2", "@babel/types": "^7.28.2" } }, "sha512-PTNtvUQihsAsDHMOP5pfobP8C6CM4JWXmP8DrEIt46c3r2bf87Ua1zoqevsMo9g+tWDwgWrFP5EIxuBx5RudAw=="], + + "@astrojs/react/@vitejs/plugin-react/@babel/core/@babel/parser": ["@babel/parser@7.28.3", "", { "dependencies": { "@babel/types": "^7.28.2" }, "bin": "./bin/babel-parser.js" }, "sha512-7+Ey1mAgYqFAx2h0RuoxcQT5+MlG3GTV0TQrgr7/ZliKsm/MNDxVVutlWaziMq7wJNAz8MTqz55XLpWvva6StA=="], + + "@astrojs/react/@vitejs/plugin-react/@babel/core/@babel/traverse": ["@babel/traverse@7.28.3", "", { "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/generator": "^7.28.3", "@babel/helper-globals": "^7.28.0", "@babel/parser": "^7.28.3", "@babel/template": "^7.27.2", "@babel/types": "^7.28.2", "debug": "^4.3.1" } }, "sha512-7w4kZYHneL3A6NP2nxzHvT3HCZ7puDZZjFMqDpBPECub79sTtSO5CGXDkKrTQq8ksAwfD/XI2MRFX23njdDaIQ=="], + + "@astrojs/react/@vitejs/plugin-react/@babel/core/@babel/types": ["@babel/types@7.28.2", "", { "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.27.1" } }, "sha512-ruv7Ae4J5dUYULmeXw1gmb7rYRz57OWCPM57pHojnLq/3Z1CK2lNSLTCVjxVk1F/TZHwOZZrOWi0ur95BbLxNQ=="], + + "@astrojs/react/@vitejs/plugin-react/@babel/core/debug": ["debug@4.4.1", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ=="], + + "@astrojs/react/@vitejs/plugin-react/@babel/core/semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="], + + "@babel/helper-module-imports/@babel/traverse/@babel/generator/@jridgewell/gen-mapping": ["@jridgewell/gen-mapping@0.3.8", "", { "dependencies": { "@jridgewell/set-array": "^1.2.1", "@jridgewell/sourcemap-codec": "^1.4.10", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA=="], + + "@babel/helper-module-imports/@babel/traverse/@babel/generator/@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.25", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ=="], + + "@babel/helper-module-imports/@babel/traverse/debug/ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="], + + "@babel/helper-module-transforms/@babel/traverse/debug/ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="], + + "boxen/string-width/strip-ansi/ansi-regex": ["ansi-regex@6.1.0", "", {}, "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA=="], + + "widest-line/string-width/strip-ansi/ansi-regex": ["ansi-regex@6.1.0", "", {}, "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA=="], + + "@astrojs/react/@vitejs/plugin-react/@babel/core/debug/ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="], + + "@babel/helper-module-imports/@babel/traverse/@babel/generator/@jridgewell/gen-mapping/@jridgewell/sourcemap-codec": ["@jridgewell/sourcemap-codec@1.5.0", "", {}, "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ=="], + + "@babel/helper-module-imports/@babel/traverse/@babel/generator/@jridgewell/trace-mapping/@jridgewell/sourcemap-codec": ["@jridgewell/sourcemap-codec@1.5.0", "", {}, "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ=="], + } +} diff --git a/Divers/gitea-mirror/bunfig.toml b/Divers/gitea-mirror/bunfig.toml new file mode 100644 index 0000000..b0cfcc4 --- /dev/null +++ b/Divers/gitea-mirror/bunfig.toml @@ -0,0 +1,6 @@ +[test] +# Set test timeout to 5 seconds (5000ms) to prevent hanging tests +timeout = 5000 + +# Preload the setup file +preload = ["./src/tests/setup.bun.ts"] \ No newline at end of file diff --git a/Divers/gitea-mirror/certs/README.md b/Divers/gitea-mirror/certs/README.md new file mode 100644 index 0000000..98c0a94 --- /dev/null +++ b/Divers/gitea-mirror/certs/README.md @@ -0,0 +1,236 @@ +# CA Certificates Configuration + +This document explains how to configure custom Certificate Authority (CA) certificates for Gitea Mirror when connecting to self-signed or privately signed Gitea instances. + +## Overview + +When your Gitea instance uses a self-signed certificate or a certificate signed by a private Certificate Authority (CA), you need to configure Gitea Mirror to trust these certificates. + +## Common SSL/TLS Errors + +If you encounter any of these errors, you need to configure CA certificates: + +- `UNABLE_TO_VERIFY_LEAF_SIGNATURE` +- `SELF_SIGNED_CERT_IN_CHAIN` +- `UNABLE_TO_GET_ISSUER_CERT_LOCALLY` +- `CERT_UNTRUSTED` +- `unable to verify the first certificate` + +## Configuration by Deployment Method + +### Docker + +#### Method 1: Volume Mount (Recommended) + +1. Create a certificates directory: + ```bash + mkdir -p ./certs + ``` + +2. Copy your CA certificate(s): + ```bash + cp /path/to/your-ca-cert.crt ./certs/ + ``` + +3. Update `docker-compose.yml`: + ```yaml + version: '3.8' + services: + gitea-mirror: + image: raylabs/gitea-mirror:latest + volumes: + - ./data:/app/data + - ./certs:/usr/local/share/ca-certificates:ro + environment: + - NODE_EXTRA_CA_CERTS=/usr/local/share/ca-certificates/your-ca-cert.crt + ``` + +4. Restart the container: + ```bash + docker-compose down && docker-compose up -d + ``` + +#### Method 2: Custom Docker Image + +Create a `Dockerfile`: + +```dockerfile +FROM raylabs/gitea-mirror:latest + +# Copy CA certificates +COPY ./certs/*.crt /usr/local/share/ca-certificates/ + +# Update CA certificates +RUN update-ca-certificates + +# Set environment variable +ENV NODE_EXTRA_CA_CERTS=/usr/local/share/ca-certificates/your-ca-cert.crt +``` + +Build and use: +```bash +docker build -t my-gitea-mirror . +``` + +### Native/Bun + +#### Method 1: Environment Variable + +```bash +export NODE_EXTRA_CA_CERTS=/path/to/your-ca-cert.crt +bun run start +``` + +#### Method 2: .env File + +Add to your `.env` file: +``` +NODE_EXTRA_CA_CERTS=/path/to/your-ca-cert.crt +``` + +#### Method 3: System CA Store + +**Ubuntu/Debian:** +```bash +sudo cp your-ca-cert.crt /usr/local/share/ca-certificates/ +sudo update-ca-certificates +``` + +**RHEL/CentOS/Fedora:** +```bash +sudo cp your-ca-cert.crt /etc/pki/ca-trust/source/anchors/ +sudo update-ca-trust +``` + +**macOS:** +```bash +sudo security add-trusted-cert -d -r trustRoot \ + -k /Library/Keychains/System.keychain your-ca-cert.crt +``` + +### LXC Container (Proxmox VE) + +1. Enter the container: + ```bash + pct enter + ``` + +2. Create certificates directory: + ```bash + mkdir -p /usr/local/share/ca-certificates + ``` + +3. Copy your CA certificate: + ```bash + cat > /usr/local/share/ca-certificates/your-ca.crt + ``` + (Paste certificate content and press Ctrl+D) + +4. Update the systemd service: + ```bash + cat >> /etc/systemd/system/gitea-mirror.service << EOF + Environment="NODE_EXTRA_CA_CERTS=/usr/local/share/ca-certificates/your-ca.crt" + EOF + ``` + +5. Reload and restart: + ```bash + systemctl daemon-reload + systemctl restart gitea-mirror + ``` + +## Multiple CA Certificates + +### Option 1: Bundle Certificates + +```bash +cat ca-cert1.crt ca-cert2.crt ca-cert3.crt > ca-bundle.crt +export NODE_EXTRA_CA_CERTS=/path/to/ca-bundle.crt +``` + +### Option 2: System CA Store + +```bash +# Copy all certificates +cp *.crt /usr/local/share/ca-certificates/ +update-ca-certificates +``` + +## Verification + +### 1. Test Gitea Connection +Use the "Test Connection" button in the Gitea configuration section. + +### 2. Check Logs + +**Docker:** +```bash +docker logs gitea-mirror +``` + +**Native:** +Check terminal output + +**LXC:** +```bash +journalctl -u gitea-mirror -f +``` + +### 3. Manual Certificate Test + +```bash +openssl s_client -connect your-gitea-domain.com:443 -CAfile /path/to/ca-cert.crt +``` + +## Best Practices + +1. **Certificate Security** + - Keep CA certificates secure + - Use read-only mounts in Docker + - Limit certificate file permissions + - Regularly update certificates + +2. **Certificate Management** + - Use descriptive certificate filenames + - Document certificate purposes + - Track certificate expiration dates + - Maintain certificate backups + +3. **Production Deployment** + - Use proper SSL certificates when possible + - Consider Let's Encrypt for public instances + - Implement certificate rotation procedures + - Monitor certificate expiration + +## Troubleshooting + +### Certificate not being recognized +- Ensure the certificate is in PEM format +- Check that `NODE_EXTRA_CA_CERTS` points to the correct file +- Restart the application after adding certificates + +### Still getting SSL errors +- Verify the complete certificate chain is included +- Check if intermediate certificates are needed +- Ensure the certificate matches the server hostname + +### Certificate expired +- Check validity: `openssl x509 -in cert.crt -noout -dates` +- Update with new certificate from your CA +- Restart Gitea Mirror after updating + +## Certificate Format + +Certificates must be in PEM format. Example: + +``` +-----BEGIN CERTIFICATE----- +MIIDXTCCAkWgAwIBAgIJAKl8bUgMdErlMA0GCSqGSIb3DQEBCwUAMEUxCzAJBgNV +[... certificate content ...] +-----END CERTIFICATE----- +``` + +If your certificate is in DER format, convert it: +```bash +openssl x509 -inform der -in certificate.cer -out certificate.crt +``` \ No newline at end of file diff --git a/Divers/gitea-mirror/components.json b/Divers/gitea-mirror/components.json new file mode 100644 index 0000000..750bc1b --- /dev/null +++ b/Divers/gitea-mirror/components.json @@ -0,0 +1,21 @@ +{ + "$schema": "https://ui.shadcn.com/schema.json", + "style": "new-york", + "rsc": false, + "tsx": true, + "tailwind": { + "config": "", + "css": "src/styles/global.css", + "baseColor": "neutral", + "cssVariables": true, + "prefix": "" + }, + "aliases": { + "components": "@/components", + "utils": "@/lib/utils", + "ui": "@/components/ui", + "lib": "@/lib", + "hooks": "@/hooks" + }, + "iconLibrary": "lucide" +} \ No newline at end of file diff --git a/Divers/gitea-mirror/data/README.md b/Divers/gitea-mirror/data/README.md new file mode 100644 index 0000000..2a46038 --- /dev/null +++ b/Divers/gitea-mirror/data/README.md @@ -0,0 +1,32 @@ +# Data Directory + +This directory contains the SQLite database file for the Gitea Mirror application. + +## Files + +- `gitea-mirror.db`: The main database file. This file is **not** committed to the repository as it may contain sensitive information like tokens. + +## Important Notes + +- **Never commit `gitea-mirror.db` to the repository** as it may contain sensitive information like GitHub and Gitea tokens. +- The application will create this database file automatically on first run. + +## Database Initialization + +To initialize the database for real data mode, run: + +```bash +pnpm init-db +``` + +This will create the necessary tables. On first launch, you'll be guided through creating an admin account with your chosen credentials. + +## User Management + +To reset users (for testing the first-time setup flow), run: + +```bash +pnpm reset-users +``` + +This will remove all users and their associated data from the database, allowing you to test the signup flow. diff --git a/Divers/gitea-mirror/docker-compose.alt.yml b/Divers/gitea-mirror/docker-compose.alt.yml new file mode 100644 index 0000000..2d412d1 --- /dev/null +++ b/Divers/gitea-mirror/docker-compose.alt.yml @@ -0,0 +1,61 @@ +# Minimal Gitea Mirror deployment +# Only includes what CANNOT be configured via the Web UI +# Everything else can be set up through the web interface after deployment + +services: + gitea-mirror: + image: ghcr.io/raylabshq/gitea-mirror:latest + container_name: gitea-mirror + restart: unless-stopped + ports: + - "${PORT:-4321}:4321" + user: ${PUID:-1000}:${PGID:-1000} + volumes: + - ./data:/app/data + environment: + # === ABSOLUTELY REQUIRED === + # This MUST be set and CANNOT be changed via UI + - BETTER_AUTH_SECRET=${BETTER_AUTH_SECRET} # Min 32 chars, required for sessions + - BETTER_AUTH_URL=${BETTER_AUTH_URL:-http://localhost:4321} + - BETTER_AUTH_TRUSTED_ORIGINS=${BETTER_AUTH_TRUSTED_ORIGINS:-http://localhost:4321} + + # === CORE SETTINGS === + # These are technically required but have working defaults + - NODE_ENV=production + - DATABASE_URL=file:data/gitea-mirror.db + - HOST=0.0.0.0 + - PORT=4321 + - PUBLIC_BETTER_AUTH_URL=${PUBLIC_BETTER_AUTH_URL:-http://localhost:4321} + # Optional concurrency controls (defaults match in-app defaults) + # If you want perfect ordering of issues and PRs, set these at 1 + - MIRROR_ISSUE_CONCURRENCY=${MIRROR_ISSUE_CONCURRENCY:-3} + - MIRROR_PULL_REQUEST_CONCURRENCY=${MIRROR_PULL_REQUEST_CONCURRENCY:-5} + + healthcheck: + test: ["CMD", "wget", "--no-verbose", "--tries=3", "--spider", "http://localhost:4321/api/health"] + interval: 30s + timeout: 10s + retries: 5 + start_period: 15s + +# === QUICK START === +# +# 1. Create a .env file with only ONE required variable: +# BETTER_AUTH_SECRET=your-32-character-minimum-secret-key-here +# +# 2. Run: +# docker-compose -f docker-compose.alt.yml up -d +# +# 3. Access at http://localhost:4321 +# +# 4. Sign up for an account (first user becomes admin) +# +# 5. Configure everything else through the web UI: +# - GitHub credentials +# - Gitea credentials +# - Mirror settings +# - Scheduling options +# - Auto-import settings +# - Cleanup preferences +# +# That's it! Everything else can be configured via the web interface. diff --git a/Divers/gitea-mirror/docker-compose.dev.yml b/Divers/gitea-mirror/docker-compose.dev.yml new file mode 100644 index 0000000..b7b97d5 --- /dev/null +++ b/Divers/gitea-mirror/docker-compose.dev.yml @@ -0,0 +1,114 @@ +# Development environment with local Gitea instance for testing +# Run with: docker compose -f docker-compose.dev.yml up -d + +services: + # Local Gitea instance for testing + gitea: + image: gitea/gitea:latest + container_name: gitea + restart: unless-stopped + entrypoint: ["/tmp/gitea-dev-init.sh"] + environment: + - USER_UID=1000 + - USER_GID=1000 + - GITEA__database__DB_TYPE=sqlite3 + - GITEA__database__PATH=/data/gitea/gitea.db + - GITEA__server__DOMAIN=localhost + - GITEA__server__ROOT_URL=http://localhost:3001/ + - GITEA__server__SSH_DOMAIN=localhost + - GITEA__server__SSH_PORT=2222 + - GITEA__server__START_SSH_SERVER=true + - GITEA__security__INSTALL_LOCK=true + - GITEA__service__DISABLE_REGISTRATION=false + - GITEA__log__MODE=console + - GITEA__log__LEVEL=Info + ports: + - "3001:3000" + - "2222:22" + volumes: + - gitea-data:/data + - gitea-config:/etc/gitea + - ./scripts/gitea-app.ini:/tmp/app.ini:ro + - ./scripts/gitea-dev-init.sh:/tmp/gitea-dev-init.sh:ro + networks: + - gitea-network + healthcheck: + test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:3000/"] + interval: 30s + timeout: 10s + retries: 5 + start_period: 60s + + # Development service connected to local Gitea + gitea-mirror-dev: + # For dev environment, always build from local sources + build: + context: . + dockerfile: Dockerfile + platforms: + - linux/amd64 + - linux/arm64 + container_name: gitea-mirror-dev + restart: unless-stopped + ports: + - "4321:4321" + volumes: + - gitea-mirror-data:/app/data + # Mount custom CA certificates - choose one option: + # Option 1: Mount individual CA certificates from certs directory + # - ./certs:/app/certs:ro + # Option 2: Mount system CA bundle (if your CA is already in system store) + # - /etc/ssl/certs/ca-certificates.crt:/etc/ssl/certs/ca-certificates.crt:ro + depends_on: + - gitea + environment: + - NODE_ENV=development + - DATABASE_URL=file:data/gitea-mirror.db + - HOST=0.0.0.0 + - PORT=4321 + - BETTER_AUTH_SECRET=dev-secret-key + # GitHub/Gitea Mirror Config + - GITHUB_USERNAME=${GITHUB_USERNAME:-your-github-username} + - GITHUB_TOKEN=${GITHUB_TOKEN:-your-github-token} + - GITHUB_EXCLUDED_ORGS=${GITHUB_EXCLUDED_ORGS:-} + - SKIP_FORKS=${SKIP_FORKS:-false} + - PRIVATE_REPOSITORIES=${PRIVATE_REPOSITORIES:-false} + - MIRROR_ISSUES=${MIRROR_ISSUES:-false} + - MIRROR_WIKI=${MIRROR_WIKI:-false} + - MIRROR_STARRED=${MIRROR_STARRED:-false} + - MIRROR_ORGANIZATIONS=${MIRROR_ORGANIZATIONS:-false} + - PRESERVE_ORG_STRUCTURE=${PRESERVE_ORG_STRUCTURE:-false} + - ONLY_MIRROR_ORGS=${ONLY_MIRROR_ORGS:-false} + - SKIP_STARRED_ISSUES=${SKIP_STARRED_ISSUES:-false} + - GITEA_URL=http://gitea:3000 + - GITEA_TOKEN=${GITEA_TOKEN:-your-local-gitea-token} + - GITEA_USERNAME=${GITEA_USERNAME:-your-local-gitea-username} + - GITEA_ORGANIZATION=${GITEA_ORGANIZATION:-github-mirrors} + - GITEA_ORG_VISIBILITY=${GITEA_ORG_VISIBILITY:-public} + - DELAY=${DELAY:-3600} + # Optional: Skip TLS verification (insecure, use only for testing) + # - GITEA_SKIP_TLS_VERIFY=${GITEA_SKIP_TLS_VERIFY:-false} + healthcheck: + test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:4321/api/health"] + interval: 30s + timeout: 5s + retries: 3 + start_period: 5s + networks: + - gitea-network + + + +# Define named volumes for data persistence +volumes: + gitea-data: # Gitea data volume + gitea-config: # Gitea config volume + gitea-mirror-data: # Gitea Mirror database volume + +# Define networks +networks: + gitea-network: + name: gitea-network + # Let Docker Compose manage this network + # If you need to use an existing network, uncomment the line below + # external: true diff --git a/Divers/gitea-mirror/docker-compose.yml b/Divers/gitea-mirror/docker-compose.yml new file mode 100644 index 0000000..00d8cd0 --- /dev/null +++ b/Divers/gitea-mirror/docker-compose.yml @@ -0,0 +1,85 @@ +# Gitea Mirror deployment configuration +# Standard deployment with automatic database maintenance + +services: + gitea-mirror: + image: ${DOCKER_REGISTRY:-ghcr.io}/${DOCKER_IMAGE:-raylabshq/gitea-mirror}:${DOCKER_TAG:-latest} + build: + context: . + dockerfile: Dockerfile + platforms: + - linux/amd64 + - linux/arm64 + cache_from: + - ${DOCKER_REGISTRY:-ghcr.io}/${DOCKER_IMAGE:-raylabshq/gitea-mirror}:${DOCKER_TAG:-latest} + container_name: gitea-mirror + restart: unless-stopped + ports: + - "4321:4321" + volumes: + - gitea-mirror-data:/app/data + # Mount custom CA certificates - choose one option: + # Option 1: Mount individual CA certificates from certs directory + # - ./certs:/app/certs:ro + # Option 2: Mount system CA bundle (if your CA is already in system store) + # - /etc/ssl/certs/ca-certificates.crt:/etc/ssl/certs/ca-certificates.crt:ro + environment: + # For a complete list of all supported environment variables, see: + # docs/ENVIRONMENT_VARIABLES.md or .env.example + - NODE_ENV=production + - DATABASE_URL=file:data/gitea-mirror.db + - HOST=0.0.0.0 + - PORT=4321 + - BETTER_AUTH_SECRET=${BETTER_AUTH_SECRET:-your-secret-key-change-this-in-production} + - BETTER_AUTH_URL=${BETTER_AUTH_URL:-http://localhost:4321} + # Optional: ENCRYPTION_SECRET will be auto-generated if not provided + # - ENCRYPTION_SECRET=${ENCRYPTION_SECRET:-} + # GitHub/Gitea Mirror Config + - GITHUB_USERNAME=${GITHUB_USERNAME:-} + - GITHUB_TOKEN=${GITHUB_TOKEN:-} + - GITHUB_EXCLUDED_ORGS=${GITHUB_EXCLUDED_ORGS:-} + - SKIP_FORKS=${SKIP_FORKS:-false} + - PRIVATE_REPOSITORIES=${PRIVATE_REPOSITORIES:-false} + - MIRROR_ISSUES=${MIRROR_ISSUES:-false} + - MIRROR_WIKI=${MIRROR_WIKI:-false} + - MIRROR_STARRED=${MIRROR_STARRED:-false} + - MIRROR_ORGANIZATIONS=${MIRROR_ORGANIZATIONS:-false} + - PRESERVE_ORG_STRUCTURE=${PRESERVE_ORG_STRUCTURE:-false} + - ONLY_MIRROR_ORGS=${ONLY_MIRROR_ORGS:-false} + - SKIP_STARRED_ISSUES=${SKIP_STARRED_ISSUES:-false} + - MIRROR_ISSUE_CONCURRENCY=${MIRROR_ISSUE_CONCURRENCY:-3} + - MIRROR_PULL_REQUEST_CONCURRENCY=${MIRROR_PULL_REQUEST_CONCURRENCY:-5} + - GITEA_URL=${GITEA_URL:-} + - GITEA_TOKEN=${GITEA_TOKEN:-} + - GITEA_USERNAME=${GITEA_USERNAME:-} + - GITEA_ORGANIZATION=${GITEA_ORGANIZATION:-github-mirrors} + - GITEA_ORG_VISIBILITY=${GITEA_ORG_VISIBILITY:-public} + - DELAY=${DELAY:-3600} + # Scheduling and Sync Configuration (Issue #72 fixes) + - SCHEDULE_ENABLED=${SCHEDULE_ENABLED:-false} + - GITEA_MIRROR_INTERVAL=${GITEA_MIRROR_INTERVAL:-8h} + - AUTO_IMPORT_REPOS=${AUTO_IMPORT_REPOS:-true} + - AUTO_MIRROR_REPOS=${AUTO_MIRROR_REPOS:-false} + # Repository Cleanup Configuration + - CLEANUP_DELETE_IF_NOT_IN_GITHUB=${CLEANUP_DELETE_IF_NOT_IN_GITHUB:-false} + - CLEANUP_ORPHANED_REPO_ACTION=${CLEANUP_ORPHANED_REPO_ACTION:-archive} + - CLEANUP_DRY_RUN=${CLEANUP_DRY_RUN:-true} + # Optional: Skip TLS verification (insecure, use only for testing) + # - GITEA_SKIP_TLS_VERIFY=${GITEA_SKIP_TLS_VERIFY:-false} + # Header Authentication (for Reverse Proxy SSO) + - HEADER_AUTH_ENABLED=${HEADER_AUTH_ENABLED:-false} + - HEADER_AUTH_USER_HEADER=${HEADER_AUTH_USER_HEADER:-X-Authentik-Username} + - HEADER_AUTH_EMAIL_HEADER=${HEADER_AUTH_EMAIL_HEADER:-X-Authentik-Email} + - HEADER_AUTH_NAME_HEADER=${HEADER_AUTH_NAME_HEADER:-X-Authentik-Name} + - HEADER_AUTH_AUTO_PROVISION=${HEADER_AUTH_AUTO_PROVISION:-false} + - HEADER_AUTH_ALLOWED_DOMAINS=${HEADER_AUTH_ALLOWED_DOMAINS:-} + healthcheck: + test: ["CMD", "wget", "--no-verbose", "--tries=3", "--spider", "http://localhost:4321/api/health"] + interval: 30s + timeout: 10s + retries: 5 + start_period: 15s + +# Define named volumes for database persistence +volumes: + gitea-mirror-data: # Database volume diff --git a/Divers/gitea-mirror/docker-entrypoint.sh b/Divers/gitea-mirror/docker-entrypoint.sh new file mode 100644 index 0000000..4e25c3e --- /dev/null +++ b/Divers/gitea-mirror/docker-entrypoint.sh @@ -0,0 +1,227 @@ +#!/bin/sh +set -e + + +# Ensure data directory exists +mkdir -p /app/data + +# Handle custom CA certificates +if [ -d "/app/certs" ] && [ "$(ls -A /app/certs/*.crt 2>/dev/null)" ]; then + echo "Custom CA certificates found, configuring Node.js to use them..." + + # Combine all CA certificates into a bundle for Node.js + CA_BUNDLE="/app/certs/ca-bundle.crt" + > "$CA_BUNDLE" + + for cert in /app/certs/*.crt; do + if [ -f "$cert" ]; then + echo "Adding certificate: $(basename "$cert")" + cat "$cert" >> "$CA_BUNDLE" + echo "" >> "$CA_BUNDLE" # Add newline between certificates + fi + done + + # Set Node.js to use the custom CA bundle + export NODE_EXTRA_CA_CERTS="$CA_BUNDLE" + echo "NODE_EXTRA_CA_CERTS set to: $NODE_EXTRA_CA_CERTS" + + # For Bun compatibility, also set the CA bundle in system location if writable + if [ -f "/etc/ssl/certs/ca-certificates.crt" ] && [ -w "/etc/ssl/certs/" ]; then + echo "Appending custom certificates to system CA bundle..." + cat "$CA_BUNDLE" >> /etc/ssl/certs/ca-certificates.crt + fi + +else + echo "No custom CA certificates found in /app/certs" +fi + +# Check if system CA bundle is mounted and use it (only if not already set) +if [ -z "$NODE_EXTRA_CA_CERTS" ] && [ -f "/etc/ssl/certs/ca-certificates.crt" ] && [ ! -L "/etc/ssl/certs/ca-certificates.crt" ]; then + # Check if it's a mounted file (not the default symlink) + if [ "$(stat -c '%d' /etc/ssl/certs/ca-certificates.crt 2>/dev/null)" != "$(stat -c '%d' / 2>/dev/null)" ] || \ + [ "$(stat -f '%d' /etc/ssl/certs/ca-certificates.crt 2>/dev/null)" != "$(stat -f '%d' / 2>/dev/null)" ]; then + echo "System CA bundle mounted, configuring Node.js to use it..." + export NODE_EXTRA_CA_CERTS="/etc/ssl/certs/ca-certificates.crt" + echo "NODE_EXTRA_CA_CERTS set to: $NODE_EXTRA_CA_CERTS" + fi +fi + +# Optional: If GITEA_SKIP_TLS_VERIFY is set, configure accordingly +if [ "$GITEA_SKIP_TLS_VERIFY" = "true" ]; then + echo "Warning: GITEA_SKIP_TLS_VERIFY is set to true. This is insecure!" + export NODE_TLS_REJECT_UNAUTHORIZED=0 +fi + +# Generate a secure BETTER_AUTH_SECRET if one isn't provided or is using the default value +BETTER_AUTH_SECRET_FILE="/app/data/.better_auth_secret" +JWT_SECRET_FILE="/app/data/.jwt_secret" # Old file for backward compatibility + +if [ "$BETTER_AUTH_SECRET" = "your-secret-key-change-this-in-production" ] || [ -z "$BETTER_AUTH_SECRET" ]; then + # Check if we have a previously generated secret + if [ -f "$BETTER_AUTH_SECRET_FILE" ]; then + echo "Using previously generated BETTER_AUTH_SECRET" + export BETTER_AUTH_SECRET=$(cat "$BETTER_AUTH_SECRET_FILE") + # Check for old JWT_SECRET file for backward compatibility + elif [ -f "$JWT_SECRET_FILE" ]; then + echo "Migrating from old JWT_SECRET to BETTER_AUTH_SECRET" + export BETTER_AUTH_SECRET=$(cat "$JWT_SECRET_FILE") + # Save to new file + echo "$BETTER_AUTH_SECRET" > "$BETTER_AUTH_SECRET_FILE" + chmod 600 "$BETTER_AUTH_SECRET_FILE" + # Optionally remove old file after successful migration + rm -f "$JWT_SECRET_FILE" + else + echo "Generating a secure random BETTER_AUTH_SECRET" + # Try to generate a secure random string using OpenSSL + if command -v openssl >/dev/null 2>&1; then + GENERATED_SECRET=$(openssl rand -hex 32) + else + # Fallback to using /dev/urandom if openssl is not available + echo "OpenSSL not found, using fallback method for random generation" + GENERATED_SECRET=$(head -c 32 /dev/urandom | sha256sum | cut -d' ' -f1) + fi + export BETTER_AUTH_SECRET="$GENERATED_SECRET" + # Save the secret to a file for persistence across container restarts + echo "$GENERATED_SECRET" > "$BETTER_AUTH_SECRET_FILE" + chmod 600 "$BETTER_AUTH_SECRET_FILE" + fi + echo "BETTER_AUTH_SECRET has been set to a secure random value" +fi + +# Generate a secure ENCRYPTION_SECRET if one isn't provided +ENCRYPTION_SECRET_FILE="/app/data/.encryption_secret" + +if [ -z "$ENCRYPTION_SECRET" ]; then + # Check if we have a previously generated secret + if [ -f "$ENCRYPTION_SECRET_FILE" ]; then + echo "Using previously generated ENCRYPTION_SECRET" + export ENCRYPTION_SECRET=$(cat "$ENCRYPTION_SECRET_FILE") + else + echo "Generating a secure random ENCRYPTION_SECRET" + # Generate a 48-character secret for encryption + if command -v openssl >/dev/null 2>&1; then + GENERATED_ENCRYPTION_SECRET=$(openssl rand -base64 36) + else + # Fallback to using /dev/urandom if openssl is not available + echo "OpenSSL not found, using fallback method for encryption secret generation" + GENERATED_ENCRYPTION_SECRET=$(head -c 36 /dev/urandom | base64 | tr -d '\n' | head -c 48) + fi + export ENCRYPTION_SECRET="$GENERATED_ENCRYPTION_SECRET" + # Save the secret to a file for persistence across container restarts + echo "$GENERATED_ENCRYPTION_SECRET" > "$ENCRYPTION_SECRET_FILE" + chmod 600 "$ENCRYPTION_SECRET_FILE" + fi + echo "ENCRYPTION_SECRET has been set to a secure random value" +fi + + + +# Skip dependency installation entirely for pre-built images +# Dependencies are already installed during the Docker build process + +# Initialize the database if it doesn't exist +# Note: Drizzle migrations will be run automatically when the app starts (see src/lib/db/index.ts) +if [ ! -f "/app/data/gitea-mirror.db" ]; then + echo "Database not found. It will be created and initialized via Drizzle migrations on first app startup..." + # Create empty database file so migrations can run + touch /app/data/gitea-mirror.db +else + echo "Database already exists, Drizzle will check for pending migrations on startup..." +fi + +# Extract version from package.json and set as environment variable +if [ -f "package.json" ]; then + export npm_package_version=$(grep -o '"version": *"[^"]*"' package.json | cut -d'"' -f4) + echo "Setting application version: $npm_package_version" +fi + + + +# Initialize configuration from environment variables if provided +echo "Checking for environment configuration..." +if [ -f "dist/scripts/startup-env-config.js" ]; then + echo "Loading configuration from environment variables..." + bun dist/scripts/startup-env-config.js + ENV_CONFIG_EXIT_CODE=$? +elif [ -f "scripts/startup-env-config.ts" ]; then + echo "Loading configuration from environment variables..." + bun scripts/startup-env-config.ts + ENV_CONFIG_EXIT_CODE=$? +else + echo "Environment configuration script not found. Skipping." + ENV_CONFIG_EXIT_CODE=0 +fi + +# Log environment config result +if [ $ENV_CONFIG_EXIT_CODE -eq 0 ]; then + echo "✅ Environment configuration loaded successfully" +else + echo "⚠️ Environment configuration loading completed with warnings" +fi + +# Run startup recovery to handle any interrupted jobs +echo "Running startup recovery..." +if [ -f "dist/scripts/startup-recovery.js" ]; then + echo "Running startup recovery using compiled script..." + bun dist/scripts/startup-recovery.js --timeout=30000 + RECOVERY_EXIT_CODE=$? +elif [ -f "scripts/startup-recovery.ts" ]; then + echo "Running startup recovery using TypeScript script..." + bun scripts/startup-recovery.ts --timeout=30000 + RECOVERY_EXIT_CODE=$? +else + echo "Warning: Startup recovery script not found. Skipping recovery." + RECOVERY_EXIT_CODE=0 +fi + +# Log recovery result +if [ $RECOVERY_EXIT_CODE -eq 0 ]; then + echo "✅ Startup recovery completed successfully" +elif [ $RECOVERY_EXIT_CODE -eq 1 ]; then + echo "⚠️ Startup recovery completed with warnings" +else + echo "❌ Startup recovery failed with exit code $RECOVERY_EXIT_CODE" +fi + +# Run repository status repair to fix any inconsistent mirroring states +echo "Running repository status repair..." +if [ -f "dist/scripts/repair-mirrored-repos.js" ]; then + echo "Running repository repair using compiled script..." + bun dist/scripts/repair-mirrored-repos.js --startup + REPAIR_EXIT_CODE=$? +elif [ -f "scripts/repair-mirrored-repos.ts" ]; then + echo "Running repository repair using TypeScript script..." + bun scripts/repair-mirrored-repos.ts --startup + REPAIR_EXIT_CODE=$? +else + echo "Warning: Repository repair script not found. Skipping repair." + REPAIR_EXIT_CODE=0 +fi + +# Log repair result +if [ $REPAIR_EXIT_CODE -eq 0 ]; then + echo "✅ Repository status repair completed successfully" +else + echo "⚠️ Repository status repair completed with warnings (exit code $REPAIR_EXIT_CODE)" +fi + +# Function to handle shutdown signals +shutdown_handler() { + echo "🛑 Received shutdown signal, forwarding to application..." + if [ ! -z "$APP_PID" ]; then + kill -TERM "$APP_PID" + wait "$APP_PID" + fi + exit 0 +} + +# Set up signal handlers +trap 'shutdown_handler' TERM INT HUP + +# Start the application +echo "Starting Gitea Mirror..." +bun ./dist/server/entry.mjs & +APP_PID=$! + +# Wait for the application to finish +wait "$APP_PID" diff --git a/Divers/gitea-mirror/docs/DEVELOPMENT_WORKFLOW.md b/Divers/gitea-mirror/docs/DEVELOPMENT_WORKFLOW.md new file mode 100644 index 0000000..bd2a6b0 --- /dev/null +++ b/Divers/gitea-mirror/docs/DEVELOPMENT_WORKFLOW.md @@ -0,0 +1,354 @@ +# Development Workflow + +This guide covers the development workflow for the open-source Gitea Mirror. + +## Getting Started + +### Prerequisites + +- Bun >= 1.2.9 +- Node.js >= 20 +- Git +- GitHub account (for API access) +- Gitea instance (for testing) + +### Initial Setup + +1. **Clone the repository**: +```bash +git clone https://github.com/RayLabsHQ/gitea-mirror.git +cd gitea-mirror +``` + +2. **Install dependencies and seed the SQLite database**: +```bash +bun run setup +``` + +3. **Configure environment (optional)**: +```bash +cp .env.example .env +# Edit .env with your settings +``` + +4. **Start the development server**: +```bash +bun run dev +``` + +## Development Commands + +| Command | Description | +|---------|-------------| +| `bun run dev` | Start the Bun + Astro dev server with hot reload | +| `bun run build` | Build the production bundle | +| `bun run preview` | Preview the production build locally | +| `bun test` | Run the Bun test suite | +| `bun test:watch` | Run tests in watch mode | +| `bun run db:studio` | Launch Drizzle Kit Studio | + +## Project Structure + +``` +gitea-mirror/ +├── src/ # Application UI, API routes, and services +│ ├── components/ # React components rendered inside Astro pages +│ ├── pages/ # Astro pages and API routes (e.g., /api/*) +│ ├── lib/ # Core logic: GitHub/Gitea clients, scheduler, recovery, db helpers +│ │ ├── db/ # Drizzle adapter + schema +│ │ ├── modules/ # Module wiring (jobs, integrations) +│ │ └── utils/ # Shared utilities +│ ├── hooks/ # React hooks +│ ├── content/ # In-app documentation and templated content +│ ├── layouts/ # Shared layout components +│ ├── styles/ # Tailwind CSS entrypoints +│ └── types/ # TypeScript types +├── scripts/ # Bun scripts for DB management and maintenance +├── www/ # Marketing site (Astro + MDX use cases) +├── public/ # Static assets served by Vite/Astro +└── tests/ # Dedicated integration/unit test helpers +``` + +## Feature Development + +### Adding a New Feature + +1. **Create feature branch**: +```bash +git checkout -b feature/my-feature +``` + +2. **Plan your changes**: +- UI components live in `src/components/` +- API endpoints live in `src/pages/api/` +- Database logic is under `src/lib/db/` (schema + adapter) +- Shared types are in `src/types/` + +3. **Implement the feature**: + +**Example: Adding a new API endpoint** +```typescript +// src/pages/api/my-endpoint.ts +import type { APIRoute } from 'astro'; +import { getUserFromCookie } from '@/lib/auth-utils'; + +export const GET: APIRoute = async ({ request }) => { + const user = await getUserFromCookie(request); + if (!user) { + return new Response('Unauthorized', { status: 401 }); + } + + // Your logic here + return new Response(JSON.stringify({ data: 'success' }), { + headers: { 'Content-Type': 'application/json' } + }); +}; +``` + +4. **Write tests**: +```typescript +// src/lib/my-feature.test.ts +import { describe, it, expect } from 'bun:test'; + +describe('My Feature', () => { + it('should work correctly', () => { + expect(myFunction()).toBe('expected'); + }); +}); +``` + +5. **Update documentation**: +- Add JSDoc comments +- Update README/docs if needed +- Document API changes + +## Database Development + +### Schema Changes + +1. **Modify schema**: +```typescript +// src/lib/db/schema.ts +export const myTable = sqliteTable('my_table', { + id: text('id').primaryKey(), + name: text('name').notNull(), + createdAt: integer('created_at').notNull(), +}); +``` + +2. **Generate migration**: +```bash +bun run db:generate +``` + +3. **Apply migration**: +```bash +bun run db:migrate +``` + +### Writing Queries + +```typescript +// src/lib/db/queries/my-queries.ts +import { db } from '../index'; +import { myTable } from '../schema'; + +export async function getMyData(userId: string) { + return db.select() + .from(myTable) + .where(eq(myTable.userId, userId)); +} +``` + +## Testing + +### Unit Tests + +```bash +# Run all tests +bun test + +# Run specific test file +bun test auth + +# Watch mode +bun test:watch + +# Coverage +bun test:coverage +``` + +### Manual Testing Checklist + +- [ ] Feature works as expected +- [ ] No console errors +- [ ] Responsive on mobile +- [ ] Handles errors gracefully +- [ ] Loading states work +- [ ] Form validation works +- [ ] API returns correct status codes + +## Debugging + +### VSCode Configuration + +Create `.vscode/launch.json`: +```json +{ + "version": "0.2.0", + "configurations": [ + { + "type": "bun", + "request": "launch", + "name": "Debug Bun", + "program": "${workspaceFolder}/src/index.ts", + "cwd": "${workspaceFolder}", + "env": { + "NODE_ENV": "development" + } + } + ] +} +``` + +### Debug Logging + +```typescript +// Development only logging +if (import.meta.env.DEV) { + console.log('[Debug]', data); +} +``` + +## Code Style + +### TypeScript + +- Use strict mode +- Define interfaces for all data structures +- Avoid `any` type +- Use proper error handling + +### React Components + +- Use functional components +- Implement proper loading states +- Handle errors with error boundaries +- Use TypeScript for props + +### API Routes + +- Always validate input +- Return proper status codes +- Use consistent error format +- Document with JSDoc + +## Git Workflow + +### Commit Messages + +Follow conventional commits: +``` +feat: add repository filtering +fix: resolve sync timeout issue +docs: update API documentation +style: format code with prettier +refactor: simplify auth logic +test: add user creation tests +chore: update dependencies +``` + +### Pull Request Process + +1. Create feature branch +2. Make changes +3. Write/update tests +4. Update documentation +5. Create PR with description +6. Address review feedback +7. Squash and merge + +## Performance + +### Development Tips + +- Use React DevTools +- Monitor bundle size +- Profile database queries +- Check memory usage + +### Optimization + +- Lazy load components +- Optimize images +- Use database indexes +- Cache API responses + +## Common Issues + +### Port Already in Use + +```bash +# Use different port +PORT=3001 bun run dev +``` + +### Database Locked + +```bash +# Reset database +bun run cleanup-db +bun run init-db +``` + +### Type Errors + +```bash +# Check types +bunx tsc --noEmit +``` + +## Release Process + +1. **Update version**: +```bash +npm version patch # or minor/major +``` + +2. **Update CHANGELOG.md** + +3. **Build and test**: +```bash +bun run build +bun test +``` + +4. **Create release**: +```bash +git tag v2.23.0 +git push origin v2.23.0 +``` + +5. **Create GitHub release** + +## Contributing + +1. Fork the repository +2. Create your feature branch +3. Commit your changes +4. Push to your fork +5. Create a Pull Request + +## Resources + +- [Astro Documentation](https://docs.astro.build) +- [Bun Documentation](https://bun.sh/docs) +- [Drizzle ORM](https://orm.drizzle.team) +- [React Documentation](https://react.dev) +- [TypeScript Handbook](https://www.typescriptlang.org/docs/) + +## Getting Help + +- Check existing [issues](https://github.com/yourusername/gitea-mirror/issues) +- Join [discussions](https://github.com/yourusername/gitea-mirror/discussions) +- Read the [FAQ](./FAQ.md) diff --git a/Divers/gitea-mirror/docs/ENVIRONMENT_VARIABLES.md b/Divers/gitea-mirror/docs/ENVIRONMENT_VARIABLES.md new file mode 100644 index 0000000..3e0b314 --- /dev/null +++ b/Divers/gitea-mirror/docs/ENVIRONMENT_VARIABLES.md @@ -0,0 +1,416 @@ +# Environment Variables Documentation + +This document provides a comprehensive list of all environment variables supported by Gitea Mirror. These can be used to configure the application via Docker or other deployment methods. + +## Environment Variables and UI Interaction + +When environment variables are set: +1. They are loaded on application startup +2. Values are stored in the database on first load +3. The UI will display these values and they can be modified +4. UI changes are saved to the database and persist +5. Environment variables provide initial defaults but don't override UI changes + +**Note**: Some critical settings like `GITEA_LFS`, `MIRROR_RELEASES`, and `MIRROR_METADATA` will be visible and configurable in the UI even when set via environment variables. + +## Table of Contents + +- [Core Configuration](#core-configuration) +- [GitHub Configuration](#github-configuration) +- [Gitea Configuration](#gitea-configuration) +- [Mirror Options](#mirror-options) +- [Automation Configuration](#automation-configuration) +- [Database Cleanup Configuration](#database-cleanup-configuration) +- [Authentication Configuration](#authentication-configuration) +- [Docker Configuration](#docker-configuration) + +## Core Configuration + +Essential application settings required for running Gitea Mirror. + +| Variable | Description | Default | Required | +|----------|-------------|---------|----------| +| `NODE_ENV` | Application environment | `production` | No | +| `HOST` | Server host binding | `0.0.0.0` | No | +| `PORT` | Server port | `4321` | No | +| `DATABASE_URL` | Database connection URL | `sqlite://data/gitea-mirror.db` | No | +| `BETTER_AUTH_SECRET` | Secret key for session signing (generate with: `openssl rand -base64 32`) | - | Yes | +| `BETTER_AUTH_URL` | Primary base URL for authentication. This should be the main URL where your application is accessed. | `http://localhost:4321` | No | +| `PUBLIC_BETTER_AUTH_URL` | Client-side auth URL for multi-origin access. Set this to your primary domain when you need to access the app from different origins (e.g., both IP and domain). The client will use this URL for all auth requests instead of the current browser origin. | - | No | +| `BETTER_AUTH_TRUSTED_ORIGINS` | Trusted origins for authentication requests. Comma-separated list of URLs. Use this to specify additional access URLs (e.g., local IP + domain: `http://10.10.20.45:4321,https://gitea-mirror.mydomain.tld`), SSO providers, reverse proxies, etc. | - | No | +| `ENCRYPTION_SECRET` | Optional encryption key for tokens (generate with: `openssl rand -base64 48`) | - | No | + +## GitHub Configuration + +Settings for connecting to and configuring GitHub repository sources. + +### Basic Settings + +| Variable | Description | Default | Options | +|----------|-------------|---------|---------| +| `GITHUB_USERNAME` | Your GitHub username | - | - | +| `GITHUB_TOKEN` | GitHub personal access token (requires repo and admin:org scopes) | - | - | +| `GITHUB_TYPE` | GitHub account type | `personal` | `personal`, `organization` | + +### Repository Selection + +| Variable | Description | Default | Options | +|----------|-------------|---------|---------| +| `PRIVATE_REPOSITORIES` | Include private repositories | `false` | `true`, `false` | +| `PUBLIC_REPOSITORIES` | Include public repositories | `true` | `true`, `false` | +| `INCLUDE_ARCHIVED` | Include archived repositories | `false` | `true`, `false` | +| `SKIP_FORKS` | Skip forked repositories | `false` | `true`, `false` | +| `MIRROR_STARRED` | Mirror starred repositories | `false` | `true`, `false` | +| `STARRED_REPOS_ORG` | Organization name for starred repos | `starred` | Any string | + +### Organization Settings + +| Variable | Description | Default | Options | +|----------|-------------|---------|---------| +| `MIRROR_ORGANIZATIONS` | Mirror organization repositories | `false` | `true`, `false` | +| `PRESERVE_ORG_STRUCTURE` | Preserve GitHub organization structure in Gitea | `false` | `true`, `false` | +| `ONLY_MIRROR_ORGS` | Only mirror organization repos (skip personal) | `false` | `true`, `false` | +| `MIRROR_STRATEGY` | Repository organization strategy | `preserve` | `preserve`, `single-org`, `flat-user`, `mixed` | + +### Advanced Settings + +| Variable | Description | Default | Options | +|----------|-------------|---------|---------| +| `SKIP_STARRED_ISSUES` | Enable lightweight mode for starred repos (skip issues) | `false` | `true`, `false` | + +## Gitea Configuration + +Settings for the destination Gitea instance. + +### Connection Settings + +| Variable | Description | Default | Options | +|----------|-------------|---------|---------| +| `GITEA_URL` | Gitea instance URL | - | Valid URL | +| `GITEA_TOKEN` | Gitea access token | - | - | +| `GITEA_USERNAME` | Gitea username | - | - | +| `GITEA_ORGANIZATION` | Default organization for single-org strategy | `github-mirrors` | Any string | + +### Repository Settings + +| Variable | Description | Default | Options | +|----------|-------------|---------|---------| +| `GITEA_ORG_VISIBILITY` | Default organization visibility | `public` | `public`, `private`, `limited`, `default` | +| `GITEA_MIRROR_INTERVAL` | Mirror sync interval - **automatically enables scheduled mirroring when set** | `8h` | Duration string (e.g., `30m`, `1h`, `8h`, `24h`, `1d`) or seconds | +| `GITEA_LFS` | Enable LFS support (requires LFS on Gitea server) - Shows in UI | `false` | `true`, `false` | +| `GITEA_CREATE_ORG` | Auto-create organizations | `true` | `true`, `false` | +| `GITEA_PRESERVE_VISIBILITY` | Preserve GitHub repo visibility in Gitea | `false` | `true`, `false` | + +### Template Settings + +| Variable | Description | Default | Options | +|----------|-------------|---------|---------| +| `GITEA_TEMPLATE_OWNER` | Template repository owner | - | Any string | +| `GITEA_TEMPLATE_REPO` | Template repository name | - | Any string | + +### Topic Settings + +| Variable | Description | Default | Options | +|----------|-------------|---------|---------| +| `GITEA_ADD_TOPICS` | Add topics to repositories | `true` | `true`, `false` | +| `GITEA_TOPIC_PREFIX` | Prefix for repository topics | - | Any string | + +### Fork Handling + +| Variable | Description | Default | Options | +|----------|-------------|---------|---------| +| `GITEA_FORK_STRATEGY` | How to handle forked repositories | `reference` | `skip`, `reference`, `full-copy` | + +### Additional Settings + +| Variable | Description | Default | Options | +|----------|-------------|---------|---------| +| `GITEA_SKIP_TLS_VERIFY` | Skip TLS certificate verification (WARNING: insecure) | `false` | `true`, `false` | + +## Mirror Options + +Control what content gets mirrored from GitHub to Gitea. + +| Variable | Description | Default | Options | +|----------|-------------|---------|---------| +| `MIRROR_RELEASES` | Mirror GitHub releases | `false` | `true`, `false` | +| `RELEASE_LIMIT` | Maximum number of releases to mirror per repository | `10` | Number (1-100) | +| `MIRROR_WIKI` | Mirror wiki content | `false` | `true`, `false` | +| `MIRROR_METADATA` | Master toggle for metadata mirroring | `false` | `true`, `false` | +| `MIRROR_ISSUES` | Mirror issues (requires MIRROR_METADATA=true) | `false` | `true`, `false` | +| `MIRROR_PULL_REQUESTS` | Mirror pull requests (requires MIRROR_METADATA=true) | `false` | `true`, `false` | +| `MIRROR_LABELS` | Mirror labels (requires MIRROR_METADATA=true) | `false` | `true`, `false` | +| `MIRROR_MILESTONES` | Mirror milestones (requires MIRROR_METADATA=true) | `false` | `true`, `false` | +| `MIRROR_ISSUE_CONCURRENCY` | Number of issues processed in parallel. Set above `1` to speed up mirroring at the risk of out-of-order creation. | `3` | Integer ≥ 1 | +| `MIRROR_PULL_REQUEST_CONCURRENCY` | Number of pull requests processed in parallel. Values above `1` may cause ordering differences. | `5` | Integer ≥ 1 | + +> **Ordering vs Throughput:** Metadata now mirrors sequentially by default to preserve chronology. Increase the concurrency variables only if you can tolerate minor out-of-order entries. + +## Automation Configuration + +Configure automatic scheduled mirroring. + +### Basic Schedule Settings + +| Variable | Description | Default | Options | +|----------|-------------|---------|---------| +| `SCHEDULE_ENABLED` | Enable automatic mirroring. **When set to `true`, automatically imports and mirrors all repositories on startup** (v3.5.3+) | `false` | `true`, `false` | +| `SCHEDULE_INTERVAL` | Interval in seconds or cron expression. **Supports cron syntax for scheduled runs** (e.g., `"0 2 * * *"` for 2 AM daily) | `3600` | Number (seconds) or cron string | +| `DELAY` | Legacy: same as SCHEDULE_INTERVAL | `3600` | Number (seconds) | + +> **🚀 Auto-Start Feature (v3.5.3+)** +> Setting either `SCHEDULE_ENABLED=true` or `GITEA_MIRROR_INTERVAL` triggers auto-start functionality where the service will: +> 1. **Import** all GitHub repositories on startup +> 2. **Mirror** them to Gitea immediately +> 3. **Continue syncing** at the configured interval +> 4. **Auto-discover** new repositories +> 5. **Clean up** deleted repositories (if configured) +> +> This eliminates the need for manual button clicks - perfect for Docker/Kubernetes deployments! + +> **⏰ Scheduling with Cron Expressions** +> Use cron expressions in `SCHEDULE_INTERVAL` to run at specific times: +> - `"0 2 * * *"` - Daily at 2 AM +> - `"0 */6 * * *"` - Every 6 hours +> - `"0 0 * * 0"` - Weekly on Sunday at midnight +> - `"0 3 * * 1-5"` - Weekdays at 3 AM (Monday-Friday) +> +> This is useful for optimizing bandwidth usage during low-activity periods. + +### Execution Settings + +| Variable | Description | Default | Options | +|----------|-------------|---------|---------| +| `SCHEDULE_CONCURRENT` | Allow concurrent mirror operations | `false` | `true`, `false` | +| `SCHEDULE_BATCH_SIZE` | Number of repos to process in parallel | `10` | Number | +| `SCHEDULE_PAUSE_BETWEEN_BATCHES` | Pause between batches (milliseconds) | `5000` | Number | + +### Retry Configuration + +| Variable | Description | Default | Options | +|----------|-------------|---------|---------| +| `SCHEDULE_RETRY_ATTEMPTS` | Number of retry attempts | `3` | Number | +| `SCHEDULE_RETRY_DELAY` | Delay between retries (milliseconds) | `60000` | Number | +| `SCHEDULE_TIMEOUT` | Max time for a mirror operation (milliseconds) | `3600000` | Number | +| `SCHEDULE_AUTO_RETRY` | Automatically retry failed operations | `true` | `true`, `false` | + +### Update Detection + +| Variable | Description | Default | Options | +|----------|-------------|---------|---------| +| `AUTO_IMPORT_REPOS` | Automatically discover and import new GitHub repositories during scheduled syncs | `true` | `true`, `false` | +| `AUTO_MIRROR_REPOS` | Automatically mirror newly imported repositories during scheduled syncs (no manual “Mirror All” required) | `false` | `true`, `false` | +| `SCHEDULE_ONLY_MIRROR_UPDATED` | Only mirror repos with updates | `false` | `true`, `false` | +| `SCHEDULE_UPDATE_INTERVAL` | Check for updates interval (milliseconds) | `86400000` | Number | +| `SCHEDULE_SKIP_RECENTLY_MIRRORED` | Skip recently mirrored repos | `true` | `true`, `false` | +| `SCHEDULE_RECENT_THRESHOLD` | Skip if mirrored within this time (milliseconds) | `3600000` | Number | + +### Maintenance & Notifications + +| Variable | Description | Default | Options | +|----------|-------------|---------|---------| +| `SCHEDULE_CLEANUP_BEFORE_MIRROR` | Run cleanup before mirroring | `false` | `true`, `false` | +| `SCHEDULE_NOTIFY_ON_FAILURE` | Send notifications on failure | `true` | `true`, `false` | +| `SCHEDULE_NOTIFY_ON_SUCCESS` | Send notifications on success | `false` | `true`, `false` | +| `SCHEDULE_LOG_LEVEL` | Logging level | `info` | `error`, `warn`, `info`, `debug` | +| `SCHEDULE_TIMEZONE` | Timezone for scheduling | `UTC` | Valid timezone string | + +## Database Cleanup Configuration + +Configure automatic cleanup of old events and data. + +### Basic Settings + +| Variable | Description | Default | Options | +|----------|-------------|---------|---------| +| `CLEANUP_ENABLED` | Enable automatic cleanup | `false` | `true`, `false` | +| `CLEANUP_RETENTION_DAYS` | Days to keep events | `7` | Number | + +### Repository Cleanup + +| Variable | Description | Default | Options | +|----------|-------------|---------|---------| +| `CLEANUP_DELETE_FROM_GITEA` | Delete repositories from Gitea | `false` | `true`, `false` | +| `CLEANUP_DELETE_IF_NOT_IN_GITHUB` | Delete repos not found in GitHub (automatically enables cleanup) | `true` | `true`, `false` | +| `CLEANUP_ORPHANED_REPO_ACTION` | Action for orphaned repositories. **Note**: `archive` is recommended to preserve backups | `archive` | `skip`, `archive`, `delete` | +| `CLEANUP_DRY_RUN` | Test mode without actual deletion | `false` | `true`, `false` | +| `CLEANUP_PROTECTED_REPOS` | Comma-separated list of protected repository names | - | Comma-separated strings | + +**🛡️ Safety Features (Backup Protection)**: +- **GitHub Failures Don't Delete Backups**: Cleanup is automatically skipped if GitHub API returns errors (404, 403, connection issues) +- **Archive Never Deletes**: The `archive` action ALWAYS preserves repository data, it never deletes +- **Graceful Degradation**: If marking as archived fails, the repository remains fully accessible in Gitea +- **The Purpose of Backups**: Your mirrors are preserved even when GitHub sources disappear - that's the whole point! + +**Archive Behavior (Aligned with Gitea API)**: +- **Regular repositories**: Uses Gitea's native archive feature (PATCH `/repos/{owner}/{repo}` with `archived: true`) + - Makes repository read-only while preserving all data +- **Mirror repositories**: Uses rename strategy (Gitea API returns 422 for archiving mirrors) + - Renamed with `archived-` prefix for clear identification + - Description updated with preservation notice and timestamp + - Mirror interval set to 8760h (1 year) to minimize sync attempts + - Repository remains fully accessible and cloneable +- **Manual Sync Option**: Archived mirrors are still available on the Repositories page with automatic syncs disabled—use the `Manual Sync` action to refresh them on demand. + +### Execution Settings + +| Variable | Description | Default | Options | +|----------|-------------|---------|---------| +| `CLEANUP_BATCH_SIZE` | Number of items to process per batch | `10` | Number | +| `CLEANUP_PAUSE_BETWEEN_DELETES` | Pause between deletions (milliseconds) | `2000` | Number | + +## Authentication Configuration + +Configure authentication methods and SSO. + +### Header Authentication (Reverse Proxy SSO) + +| Variable | Description | Default | Options | +|----------|-------------|---------|---------| +| `HEADER_AUTH_ENABLED` | Enable header-based authentication | `false` | `true`, `false` | +| `HEADER_AUTH_USER_HEADER` | Header containing username | `X-Authentik-Username` | Header name | +| `HEADER_AUTH_EMAIL_HEADER` | Header containing email | `X-Authentik-Email` | Header name | +| `HEADER_AUTH_NAME_HEADER` | Header containing display name | `X-Authentik-Name` | Header name | +| `HEADER_AUTH_AUTO_PROVISION` | Auto-create users from headers | `false` | `true`, `false` | +| `HEADER_AUTH_ALLOWED_DOMAINS` | Comma-separated list of allowed email domains | - | Comma-separated domains | + +## Docker Configuration + +Settings specific to Docker deployments. + +| Variable | Description | Default | Options | +|----------|-------------|---------|---------| +| `DOCKER_REGISTRY` | Docker registry URL | `ghcr.io` | Registry URL | +| `DOCKER_IMAGE` | Docker image name | `raylabshq/gitea-mirror:` | Image name | +| `DOCKER_TAG` | Docker image tag | `latest` | Tag name | + +## Example Docker Compose Configuration + +Here's an example of how to use these environment variables in a `docker-compose.yml` file: + +```yaml +version: '3.8' + +services: + gitea-mirror: + image: ghcr.io/raylabshq/gitea-mirror:latest + container_name: gitea-mirror + environment: + # Core Configuration + - NODE_ENV=production + - DATABASE_URL=file:data/gitea-mirror.db + - BETTER_AUTH_SECRET=your-secure-secret-here + # Primary access URL: + - BETTER_AUTH_URL=https://gitea-mirror.mydomain.tld + # Additional access URLs (local network + SSO providers): + # - BETTER_AUTH_TRUSTED_ORIGINS=http://10.10.20.45:4321,http://192.168.1.100:4321,https://auth.provider.com + + # GitHub Configuration + - GITHUB_USERNAME=your-username + - GITHUB_TOKEN=ghp_your_token_here + - PRIVATE_REPOSITORIES=true + - MIRROR_STARRED=true + - SKIP_FORKS=false + + # Gitea Configuration + - GITEA_URL=http://gitea:3000 + - GITEA_USERNAME=admin + - GITEA_TOKEN=your-gitea-token + - GITEA_ORGANIZATION=github-mirrors + - GITEA_ORG_VISIBILITY=public + + # Mirror Options + - MIRROR_RELEASES=true + - MIRROR_WIKI=true + - MIRROR_METADATA=true + - MIRROR_ISSUES=true + - MIRROR_PULL_REQUESTS=true + + # Automation + - SCHEDULE_ENABLED=true + - SCHEDULE_INTERVAL=3600 + + # Cleanup + - CLEANUP_ENABLED=true + - CLEANUP_RETENTION_DAYS=30 + volumes: + - ./data:/app/data + ports: + - "4321:4321" +``` + +## Authentication URL Configuration + +### Multiple Access URLs + +To allow access to Gitea Mirror through multiple URLs (e.g., local IP and public domain), you need to configure both server and client settings: + +**Example Configuration:** +```bash +# Primary URL (required) - where the auth server is hosted +BETTER_AUTH_URL=https://gitea-mirror.mydomain.tld + +# Client-side URL (optional) - tells the browser where to send auth requests +# Set this to your primary domain when accessing from different origins +PUBLIC_BETTER_AUTH_URL=https://gitea-mirror.mydomain.tld + +# Additional trusted origins (optional) - origins allowed to make auth requests +BETTER_AUTH_TRUSTED_ORIGINS=http://10.10.20.45:4321,http://192.168.1.100:4321 +``` + +This setup allows you to: +- Access via local network IP: `http://10.10.20.45:4321` +- Access via public domain: `https://gitea-mirror.mydomain.tld` +- Auth requests from the IP will be sent to the domain (via `PUBLIC_BETTER_AUTH_URL`) +- Each origin requires separate login due to browser cookie isolation + +**Important:** When accessing from different origins (IP vs domain), you'll need to log in separately on each origin as cookies cannot be shared across different origins for security reasons. + +### Trusted Origins + +The `BETTER_AUTH_TRUSTED_ORIGINS` variable serves multiple purposes: + +1. **SSO/OIDC Providers**: When using external authentication providers (Google, Authentik, Okta) +2. **Reverse Proxies**: When running behind nginx, Traefik, or other proxies +3. **Cross-Origin Requests**: When the frontend and backend are on different domains +4. **Development**: When testing from different URLs + +**Example Scenarios:** +```bash +# For Authentik SSO integration +BETTER_AUTH_TRUSTED_ORIGINS=https://authentik.company.com,https://auth.company.com + +# For reverse proxy setup +BETTER_AUTH_TRUSTED_ORIGINS=https://proxy.internal,https://public.domain.com + +# For development with multiple environments +BETTER_AUTH_TRUSTED_ORIGINS=http://localhost:3000,http://192.168.1.100:3000 +``` + +**Important Notes:** +- All URLs from `BETTER_AUTH_URL` are automatically trusted +- URLs must be complete with protocol (http/https) +- Multiple origins are separated by commas +- No trailing slashes needed + +## Notes + +1. **First Run**: Environment variables are loaded when the container starts. The configuration is applied after the first user account is created. + +2. **UI Priority**: Manual changes made through the web UI will be preserved. Environment variables only set values for empty fields. + +3. **Token Security**: All tokens are encrypted before being stored in the database. + +4. **Auto-Enabling Features**: Certain environment variables automatically enable features when set: + - `GITEA_MIRROR_INTERVAL` - Automatically enables scheduled mirroring + - `CLEANUP_DELETE_IF_NOT_IN_GITHUB=true` - Automatically enables repository cleanup + - `SCHEDULE_INTERVAL` or `DELAY` - Automatically enables the scheduler + +5. **Backward Compatibility**: The `DELAY` variable is maintained for backward compatibility but `SCHEDULE_INTERVAL` is preferred. + +6. **Required Scopes**: The GitHub token requires the following scopes: + - `repo` (full control of private repositories) + - `admin:org` (read organization data) + - Additional scopes may be required for specific features + +For more examples and detailed configuration, see the `.env.example` file in the repository. diff --git a/Divers/gitea-mirror/docs/GRACEFUL_SHUTDOWN.md b/Divers/gitea-mirror/docs/GRACEFUL_SHUTDOWN.md new file mode 100644 index 0000000..0b11dfc --- /dev/null +++ b/Divers/gitea-mirror/docs/GRACEFUL_SHUTDOWN.md @@ -0,0 +1,249 @@ +# Graceful Shutdown and Enhanced Job Recovery + +This document describes the graceful shutdown and enhanced job recovery capabilities implemented in gitea-mirror v2.8.0+. + +## Overview + +The gitea-mirror application now includes comprehensive graceful shutdown handling and enhanced job recovery mechanisms designed specifically for containerized environments. These features ensure: + +- **No data loss** during container restarts or shutdowns +- **Automatic job resumption** after application restarts +- **Clean termination** of all active processes and connections +- **Container-aware design** optimized for Docker/LXC deployments + +## Features + +### 1. Graceful Shutdown Manager + +The shutdown manager (`src/lib/shutdown-manager.ts`) provides centralized coordination of application termination: + +#### Key Capabilities: +- **Active Job Tracking**: Monitors all running mirroring/sync jobs +- **State Persistence**: Saves job progress to database before shutdown +- **Callback System**: Allows services to register cleanup functions +- **Timeout Protection**: Prevents hanging shutdowns with configurable timeouts +- **Signal Coordination**: Works with signal handlers for proper container lifecycle + +#### Configuration: +- **Shutdown Timeout**: 30 seconds maximum (configurable) +- **Job Save Timeout**: 10 seconds per job (configurable) + +### 2. Signal Handlers + +The signal handler system (`src/lib/signal-handlers.ts`) ensures proper response to container lifecycle events: + +#### Supported Signals: +- **SIGTERM**: Docker stop, Kubernetes pod termination +- **SIGINT**: Ctrl+C, manual interruption +- **SIGHUP**: Terminal hangup, service reload +- **Uncaught Exceptions**: Emergency shutdown on critical errors +- **Unhandled Rejections**: Graceful handling of promise failures + +### 3. Enhanced Job Recovery + +Building on the existing recovery system, new enhancements include: + +#### Shutdown-Aware Processing: +- Jobs check for shutdown signals during execution +- Automatic state saving when shutdown is detected +- Proper job status management (interrupted vs failed) + +#### Container Integration: +- Docker entrypoint script forwards signals correctly +- Startup recovery runs before main application +- Recovery timeouts prevent startup delays + +## Usage + +### Basic Operation + +The graceful shutdown system is automatically initialized when the application starts. No manual configuration is required for basic operation. + +### Testing + +Test the graceful shutdown functionality: + +```bash +# Run the integration test +bun run test-shutdown + +# Clean up test data +bun run test-shutdown-cleanup + +# Run unit tests +bun test src/lib/shutdown-manager.test.ts +bun test src/lib/signal-handlers.test.ts +``` + +### Manual Testing + +1. **Start the application**: + ```bash + bun run dev + # or in production + bun run start + ``` + +2. **Start a mirroring job** through the web interface + +3. **Send shutdown signal**: + ```bash + # Send SIGTERM (recommended) + kill -TERM + + # Or use Ctrl+C for SIGINT + ``` + +4. **Verify job state** is saved and can be resumed on restart + +### Container Testing + +Test with Docker: + +```bash +# Build and run container +docker build -t gitea-mirror . +docker run -d --name test-shutdown gitea-mirror + +# Start a job, then stop container +docker stop test-shutdown + +# Restart and verify recovery +docker start test-shutdown +docker logs test-shutdown +``` + +## Implementation Details + +### Shutdown Flow + +1. **Signal Reception**: Signal handlers detect termination request +2. **Shutdown Initiation**: Shutdown manager begins graceful termination +3. **Job State Saving**: All active jobs save current progress to database +4. **Service Cleanup**: Registered callbacks stop background services +5. **Connection Cleanup**: Database connections and resources are released +6. **Process Termination**: Application exits with appropriate code + +### Job State Management + +During shutdown, active jobs are updated with: +- `inProgress: false` - Mark as not currently running +- `lastCheckpoint: ` - Record shutdown time +- `message: "Job interrupted by application shutdown - will resume on restart"` +- Status remains as `"imported"` (not `"failed"`) to enable recovery + +### Recovery Integration + +The existing recovery system automatically detects and resumes interrupted jobs: +- Jobs with `inProgress: false` and incomplete status are candidates for recovery +- Recovery runs during application startup (before serving requests) +- Jobs resume from their last checkpoint with remaining items + +## Configuration + +### Environment Variables + +```bash +# Optional: Adjust shutdown timeout (default: 30000ms) +SHUTDOWN_TIMEOUT=30000 + +# Optional: Adjust job save timeout (default: 10000ms) +JOB_SAVE_TIMEOUT=10000 +``` + +### Docker Configuration + +The Docker entrypoint script includes proper signal handling: + +```dockerfile +# Signals are forwarded to the application process +# SIGTERM is handled gracefully with 30-second timeout +# Container stops cleanly without force-killing processes +``` + +### Kubernetes Configuration + +For Kubernetes deployments, configure appropriate termination grace period: + +```yaml +apiVersion: v1 +kind: Pod +spec: + terminationGracePeriodSeconds: 45 # Allow time for graceful shutdown + containers: + - name: gitea-mirror + # ... other configuration +``` + +## Monitoring and Debugging + +### Logs + +The application provides detailed logging during shutdown: + +``` +🛑 Graceful shutdown initiated by signal: SIGTERM +📊 Shutdown status: 2 active jobs, 1 callbacks +📝 Step 1: Saving active job states... +Saving state for job abc-123... +✅ Saved state for job abc-123 +🔧 Step 2: Executing shutdown callbacks... +✅ Shutdown callback 1 completed +💾 Step 3: Closing database connections... +✅ Graceful shutdown completed successfully +``` + +### Status Endpoints + +Check shutdown manager status via API: + +```bash +# Get current status (if application is running) +curl http://localhost:4321/api/health +``` + +### Troubleshooting + +**Problem**: Jobs not resuming after restart +- **Check**: Startup recovery logs for errors +- **Verify**: Database contains interrupted jobs with correct status +- **Test**: Run `bun run startup-recovery` manually + +**Problem**: Shutdown timeout reached +- **Check**: Job complexity and database performance +- **Adjust**: Increase `SHUTDOWN_TIMEOUT` environment variable +- **Monitor**: Database connection and disk I/O during shutdown + +**Problem**: Container force-killed +- **Check**: Container orchestrator termination grace period +- **Adjust**: Increase grace period to allow shutdown completion +- **Monitor**: Application shutdown logs for timing issues + +## Best Practices + +### Development +- Always test graceful shutdown during development +- Use the provided test scripts to verify functionality +- Monitor logs for shutdown timing and job state persistence + +### Production +- Set appropriate container termination grace periods +- Monitor shutdown logs for performance issues +- Use health checks to verify application readiness after restart +- Consider job complexity when planning maintenance windows + +### Monitoring +- Track job recovery success rates +- Monitor shutdown duration metrics +- Alert on forced terminations or recovery failures +- Log analysis for shutdown pattern optimization + +## Future Enhancements + +Planned improvements for future versions: + +1. **Configurable Timeouts**: Environment variable configuration for all timeouts +2. **Shutdown Metrics**: Prometheus metrics for shutdown performance +3. **Progressive Shutdown**: Graceful degradation of service capabilities +4. **Job Prioritization**: Priority-based job saving during shutdown +5. **Health Check Integration**: Readiness probes during shutdown process diff --git a/Divers/gitea-mirror/docs/NIX_DEPLOYMENT.md b/Divers/gitea-mirror/docs/NIX_DEPLOYMENT.md new file mode 100644 index 0000000..2de0bae --- /dev/null +++ b/Divers/gitea-mirror/docs/NIX_DEPLOYMENT.md @@ -0,0 +1,486 @@ +# Nix Deployment Guide + +This guide covers deploying Gitea Mirror using Nix flakes. The Nix deployment follows the same minimal configuration philosophy as `docker-compose.alt.yml` - secrets are auto-generated, and everything else can be configured via the web UI. + +## Prerequisites + +- Nix 2.4+ installed +- For NixOS module: NixOS 23.05+ + +### Enable Flakes (Recommended) + +To enable flakes permanently and avoid typing flags, add to `/etc/nix/nix.conf` or `~/.config/nix/nix.conf`: +``` +experimental-features = nix-command flakes +``` + +**Note:** If you don't enable flakes globally, add `--extra-experimental-features 'nix-command flakes'` to all nix commands shown below. + +## Quick Start (Zero Configuration!) + +### Run Immediately - No Setup Required + +```bash +# Run directly from the flake (local) +nix run --extra-experimental-features 'nix-command flakes' .#gitea-mirror + +# Or from GitHub (once published) +nix run --extra-experimental-features 'nix-command flakes' github:RayLabsHQ/gitea-mirror + +# If you have flakes enabled globally, simply: +nix run .#gitea-mirror +``` + +That's it! On first run: +- Secrets (`BETTER_AUTH_SECRET` and `ENCRYPTION_SECRET`) are auto-generated +- Database is automatically created and initialized +- Startup recovery and repair scripts run automatically +- Access the web UI at http://localhost:4321 + +Everything else (GitHub credentials, Gitea settings, mirror options) is configured through the web interface after signup. + +### Development Environment + +```bash +# Enter development shell with all dependencies +nix develop --extra-experimental-features 'nix-command flakes' + +# Or use direnv for automatic environment loading (handles flags automatically) +echo "use flake" > .envrc +direnv allow +``` + +### Build and Install + +```bash +# Build the package +nix build --extra-experimental-features 'nix-command flakes' + +# Run the built package +./result/bin/gitea-mirror + +# Install to your profile +nix profile install --extra-experimental-features 'nix-command flakes' .#gitea-mirror +``` + +## What Happens on First Run? + +Following the same pattern as the Docker deployment, the Nix package automatically: + +1. **Creates data directory**: `~/.local/share/gitea-mirror` (or `$DATA_DIR`) +2. **Generates secrets** (stored securely in data directory): + - `BETTER_AUTH_SECRET` - Session authentication (32-char hex) + - `ENCRYPTION_SECRET` - Token encryption (48-char base64) +3. **Initializes database**: SQLite database with Drizzle migrations +4. **Runs startup scripts**: + - Environment configuration loader + - Crash recovery for interrupted jobs + - Repository status repair +5. **Starts the application** with graceful shutdown handling + +## NixOS Module - Minimal Deployment + +### Simplest Possible Configuration + +Add to your NixOS configuration (`/etc/nixos/configuration.nix`): + +```nix +{ + inputs = { + nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; + gitea-mirror.url = "github:RayLabsHQ/gitea-mirror"; + }; + + outputs = { nixpkgs, gitea-mirror, ... }: { + nixosConfigurations.your-hostname = nixpkgs.lib.nixosSystem { + system = "x86_64-linux"; + modules = [ + gitea-mirror.nixosModules.default + { + # That's it! Just enable the service + services.gitea-mirror.enable = true; + } + ]; + }; + }; +} +``` + +Apply with: +```bash +sudo nixos-rebuild switch +``` + +Access at http://localhost:4321, sign up (first user is admin), and configure everything via the web UI. + +### Production Configuration + +For production with custom domain and firewall: + +```nix +{ + services.gitea-mirror = { + enable = true; + host = "0.0.0.0"; + port = 4321; + betterAuthUrl = "https://mirror.example.com"; + betterAuthTrustedOrigins = "https://mirror.example.com"; + openFirewall = true; + }; + + # Optional: Use with nginx reverse proxy + services.nginx = { + enable = true; + virtualHosts."mirror.example.com" = { + locations."/" = { + proxyPass = "http://127.0.0.1:4321"; + proxyWebsockets = true; + }; + enableACME = true; + forceSSL = true; + }; + }; +} +``` + +### Advanced: Manual Secret Management + +If you prefer to manage secrets manually (e.g., with sops-nix or agenix): + +1. Create a secrets file: +```bash +# /var/lib/gitea-mirror/secrets.env +BETTER_AUTH_SECRET=your-32-character-minimum-secret-key-here +ENCRYPTION_SECRET=your-encryption-secret-here +``` + +2. Reference it in your configuration: +```nix +{ + services.gitea-mirror = { + enable = true; + environmentFile = "/var/lib/gitea-mirror/secrets.env"; + }; +} +``` + +### Full Configuration Options + +```nix +{ + services.gitea-mirror = { + enable = true; + package = gitea-mirror.packages.x86_64-linux.default; # Override package + dataDir = "/var/lib/gitea-mirror"; + user = "gitea-mirror"; + group = "gitea-mirror"; + host = "0.0.0.0"; + port = 4321; + betterAuthUrl = "https://mirror.example.com"; + betterAuthTrustedOrigins = "https://mirror.example.com"; + + # Concurrency controls (match docker-compose.alt.yml) + mirrorIssueConcurrency = 3; # Set to 1 for perfect chronological order + mirrorPullRequestConcurrency = 5; # Set to 1 for perfect chronological order + + environmentFile = null; # Optional secrets file + openFirewall = true; + }; +} +``` + +## Service Management (NixOS) + +```bash +# Start the service +sudo systemctl start gitea-mirror + +# Stop the service +sudo systemctl stop gitea-mirror + +# Restart the service +sudo systemctl restart gitea-mirror + +# Check status +sudo systemctl status gitea-mirror + +# View logs +sudo journalctl -u gitea-mirror -f + +# Health check +curl http://localhost:4321/api/health +``` + +## Environment Variables + +All variables from `docker-compose.alt.yml` are supported: + +```bash +# === AUTO-GENERATED (Don't set unless you want specific values) === +BETTER_AUTH_SECRET # Auto-generated, stored in data dir +ENCRYPTION_SECRET # Auto-generated, stored in data dir + +# === CORE SETTINGS (Have good defaults) === +DATA_DIR="$HOME/.local/share/gitea-mirror" +DATABASE_URL="file:$DATA_DIR/gitea-mirror.db" +HOST="0.0.0.0" +PORT="4321" +NODE_ENV="production" + +# === BETTER AUTH (Override for custom domains) === +BETTER_AUTH_URL="http://localhost:4321" +BETTER_AUTH_TRUSTED_ORIGINS="http://localhost:4321" +PUBLIC_BETTER_AUTH_URL="http://localhost:4321" + +# === CONCURRENCY CONTROLS === +MIRROR_ISSUE_CONCURRENCY=3 # Default: 3 (set to 1 for perfect order) +MIRROR_PULL_REQUEST_CONCURRENCY=5 # Default: 5 (set to 1 for perfect order) + +# === CONFIGURE VIA WEB UI (Not needed at startup) === +# GitHub credentials, Gitea settings, mirror options, scheduling, etc. +# All configured after signup through the web interface +``` + +## Database Management + +The Nix package includes a database management helper: + +```bash +# Initialize database (done automatically on first run) +gitea-mirror-db init + +# Check database health +gitea-mirror-db check + +# Fix database issues +gitea-mirror-db fix + +# Reset users +gitea-mirror-db reset-users +``` + +## Home Manager Integration + +For single-user deployments: + +```nix +{ config, pkgs, ... }: +let + gitea-mirror = (import (fetchTarball "https://github.com/RayLabsHQ/gitea-mirror/archive/main.tar.gz")).packages.${pkgs.system}.default; +in { + home.packages = [ gitea-mirror ]; + + # Optional: Run as user service + systemd.user.services.gitea-mirror = { + Unit = { + Description = "Gitea Mirror Service"; + After = [ "network.target" ]; + }; + + Service = { + Type = "simple"; + ExecStart = "${gitea-mirror}/bin/gitea-mirror"; + Restart = "always"; + Environment = [ + "DATA_DIR=%h/.local/share/gitea-mirror" + "HOST=127.0.0.1" + "PORT=4321" + ]; + }; + + Install = { + WantedBy = [ "default.target" ]; + }; + }; +} +``` + +## Docker Image from Nix (Optional) + +You can also use Nix to create a Docker image: + +```nix +# Add to flake.nix packages section +dockerImage = pkgs.dockerTools.buildLayeredImage { + name = "gitea-mirror"; + tag = "latest"; + contents = [ self.packages.${system}.default pkgs.cacert pkgs.openssl ]; + config = { + Cmd = [ "${self.packages.${system}.default}/bin/gitea-mirror" ]; + ExposedPorts = { "4321/tcp" = {}; }; + Env = [ + "DATA_DIR=/data" + "DATABASE_URL=file:/data/gitea-mirror.db" + ]; + Volumes = { "/data" = {}; }; + }; +}; +``` + +Build and load: +```bash +nix build --extra-experimental-features 'nix-command flakes' .#dockerImage +docker load < result +docker run -p 4321:4321 -v gitea-mirror-data:/data gitea-mirror:latest +``` + +## Comparison: Docker vs Nix + +Both deployment methods follow the same philosophy: + +| Feature | Docker Compose | Nix | +|---------|---------------|-----| +| **Configuration** | Minimal (only BETTER_AUTH_SECRET) | Zero config (auto-generated) | +| **Secret Generation** | Auto-generated & persisted | Auto-generated & persisted | +| **Database Init** | Automatic on first run | Automatic on first run | +| **Startup Scripts** | Runs recovery/repair/env-config | Runs recovery/repair/env-config | +| **Graceful Shutdown** | Signal handling in entrypoint | Signal handling in wrapper | +| **Health Check** | Docker healthcheck | systemd timer (optional) | +| **Updates** | `docker pull` | `nix flake update && nixos-rebuild` | + +## Troubleshooting + +### Check Auto-Generated Secrets +```bash +# For standalone +cat ~/.local/share/gitea-mirror/.better_auth_secret +cat ~/.local/share/gitea-mirror/.encryption_secret + +# For NixOS service +sudo cat /var/lib/gitea-mirror/.better_auth_secret +sudo cat /var/lib/gitea-mirror/.encryption_secret +``` + +### Database Issues +```bash +# Check if database exists +ls -la ~/.local/share/gitea-mirror/gitea-mirror.db + +# Reinitialize (deletes all data!) +rm ~/.local/share/gitea-mirror/gitea-mirror.db +gitea-mirror-db init +``` + +### Permission Issues (NixOS) +```bash +sudo chown -R gitea-mirror:gitea-mirror /var/lib/gitea-mirror +sudo chmod 700 /var/lib/gitea-mirror +``` + +### Port Already in Use +```bash +# Change port +export PORT=8080 +gitea-mirror + +# Or in NixOS config +services.gitea-mirror.port = 8080; +``` + +### View Startup Logs +```bash +# Standalone (verbose output on console) +gitea-mirror + +# NixOS service +sudo journalctl -u gitea-mirror -f --since "5 minutes ago" +``` + +## Updating + +### Standalone Installation +```bash +# Update flake lock +nix flake update --extra-experimental-features 'nix-command flakes' + +# Rebuild +nix build --extra-experimental-features 'nix-command flakes' + +# Or update profile +nix profile upgrade --extra-experimental-features 'nix-command flakes' gitea-mirror +``` + +### NixOS +```bash +# Update input +sudo nix flake lock --update-input gitea-mirror --extra-experimental-features 'nix-command flakes' + +# Rebuild system +sudo nixos-rebuild switch --flake .#your-hostname +``` + +## Migration from Docker + +To migrate from Docker to Nix while keeping your data: + +1. **Stop Docker container:** + ```bash + docker-compose -f docker-compose.alt.yml down + ``` + +2. **Copy data directory:** + ```bash + # For standalone + cp -r ./data ~/.local/share/gitea-mirror + + # For NixOS + sudo cp -r ./data /var/lib/gitea-mirror + sudo chown -R gitea-mirror:gitea-mirror /var/lib/gitea-mirror + ``` + +3. **Copy secrets (if you want to keep them):** + ```bash + # Extract from Docker volume + docker run --rm -v gitea-mirror_data:/data alpine \ + cat /data/.better_auth_secret > better_auth_secret + docker run --rm -v gitea-mirror_data:/data alpine \ + cat /data/.encryption_secret > encryption_secret + + # Copy to new location + cp better_auth_secret ~/.local/share/gitea-mirror/.better_auth_secret + cp encryption_secret ~/.local/share/gitea-mirror/.encryption_secret + chmod 600 ~/.local/share/gitea-mirror/.*_secret + ``` + +4. **Start Nix version:** + ```bash + gitea-mirror + ``` + +## CI/CD Integration + +Example GitHub Actions workflow (see `.github/workflows/nix-build.yml`): + +```yaml +name: Nix Build + +on: [push, pull_request] + +permissions: + contents: read + +jobs: + build: + strategy: + matrix: + os: [ubuntu-latest, macos-latest] + runs-on: ${{ matrix.os }} + steps: + - uses: actions/checkout@v4 + - uses: DeterminateSystems/nix-installer-action@main + - uses: DeterminateSystems/magic-nix-cache-action@main + - run: nix flake check + - run: nix build --print-build-logs +``` + +This uses: +- **Determinate Nix Installer** - Fast, reliable Nix installation with flakes enabled by default +- **Magic Nix Cache** - Free caching using GitHub Actions cache (no account needed) + +## Resources + +- [Nix Manual](https://nixos.org/manual/nix/stable/) +- [NixOS Options Search](https://search.nixos.org/options) +- [Nix Pills Tutorial](https://nixos.org/guides/nix-pills/) +- [Project Documentation](../README.md) +- [Docker Deployment](../docker-compose.alt.yml) - Equivalent minimal config diff --git a/Divers/gitea-mirror/docs/NIX_DISTRIBUTION.md b/Divers/gitea-mirror/docs/NIX_DISTRIBUTION.md new file mode 100644 index 0000000..3f73258 --- /dev/null +++ b/Divers/gitea-mirror/docs/NIX_DISTRIBUTION.md @@ -0,0 +1,322 @@ +# Nix Package Distribution Guide + +This guide explains how Gitea Mirror is distributed via Nix and how users can consume it. + +## Distribution Methods + +### Method 1: Direct GitHub Usage (Zero Infrastructure) + +**No CI, releases, or setup needed!** Users can consume directly from GitHub: + +```bash +# Latest from main branch +nix run --extra-experimental-features 'nix-command flakes' github:RayLabsHQ/gitea-mirror + +# Pin to specific commit +nix run github:RayLabsHQ/gitea-mirror/abc123def + +# Pin to git tag +nix run github:RayLabsHQ/gitea-mirror/v3.8.11 +``` + +**How it works:** +1. Nix fetches the repository from GitHub +2. Nix reads `flake.nix` and `flake.lock` +3. Nix builds the package locally on the user's machine +4. Package is cached in `/nix/store` for reuse + +**Pros:** +- Zero infrastructure needed +- Works immediately after pushing code +- Users always get reproducible builds + +**Cons:** +- Users must build from source (slower first time) +- Requires build dependencies (Bun, etc.) + +--- + +### Method 2: CI Build Caching + +The GitHub Actions workflow uses **Magic Nix Cache** (by Determinate Systems) to cache builds: + +- **Zero configuration required** - no accounts or tokens needed +- **Automatic** - CI workflow handles everything +- **Uses GitHub Actions cache** - fast, reliable, free + +#### How It Works: + +1. GitHub Actions builds the package on each push/PR +2. Build artifacts are cached in GitHub Actions cache +3. Subsequent builds reuse cached dependencies (faster CI) + +Note: This caches CI builds. Users still build locally, but the flake.lock ensures reproducibility. + +--- + +### Method 3: nixpkgs Submission (Official Distribution) + +Submit to the official Nix package repository for maximum visibility. + +#### Process: + +1. **Prepare package** (already done with `flake.nix`) +2. **Test thoroughly** +3. **Submit PR to nixpkgs:** https://github.com/NixOS/nixpkgs + +#### User Experience: + +```bash +# After acceptance into nixpkgs +nix run nixpkgs#gitea-mirror + +# NixOS configuration +environment.systemPackages = [ pkgs.gitea-mirror ]; +``` + +**Pros:** +- Maximum discoverability (official repo) +- Trusted by Nix community +- Included in NixOS search +- Binary caching by cache.nixos.org + +**Cons:** +- Submission/review process +- Must follow nixpkgs guidelines +- Updates require PRs + +--- + +## Current Distribution Strategy + +### Phase 1: Direct GitHub (Immediate) ✅ + +Already working! Users can: + +```bash +nix run github:RayLabsHQ/gitea-mirror +``` + +### Phase 2: CI Build Validation ✅ + +GitHub Actions workflow validates builds on every push/PR: + +- Uses Magic Nix Cache for fast CI builds +- Tests on both Linux and macOS +- No setup required - works automatically + +### Phase 3: Version Releases (Optional) + +Tag releases for version pinning: + +```bash +git tag v3.8.11 +git push origin v3.8.11 + +# Users can then pin: +nix run github:RayLabsHQ/gitea-mirror/v3.8.11 +``` + +### Phase 4: nixpkgs Submission (Long Term) + +Once package is stable and well-tested, submit to nixpkgs. + +--- + +## User Documentation + +### For Users: How to Install + +Add this to your `docs/NIX_DEPLOYMENT.md`: + +#### Option 1: Direct Install (No Configuration) + +```bash +# Run immediately +nix run --extra-experimental-features 'nix-command flakes' github:RayLabsHQ/gitea-mirror + +# Install to profile +nix profile install --extra-experimental-features 'nix-command flakes' github:RayLabsHQ/gitea-mirror +``` + +#### Option 2: Pin to Specific Version + +```bash +# Pin to git tag +nix run github:RayLabsHQ/gitea-mirror/v3.8.11 + +# Pin to commit +nix run github:RayLabsHQ/gitea-mirror/abc123def + +# Lock in flake.nix +inputs.gitea-mirror.url = "github:RayLabsHQ/gitea-mirror/v3.8.11"; +``` + +#### Option 3: NixOS Configuration + +```nix +{ + inputs = { + nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; + gitea-mirror.url = "github:RayLabsHQ/gitea-mirror"; + # Or pin to version: + # gitea-mirror.url = "github:RayLabsHQ/gitea-mirror/v3.8.11"; + }; + + outputs = { nixpkgs, gitea-mirror, ... }: { + nixosConfigurations.your-host = nixpkgs.lib.nixosSystem { + modules = [ + gitea-mirror.nixosModules.default + { + services.gitea-mirror = { + enable = true; + betterAuthUrl = "https://mirror.example.com"; + openFirewall = true; + }; + } + ]; + }; + }; +} +``` + +--- + +## Maintaining the Distribution + +### Releasing New Versions + +```bash +# 1. Update version in package.json +vim package.json # Update version field + +# 2. Update flake.nix version (line 17) +vim flake.nix # Update version = "X.Y.Z"; + +# 3. Commit changes +git add package.json flake.nix +git commit -m "chore: bump version to vX.Y.Z" + +# 4. Create git tag +git tag vX.Y.Z +git push origin main +git push origin vX.Y.Z + +# 5. GitHub Actions builds and caches automatically +``` + +Users can then pin to the new version: +```bash +nix run github:RayLabsHQ/gitea-mirror/vX.Y.Z +``` + +### Updating Flake Lock + +The `flake.lock` file pins all dependencies. Update it periodically: + +```bash +# Update all inputs +nix flake update + +# Update specific input +nix flake lock --update-input nixpkgs + +# Test after update +nix build +nix flake check + +# Commit the updated lock file +git add flake.lock +git commit -m "chore: update flake dependencies" +git push +``` + +--- + +## Troubleshooting Distribution Issues + +### Users Report Build Failures + +1. **Check GitHub Actions:** Ensure CI is passing +2. **Test locally:** `nix flake check` +3. **Check flake.lock:** May need update if dependencies changed + +### CI Cache Not Working + +1. **Check workflow logs:** Review GitHub Actions for errors +2. **Clear cache:** GitHub Actions → Caches → Delete relevant cache +3. **Verify flake.lock:** May need `nix flake update` if dependencies changed + +### Version Pinning Not Working + +```bash +# Verify tag exists +git tag -l + +# Ensure tag is pushed +git ls-remote --tags origin + +# Test specific tag +nix run github:RayLabsHQ/gitea-mirror/v3.8.11 +``` + +--- + +## Advanced: Custom Binary Cache + +If you prefer self-hosting instead of Cachix: + +### Option 1: S3-Compatible Storage + +```nix +# Generate signing key +nix-store --generate-binary-cache-key cache.example.com cache-priv-key.pem cache-pub-key.pem + +# Push to S3 +nix copy --to s3://my-nix-cache?region=us-east-1 $(nix-build) +``` + +Users configure: +```nix +substituters = https://my-bucket.s3.amazonaws.com/nix-cache +trusted-public-keys = cache.example.com:BASE64_PUBLIC_KEY +``` + +### Option 2: Self-Hosted Nix Store + +Run `nix-serve` on your server: + +```bash +# On server +nix-serve -p 8080 + +# Behind nginx/caddy +proxy_pass http://localhost:8080; +``` + +Users configure: +```nix +substituters = https://cache.example.com +trusted-public-keys = YOUR_KEY +``` + +--- + +## Comparison: Distribution Methods + +| Method | Setup Time | User Speed | Cost | Discoverability | +|--------|-----------|------------|------|-----------------| +| Direct GitHub | 0 min | Slow (build) | Free | Low | +| nixpkgs | Hours/days | Fast (binary) | Free | High | +| Self-hosted cache | 30+ min | Fast (binary) | Server cost | Low | + +**Current approach:** Direct GitHub consumption with CI validation using Magic Nix Cache. Users build locally (reproducible via flake.lock). Consider **nixpkgs** submission for maximum reach once the package is mature. + +--- + +## Resources + +- [Nix Flakes Documentation](https://nixos.wiki/wiki/Flakes) +- [Magic Nix Cache](https://github.com/DeterminateSystems/magic-nix-cache-action) +- [nixpkgs Contributing Guide](https://github.com/NixOS/nixpkgs/blob/master/CONTRIBUTING.md) +- [Nix Binary Cache Setup](https://nixos.org/manual/nix/stable/package-management/binary-cache-substituter.html) diff --git a/Divers/gitea-mirror/docs/README.md b/Divers/gitea-mirror/docs/README.md new file mode 100644 index 0000000..9b84818 --- /dev/null +++ b/Divers/gitea-mirror/docs/README.md @@ -0,0 +1,39 @@ +# Gitea Mirror Documentation + +This folder contains engineering and operations references for the open-source Gitea Mirror project. Each guide focuses on the parts of the system that still require bespoke explanation beyond the in-app help and the main `README.md`. + +## Available Guides + +### Core workflow +- **[DEVELOPMENT_WORKFLOW.md](./DEVELOPMENT_WORKFLOW.md)** – Set up a local environment, run scripts, and understand the repo layout (app + marketing site). +- **[ENVIRONMENT_VARIABLES.md](./ENVIRONMENT_VARIABLES.md)** – Complete reference for every configuration flag supported by the app and Docker images. + +### Reliability & recovery +- **[GRACEFUL_SHUTDOWN.md](./GRACEFUL_SHUTDOWN.md)** – How signal handling, shutdown coordination, and job persistence work in v3. +- **[RECOVERY_IMPROVEMENTS.md](./RECOVERY_IMPROVEMENTS.md)** – Deep dive into the startup recovery workflow and supporting scripts. + +### Authentication +- **[SSO-OIDC-SETUP.md](./SSO-OIDC-SETUP.md)** – Configure OIDC/SSO providers through the admin UI. +- **[SSO_TESTING.md](./SSO_TESTING.md)** – Recipes for local and staging SSO testing (Google, Keycloak, mock providers). + +If you are looking for customer-facing playbooks, see the MDX use cases under `www/src/pages/use-cases/`. + +## Quick start for local development + +```bash +git clone https://github.com/RayLabsHQ/gitea-mirror.git +cd gitea-mirror +bun run setup # installs deps and seeds the SQLite DB +bun run dev # starts the Astro/Bun app on http://localhost:4321 +``` + +The first user you create locally becomes the administrator. All other configuration—GitHub owners, Gitea targets, scheduling, cleanup—is done through the **Configuration** screen in the UI. + +## Contributing & support + +- 🎯 Contribution guide: [../CONTRIBUTING.md](../CONTRIBUTING.md) +- 📘 Code of conduct: [../CODE_OF_CONDUCT.md](../CODE_OF_CONDUCT.md) +- 🐞 Issues & feature requests: +- 💬 Discussions: + +Security disclosures should follow the process in [../SECURITY.md](../SECURITY.md). diff --git a/Divers/gitea-mirror/docs/RECOVERY_IMPROVEMENTS.md b/Divers/gitea-mirror/docs/RECOVERY_IMPROVEMENTS.md new file mode 100644 index 0000000..80348c7 --- /dev/null +++ b/Divers/gitea-mirror/docs/RECOVERY_IMPROVEMENTS.md @@ -0,0 +1,170 @@ +# Job Recovery and Resume Process Improvements + +This document outlines the comprehensive improvements made to the job recovery and resume process to make it more robust to application restarts, container restarts, and application crashes. + +## Problems Addressed + +The original recovery system had several critical issues: + +1. **Middleware-based initialization**: Recovery only ran when the first request came in +2. **Database connection issues**: No validation of database connectivity before recovery attempts +3. **Limited error handling**: Insufficient error handling for various failure scenarios +4. **No startup recovery**: No mechanism to handle recovery before serving requests +5. **Incomplete job state management**: Jobs could remain in inconsistent states +6. **No retry mechanisms**: Single-attempt recovery with no fallback strategies + +## Improvements Implemented + +### 1. Enhanced Recovery System (`src/lib/recovery.ts`) + +#### New Features: +- **Database connection validation** before attempting recovery +- **Stale job cleanup** for jobs older than 24 hours +- **Retry mechanisms** with configurable attempts and delays +- **Individual job error handling** to prevent one failed job from stopping recovery +- **Recovery state tracking** to prevent concurrent recovery attempts +- **Enhanced logging** with detailed job information + +#### Key Functions: +- `initializeRecovery()` - Main recovery function with enhanced error handling +- `validateDatabaseConnection()` - Ensures database is accessible +- `cleanupStaleJobs()` - Removes jobs that are too old to recover +- `getRecoveryStatus()` - Returns current recovery system status +- `forceRecovery()` - Bypasses recent attempt checks +- `hasJobsNeedingRecovery()` - Checks if recovery is needed + +### 2. Startup Recovery Script (`scripts/startup-recovery.ts`) + +A dedicated script that runs recovery before the application starts serving requests: + +#### Features: +- **Timeout protection** (default: 30 seconds) +- **Force recovery option** to bypass recent attempt checks +- **Graceful signal handling** (SIGINT, SIGTERM) +- **Detailed logging** with progress indicators +- **Exit codes** for different scenarios (success, warnings, errors) + +#### Usage: +```bash +bun scripts/startup-recovery.ts [--force] [--timeout=30000] +``` + +### 3. Improved Middleware (`src/middleware.ts`) + +The middleware now serves as a fallback recovery mechanism: + +#### Changes: +- **Checks if recovery is needed** before attempting +- **Shorter timeout** (15 seconds) for request-time recovery +- **Better error handling** with status logging +- **Prevents multiple attempts** with proper state tracking + +### 4. Enhanced Database Queries (`src/lib/helpers.ts`) + +#### Improvements: +- **Proper Drizzle ORM syntax** for all database queries +- **Enhanced interrupted job detection** with multiple criteria: + - Jobs with no recent checkpoint (10+ minutes) + - Jobs running too long (2+ hours) +- **Detailed logging** of found interrupted jobs +- **Better error handling** for database operations + +### 5. Docker Integration (`docker-entrypoint.sh`) + +#### Changes: +- **Automatic startup recovery** runs before application start +- **Exit code handling** with appropriate logging +- **Fallback mechanisms** if recovery script is not found +- **Non-blocking execution** - application starts even if recovery fails + +### 6. Health Check Integration (`src/pages/api/health.ts`) + +#### New Features: +- **Recovery system status** in health endpoint +- **Job recovery metrics** (jobs needing recovery, recovery in progress) +- **Overall health status** considers recovery state +- **Detailed recovery information** for monitoring + +### 7. Testing Infrastructure (`scripts/test-recovery.ts`) + +A comprehensive test script to verify recovery functionality: + +#### Features: +- **Creates test interrupted jobs** with realistic scenarios +- **Verifies recovery detection** and execution +- **Checks final job states** after recovery +- **Cleanup functionality** for test data +- **Comprehensive logging** of test progress + +## Configuration Options + +### Recovery System Options: +- `maxRetries`: Number of recovery attempts (default: 3) +- `retryDelay`: Delay between attempts in ms (default: 5000) +- `skipIfRecentAttempt`: Skip if recent attempt made (default: true) + +### Startup Recovery Options: +- `--force`: Force recovery even if recent attempt was made +- `--timeout`: Maximum time to wait for recovery (default: 30000ms) + +## Usage Examples + +### Manual Recovery: +```bash +# Run startup recovery +bun run startup-recovery + +# Force recovery +bun run startup-recovery-force + +# Test recovery system +bun run test-recovery + +# Clean up test data +bun run test-recovery-cleanup +``` + +### Programmatic Usage: +```typescript +import { initializeRecovery, hasJobsNeedingRecovery } from '@/lib/recovery'; + +// Check if recovery is needed +const needsRecovery = await hasJobsNeedingRecovery(); + +// Run recovery with custom options +const success = await initializeRecovery({ + maxRetries: 5, + retryDelay: 3000, + skipIfRecentAttempt: false +}); +``` + +## Monitoring and Observability + +### Health Check Endpoint: +- **URL**: `/api/health` +- **Recovery Status**: Included in response +- **Monitoring**: Can be used with external monitoring systems + +### Log Messages: +- **Startup**: Clear indicators of recovery attempts and results +- **Progress**: Detailed logging of recovery steps +- **Errors**: Comprehensive error information for debugging + +## Benefits + +1. **Reliability**: Jobs are automatically recovered after application restarts +2. **Resilience**: Multiple retry mechanisms and fallback strategies +3. **Observability**: Comprehensive logging and health check integration +4. **Performance**: Efficient detection and processing of interrupted jobs +5. **Maintainability**: Clear separation of concerns and modular design +6. **Testing**: Built-in testing infrastructure for verification + +## Migration Notes + +- **Backward Compatible**: All existing functionality is preserved +- **Automatic**: Recovery runs automatically on startup +- **Configurable**: All timeouts and retry counts can be adjusted +- **Monitoring**: Health checks now include recovery status + +This comprehensive improvement ensures that the gitea-mirror application can reliably handle job recovery in all deployment scenarios, from development to production container environments. diff --git a/Divers/gitea-mirror/docs/SSO-OIDC-SETUP.md b/Divers/gitea-mirror/docs/SSO-OIDC-SETUP.md new file mode 100644 index 0000000..3d33662 --- /dev/null +++ b/Divers/gitea-mirror/docs/SSO-OIDC-SETUP.md @@ -0,0 +1,226 @@ +# SSO and OIDC Setup Guide + +This guide explains how to configure Single Sign-On (SSO) and OpenID Connect (OIDC) provider functionality in Gitea Mirror. + +## Overview + +Gitea Mirror supports three authentication methods: + +1. **Email & Password** - Traditional authentication (always enabled) +2. **SSO (Single Sign-On)** - Allow users to authenticate using external OIDC providers +3. **OIDC Provider** - Allow other applications to authenticate users through Gitea Mirror + +## Configuration + +All SSO and OIDC settings are managed through the web UI in the Configuration page under the "Authentication" tab. + +## Setting up SSO (Single Sign-On) + +SSO allows your users to sign in using external identity providers like Google, Okta, Azure AD, etc. + +### Adding an SSO Provider + +1. Navigate to Configuration → Authentication → SSO Providers +2. Click "Add Provider" +3. Fill in the provider details: + +#### Required Fields + +- **Issuer URL**: The OIDC issuer URL (e.g., `https://accounts.google.com`) +- **Domain**: The email domain for this provider (e.g., `example.com`) +- **Provider ID**: A unique identifier for this provider (e.g., `google-sso`) +- **Client ID**: The OAuth client ID from your provider +- **Client Secret**: The OAuth client secret from your provider + +#### Auto-Discovery + +If your provider supports OIDC discovery, you can: +1. Enter the Issuer URL +2. Click "Discover" +3. The system will automatically fetch the authorization and token endpoints + +#### Manual Configuration + +For providers without discovery support, manually enter: +- **Authorization Endpoint**: The OAuth authorization URL +- **Token Endpoint**: The OAuth token exchange URL +- **JWKS Endpoint**: The JSON Web Key Set URL (optional) +- **UserInfo Endpoint**: The user information endpoint (optional) + +### Redirect URL + +When configuring your SSO provider, use this redirect URL: +``` +https://your-domain.com/api/auth/sso/callback/{provider-id} +``` + +Replace `{provider-id}` with your chosen Provider ID. + +### Example: Google SSO Setup + +1. Go to [Google Cloud Console](https://console.cloud.google.com/) +2. Create a new OAuth 2.0 Client ID +3. Add authorized redirect URI: `https://your-domain.com/api/auth/sso/callback/google-sso` +4. In Gitea Mirror: + - Issuer URL: `https://accounts.google.com` + - Domain: `your-company.com` + - Provider ID: `google-sso` + - Client ID: [Your Google Client ID] + - Client Secret: [Your Google Client Secret] + - Click "Discover" to auto-fill endpoints + +### Example: Okta SSO Setup + +1. In Okta Admin Console, create a new OIDC Web Application +2. Set redirect URI: `https://your-domain.com/api/auth/sso/callback/okta-sso` +3. In Gitea Mirror: + - Issuer URL: `https://your-okta-domain.okta.com` + - Domain: `your-company.com` + - Provider ID: `okta-sso` + - Client ID: [Your Okta Client ID] + - Client Secret: [Your Okta Client Secret] + - Click "Discover" to auto-fill endpoints + +### Example: Authentik SSO Setup + +Working Authentik deployments (see [#134](https://github.com/RayLabsHQ/gitea-mirror/issues/134)) follow these steps: + +1. In Authentik, create a new **Application** and OIDC **Provider** (implicit flow works well for testing). +2. Start creating an SSO provider inside Gitea Mirror so you can copy the redirect URL shown (`https://your-domain.com/api/auth/sso/callback/authentik` if you pick `authentik` as your Provider ID). +3. Paste that redirect URL into the Authentik Provider configuration and finish creating the provider. +4. Copy the Authentik issuer URL, client ID, and client secret. +5. Back in Gitea Mirror: + - Issuer URL: the exact value from Authentik (keep any trailing slash Authentik shows). + - Provider ID: match the one you used in step 2. + - Click **Discover** so Gitea Mirror stores the authorization, token, and JWKS endpoints (Authentik publishes them via discovery). + - Domain: enter the email domain you expect to match (e.g. `example.com`). +6. Save the provider and test the login flow. + +Notes: +- Make sure `BETTER_AUTH_URL` and (if you serve the UI from multiple origins) `BETTER_AUTH_TRUSTED_ORIGINS` point at the public URL users reach. A mismatch can surface as 500 errors after redirect. +- Authentik must report the user’s email as verified (default behavior) so Gitea Mirror can auto-link accounts. +- If you created an Authentik provider before v3.8.10 you should delete it and re-add it after upgrading; older versions saved incomplete endpoint data which leads to the `url.startsWith` error explained in the Troubleshooting section. + +## Setting up OIDC Provider + +The OIDC Provider feature allows other applications to use Gitea Mirror as their authentication provider. + +### Creating OAuth Applications + +1. Navigate to Configuration → Authentication → OAuth Applications +2. Click "Create Application" +3. Fill in the application details: + - **Application Name**: Display name for the application + - **Application Type**: Web, Mobile, or Desktop + - **Redirect URLs**: One or more redirect URLs (one per line) + +4. After creation, you'll receive: + - **Client ID**: Share this with the application + - **Client Secret**: Keep this secure and share only once + +### OIDC Endpoints + +Applications can use these standard OIDC endpoints: + +- **Discovery**: `https://your-domain.com/.well-known/openid-configuration` +- **Authorization**: `https://your-domain.com/api/auth/oauth2/authorize` +- **Token**: `https://your-domain.com/api/auth/oauth2/token` +- **UserInfo**: `https://your-domain.com/api/auth/oauth2/userinfo` +- **JWKS**: `https://your-domain.com/api/auth/jwks` + +### Supported Scopes + +- `openid` - Required, provides user ID +- `profile` - User's name, username, and profile picture +- `email` - User's email address and verification status + +### Example: Configuring Another Application + +For an application to use Gitea Mirror as its OIDC provider: + +```javascript +// Example configuration for another app +const oidcConfig = { + issuer: 'https://gitea-mirror.example.com', + clientId: 'client_xxxxxxxxxxxxx', + clientSecret: 'secret_xxxxxxxxxxxxx', + redirectUri: 'https://myapp.com/auth/callback', + scope: 'openid profile email' +}; +``` + +## User Experience + +### Logging In with SSO + +When SSO is configured: + +1. Users see tabs for "Email" and "SSO" on the login page +2. In the SSO tab, they can: + - Click a specific provider button (if configured) + - Enter their work email to be redirected to the appropriate provider + +### OAuth Consent Flow + +When an application requests authentication: + +1. Users are redirected to Gitea Mirror +2. If not logged in, they authenticate first +3. They see a consent screen showing: + - Application name + - Requested permissions + - Option to approve or deny + +## Security Considerations + +1. **Client Secrets**: Store OAuth client secrets securely +2. **Redirect URLs**: Only add trusted redirect URLs for applications +3. **Scopes**: Applications only receive the data for approved scopes +4. **Token Security**: Access tokens expire and can be revoked + +## Troubleshooting + +### SSO Login Issues + +1. **"Invalid origin" error**: Check that your Gitea Mirror URL matches the configured redirect URI +2. **"Provider not found" error**: Ensure the provider is properly configured and enabled +3. **Redirect loop**: Verify the redirect URI in both Gitea Mirror and the SSO provider match exactly +4. **`TypeError: undefined is not an object (evaluating 'url.startsWith')`**: This indicates the stored provider configuration is missing OIDC endpoints. Delete the provider from Gitea Mirror and re-register it using the **Discover** button so authorization/token URLs are saved (see [#73](https://github.com/RayLabsHQ/gitea-mirror/issues/73) and [#122](https://github.com/RayLabsHQ/gitea-mirror/issues/122) for examples). + +### OIDC Provider Issues + +1. **Application not found**: Ensure the client ID is correct +2. **Invalid redirect URI**: The redirect URI must match exactly what's configured +3. **Consent not working**: Check browser cookies are enabled + +## Managing Access + +### Revoking SSO Access + +Currently, SSO sessions are managed through the identity provider. To revoke access: +1. Log out of Gitea Mirror +2. Revoke access in your identity provider's settings + +### Disabling OAuth Applications + +To disable an application: +1. Go to Configuration → Authentication → OAuth Applications +2. Find the application +3. Click the delete button + +This immediately prevents the application from authenticating new users. + +## Best Practices + +1. **Use HTTPS**: Always use HTTPS in production for security +2. **Regular Audits**: Periodically review configured SSO providers and OAuth applications +3. **Principle of Least Privilege**: Only grant necessary scopes to applications +4. **Monitor Usage**: Keep track of which applications are accessing your OIDC provider +5. **Secure Storage**: Store client secrets in a secure location, never in code + +## Migration Notes + +If migrating from the previous JWT-based authentication: +- Existing users remain unaffected +- Users can continue using email/password authentication +- SSO can be added as an additional authentication method diff --git a/Divers/gitea-mirror/docs/SSO_TESTING.md b/Divers/gitea-mirror/docs/SSO_TESTING.md new file mode 100644 index 0000000..1a87676 --- /dev/null +++ b/Divers/gitea-mirror/docs/SSO_TESTING.md @@ -0,0 +1,193 @@ +# Local SSO Testing Guide + +This guide explains how to test SSO authentication locally with Gitea Mirror. + +## Option 1: Using Google OAuth (Recommended for Quick Testing) + +### Setup Steps: + +1. **Create a Google OAuth Application** + - Go to [Google Cloud Console](https://console.cloud.google.com/) + - Create a new project or select existing + - Enable Google+ API + - Go to "Credentials" → "Create Credentials" → "OAuth client ID" + - Choose "Web application" + - Add authorized redirect URIs: + - `http://localhost:3000/api/auth/sso/callback/google-sso` + - `http://localhost:9876/api/auth/sso/callback/google-sso` + +2. **Configure in Gitea Mirror** + - Go to Configuration → Authentication tab + - Click "Add Provider" + - Select "OIDC / OAuth2" + - Fill in: + - Provider ID: `google-sso` + - Email Domain: `gmail.com` (or your domain) + - Issuer URL: `https://accounts.google.com` + - Click "Discover" to auto-fill endpoints + - Client ID: (from Google Console) + - Client Secret: (from Google Console) + - Save the provider + +## Option 2: Using Keycloak (Local Identity Provider) + +### Setup with Docker: + +```bash +# Run Keycloak +docker run -d --name keycloak \ + -p 8080:8080 \ + -e KEYCLOAK_ADMIN=admin \ + -e KEYCLOAK_ADMIN_PASSWORD=admin \ + quay.io/keycloak/keycloak:latest start-dev + +# Access at http://localhost:8080 +# Login with admin/admin +``` + +### Configure Keycloak: + +1. Create a new realm (e.g., "gitea-mirror") +2. Create a client: + - Client ID: `gitea-mirror` + - Client Protocol: `openid-connect` + - Access Type: `confidential` + - Valid Redirect URIs: `http://localhost:*/api/auth/sso/callback/keycloak` +3. Get credentials from the "Credentials" tab +4. Create test users in "Users" section + +### Configure in Gitea Mirror: + +- Provider ID: `keycloak` +- Email Domain: `example.com` +- Issuer URL: `http://localhost:8080/realms/gitea-mirror` +- Client ID: `gitea-mirror` +- Client Secret: (from Keycloak) +- Click "Discover" to auto-fill endpoints + +## Option 3: Using Mock SSO Provider (Development) + +For testing without external dependencies, you can use a mock OIDC provider. + +### Using oidc-provider-example: + +```bash +# Clone and run mock provider +git clone https://github.com/panva/node-oidc-provider-example.git +cd node-oidc-provider-example +npm install +npm start + +# Runs on http://localhost:3001 +``` + +### Configure in Gitea Mirror: + +- Provider ID: `mock-provider` +- Email Domain: `test.com` +- Issuer URL: `http://localhost:3001` +- Client ID: `foo` +- Client Secret: `bar` +- Authorization Endpoint: `http://localhost:3001/auth` +- Token Endpoint: `http://localhost:3001/token` + +## Testing the SSO Flow + +1. **Logout** from Gitea Mirror if logged in +2. Go to `/login` +3. Click on the **SSO** tab +4. Either: + - Click the provider button (e.g., "Sign in with gmail.com") + - Or enter your email and click "Continue with SSO" +5. You'll be redirected to the identity provider +6. Complete authentication +7. You'll be redirected back and logged in + +## Troubleshooting + +### Common Issues: + +1. **"Invalid origin" error** + - Check that `trustedOrigins` in `/src/lib/auth.ts` includes your dev URL + - Restart the dev server after changes + +2. **Provider not showing in login** + - Check browser console for errors + - Verify provider was saved successfully + - Check `/api/sso/providers` returns your providers + +3. **Redirect URI mismatch** + - Ensure the redirect URI in your OAuth app matches exactly: + `http://localhost:PORT/api/auth/sso/callback/PROVIDER_ID` + +4. **CORS errors** + - Add your identity provider domain to CORS allowed origins if needed + +### Debug Mode: + +Enable debug logging by setting environment variable: +```bash +DEBUG=better-auth:* bun run dev +``` + +## Testing Different Scenarios + +### 1. New User Registration +- Use an email not in the system +- SSO should create a new user automatically + +### 2. Existing User Login +- Create a user with email/password first +- Login with SSO using same email +- Should link to existing account + +### 3. Domain-based Routing +- Configure multiple providers with different domains +- Test that entering email routes to correct provider + +### 4. Organization Provisioning +- Set organizationId on provider +- Test that users are added to correct organization + +## Security Testing + +1. **Token Expiration** + - Wait for session to expire + - Test refresh flow + +2. **Invalid State** + - Modify state parameter in callback + - Should reject authentication + +3. **PKCE Flow** + - Enable/disable PKCE + - Verify code challenge works + +## Using with Better Auth CLI + +Better Auth provides CLI tools for testing: + +```bash +# List registered providers +bun run auth:providers list + +# Test provider configuration +bun run auth:providers test google-sso +``` + +## Environment Variables + +For production-like testing: + +```env +BETTER_AUTH_URL=http://localhost:3000 +BETTER_AUTH_SECRET=your-secret-key +``` + +## Next Steps + +After successful SSO setup: +1. Test user attribute mapping +2. Configure role-based access +3. Set up SAML if needed +4. Test with your organization's actual IdP \ No newline at end of file diff --git a/Divers/gitea-mirror/drizzle.config.ts b/Divers/gitea-mirror/drizzle.config.ts new file mode 100644 index 0000000..ec27a84 --- /dev/null +++ b/Divers/gitea-mirror/drizzle.config.ts @@ -0,0 +1,16 @@ +import { defineConfig } from "drizzle-kit"; + +export default defineConfig({ + dialect: "sqlite", + schema: "./src/lib/db/schema.ts", + out: "./drizzle", + dbCredentials: { + url: "./data/gitea-mirror.db", + }, + verbose: true, + strict: true, + migrations: { + table: "__drizzle_migrations", + schema: "main", + }, +}); \ No newline at end of file diff --git a/Divers/gitea-mirror/drizzle/0000_init.sql b/Divers/gitea-mirror/drizzle/0000_init.sql new file mode 100644 index 0000000..99809c7 --- /dev/null +++ b/Divers/gitea-mirror/drizzle/0000_init.sql @@ -0,0 +1,180 @@ +CREATE TABLE `accounts` ( + `id` text PRIMARY KEY NOT NULL, + `account_id` text NOT NULL, + `user_id` text NOT NULL, + `provider_id` text NOT NULL, + `provider_user_id` text, + `access_token` text, + `refresh_token` text, + `expires_at` integer, + `password` text, + `created_at` integer DEFAULT (unixepoch()) NOT NULL, + `updated_at` integer DEFAULT (unixepoch()) NOT NULL, + FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE no action +); +--> statement-breakpoint +CREATE INDEX `idx_accounts_account_id` ON `accounts` (`account_id`);--> statement-breakpoint +CREATE INDEX `idx_accounts_user_id` ON `accounts` (`user_id`);--> statement-breakpoint +CREATE INDEX `idx_accounts_provider` ON `accounts` (`provider_id`,`provider_user_id`);--> statement-breakpoint +CREATE TABLE `configs` ( + `id` text PRIMARY KEY NOT NULL, + `user_id` text NOT NULL, + `name` text NOT NULL, + `is_active` integer DEFAULT true NOT NULL, + `github_config` text NOT NULL, + `gitea_config` text NOT NULL, + `include` text DEFAULT '["*"]' NOT NULL, + `exclude` text DEFAULT '[]' NOT NULL, + `schedule_config` text NOT NULL, + `cleanup_config` text NOT NULL, + `created_at` integer DEFAULT (unixepoch()) NOT NULL, + `updated_at` integer DEFAULT (unixepoch()) NOT NULL, + FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE no action +); +--> statement-breakpoint +CREATE TABLE `events` ( + `id` text PRIMARY KEY NOT NULL, + `user_id` text NOT NULL, + `channel` text NOT NULL, + `payload` text NOT NULL, + `read` integer DEFAULT false NOT NULL, + `created_at` integer DEFAULT (unixepoch()) NOT NULL, + FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE no action +); +--> statement-breakpoint +CREATE INDEX `idx_events_user_channel` ON `events` (`user_id`,`channel`);--> statement-breakpoint +CREATE INDEX `idx_events_created_at` ON `events` (`created_at`);--> statement-breakpoint +CREATE INDEX `idx_events_read` ON `events` (`read`);--> statement-breakpoint +CREATE TABLE `mirror_jobs` ( + `id` text PRIMARY KEY NOT NULL, + `user_id` text NOT NULL, + `repository_id` text, + `repository_name` text, + `organization_id` text, + `organization_name` text, + `details` text, + `status` text DEFAULT 'imported' NOT NULL, + `message` text NOT NULL, + `timestamp` integer DEFAULT (unixepoch()) NOT NULL, + `job_type` text DEFAULT 'mirror' NOT NULL, + `batch_id` text, + `total_items` integer, + `completed_items` integer DEFAULT 0, + `item_ids` text, + `completed_item_ids` text DEFAULT '[]', + `in_progress` integer DEFAULT false NOT NULL, + `started_at` integer, + `completed_at` integer, + `last_checkpoint` integer, + FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE no action +); +--> statement-breakpoint +CREATE INDEX `idx_mirror_jobs_user_id` ON `mirror_jobs` (`user_id`);--> statement-breakpoint +CREATE INDEX `idx_mirror_jobs_batch_id` ON `mirror_jobs` (`batch_id`);--> statement-breakpoint +CREATE INDEX `idx_mirror_jobs_in_progress` ON `mirror_jobs` (`in_progress`);--> statement-breakpoint +CREATE INDEX `idx_mirror_jobs_job_type` ON `mirror_jobs` (`job_type`);--> statement-breakpoint +CREATE INDEX `idx_mirror_jobs_timestamp` ON `mirror_jobs` (`timestamp`);--> statement-breakpoint +CREATE TABLE `organizations` ( + `id` text PRIMARY KEY NOT NULL, + `user_id` text NOT NULL, + `config_id` text NOT NULL, + `name` text NOT NULL, + `avatar_url` text NOT NULL, + `membership_role` text DEFAULT 'member' NOT NULL, + `is_included` integer DEFAULT true NOT NULL, + `destination_org` text, + `status` text DEFAULT 'imported' NOT NULL, + `last_mirrored` integer, + `error_message` text, + `repository_count` integer DEFAULT 0 NOT NULL, + `created_at` integer DEFAULT (unixepoch()) NOT NULL, + `updated_at` integer DEFAULT (unixepoch()) NOT NULL, + FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE no action, + FOREIGN KEY (`config_id`) REFERENCES `configs`(`id`) ON UPDATE no action ON DELETE no action +); +--> statement-breakpoint +CREATE INDEX `idx_organizations_user_id` ON `organizations` (`user_id`);--> statement-breakpoint +CREATE INDEX `idx_organizations_config_id` ON `organizations` (`config_id`);--> statement-breakpoint +CREATE INDEX `idx_organizations_status` ON `organizations` (`status`);--> statement-breakpoint +CREATE INDEX `idx_organizations_is_included` ON `organizations` (`is_included`);--> statement-breakpoint +CREATE TABLE `repositories` ( + `id` text PRIMARY KEY NOT NULL, + `user_id` text NOT NULL, + `config_id` text NOT NULL, + `name` text NOT NULL, + `full_name` text NOT NULL, + `url` text NOT NULL, + `clone_url` text NOT NULL, + `owner` text NOT NULL, + `organization` text, + `mirrored_location` text DEFAULT '', + `is_private` integer DEFAULT false NOT NULL, + `is_fork` integer DEFAULT false NOT NULL, + `forked_from` text, + `has_issues` integer DEFAULT false NOT NULL, + `is_starred` integer DEFAULT false NOT NULL, + `is_archived` integer DEFAULT false NOT NULL, + `size` integer DEFAULT 0 NOT NULL, + `has_lfs` integer DEFAULT false NOT NULL, + `has_submodules` integer DEFAULT false NOT NULL, + `language` text, + `description` text, + `default_branch` text NOT NULL, + `visibility` text DEFAULT 'public' NOT NULL, + `status` text DEFAULT 'imported' NOT NULL, + `last_mirrored` integer, + `error_message` text, + `destination_org` text, + `created_at` integer DEFAULT (unixepoch()) NOT NULL, + `updated_at` integer DEFAULT (unixepoch()) NOT NULL, + FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE no action, + FOREIGN KEY (`config_id`) REFERENCES `configs`(`id`) ON UPDATE no action ON DELETE no action +); +--> statement-breakpoint +CREATE INDEX `idx_repositories_user_id` ON `repositories` (`user_id`);--> statement-breakpoint +CREATE INDEX `idx_repositories_config_id` ON `repositories` (`config_id`);--> statement-breakpoint +CREATE INDEX `idx_repositories_status` ON `repositories` (`status`);--> statement-breakpoint +CREATE INDEX `idx_repositories_owner` ON `repositories` (`owner`);--> statement-breakpoint +CREATE INDEX `idx_repositories_organization` ON `repositories` (`organization`);--> statement-breakpoint +CREATE INDEX `idx_repositories_is_fork` ON `repositories` (`is_fork`);--> statement-breakpoint +CREATE INDEX `idx_repositories_is_starred` ON `repositories` (`is_starred`);--> statement-breakpoint +CREATE TABLE `sessions` ( + `id` text PRIMARY KEY NOT NULL, + `token` text NOT NULL, + `user_id` text NOT NULL, + `expires_at` integer NOT NULL, + `ip_address` text, + `user_agent` text, + `created_at` integer DEFAULT (unixepoch()) NOT NULL, + `updated_at` integer DEFAULT (unixepoch()) NOT NULL, + FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE no action +); +--> statement-breakpoint +CREATE UNIQUE INDEX `sessions_token_unique` ON `sessions` (`token`);--> statement-breakpoint +CREATE INDEX `idx_sessions_user_id` ON `sessions` (`user_id`);--> statement-breakpoint +CREATE INDEX `idx_sessions_token` ON `sessions` (`token`);--> statement-breakpoint +CREATE INDEX `idx_sessions_expires_at` ON `sessions` (`expires_at`);--> statement-breakpoint +CREATE TABLE `users` ( + `id` text PRIMARY KEY NOT NULL, + `name` text, + `email` text NOT NULL, + `email_verified` integer DEFAULT false NOT NULL, + `image` text, + `created_at` integer DEFAULT (unixepoch()) NOT NULL, + `updated_at` integer DEFAULT (unixepoch()) NOT NULL, + `username` text +); +--> statement-breakpoint +CREATE UNIQUE INDEX `users_email_unique` ON `users` (`email`);--> statement-breakpoint +CREATE TABLE `verification_tokens` ( + `id` text PRIMARY KEY NOT NULL, + `token` text NOT NULL, + `identifier` text NOT NULL, + `type` text NOT NULL, + `expires_at` integer NOT NULL, + `created_at` integer DEFAULT (unixepoch()) NOT NULL +); +--> statement-breakpoint +CREATE UNIQUE INDEX `verification_tokens_token_unique` ON `verification_tokens` (`token`);--> statement-breakpoint +CREATE INDEX `idx_verification_tokens_token` ON `verification_tokens` (`token`);--> statement-breakpoint +CREATE INDEX `idx_verification_tokens_identifier` ON `verification_tokens` (`identifier`); \ No newline at end of file diff --git a/Divers/gitea-mirror/drizzle/0001_polite_exodus.sql b/Divers/gitea-mirror/drizzle/0001_polite_exodus.sql new file mode 100644 index 0000000..5204a57 --- /dev/null +++ b/Divers/gitea-mirror/drizzle/0001_polite_exodus.sql @@ -0,0 +1,64 @@ +CREATE TABLE `oauth_access_tokens` ( + `id` text PRIMARY KEY NOT NULL, + `access_token` text NOT NULL, + `refresh_token` text, + `access_token_expires_at` integer NOT NULL, + `refresh_token_expires_at` integer, + `client_id` text NOT NULL, + `user_id` text NOT NULL, + `scopes` text NOT NULL, + `created_at` integer DEFAULT (unixepoch()) NOT NULL, + `updated_at` integer DEFAULT (unixepoch()) NOT NULL, + FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE no action +); +--> statement-breakpoint +CREATE INDEX `idx_oauth_access_tokens_access_token` ON `oauth_access_tokens` (`access_token`);--> statement-breakpoint +CREATE INDEX `idx_oauth_access_tokens_user_id` ON `oauth_access_tokens` (`user_id`);--> statement-breakpoint +CREATE INDEX `idx_oauth_access_tokens_client_id` ON `oauth_access_tokens` (`client_id`);--> statement-breakpoint +CREATE TABLE `oauth_applications` ( + `id` text PRIMARY KEY NOT NULL, + `client_id` text NOT NULL, + `client_secret` text NOT NULL, + `name` text NOT NULL, + `redirect_urls` text NOT NULL, + `metadata` text, + `type` text NOT NULL, + `disabled` integer DEFAULT false NOT NULL, + `user_id` text, + `created_at` integer DEFAULT (unixepoch()) NOT NULL, + `updated_at` integer DEFAULT (unixepoch()) NOT NULL +); +--> statement-breakpoint +CREATE UNIQUE INDEX `oauth_applications_client_id_unique` ON `oauth_applications` (`client_id`);--> statement-breakpoint +CREATE INDEX `idx_oauth_applications_client_id` ON `oauth_applications` (`client_id`);--> statement-breakpoint +CREATE INDEX `idx_oauth_applications_user_id` ON `oauth_applications` (`user_id`);--> statement-breakpoint +CREATE TABLE `oauth_consent` ( + `id` text PRIMARY KEY NOT NULL, + `user_id` text NOT NULL, + `client_id` text NOT NULL, + `scopes` text NOT NULL, + `consent_given` integer NOT NULL, + `created_at` integer DEFAULT (unixepoch()) NOT NULL, + `updated_at` integer DEFAULT (unixepoch()) NOT NULL, + FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE no action +); +--> statement-breakpoint +CREATE INDEX `idx_oauth_consent_user_id` ON `oauth_consent` (`user_id`);--> statement-breakpoint +CREATE INDEX `idx_oauth_consent_client_id` ON `oauth_consent` (`client_id`);--> statement-breakpoint +CREATE INDEX `idx_oauth_consent_user_client` ON `oauth_consent` (`user_id`,`client_id`);--> statement-breakpoint +CREATE TABLE `sso_providers` ( + `id` text PRIMARY KEY NOT NULL, + `issuer` text NOT NULL, + `domain` text NOT NULL, + `oidc_config` text NOT NULL, + `user_id` text NOT NULL, + `provider_id` text NOT NULL, + `organization_id` text, + `created_at` integer DEFAULT (unixepoch()) NOT NULL, + `updated_at` integer DEFAULT (unixepoch()) NOT NULL +); +--> statement-breakpoint +CREATE UNIQUE INDEX `sso_providers_provider_id_unique` ON `sso_providers` (`provider_id`);--> statement-breakpoint +CREATE INDEX `idx_sso_providers_provider_id` ON `sso_providers` (`provider_id`);--> statement-breakpoint +CREATE INDEX `idx_sso_providers_domain` ON `sso_providers` (`domain`);--> statement-breakpoint +CREATE INDEX `idx_sso_providers_issuer` ON `sso_providers` (`issuer`); \ No newline at end of file diff --git a/Divers/gitea-mirror/drizzle/0002_bored_captain_cross.sql b/Divers/gitea-mirror/drizzle/0002_bored_captain_cross.sql new file mode 100644 index 0000000..b6e35ab --- /dev/null +++ b/Divers/gitea-mirror/drizzle/0002_bored_captain_cross.sql @@ -0,0 +1,10 @@ +CREATE TABLE `verifications` ( + `id` text PRIMARY KEY NOT NULL, + `identifier` text NOT NULL, + `value` text NOT NULL, + `expires_at` integer NOT NULL, + `created_at` integer DEFAULT (unixepoch()) NOT NULL, + `updated_at` integer DEFAULT (unixepoch()) NOT NULL +); +--> statement-breakpoint +CREATE INDEX `idx_verifications_identifier` ON `verifications` (`identifier`); \ No newline at end of file diff --git a/Divers/gitea-mirror/drizzle/0003_open_spacker_dave.sql b/Divers/gitea-mirror/drizzle/0003_open_spacker_dave.sql new file mode 100644 index 0000000..1665007 --- /dev/null +++ b/Divers/gitea-mirror/drizzle/0003_open_spacker_dave.sql @@ -0,0 +1,3 @@ +ALTER TABLE `organizations` ADD `public_repository_count` integer;--> statement-breakpoint +ALTER TABLE `organizations` ADD `private_repository_count` integer;--> statement-breakpoint +ALTER TABLE `organizations` ADD `fork_repository_count` integer; \ No newline at end of file diff --git a/Divers/gitea-mirror/drizzle/0004_grey_butterfly.sql b/Divers/gitea-mirror/drizzle/0004_grey_butterfly.sql new file mode 100644 index 0000000..60fe22a --- /dev/null +++ b/Divers/gitea-mirror/drizzle/0004_grey_butterfly.sql @@ -0,0 +1,18 @@ +CREATE TABLE `rate_limits` ( + `id` text PRIMARY KEY NOT NULL, + `user_id` text NOT NULL, + `provider` text DEFAULT 'github' NOT NULL, + `limit` integer NOT NULL, + `remaining` integer NOT NULL, + `used` integer NOT NULL, + `reset` integer NOT NULL, + `retry_after` integer, + `status` text DEFAULT 'ok' NOT NULL, + `last_checked` integer NOT NULL, + `created_at` integer DEFAULT (unixepoch()) NOT NULL, + `updated_at` integer DEFAULT (unixepoch()) NOT NULL, + FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE no action +); +--> statement-breakpoint +CREATE INDEX `idx_rate_limits_user_provider` ON `rate_limits` (`user_id`,`provider`);--> statement-breakpoint +CREATE INDEX `idx_rate_limits_status` ON `rate_limits` (`status`); \ No newline at end of file diff --git a/Divers/gitea-mirror/drizzle/0005_polite_preak.sql b/Divers/gitea-mirror/drizzle/0005_polite_preak.sql new file mode 100644 index 0000000..6eaa110 --- /dev/null +++ b/Divers/gitea-mirror/drizzle/0005_polite_preak.sql @@ -0,0 +1,11 @@ +-- Step 1: Remove duplicate repositories, keeping the most recently updated one +-- This handles cases where users have duplicate entries from before the unique constraint +DELETE FROM repositories +WHERE rowid NOT IN ( + SELECT MAX(rowid) + FROM repositories + GROUP BY user_id, full_name +); +--> statement-breakpoint +-- Step 2: Now create the unique index safely +CREATE UNIQUE INDEX uniq_repositories_user_full_name ON repositories (user_id, full_name); \ No newline at end of file diff --git a/Divers/gitea-mirror/drizzle/0006_military_la_nuit.sql b/Divers/gitea-mirror/drizzle/0006_military_la_nuit.sql new file mode 100644 index 0000000..2c043eb --- /dev/null +++ b/Divers/gitea-mirror/drizzle/0006_military_la_nuit.sql @@ -0,0 +1,4 @@ +ALTER TABLE `accounts` ADD `id_token` text;--> statement-breakpoint +ALTER TABLE `accounts` ADD `access_token_expires_at` integer;--> statement-breakpoint +ALTER TABLE `accounts` ADD `refresh_token_expires_at` integer;--> statement-breakpoint +ALTER TABLE `accounts` ADD `scope` text; \ No newline at end of file diff --git a/Divers/gitea-mirror/drizzle/0007_whole_hellion.sql b/Divers/gitea-mirror/drizzle/0007_whole_hellion.sql new file mode 100644 index 0000000..e501b45 --- /dev/null +++ b/Divers/gitea-mirror/drizzle/0007_whole_hellion.sql @@ -0,0 +1,18 @@ +ALTER TABLE `organizations` ADD `normalized_name` text NOT NULL DEFAULT '';--> statement-breakpoint +UPDATE `organizations` SET `normalized_name` = lower(trim(`name`));--> statement-breakpoint +DELETE FROM `organizations` +WHERE rowid NOT IN ( + SELECT MIN(rowid) + FROM `organizations` + GROUP BY `user_id`, `normalized_name` +);--> statement-breakpoint +CREATE UNIQUE INDEX `uniq_organizations_user_normalized_name` ON `organizations` (`user_id`,`normalized_name`);--> statement-breakpoint +ALTER TABLE `repositories` ADD `normalized_full_name` text NOT NULL DEFAULT '';--> statement-breakpoint +UPDATE `repositories` SET `normalized_full_name` = lower(trim(`full_name`));--> statement-breakpoint +DELETE FROM `repositories` +WHERE rowid NOT IN ( + SELECT MIN(rowid) + FROM `repositories` + GROUP BY `user_id`, `normalized_full_name` +);--> statement-breakpoint +CREATE UNIQUE INDEX `uniq_repositories_user_normalized_full_name` ON `repositories` (`user_id`,`normalized_full_name`); diff --git a/Divers/gitea-mirror/drizzle/0008_serious_thena.sql b/Divers/gitea-mirror/drizzle/0008_serious_thena.sql new file mode 100644 index 0000000..ce63bef --- /dev/null +++ b/Divers/gitea-mirror/drizzle/0008_serious_thena.sql @@ -0,0 +1 @@ +ALTER TABLE `repositories` ADD `metadata` text; \ No newline at end of file diff --git a/Divers/gitea-mirror/drizzle/meta/0000_snapshot.json b/Divers/gitea-mirror/drizzle/meta/0000_snapshot.json new file mode 100644 index 0000000..231493d --- /dev/null +++ b/Divers/gitea-mirror/drizzle/meta/0000_snapshot.json @@ -0,0 +1,1290 @@ +{ + "version": "6", + "dialect": "sqlite", + "id": "7782b8ba-bdae-42e8-b8a7-614f8be30a58", + "prevId": "00000000-0000-0000-0000-000000000000", + "tables": { + "accounts": { + "name": "accounts", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "account_id": { + "name": "account_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "provider_id": { + "name": "provider_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "provider_user_id": { + "name": "provider_user_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "access_token": { + "name": "access_token", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "refresh_token": { + "name": "refresh_token", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "expires_at": { + "name": "expires_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "password": { + "name": "password", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + } + }, + "indexes": { + "idx_accounts_account_id": { + "name": "idx_accounts_account_id", + "columns": [ + "account_id" + ], + "isUnique": false + }, + "idx_accounts_user_id": { + "name": "idx_accounts_user_id", + "columns": [ + "user_id" + ], + "isUnique": false + }, + "idx_accounts_provider": { + "name": "idx_accounts_provider", + "columns": [ + "provider_id", + "provider_user_id" + ], + "isUnique": false + } + }, + "foreignKeys": { + "accounts_user_id_users_id_fk": { + "name": "accounts_user_id_users_id_fk", + "tableFrom": "accounts", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "configs": { + "name": "configs", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "is_active": { + "name": "is_active", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": true + }, + "github_config": { + "name": "github_config", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "gitea_config": { + "name": "gitea_config", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "include": { + "name": "include", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'[\"*\"]'" + }, + "exclude": { + "name": "exclude", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'[]'" + }, + "schedule_config": { + "name": "schedule_config", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "cleanup_config": { + "name": "cleanup_config", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + } + }, + "indexes": {}, + "foreignKeys": { + "configs_user_id_users_id_fk": { + "name": "configs_user_id_users_id_fk", + "tableFrom": "configs", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "events": { + "name": "events", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "channel": { + "name": "channel", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "payload": { + "name": "payload", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "read": { + "name": "read", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + } + }, + "indexes": { + "idx_events_user_channel": { + "name": "idx_events_user_channel", + "columns": [ + "user_id", + "channel" + ], + "isUnique": false + }, + "idx_events_created_at": { + "name": "idx_events_created_at", + "columns": [ + "created_at" + ], + "isUnique": false + }, + "idx_events_read": { + "name": "idx_events_read", + "columns": [ + "read" + ], + "isUnique": false + } + }, + "foreignKeys": { + "events_user_id_users_id_fk": { + "name": "events_user_id_users_id_fk", + "tableFrom": "events", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "mirror_jobs": { + "name": "mirror_jobs", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "repository_id": { + "name": "repository_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "repository_name": { + "name": "repository_name", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "organization_name": { + "name": "organization_name", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "details": { + "name": "details", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'imported'" + }, + "message": { + "name": "message", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "timestamp": { + "name": "timestamp", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + }, + "job_type": { + "name": "job_type", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'mirror'" + }, + "batch_id": { + "name": "batch_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "total_items": { + "name": "total_items", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "completed_items": { + "name": "completed_items", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": 0 + }, + "item_ids": { + "name": "item_ids", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "completed_item_ids": { + "name": "completed_item_ids", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'[]'" + }, + "in_progress": { + "name": "in_progress", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "started_at": { + "name": "started_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "completed_at": { + "name": "completed_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "last_checkpoint": { + "name": "last_checkpoint", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": { + "idx_mirror_jobs_user_id": { + "name": "idx_mirror_jobs_user_id", + "columns": [ + "user_id" + ], + "isUnique": false + }, + "idx_mirror_jobs_batch_id": { + "name": "idx_mirror_jobs_batch_id", + "columns": [ + "batch_id" + ], + "isUnique": false + }, + "idx_mirror_jobs_in_progress": { + "name": "idx_mirror_jobs_in_progress", + "columns": [ + "in_progress" + ], + "isUnique": false + }, + "idx_mirror_jobs_job_type": { + "name": "idx_mirror_jobs_job_type", + "columns": [ + "job_type" + ], + "isUnique": false + }, + "idx_mirror_jobs_timestamp": { + "name": "idx_mirror_jobs_timestamp", + "columns": [ + "timestamp" + ], + "isUnique": false + } + }, + "foreignKeys": { + "mirror_jobs_user_id_users_id_fk": { + "name": "mirror_jobs_user_id_users_id_fk", + "tableFrom": "mirror_jobs", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "organizations": { + "name": "organizations", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "config_id": { + "name": "config_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "avatar_url": { + "name": "avatar_url", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "membership_role": { + "name": "membership_role", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'member'" + }, + "is_included": { + "name": "is_included", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": true + }, + "destination_org": { + "name": "destination_org", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'imported'" + }, + "last_mirrored": { + "name": "last_mirrored", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "error_message": { + "name": "error_message", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "repository_count": { + "name": "repository_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + } + }, + "indexes": { + "idx_organizations_user_id": { + "name": "idx_organizations_user_id", + "columns": [ + "user_id" + ], + "isUnique": false + }, + "idx_organizations_config_id": { + "name": "idx_organizations_config_id", + "columns": [ + "config_id" + ], + "isUnique": false + }, + "idx_organizations_status": { + "name": "idx_organizations_status", + "columns": [ + "status" + ], + "isUnique": false + }, + "idx_organizations_is_included": { + "name": "idx_organizations_is_included", + "columns": [ + "is_included" + ], + "isUnique": false + } + }, + "foreignKeys": { + "organizations_user_id_users_id_fk": { + "name": "organizations_user_id_users_id_fk", + "tableFrom": "organizations", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "organizations_config_id_configs_id_fk": { + "name": "organizations_config_id_configs_id_fk", + "tableFrom": "organizations", + "tableTo": "configs", + "columnsFrom": [ + "config_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "repositories": { + "name": "repositories", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "config_id": { + "name": "config_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "full_name": { + "name": "full_name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "url": { + "name": "url", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "clone_url": { + "name": "clone_url", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "owner": { + "name": "owner", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "organization": { + "name": "organization", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "mirrored_location": { + "name": "mirrored_location", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "''" + }, + "is_private": { + "name": "is_private", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "is_fork": { + "name": "is_fork", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "forked_from": { + "name": "forked_from", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "has_issues": { + "name": "has_issues", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "is_starred": { + "name": "is_starred", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "is_archived": { + "name": "is_archived", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "size": { + "name": "size", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "has_lfs": { + "name": "has_lfs", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "has_submodules": { + "name": "has_submodules", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "language": { + "name": "language", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "default_branch": { + "name": "default_branch", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "visibility": { + "name": "visibility", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'public'" + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'imported'" + }, + "last_mirrored": { + "name": "last_mirrored", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "error_message": { + "name": "error_message", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "destination_org": { + "name": "destination_org", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + } + }, + "indexes": { + "idx_repositories_user_id": { + "name": "idx_repositories_user_id", + "columns": [ + "user_id" + ], + "isUnique": false + }, + "idx_repositories_config_id": { + "name": "idx_repositories_config_id", + "columns": [ + "config_id" + ], + "isUnique": false + }, + "idx_repositories_status": { + "name": "idx_repositories_status", + "columns": [ + "status" + ], + "isUnique": false + }, + "idx_repositories_owner": { + "name": "idx_repositories_owner", + "columns": [ + "owner" + ], + "isUnique": false + }, + "idx_repositories_organization": { + "name": "idx_repositories_organization", + "columns": [ + "organization" + ], + "isUnique": false + }, + "idx_repositories_is_fork": { + "name": "idx_repositories_is_fork", + "columns": [ + "is_fork" + ], + "isUnique": false + }, + "idx_repositories_is_starred": { + "name": "idx_repositories_is_starred", + "columns": [ + "is_starred" + ], + "isUnique": false + } + }, + "foreignKeys": { + "repositories_user_id_users_id_fk": { + "name": "repositories_user_id_users_id_fk", + "tableFrom": "repositories", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "repositories_config_id_configs_id_fk": { + "name": "repositories_config_id_configs_id_fk", + "tableFrom": "repositories", + "tableTo": "configs", + "columnsFrom": [ + "config_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "sessions": { + "name": "sessions", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "token": { + "name": "token", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "expires_at": { + "name": "expires_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "ip_address": { + "name": "ip_address", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "user_agent": { + "name": "user_agent", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + } + }, + "indexes": { + "sessions_token_unique": { + "name": "sessions_token_unique", + "columns": [ + "token" + ], + "isUnique": true + }, + "idx_sessions_user_id": { + "name": "idx_sessions_user_id", + "columns": [ + "user_id" + ], + "isUnique": false + }, + "idx_sessions_token": { + "name": "idx_sessions_token", + "columns": [ + "token" + ], + "isUnique": false + }, + "idx_sessions_expires_at": { + "name": "idx_sessions_expires_at", + "columns": [ + "expires_at" + ], + "isUnique": false + } + }, + "foreignKeys": { + "sessions_user_id_users_id_fk": { + "name": "sessions_user_id_users_id_fk", + "tableFrom": "sessions", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "users": { + "name": "users", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "email_verified": { + "name": "email_verified", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "image": { + "name": "image", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + }, + "username": { + "name": "username", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": { + "users_email_unique": { + "name": "users_email_unique", + "columns": [ + "email" + ], + "isUnique": true + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "verification_tokens": { + "name": "verification_tokens", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "token": { + "name": "token", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "identifier": { + "name": "identifier", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "type": { + "name": "type", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "expires_at": { + "name": "expires_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + } + }, + "indexes": { + "verification_tokens_token_unique": { + "name": "verification_tokens_token_unique", + "columns": [ + "token" + ], + "isUnique": true + }, + "idx_verification_tokens_token": { + "name": "idx_verification_tokens_token", + "columns": [ + "token" + ], + "isUnique": false + }, + "idx_verification_tokens_identifier": { + "name": "idx_verification_tokens_identifier", + "columns": [ + "identifier" + ], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + } + }, + "views": {}, + "enums": {}, + "_meta": { + "schemas": {}, + "tables": {}, + "columns": {} + }, + "internal": { + "indexes": {} + } +} \ No newline at end of file diff --git a/Divers/gitea-mirror/drizzle/meta/0001_snapshot.json b/Divers/gitea-mirror/drizzle/meta/0001_snapshot.json new file mode 100644 index 0000000..92545f6 --- /dev/null +++ b/Divers/gitea-mirror/drizzle/meta/0001_snapshot.json @@ -0,0 +1,1722 @@ +{ + "version": "6", + "dialect": "sqlite", + "id": "4e9ce026-e4e3-4a68-a7f2-37ac7747e2a3", + "prevId": "7782b8ba-bdae-42e8-b8a7-614f8be30a58", + "tables": { + "accounts": { + "name": "accounts", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "account_id": { + "name": "account_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "provider_id": { + "name": "provider_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "provider_user_id": { + "name": "provider_user_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "access_token": { + "name": "access_token", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "refresh_token": { + "name": "refresh_token", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "expires_at": { + "name": "expires_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "password": { + "name": "password", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + } + }, + "indexes": { + "idx_accounts_account_id": { + "name": "idx_accounts_account_id", + "columns": [ + "account_id" + ], + "isUnique": false + }, + "idx_accounts_user_id": { + "name": "idx_accounts_user_id", + "columns": [ + "user_id" + ], + "isUnique": false + }, + "idx_accounts_provider": { + "name": "idx_accounts_provider", + "columns": [ + "provider_id", + "provider_user_id" + ], + "isUnique": false + } + }, + "foreignKeys": { + "accounts_user_id_users_id_fk": { + "name": "accounts_user_id_users_id_fk", + "tableFrom": "accounts", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "configs": { + "name": "configs", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "is_active": { + "name": "is_active", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": true + }, + "github_config": { + "name": "github_config", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "gitea_config": { + "name": "gitea_config", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "include": { + "name": "include", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'[\"*\"]'" + }, + "exclude": { + "name": "exclude", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'[]'" + }, + "schedule_config": { + "name": "schedule_config", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "cleanup_config": { + "name": "cleanup_config", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + } + }, + "indexes": {}, + "foreignKeys": { + "configs_user_id_users_id_fk": { + "name": "configs_user_id_users_id_fk", + "tableFrom": "configs", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "events": { + "name": "events", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "channel": { + "name": "channel", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "payload": { + "name": "payload", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "read": { + "name": "read", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + } + }, + "indexes": { + "idx_events_user_channel": { + "name": "idx_events_user_channel", + "columns": [ + "user_id", + "channel" + ], + "isUnique": false + }, + "idx_events_created_at": { + "name": "idx_events_created_at", + "columns": [ + "created_at" + ], + "isUnique": false + }, + "idx_events_read": { + "name": "idx_events_read", + "columns": [ + "read" + ], + "isUnique": false + } + }, + "foreignKeys": { + "events_user_id_users_id_fk": { + "name": "events_user_id_users_id_fk", + "tableFrom": "events", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "mirror_jobs": { + "name": "mirror_jobs", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "repository_id": { + "name": "repository_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "repository_name": { + "name": "repository_name", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "organization_name": { + "name": "organization_name", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "details": { + "name": "details", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'imported'" + }, + "message": { + "name": "message", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "timestamp": { + "name": "timestamp", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + }, + "job_type": { + "name": "job_type", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'mirror'" + }, + "batch_id": { + "name": "batch_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "total_items": { + "name": "total_items", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "completed_items": { + "name": "completed_items", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": 0 + }, + "item_ids": { + "name": "item_ids", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "completed_item_ids": { + "name": "completed_item_ids", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'[]'" + }, + "in_progress": { + "name": "in_progress", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "started_at": { + "name": "started_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "completed_at": { + "name": "completed_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "last_checkpoint": { + "name": "last_checkpoint", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": { + "idx_mirror_jobs_user_id": { + "name": "idx_mirror_jobs_user_id", + "columns": [ + "user_id" + ], + "isUnique": false + }, + "idx_mirror_jobs_batch_id": { + "name": "idx_mirror_jobs_batch_id", + "columns": [ + "batch_id" + ], + "isUnique": false + }, + "idx_mirror_jobs_in_progress": { + "name": "idx_mirror_jobs_in_progress", + "columns": [ + "in_progress" + ], + "isUnique": false + }, + "idx_mirror_jobs_job_type": { + "name": "idx_mirror_jobs_job_type", + "columns": [ + "job_type" + ], + "isUnique": false + }, + "idx_mirror_jobs_timestamp": { + "name": "idx_mirror_jobs_timestamp", + "columns": [ + "timestamp" + ], + "isUnique": false + } + }, + "foreignKeys": { + "mirror_jobs_user_id_users_id_fk": { + "name": "mirror_jobs_user_id_users_id_fk", + "tableFrom": "mirror_jobs", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "oauth_access_tokens": { + "name": "oauth_access_tokens", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "access_token": { + "name": "access_token", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "refresh_token": { + "name": "refresh_token", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "access_token_expires_at": { + "name": "access_token_expires_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "refresh_token_expires_at": { + "name": "refresh_token_expires_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "client_id": { + "name": "client_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "scopes": { + "name": "scopes", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + } + }, + "indexes": { + "idx_oauth_access_tokens_access_token": { + "name": "idx_oauth_access_tokens_access_token", + "columns": [ + "access_token" + ], + "isUnique": false + }, + "idx_oauth_access_tokens_user_id": { + "name": "idx_oauth_access_tokens_user_id", + "columns": [ + "user_id" + ], + "isUnique": false + }, + "idx_oauth_access_tokens_client_id": { + "name": "idx_oauth_access_tokens_client_id", + "columns": [ + "client_id" + ], + "isUnique": false + } + }, + "foreignKeys": { + "oauth_access_tokens_user_id_users_id_fk": { + "name": "oauth_access_tokens_user_id_users_id_fk", + "tableFrom": "oauth_access_tokens", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "oauth_applications": { + "name": "oauth_applications", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "client_id": { + "name": "client_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "client_secret": { + "name": "client_secret", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "redirect_urls": { + "name": "redirect_urls", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "metadata": { + "name": "metadata", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "type": { + "name": "type", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "disabled": { + "name": "disabled", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + } + }, + "indexes": { + "oauth_applications_client_id_unique": { + "name": "oauth_applications_client_id_unique", + "columns": [ + "client_id" + ], + "isUnique": true + }, + "idx_oauth_applications_client_id": { + "name": "idx_oauth_applications_client_id", + "columns": [ + "client_id" + ], + "isUnique": false + }, + "idx_oauth_applications_user_id": { + "name": "idx_oauth_applications_user_id", + "columns": [ + "user_id" + ], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "oauth_consent": { + "name": "oauth_consent", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "client_id": { + "name": "client_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "scopes": { + "name": "scopes", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "consent_given": { + "name": "consent_given", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + } + }, + "indexes": { + "idx_oauth_consent_user_id": { + "name": "idx_oauth_consent_user_id", + "columns": [ + "user_id" + ], + "isUnique": false + }, + "idx_oauth_consent_client_id": { + "name": "idx_oauth_consent_client_id", + "columns": [ + "client_id" + ], + "isUnique": false + }, + "idx_oauth_consent_user_client": { + "name": "idx_oauth_consent_user_client", + "columns": [ + "user_id", + "client_id" + ], + "isUnique": false + } + }, + "foreignKeys": { + "oauth_consent_user_id_users_id_fk": { + "name": "oauth_consent_user_id_users_id_fk", + "tableFrom": "oauth_consent", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "organizations": { + "name": "organizations", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "config_id": { + "name": "config_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "avatar_url": { + "name": "avatar_url", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "membership_role": { + "name": "membership_role", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'member'" + }, + "is_included": { + "name": "is_included", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": true + }, + "destination_org": { + "name": "destination_org", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'imported'" + }, + "last_mirrored": { + "name": "last_mirrored", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "error_message": { + "name": "error_message", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "repository_count": { + "name": "repository_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + } + }, + "indexes": { + "idx_organizations_user_id": { + "name": "idx_organizations_user_id", + "columns": [ + "user_id" + ], + "isUnique": false + }, + "idx_organizations_config_id": { + "name": "idx_organizations_config_id", + "columns": [ + "config_id" + ], + "isUnique": false + }, + "idx_organizations_status": { + "name": "idx_organizations_status", + "columns": [ + "status" + ], + "isUnique": false + }, + "idx_organizations_is_included": { + "name": "idx_organizations_is_included", + "columns": [ + "is_included" + ], + "isUnique": false + } + }, + "foreignKeys": { + "organizations_user_id_users_id_fk": { + "name": "organizations_user_id_users_id_fk", + "tableFrom": "organizations", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "organizations_config_id_configs_id_fk": { + "name": "organizations_config_id_configs_id_fk", + "tableFrom": "organizations", + "tableTo": "configs", + "columnsFrom": [ + "config_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "repositories": { + "name": "repositories", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "config_id": { + "name": "config_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "full_name": { + "name": "full_name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "url": { + "name": "url", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "clone_url": { + "name": "clone_url", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "owner": { + "name": "owner", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "organization": { + "name": "organization", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "mirrored_location": { + "name": "mirrored_location", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "''" + }, + "is_private": { + "name": "is_private", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "is_fork": { + "name": "is_fork", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "forked_from": { + "name": "forked_from", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "has_issues": { + "name": "has_issues", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "is_starred": { + "name": "is_starred", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "is_archived": { + "name": "is_archived", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "size": { + "name": "size", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "has_lfs": { + "name": "has_lfs", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "has_submodules": { + "name": "has_submodules", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "language": { + "name": "language", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "default_branch": { + "name": "default_branch", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "visibility": { + "name": "visibility", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'public'" + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'imported'" + }, + "last_mirrored": { + "name": "last_mirrored", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "error_message": { + "name": "error_message", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "destination_org": { + "name": "destination_org", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + } + }, + "indexes": { + "idx_repositories_user_id": { + "name": "idx_repositories_user_id", + "columns": [ + "user_id" + ], + "isUnique": false + }, + "idx_repositories_config_id": { + "name": "idx_repositories_config_id", + "columns": [ + "config_id" + ], + "isUnique": false + }, + "idx_repositories_status": { + "name": "idx_repositories_status", + "columns": [ + "status" + ], + "isUnique": false + }, + "idx_repositories_owner": { + "name": "idx_repositories_owner", + "columns": [ + "owner" + ], + "isUnique": false + }, + "idx_repositories_organization": { + "name": "idx_repositories_organization", + "columns": [ + "organization" + ], + "isUnique": false + }, + "idx_repositories_is_fork": { + "name": "idx_repositories_is_fork", + "columns": [ + "is_fork" + ], + "isUnique": false + }, + "idx_repositories_is_starred": { + "name": "idx_repositories_is_starred", + "columns": [ + "is_starred" + ], + "isUnique": false + } + }, + "foreignKeys": { + "repositories_user_id_users_id_fk": { + "name": "repositories_user_id_users_id_fk", + "tableFrom": "repositories", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "repositories_config_id_configs_id_fk": { + "name": "repositories_config_id_configs_id_fk", + "tableFrom": "repositories", + "tableTo": "configs", + "columnsFrom": [ + "config_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "sessions": { + "name": "sessions", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "token": { + "name": "token", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "expires_at": { + "name": "expires_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "ip_address": { + "name": "ip_address", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "user_agent": { + "name": "user_agent", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + } + }, + "indexes": { + "sessions_token_unique": { + "name": "sessions_token_unique", + "columns": [ + "token" + ], + "isUnique": true + }, + "idx_sessions_user_id": { + "name": "idx_sessions_user_id", + "columns": [ + "user_id" + ], + "isUnique": false + }, + "idx_sessions_token": { + "name": "idx_sessions_token", + "columns": [ + "token" + ], + "isUnique": false + }, + "idx_sessions_expires_at": { + "name": "idx_sessions_expires_at", + "columns": [ + "expires_at" + ], + "isUnique": false + } + }, + "foreignKeys": { + "sessions_user_id_users_id_fk": { + "name": "sessions_user_id_users_id_fk", + "tableFrom": "sessions", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "sso_providers": { + "name": "sso_providers", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "issuer": { + "name": "issuer", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "domain": { + "name": "domain", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "oidc_config": { + "name": "oidc_config", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "provider_id": { + "name": "provider_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + } + }, + "indexes": { + "sso_providers_provider_id_unique": { + "name": "sso_providers_provider_id_unique", + "columns": [ + "provider_id" + ], + "isUnique": true + }, + "idx_sso_providers_provider_id": { + "name": "idx_sso_providers_provider_id", + "columns": [ + "provider_id" + ], + "isUnique": false + }, + "idx_sso_providers_domain": { + "name": "idx_sso_providers_domain", + "columns": [ + "domain" + ], + "isUnique": false + }, + "idx_sso_providers_issuer": { + "name": "idx_sso_providers_issuer", + "columns": [ + "issuer" + ], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "users": { + "name": "users", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "email_verified": { + "name": "email_verified", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "image": { + "name": "image", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + }, + "username": { + "name": "username", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": { + "users_email_unique": { + "name": "users_email_unique", + "columns": [ + "email" + ], + "isUnique": true + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "verification_tokens": { + "name": "verification_tokens", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "token": { + "name": "token", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "identifier": { + "name": "identifier", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "type": { + "name": "type", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "expires_at": { + "name": "expires_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + } + }, + "indexes": { + "verification_tokens_token_unique": { + "name": "verification_tokens_token_unique", + "columns": [ + "token" + ], + "isUnique": true + }, + "idx_verification_tokens_token": { + "name": "idx_verification_tokens_token", + "columns": [ + "token" + ], + "isUnique": false + }, + "idx_verification_tokens_identifier": { + "name": "idx_verification_tokens_identifier", + "columns": [ + "identifier" + ], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + } + }, + "views": {}, + "enums": {}, + "_meta": { + "schemas": {}, + "tables": {}, + "columns": {} + }, + "internal": { + "indexes": {} + } +} \ No newline at end of file diff --git a/Divers/gitea-mirror/drizzle/meta/0002_snapshot.json b/Divers/gitea-mirror/drizzle/meta/0002_snapshot.json new file mode 100644 index 0000000..c22a59d --- /dev/null +++ b/Divers/gitea-mirror/drizzle/meta/0002_snapshot.json @@ -0,0 +1,1784 @@ +{ + "version": "6", + "dialect": "sqlite", + "id": "dd92f0d1-fba9-4237-874a-b19a465b9dff", + "prevId": "4e9ce026-e4e3-4a68-a7f2-37ac7747e2a3", + "tables": { + "accounts": { + "name": "accounts", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "account_id": { + "name": "account_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "provider_id": { + "name": "provider_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "provider_user_id": { + "name": "provider_user_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "access_token": { + "name": "access_token", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "refresh_token": { + "name": "refresh_token", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "expires_at": { + "name": "expires_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "password": { + "name": "password", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + } + }, + "indexes": { + "idx_accounts_account_id": { + "name": "idx_accounts_account_id", + "columns": [ + "account_id" + ], + "isUnique": false + }, + "idx_accounts_user_id": { + "name": "idx_accounts_user_id", + "columns": [ + "user_id" + ], + "isUnique": false + }, + "idx_accounts_provider": { + "name": "idx_accounts_provider", + "columns": [ + "provider_id", + "provider_user_id" + ], + "isUnique": false + } + }, + "foreignKeys": { + "accounts_user_id_users_id_fk": { + "name": "accounts_user_id_users_id_fk", + "tableFrom": "accounts", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "configs": { + "name": "configs", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "is_active": { + "name": "is_active", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": true + }, + "github_config": { + "name": "github_config", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "gitea_config": { + "name": "gitea_config", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "include": { + "name": "include", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'[\"*\"]'" + }, + "exclude": { + "name": "exclude", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'[]'" + }, + "schedule_config": { + "name": "schedule_config", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "cleanup_config": { + "name": "cleanup_config", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + } + }, + "indexes": {}, + "foreignKeys": { + "configs_user_id_users_id_fk": { + "name": "configs_user_id_users_id_fk", + "tableFrom": "configs", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "events": { + "name": "events", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "channel": { + "name": "channel", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "payload": { + "name": "payload", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "read": { + "name": "read", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + } + }, + "indexes": { + "idx_events_user_channel": { + "name": "idx_events_user_channel", + "columns": [ + "user_id", + "channel" + ], + "isUnique": false + }, + "idx_events_created_at": { + "name": "idx_events_created_at", + "columns": [ + "created_at" + ], + "isUnique": false + }, + "idx_events_read": { + "name": "idx_events_read", + "columns": [ + "read" + ], + "isUnique": false + } + }, + "foreignKeys": { + "events_user_id_users_id_fk": { + "name": "events_user_id_users_id_fk", + "tableFrom": "events", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "mirror_jobs": { + "name": "mirror_jobs", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "repository_id": { + "name": "repository_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "repository_name": { + "name": "repository_name", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "organization_name": { + "name": "organization_name", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "details": { + "name": "details", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'imported'" + }, + "message": { + "name": "message", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "timestamp": { + "name": "timestamp", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + }, + "job_type": { + "name": "job_type", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'mirror'" + }, + "batch_id": { + "name": "batch_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "total_items": { + "name": "total_items", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "completed_items": { + "name": "completed_items", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": 0 + }, + "item_ids": { + "name": "item_ids", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "completed_item_ids": { + "name": "completed_item_ids", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'[]'" + }, + "in_progress": { + "name": "in_progress", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "started_at": { + "name": "started_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "completed_at": { + "name": "completed_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "last_checkpoint": { + "name": "last_checkpoint", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": { + "idx_mirror_jobs_user_id": { + "name": "idx_mirror_jobs_user_id", + "columns": [ + "user_id" + ], + "isUnique": false + }, + "idx_mirror_jobs_batch_id": { + "name": "idx_mirror_jobs_batch_id", + "columns": [ + "batch_id" + ], + "isUnique": false + }, + "idx_mirror_jobs_in_progress": { + "name": "idx_mirror_jobs_in_progress", + "columns": [ + "in_progress" + ], + "isUnique": false + }, + "idx_mirror_jobs_job_type": { + "name": "idx_mirror_jobs_job_type", + "columns": [ + "job_type" + ], + "isUnique": false + }, + "idx_mirror_jobs_timestamp": { + "name": "idx_mirror_jobs_timestamp", + "columns": [ + "timestamp" + ], + "isUnique": false + } + }, + "foreignKeys": { + "mirror_jobs_user_id_users_id_fk": { + "name": "mirror_jobs_user_id_users_id_fk", + "tableFrom": "mirror_jobs", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "oauth_access_tokens": { + "name": "oauth_access_tokens", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "access_token": { + "name": "access_token", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "refresh_token": { + "name": "refresh_token", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "access_token_expires_at": { + "name": "access_token_expires_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "refresh_token_expires_at": { + "name": "refresh_token_expires_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "client_id": { + "name": "client_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "scopes": { + "name": "scopes", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + } + }, + "indexes": { + "idx_oauth_access_tokens_access_token": { + "name": "idx_oauth_access_tokens_access_token", + "columns": [ + "access_token" + ], + "isUnique": false + }, + "idx_oauth_access_tokens_user_id": { + "name": "idx_oauth_access_tokens_user_id", + "columns": [ + "user_id" + ], + "isUnique": false + }, + "idx_oauth_access_tokens_client_id": { + "name": "idx_oauth_access_tokens_client_id", + "columns": [ + "client_id" + ], + "isUnique": false + } + }, + "foreignKeys": { + "oauth_access_tokens_user_id_users_id_fk": { + "name": "oauth_access_tokens_user_id_users_id_fk", + "tableFrom": "oauth_access_tokens", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "oauth_applications": { + "name": "oauth_applications", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "client_id": { + "name": "client_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "client_secret": { + "name": "client_secret", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "redirect_urls": { + "name": "redirect_urls", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "metadata": { + "name": "metadata", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "type": { + "name": "type", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "disabled": { + "name": "disabled", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + } + }, + "indexes": { + "oauth_applications_client_id_unique": { + "name": "oauth_applications_client_id_unique", + "columns": [ + "client_id" + ], + "isUnique": true + }, + "idx_oauth_applications_client_id": { + "name": "idx_oauth_applications_client_id", + "columns": [ + "client_id" + ], + "isUnique": false + }, + "idx_oauth_applications_user_id": { + "name": "idx_oauth_applications_user_id", + "columns": [ + "user_id" + ], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "oauth_consent": { + "name": "oauth_consent", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "client_id": { + "name": "client_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "scopes": { + "name": "scopes", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "consent_given": { + "name": "consent_given", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + } + }, + "indexes": { + "idx_oauth_consent_user_id": { + "name": "idx_oauth_consent_user_id", + "columns": [ + "user_id" + ], + "isUnique": false + }, + "idx_oauth_consent_client_id": { + "name": "idx_oauth_consent_client_id", + "columns": [ + "client_id" + ], + "isUnique": false + }, + "idx_oauth_consent_user_client": { + "name": "idx_oauth_consent_user_client", + "columns": [ + "user_id", + "client_id" + ], + "isUnique": false + } + }, + "foreignKeys": { + "oauth_consent_user_id_users_id_fk": { + "name": "oauth_consent_user_id_users_id_fk", + "tableFrom": "oauth_consent", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "organizations": { + "name": "organizations", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "config_id": { + "name": "config_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "avatar_url": { + "name": "avatar_url", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "membership_role": { + "name": "membership_role", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'member'" + }, + "is_included": { + "name": "is_included", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": true + }, + "destination_org": { + "name": "destination_org", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'imported'" + }, + "last_mirrored": { + "name": "last_mirrored", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "error_message": { + "name": "error_message", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "repository_count": { + "name": "repository_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + } + }, + "indexes": { + "idx_organizations_user_id": { + "name": "idx_organizations_user_id", + "columns": [ + "user_id" + ], + "isUnique": false + }, + "idx_organizations_config_id": { + "name": "idx_organizations_config_id", + "columns": [ + "config_id" + ], + "isUnique": false + }, + "idx_organizations_status": { + "name": "idx_organizations_status", + "columns": [ + "status" + ], + "isUnique": false + }, + "idx_organizations_is_included": { + "name": "idx_organizations_is_included", + "columns": [ + "is_included" + ], + "isUnique": false + } + }, + "foreignKeys": { + "organizations_user_id_users_id_fk": { + "name": "organizations_user_id_users_id_fk", + "tableFrom": "organizations", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "organizations_config_id_configs_id_fk": { + "name": "organizations_config_id_configs_id_fk", + "tableFrom": "organizations", + "tableTo": "configs", + "columnsFrom": [ + "config_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "repositories": { + "name": "repositories", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "config_id": { + "name": "config_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "full_name": { + "name": "full_name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "url": { + "name": "url", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "clone_url": { + "name": "clone_url", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "owner": { + "name": "owner", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "organization": { + "name": "organization", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "mirrored_location": { + "name": "mirrored_location", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "''" + }, + "is_private": { + "name": "is_private", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "is_fork": { + "name": "is_fork", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "forked_from": { + "name": "forked_from", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "has_issues": { + "name": "has_issues", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "is_starred": { + "name": "is_starred", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "is_archived": { + "name": "is_archived", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "size": { + "name": "size", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "has_lfs": { + "name": "has_lfs", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "has_submodules": { + "name": "has_submodules", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "language": { + "name": "language", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "default_branch": { + "name": "default_branch", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "visibility": { + "name": "visibility", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'public'" + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'imported'" + }, + "last_mirrored": { + "name": "last_mirrored", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "error_message": { + "name": "error_message", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "destination_org": { + "name": "destination_org", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + } + }, + "indexes": { + "idx_repositories_user_id": { + "name": "idx_repositories_user_id", + "columns": [ + "user_id" + ], + "isUnique": false + }, + "idx_repositories_config_id": { + "name": "idx_repositories_config_id", + "columns": [ + "config_id" + ], + "isUnique": false + }, + "idx_repositories_status": { + "name": "idx_repositories_status", + "columns": [ + "status" + ], + "isUnique": false + }, + "idx_repositories_owner": { + "name": "idx_repositories_owner", + "columns": [ + "owner" + ], + "isUnique": false + }, + "idx_repositories_organization": { + "name": "idx_repositories_organization", + "columns": [ + "organization" + ], + "isUnique": false + }, + "idx_repositories_is_fork": { + "name": "idx_repositories_is_fork", + "columns": [ + "is_fork" + ], + "isUnique": false + }, + "idx_repositories_is_starred": { + "name": "idx_repositories_is_starred", + "columns": [ + "is_starred" + ], + "isUnique": false + } + }, + "foreignKeys": { + "repositories_user_id_users_id_fk": { + "name": "repositories_user_id_users_id_fk", + "tableFrom": "repositories", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "repositories_config_id_configs_id_fk": { + "name": "repositories_config_id_configs_id_fk", + "tableFrom": "repositories", + "tableTo": "configs", + "columnsFrom": [ + "config_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "sessions": { + "name": "sessions", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "token": { + "name": "token", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "expires_at": { + "name": "expires_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "ip_address": { + "name": "ip_address", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "user_agent": { + "name": "user_agent", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + } + }, + "indexes": { + "sessions_token_unique": { + "name": "sessions_token_unique", + "columns": [ + "token" + ], + "isUnique": true + }, + "idx_sessions_user_id": { + "name": "idx_sessions_user_id", + "columns": [ + "user_id" + ], + "isUnique": false + }, + "idx_sessions_token": { + "name": "idx_sessions_token", + "columns": [ + "token" + ], + "isUnique": false + }, + "idx_sessions_expires_at": { + "name": "idx_sessions_expires_at", + "columns": [ + "expires_at" + ], + "isUnique": false + } + }, + "foreignKeys": { + "sessions_user_id_users_id_fk": { + "name": "sessions_user_id_users_id_fk", + "tableFrom": "sessions", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "sso_providers": { + "name": "sso_providers", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "issuer": { + "name": "issuer", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "domain": { + "name": "domain", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "oidc_config": { + "name": "oidc_config", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "provider_id": { + "name": "provider_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + } + }, + "indexes": { + "sso_providers_provider_id_unique": { + "name": "sso_providers_provider_id_unique", + "columns": [ + "provider_id" + ], + "isUnique": true + }, + "idx_sso_providers_provider_id": { + "name": "idx_sso_providers_provider_id", + "columns": [ + "provider_id" + ], + "isUnique": false + }, + "idx_sso_providers_domain": { + "name": "idx_sso_providers_domain", + "columns": [ + "domain" + ], + "isUnique": false + }, + "idx_sso_providers_issuer": { + "name": "idx_sso_providers_issuer", + "columns": [ + "issuer" + ], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "users": { + "name": "users", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "email_verified": { + "name": "email_verified", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "image": { + "name": "image", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + }, + "username": { + "name": "username", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": { + "users_email_unique": { + "name": "users_email_unique", + "columns": [ + "email" + ], + "isUnique": true + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "verification_tokens": { + "name": "verification_tokens", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "token": { + "name": "token", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "identifier": { + "name": "identifier", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "type": { + "name": "type", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "expires_at": { + "name": "expires_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + } + }, + "indexes": { + "verification_tokens_token_unique": { + "name": "verification_tokens_token_unique", + "columns": [ + "token" + ], + "isUnique": true + }, + "idx_verification_tokens_token": { + "name": "idx_verification_tokens_token", + "columns": [ + "token" + ], + "isUnique": false + }, + "idx_verification_tokens_identifier": { + "name": "idx_verification_tokens_identifier", + "columns": [ + "identifier" + ], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "verifications": { + "name": "verifications", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "identifier": { + "name": "identifier", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "value": { + "name": "value", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "expires_at": { + "name": "expires_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + } + }, + "indexes": { + "idx_verifications_identifier": { + "name": "idx_verifications_identifier", + "columns": [ + "identifier" + ], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + } + }, + "views": {}, + "enums": {}, + "_meta": { + "schemas": {}, + "tables": {}, + "columns": {} + }, + "internal": { + "indexes": {} + } +} \ No newline at end of file diff --git a/Divers/gitea-mirror/drizzle/meta/0003_snapshot.json b/Divers/gitea-mirror/drizzle/meta/0003_snapshot.json new file mode 100644 index 0000000..b6497cf --- /dev/null +++ b/Divers/gitea-mirror/drizzle/meta/0003_snapshot.json @@ -0,0 +1,1805 @@ +{ + "version": "6", + "dialect": "sqlite", + "id": "376b0dbc-5fd4-4251-979f-f5cc2d1455b3", + "prevId": "dd92f0d1-fba9-4237-874a-b19a465b9dff", + "tables": { + "accounts": { + "name": "accounts", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "account_id": { + "name": "account_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "provider_id": { + "name": "provider_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "provider_user_id": { + "name": "provider_user_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "access_token": { + "name": "access_token", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "refresh_token": { + "name": "refresh_token", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "expires_at": { + "name": "expires_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "password": { + "name": "password", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + } + }, + "indexes": { + "idx_accounts_account_id": { + "name": "idx_accounts_account_id", + "columns": [ + "account_id" + ], + "isUnique": false + }, + "idx_accounts_user_id": { + "name": "idx_accounts_user_id", + "columns": [ + "user_id" + ], + "isUnique": false + }, + "idx_accounts_provider": { + "name": "idx_accounts_provider", + "columns": [ + "provider_id", + "provider_user_id" + ], + "isUnique": false + } + }, + "foreignKeys": { + "accounts_user_id_users_id_fk": { + "name": "accounts_user_id_users_id_fk", + "tableFrom": "accounts", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "configs": { + "name": "configs", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "is_active": { + "name": "is_active", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": true + }, + "github_config": { + "name": "github_config", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "gitea_config": { + "name": "gitea_config", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "include": { + "name": "include", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'[\"*\"]'" + }, + "exclude": { + "name": "exclude", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'[]'" + }, + "schedule_config": { + "name": "schedule_config", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "cleanup_config": { + "name": "cleanup_config", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + } + }, + "indexes": {}, + "foreignKeys": { + "configs_user_id_users_id_fk": { + "name": "configs_user_id_users_id_fk", + "tableFrom": "configs", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "events": { + "name": "events", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "channel": { + "name": "channel", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "payload": { + "name": "payload", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "read": { + "name": "read", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + } + }, + "indexes": { + "idx_events_user_channel": { + "name": "idx_events_user_channel", + "columns": [ + "user_id", + "channel" + ], + "isUnique": false + }, + "idx_events_created_at": { + "name": "idx_events_created_at", + "columns": [ + "created_at" + ], + "isUnique": false + }, + "idx_events_read": { + "name": "idx_events_read", + "columns": [ + "read" + ], + "isUnique": false + } + }, + "foreignKeys": { + "events_user_id_users_id_fk": { + "name": "events_user_id_users_id_fk", + "tableFrom": "events", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "mirror_jobs": { + "name": "mirror_jobs", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "repository_id": { + "name": "repository_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "repository_name": { + "name": "repository_name", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "organization_name": { + "name": "organization_name", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "details": { + "name": "details", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'imported'" + }, + "message": { + "name": "message", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "timestamp": { + "name": "timestamp", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + }, + "job_type": { + "name": "job_type", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'mirror'" + }, + "batch_id": { + "name": "batch_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "total_items": { + "name": "total_items", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "completed_items": { + "name": "completed_items", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": 0 + }, + "item_ids": { + "name": "item_ids", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "completed_item_ids": { + "name": "completed_item_ids", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'[]'" + }, + "in_progress": { + "name": "in_progress", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "started_at": { + "name": "started_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "completed_at": { + "name": "completed_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "last_checkpoint": { + "name": "last_checkpoint", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": { + "idx_mirror_jobs_user_id": { + "name": "idx_mirror_jobs_user_id", + "columns": [ + "user_id" + ], + "isUnique": false + }, + "idx_mirror_jobs_batch_id": { + "name": "idx_mirror_jobs_batch_id", + "columns": [ + "batch_id" + ], + "isUnique": false + }, + "idx_mirror_jobs_in_progress": { + "name": "idx_mirror_jobs_in_progress", + "columns": [ + "in_progress" + ], + "isUnique": false + }, + "idx_mirror_jobs_job_type": { + "name": "idx_mirror_jobs_job_type", + "columns": [ + "job_type" + ], + "isUnique": false + }, + "idx_mirror_jobs_timestamp": { + "name": "idx_mirror_jobs_timestamp", + "columns": [ + "timestamp" + ], + "isUnique": false + } + }, + "foreignKeys": { + "mirror_jobs_user_id_users_id_fk": { + "name": "mirror_jobs_user_id_users_id_fk", + "tableFrom": "mirror_jobs", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "oauth_access_tokens": { + "name": "oauth_access_tokens", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "access_token": { + "name": "access_token", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "refresh_token": { + "name": "refresh_token", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "access_token_expires_at": { + "name": "access_token_expires_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "refresh_token_expires_at": { + "name": "refresh_token_expires_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "client_id": { + "name": "client_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "scopes": { + "name": "scopes", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + } + }, + "indexes": { + "idx_oauth_access_tokens_access_token": { + "name": "idx_oauth_access_tokens_access_token", + "columns": [ + "access_token" + ], + "isUnique": false + }, + "idx_oauth_access_tokens_user_id": { + "name": "idx_oauth_access_tokens_user_id", + "columns": [ + "user_id" + ], + "isUnique": false + }, + "idx_oauth_access_tokens_client_id": { + "name": "idx_oauth_access_tokens_client_id", + "columns": [ + "client_id" + ], + "isUnique": false + } + }, + "foreignKeys": { + "oauth_access_tokens_user_id_users_id_fk": { + "name": "oauth_access_tokens_user_id_users_id_fk", + "tableFrom": "oauth_access_tokens", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "oauth_applications": { + "name": "oauth_applications", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "client_id": { + "name": "client_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "client_secret": { + "name": "client_secret", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "redirect_urls": { + "name": "redirect_urls", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "metadata": { + "name": "metadata", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "type": { + "name": "type", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "disabled": { + "name": "disabled", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + } + }, + "indexes": { + "oauth_applications_client_id_unique": { + "name": "oauth_applications_client_id_unique", + "columns": [ + "client_id" + ], + "isUnique": true + }, + "idx_oauth_applications_client_id": { + "name": "idx_oauth_applications_client_id", + "columns": [ + "client_id" + ], + "isUnique": false + }, + "idx_oauth_applications_user_id": { + "name": "idx_oauth_applications_user_id", + "columns": [ + "user_id" + ], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "oauth_consent": { + "name": "oauth_consent", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "client_id": { + "name": "client_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "scopes": { + "name": "scopes", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "consent_given": { + "name": "consent_given", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + } + }, + "indexes": { + "idx_oauth_consent_user_id": { + "name": "idx_oauth_consent_user_id", + "columns": [ + "user_id" + ], + "isUnique": false + }, + "idx_oauth_consent_client_id": { + "name": "idx_oauth_consent_client_id", + "columns": [ + "client_id" + ], + "isUnique": false + }, + "idx_oauth_consent_user_client": { + "name": "idx_oauth_consent_user_client", + "columns": [ + "user_id", + "client_id" + ], + "isUnique": false + } + }, + "foreignKeys": { + "oauth_consent_user_id_users_id_fk": { + "name": "oauth_consent_user_id_users_id_fk", + "tableFrom": "oauth_consent", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "organizations": { + "name": "organizations", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "config_id": { + "name": "config_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "avatar_url": { + "name": "avatar_url", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "membership_role": { + "name": "membership_role", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'member'" + }, + "is_included": { + "name": "is_included", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": true + }, + "destination_org": { + "name": "destination_org", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'imported'" + }, + "last_mirrored": { + "name": "last_mirrored", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "error_message": { + "name": "error_message", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "repository_count": { + "name": "repository_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "public_repository_count": { + "name": "public_repository_count", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "private_repository_count": { + "name": "private_repository_count", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "fork_repository_count": { + "name": "fork_repository_count", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + } + }, + "indexes": { + "idx_organizations_user_id": { + "name": "idx_organizations_user_id", + "columns": [ + "user_id" + ], + "isUnique": false + }, + "idx_organizations_config_id": { + "name": "idx_organizations_config_id", + "columns": [ + "config_id" + ], + "isUnique": false + }, + "idx_organizations_status": { + "name": "idx_organizations_status", + "columns": [ + "status" + ], + "isUnique": false + }, + "idx_organizations_is_included": { + "name": "idx_organizations_is_included", + "columns": [ + "is_included" + ], + "isUnique": false + } + }, + "foreignKeys": { + "organizations_user_id_users_id_fk": { + "name": "organizations_user_id_users_id_fk", + "tableFrom": "organizations", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "organizations_config_id_configs_id_fk": { + "name": "organizations_config_id_configs_id_fk", + "tableFrom": "organizations", + "tableTo": "configs", + "columnsFrom": [ + "config_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "repositories": { + "name": "repositories", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "config_id": { + "name": "config_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "full_name": { + "name": "full_name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "url": { + "name": "url", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "clone_url": { + "name": "clone_url", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "owner": { + "name": "owner", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "organization": { + "name": "organization", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "mirrored_location": { + "name": "mirrored_location", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "''" + }, + "is_private": { + "name": "is_private", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "is_fork": { + "name": "is_fork", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "forked_from": { + "name": "forked_from", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "has_issues": { + "name": "has_issues", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "is_starred": { + "name": "is_starred", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "is_archived": { + "name": "is_archived", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "size": { + "name": "size", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "has_lfs": { + "name": "has_lfs", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "has_submodules": { + "name": "has_submodules", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "language": { + "name": "language", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "default_branch": { + "name": "default_branch", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "visibility": { + "name": "visibility", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'public'" + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'imported'" + }, + "last_mirrored": { + "name": "last_mirrored", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "error_message": { + "name": "error_message", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "destination_org": { + "name": "destination_org", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + } + }, + "indexes": { + "idx_repositories_user_id": { + "name": "idx_repositories_user_id", + "columns": [ + "user_id" + ], + "isUnique": false + }, + "idx_repositories_config_id": { + "name": "idx_repositories_config_id", + "columns": [ + "config_id" + ], + "isUnique": false + }, + "idx_repositories_status": { + "name": "idx_repositories_status", + "columns": [ + "status" + ], + "isUnique": false + }, + "idx_repositories_owner": { + "name": "idx_repositories_owner", + "columns": [ + "owner" + ], + "isUnique": false + }, + "idx_repositories_organization": { + "name": "idx_repositories_organization", + "columns": [ + "organization" + ], + "isUnique": false + }, + "idx_repositories_is_fork": { + "name": "idx_repositories_is_fork", + "columns": [ + "is_fork" + ], + "isUnique": false + }, + "idx_repositories_is_starred": { + "name": "idx_repositories_is_starred", + "columns": [ + "is_starred" + ], + "isUnique": false + } + }, + "foreignKeys": { + "repositories_user_id_users_id_fk": { + "name": "repositories_user_id_users_id_fk", + "tableFrom": "repositories", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "repositories_config_id_configs_id_fk": { + "name": "repositories_config_id_configs_id_fk", + "tableFrom": "repositories", + "tableTo": "configs", + "columnsFrom": [ + "config_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "sessions": { + "name": "sessions", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "token": { + "name": "token", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "expires_at": { + "name": "expires_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "ip_address": { + "name": "ip_address", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "user_agent": { + "name": "user_agent", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + } + }, + "indexes": { + "sessions_token_unique": { + "name": "sessions_token_unique", + "columns": [ + "token" + ], + "isUnique": true + }, + "idx_sessions_user_id": { + "name": "idx_sessions_user_id", + "columns": [ + "user_id" + ], + "isUnique": false + }, + "idx_sessions_token": { + "name": "idx_sessions_token", + "columns": [ + "token" + ], + "isUnique": false + }, + "idx_sessions_expires_at": { + "name": "idx_sessions_expires_at", + "columns": [ + "expires_at" + ], + "isUnique": false + } + }, + "foreignKeys": { + "sessions_user_id_users_id_fk": { + "name": "sessions_user_id_users_id_fk", + "tableFrom": "sessions", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "sso_providers": { + "name": "sso_providers", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "issuer": { + "name": "issuer", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "domain": { + "name": "domain", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "oidc_config": { + "name": "oidc_config", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "provider_id": { + "name": "provider_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + } + }, + "indexes": { + "sso_providers_provider_id_unique": { + "name": "sso_providers_provider_id_unique", + "columns": [ + "provider_id" + ], + "isUnique": true + }, + "idx_sso_providers_provider_id": { + "name": "idx_sso_providers_provider_id", + "columns": [ + "provider_id" + ], + "isUnique": false + }, + "idx_sso_providers_domain": { + "name": "idx_sso_providers_domain", + "columns": [ + "domain" + ], + "isUnique": false + }, + "idx_sso_providers_issuer": { + "name": "idx_sso_providers_issuer", + "columns": [ + "issuer" + ], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "users": { + "name": "users", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "email_verified": { + "name": "email_verified", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "image": { + "name": "image", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + }, + "username": { + "name": "username", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": { + "users_email_unique": { + "name": "users_email_unique", + "columns": [ + "email" + ], + "isUnique": true + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "verification_tokens": { + "name": "verification_tokens", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "token": { + "name": "token", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "identifier": { + "name": "identifier", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "type": { + "name": "type", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "expires_at": { + "name": "expires_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + } + }, + "indexes": { + "verification_tokens_token_unique": { + "name": "verification_tokens_token_unique", + "columns": [ + "token" + ], + "isUnique": true + }, + "idx_verification_tokens_token": { + "name": "idx_verification_tokens_token", + "columns": [ + "token" + ], + "isUnique": false + }, + "idx_verification_tokens_identifier": { + "name": "idx_verification_tokens_identifier", + "columns": [ + "identifier" + ], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "verifications": { + "name": "verifications", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "identifier": { + "name": "identifier", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "value": { + "name": "value", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "expires_at": { + "name": "expires_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + } + }, + "indexes": { + "idx_verifications_identifier": { + "name": "idx_verifications_identifier", + "columns": [ + "identifier" + ], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + } + }, + "views": {}, + "enums": {}, + "_meta": { + "schemas": {}, + "tables": {}, + "columns": {} + }, + "internal": { + "indexes": {} + } +} \ No newline at end of file diff --git a/Divers/gitea-mirror/drizzle/meta/0004_snapshot.json b/Divers/gitea-mirror/drizzle/meta/0004_snapshot.json new file mode 100644 index 0000000..69fb081 --- /dev/null +++ b/Divers/gitea-mirror/drizzle/meta/0004_snapshot.json @@ -0,0 +1,1933 @@ +{ + "version": "6", + "dialect": "sqlite", + "id": "284b8db7-ef64-4311-b890-0251bcbe5866", + "prevId": "376b0dbc-5fd4-4251-979f-f5cc2d1455b3", + "tables": { + "accounts": { + "name": "accounts", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "account_id": { + "name": "account_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "provider_id": { + "name": "provider_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "provider_user_id": { + "name": "provider_user_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "access_token": { + "name": "access_token", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "refresh_token": { + "name": "refresh_token", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "expires_at": { + "name": "expires_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "password": { + "name": "password", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + } + }, + "indexes": { + "idx_accounts_account_id": { + "name": "idx_accounts_account_id", + "columns": [ + "account_id" + ], + "isUnique": false + }, + "idx_accounts_user_id": { + "name": "idx_accounts_user_id", + "columns": [ + "user_id" + ], + "isUnique": false + }, + "idx_accounts_provider": { + "name": "idx_accounts_provider", + "columns": [ + "provider_id", + "provider_user_id" + ], + "isUnique": false + } + }, + "foreignKeys": { + "accounts_user_id_users_id_fk": { + "name": "accounts_user_id_users_id_fk", + "tableFrom": "accounts", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "configs": { + "name": "configs", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "is_active": { + "name": "is_active", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": true + }, + "github_config": { + "name": "github_config", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "gitea_config": { + "name": "gitea_config", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "include": { + "name": "include", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'[\"*\"]'" + }, + "exclude": { + "name": "exclude", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'[]'" + }, + "schedule_config": { + "name": "schedule_config", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "cleanup_config": { + "name": "cleanup_config", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + } + }, + "indexes": {}, + "foreignKeys": { + "configs_user_id_users_id_fk": { + "name": "configs_user_id_users_id_fk", + "tableFrom": "configs", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "events": { + "name": "events", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "channel": { + "name": "channel", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "payload": { + "name": "payload", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "read": { + "name": "read", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + } + }, + "indexes": { + "idx_events_user_channel": { + "name": "idx_events_user_channel", + "columns": [ + "user_id", + "channel" + ], + "isUnique": false + }, + "idx_events_created_at": { + "name": "idx_events_created_at", + "columns": [ + "created_at" + ], + "isUnique": false + }, + "idx_events_read": { + "name": "idx_events_read", + "columns": [ + "read" + ], + "isUnique": false + } + }, + "foreignKeys": { + "events_user_id_users_id_fk": { + "name": "events_user_id_users_id_fk", + "tableFrom": "events", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "mirror_jobs": { + "name": "mirror_jobs", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "repository_id": { + "name": "repository_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "repository_name": { + "name": "repository_name", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "organization_name": { + "name": "organization_name", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "details": { + "name": "details", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'imported'" + }, + "message": { + "name": "message", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "timestamp": { + "name": "timestamp", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + }, + "job_type": { + "name": "job_type", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'mirror'" + }, + "batch_id": { + "name": "batch_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "total_items": { + "name": "total_items", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "completed_items": { + "name": "completed_items", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": 0 + }, + "item_ids": { + "name": "item_ids", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "completed_item_ids": { + "name": "completed_item_ids", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'[]'" + }, + "in_progress": { + "name": "in_progress", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "started_at": { + "name": "started_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "completed_at": { + "name": "completed_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "last_checkpoint": { + "name": "last_checkpoint", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": { + "idx_mirror_jobs_user_id": { + "name": "idx_mirror_jobs_user_id", + "columns": [ + "user_id" + ], + "isUnique": false + }, + "idx_mirror_jobs_batch_id": { + "name": "idx_mirror_jobs_batch_id", + "columns": [ + "batch_id" + ], + "isUnique": false + }, + "idx_mirror_jobs_in_progress": { + "name": "idx_mirror_jobs_in_progress", + "columns": [ + "in_progress" + ], + "isUnique": false + }, + "idx_mirror_jobs_job_type": { + "name": "idx_mirror_jobs_job_type", + "columns": [ + "job_type" + ], + "isUnique": false + }, + "idx_mirror_jobs_timestamp": { + "name": "idx_mirror_jobs_timestamp", + "columns": [ + "timestamp" + ], + "isUnique": false + } + }, + "foreignKeys": { + "mirror_jobs_user_id_users_id_fk": { + "name": "mirror_jobs_user_id_users_id_fk", + "tableFrom": "mirror_jobs", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "oauth_access_tokens": { + "name": "oauth_access_tokens", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "access_token": { + "name": "access_token", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "refresh_token": { + "name": "refresh_token", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "access_token_expires_at": { + "name": "access_token_expires_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "refresh_token_expires_at": { + "name": "refresh_token_expires_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "client_id": { + "name": "client_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "scopes": { + "name": "scopes", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + } + }, + "indexes": { + "idx_oauth_access_tokens_access_token": { + "name": "idx_oauth_access_tokens_access_token", + "columns": [ + "access_token" + ], + "isUnique": false + }, + "idx_oauth_access_tokens_user_id": { + "name": "idx_oauth_access_tokens_user_id", + "columns": [ + "user_id" + ], + "isUnique": false + }, + "idx_oauth_access_tokens_client_id": { + "name": "idx_oauth_access_tokens_client_id", + "columns": [ + "client_id" + ], + "isUnique": false + } + }, + "foreignKeys": { + "oauth_access_tokens_user_id_users_id_fk": { + "name": "oauth_access_tokens_user_id_users_id_fk", + "tableFrom": "oauth_access_tokens", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "oauth_applications": { + "name": "oauth_applications", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "client_id": { + "name": "client_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "client_secret": { + "name": "client_secret", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "redirect_urls": { + "name": "redirect_urls", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "metadata": { + "name": "metadata", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "type": { + "name": "type", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "disabled": { + "name": "disabled", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + } + }, + "indexes": { + "oauth_applications_client_id_unique": { + "name": "oauth_applications_client_id_unique", + "columns": [ + "client_id" + ], + "isUnique": true + }, + "idx_oauth_applications_client_id": { + "name": "idx_oauth_applications_client_id", + "columns": [ + "client_id" + ], + "isUnique": false + }, + "idx_oauth_applications_user_id": { + "name": "idx_oauth_applications_user_id", + "columns": [ + "user_id" + ], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "oauth_consent": { + "name": "oauth_consent", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "client_id": { + "name": "client_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "scopes": { + "name": "scopes", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "consent_given": { + "name": "consent_given", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + } + }, + "indexes": { + "idx_oauth_consent_user_id": { + "name": "idx_oauth_consent_user_id", + "columns": [ + "user_id" + ], + "isUnique": false + }, + "idx_oauth_consent_client_id": { + "name": "idx_oauth_consent_client_id", + "columns": [ + "client_id" + ], + "isUnique": false + }, + "idx_oauth_consent_user_client": { + "name": "idx_oauth_consent_user_client", + "columns": [ + "user_id", + "client_id" + ], + "isUnique": false + } + }, + "foreignKeys": { + "oauth_consent_user_id_users_id_fk": { + "name": "oauth_consent_user_id_users_id_fk", + "tableFrom": "oauth_consent", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "organizations": { + "name": "organizations", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "config_id": { + "name": "config_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "avatar_url": { + "name": "avatar_url", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "membership_role": { + "name": "membership_role", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'member'" + }, + "is_included": { + "name": "is_included", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": true + }, + "destination_org": { + "name": "destination_org", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'imported'" + }, + "last_mirrored": { + "name": "last_mirrored", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "error_message": { + "name": "error_message", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "repository_count": { + "name": "repository_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "public_repository_count": { + "name": "public_repository_count", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "private_repository_count": { + "name": "private_repository_count", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "fork_repository_count": { + "name": "fork_repository_count", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + } + }, + "indexes": { + "idx_organizations_user_id": { + "name": "idx_organizations_user_id", + "columns": [ + "user_id" + ], + "isUnique": false + }, + "idx_organizations_config_id": { + "name": "idx_organizations_config_id", + "columns": [ + "config_id" + ], + "isUnique": false + }, + "idx_organizations_status": { + "name": "idx_organizations_status", + "columns": [ + "status" + ], + "isUnique": false + }, + "idx_organizations_is_included": { + "name": "idx_organizations_is_included", + "columns": [ + "is_included" + ], + "isUnique": false + } + }, + "foreignKeys": { + "organizations_user_id_users_id_fk": { + "name": "organizations_user_id_users_id_fk", + "tableFrom": "organizations", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "organizations_config_id_configs_id_fk": { + "name": "organizations_config_id_configs_id_fk", + "tableFrom": "organizations", + "tableTo": "configs", + "columnsFrom": [ + "config_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "rate_limits": { + "name": "rate_limits", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "provider": { + "name": "provider", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'github'" + }, + "limit": { + "name": "limit", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "remaining": { + "name": "remaining", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "used": { + "name": "used", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "reset": { + "name": "reset", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "retry_after": { + "name": "retry_after", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'ok'" + }, + "last_checked": { + "name": "last_checked", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + } + }, + "indexes": { + "idx_rate_limits_user_provider": { + "name": "idx_rate_limits_user_provider", + "columns": [ + "user_id", + "provider" + ], + "isUnique": false + }, + "idx_rate_limits_status": { + "name": "idx_rate_limits_status", + "columns": [ + "status" + ], + "isUnique": false + } + }, + "foreignKeys": { + "rate_limits_user_id_users_id_fk": { + "name": "rate_limits_user_id_users_id_fk", + "tableFrom": "rate_limits", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "repositories": { + "name": "repositories", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "config_id": { + "name": "config_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "full_name": { + "name": "full_name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "url": { + "name": "url", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "clone_url": { + "name": "clone_url", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "owner": { + "name": "owner", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "organization": { + "name": "organization", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "mirrored_location": { + "name": "mirrored_location", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "''" + }, + "is_private": { + "name": "is_private", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "is_fork": { + "name": "is_fork", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "forked_from": { + "name": "forked_from", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "has_issues": { + "name": "has_issues", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "is_starred": { + "name": "is_starred", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "is_archived": { + "name": "is_archived", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "size": { + "name": "size", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "has_lfs": { + "name": "has_lfs", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "has_submodules": { + "name": "has_submodules", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "language": { + "name": "language", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "default_branch": { + "name": "default_branch", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "visibility": { + "name": "visibility", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'public'" + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'imported'" + }, + "last_mirrored": { + "name": "last_mirrored", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "error_message": { + "name": "error_message", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "destination_org": { + "name": "destination_org", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + } + }, + "indexes": { + "idx_repositories_user_id": { + "name": "idx_repositories_user_id", + "columns": [ + "user_id" + ], + "isUnique": false + }, + "idx_repositories_config_id": { + "name": "idx_repositories_config_id", + "columns": [ + "config_id" + ], + "isUnique": false + }, + "idx_repositories_status": { + "name": "idx_repositories_status", + "columns": [ + "status" + ], + "isUnique": false + }, + "idx_repositories_owner": { + "name": "idx_repositories_owner", + "columns": [ + "owner" + ], + "isUnique": false + }, + "idx_repositories_organization": { + "name": "idx_repositories_organization", + "columns": [ + "organization" + ], + "isUnique": false + }, + "idx_repositories_is_fork": { + "name": "idx_repositories_is_fork", + "columns": [ + "is_fork" + ], + "isUnique": false + }, + "idx_repositories_is_starred": { + "name": "idx_repositories_is_starred", + "columns": [ + "is_starred" + ], + "isUnique": false + } + }, + "foreignKeys": { + "repositories_user_id_users_id_fk": { + "name": "repositories_user_id_users_id_fk", + "tableFrom": "repositories", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "repositories_config_id_configs_id_fk": { + "name": "repositories_config_id_configs_id_fk", + "tableFrom": "repositories", + "tableTo": "configs", + "columnsFrom": [ + "config_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "sessions": { + "name": "sessions", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "token": { + "name": "token", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "expires_at": { + "name": "expires_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "ip_address": { + "name": "ip_address", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "user_agent": { + "name": "user_agent", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + } + }, + "indexes": { + "sessions_token_unique": { + "name": "sessions_token_unique", + "columns": [ + "token" + ], + "isUnique": true + }, + "idx_sessions_user_id": { + "name": "idx_sessions_user_id", + "columns": [ + "user_id" + ], + "isUnique": false + }, + "idx_sessions_token": { + "name": "idx_sessions_token", + "columns": [ + "token" + ], + "isUnique": false + }, + "idx_sessions_expires_at": { + "name": "idx_sessions_expires_at", + "columns": [ + "expires_at" + ], + "isUnique": false + } + }, + "foreignKeys": { + "sessions_user_id_users_id_fk": { + "name": "sessions_user_id_users_id_fk", + "tableFrom": "sessions", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "sso_providers": { + "name": "sso_providers", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "issuer": { + "name": "issuer", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "domain": { + "name": "domain", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "oidc_config": { + "name": "oidc_config", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "provider_id": { + "name": "provider_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + } + }, + "indexes": { + "sso_providers_provider_id_unique": { + "name": "sso_providers_provider_id_unique", + "columns": [ + "provider_id" + ], + "isUnique": true + }, + "idx_sso_providers_provider_id": { + "name": "idx_sso_providers_provider_id", + "columns": [ + "provider_id" + ], + "isUnique": false + }, + "idx_sso_providers_domain": { + "name": "idx_sso_providers_domain", + "columns": [ + "domain" + ], + "isUnique": false + }, + "idx_sso_providers_issuer": { + "name": "idx_sso_providers_issuer", + "columns": [ + "issuer" + ], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "users": { + "name": "users", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "email_verified": { + "name": "email_verified", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "image": { + "name": "image", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + }, + "username": { + "name": "username", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": { + "users_email_unique": { + "name": "users_email_unique", + "columns": [ + "email" + ], + "isUnique": true + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "verification_tokens": { + "name": "verification_tokens", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "token": { + "name": "token", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "identifier": { + "name": "identifier", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "type": { + "name": "type", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "expires_at": { + "name": "expires_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + } + }, + "indexes": { + "verification_tokens_token_unique": { + "name": "verification_tokens_token_unique", + "columns": [ + "token" + ], + "isUnique": true + }, + "idx_verification_tokens_token": { + "name": "idx_verification_tokens_token", + "columns": [ + "token" + ], + "isUnique": false + }, + "idx_verification_tokens_identifier": { + "name": "idx_verification_tokens_identifier", + "columns": [ + "identifier" + ], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "verifications": { + "name": "verifications", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "identifier": { + "name": "identifier", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "value": { + "name": "value", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "expires_at": { + "name": "expires_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + } + }, + "indexes": { + "idx_verifications_identifier": { + "name": "idx_verifications_identifier", + "columns": [ + "identifier" + ], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + } + }, + "views": {}, + "enums": {}, + "_meta": { + "schemas": {}, + "tables": {}, + "columns": {} + }, + "internal": { + "indexes": {} + } +} \ No newline at end of file diff --git a/Divers/gitea-mirror/drizzle/meta/0005_snapshot.json b/Divers/gitea-mirror/drizzle/meta/0005_snapshot.json new file mode 100644 index 0000000..799e42e --- /dev/null +++ b/Divers/gitea-mirror/drizzle/meta/0005_snapshot.json @@ -0,0 +1,1941 @@ +{ + "version": "6", + "dialect": "sqlite", + "id": "4b92c9e0-363d-4da1-8d8d-c2e6c31cbb70", + "prevId": "284b8db7-ef64-4311-b890-0251bcbe5866", + "tables": { + "accounts": { + "name": "accounts", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "account_id": { + "name": "account_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "provider_id": { + "name": "provider_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "provider_user_id": { + "name": "provider_user_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "access_token": { + "name": "access_token", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "refresh_token": { + "name": "refresh_token", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "expires_at": { + "name": "expires_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "password": { + "name": "password", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + } + }, + "indexes": { + "idx_accounts_account_id": { + "name": "idx_accounts_account_id", + "columns": [ + "account_id" + ], + "isUnique": false + }, + "idx_accounts_user_id": { + "name": "idx_accounts_user_id", + "columns": [ + "user_id" + ], + "isUnique": false + }, + "idx_accounts_provider": { + "name": "idx_accounts_provider", + "columns": [ + "provider_id", + "provider_user_id" + ], + "isUnique": false + } + }, + "foreignKeys": { + "accounts_user_id_users_id_fk": { + "name": "accounts_user_id_users_id_fk", + "tableFrom": "accounts", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "configs": { + "name": "configs", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "is_active": { + "name": "is_active", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": true + }, + "github_config": { + "name": "github_config", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "gitea_config": { + "name": "gitea_config", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "include": { + "name": "include", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'[\"*\"]'" + }, + "exclude": { + "name": "exclude", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'[]'" + }, + "schedule_config": { + "name": "schedule_config", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "cleanup_config": { + "name": "cleanup_config", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + } + }, + "indexes": {}, + "foreignKeys": { + "configs_user_id_users_id_fk": { + "name": "configs_user_id_users_id_fk", + "tableFrom": "configs", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "events": { + "name": "events", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "channel": { + "name": "channel", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "payload": { + "name": "payload", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "read": { + "name": "read", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + } + }, + "indexes": { + "idx_events_user_channel": { + "name": "idx_events_user_channel", + "columns": [ + "user_id", + "channel" + ], + "isUnique": false + }, + "idx_events_created_at": { + "name": "idx_events_created_at", + "columns": [ + "created_at" + ], + "isUnique": false + }, + "idx_events_read": { + "name": "idx_events_read", + "columns": [ + "read" + ], + "isUnique": false + } + }, + "foreignKeys": { + "events_user_id_users_id_fk": { + "name": "events_user_id_users_id_fk", + "tableFrom": "events", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "mirror_jobs": { + "name": "mirror_jobs", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "repository_id": { + "name": "repository_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "repository_name": { + "name": "repository_name", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "organization_name": { + "name": "organization_name", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "details": { + "name": "details", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'imported'" + }, + "message": { + "name": "message", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "timestamp": { + "name": "timestamp", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + }, + "job_type": { + "name": "job_type", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'mirror'" + }, + "batch_id": { + "name": "batch_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "total_items": { + "name": "total_items", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "completed_items": { + "name": "completed_items", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": 0 + }, + "item_ids": { + "name": "item_ids", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "completed_item_ids": { + "name": "completed_item_ids", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'[]'" + }, + "in_progress": { + "name": "in_progress", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "started_at": { + "name": "started_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "completed_at": { + "name": "completed_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "last_checkpoint": { + "name": "last_checkpoint", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": { + "idx_mirror_jobs_user_id": { + "name": "idx_mirror_jobs_user_id", + "columns": [ + "user_id" + ], + "isUnique": false + }, + "idx_mirror_jobs_batch_id": { + "name": "idx_mirror_jobs_batch_id", + "columns": [ + "batch_id" + ], + "isUnique": false + }, + "idx_mirror_jobs_in_progress": { + "name": "idx_mirror_jobs_in_progress", + "columns": [ + "in_progress" + ], + "isUnique": false + }, + "idx_mirror_jobs_job_type": { + "name": "idx_mirror_jobs_job_type", + "columns": [ + "job_type" + ], + "isUnique": false + }, + "idx_mirror_jobs_timestamp": { + "name": "idx_mirror_jobs_timestamp", + "columns": [ + "timestamp" + ], + "isUnique": false + } + }, + "foreignKeys": { + "mirror_jobs_user_id_users_id_fk": { + "name": "mirror_jobs_user_id_users_id_fk", + "tableFrom": "mirror_jobs", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "oauth_access_tokens": { + "name": "oauth_access_tokens", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "access_token": { + "name": "access_token", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "refresh_token": { + "name": "refresh_token", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "access_token_expires_at": { + "name": "access_token_expires_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "refresh_token_expires_at": { + "name": "refresh_token_expires_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "client_id": { + "name": "client_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "scopes": { + "name": "scopes", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + } + }, + "indexes": { + "idx_oauth_access_tokens_access_token": { + "name": "idx_oauth_access_tokens_access_token", + "columns": [ + "access_token" + ], + "isUnique": false + }, + "idx_oauth_access_tokens_user_id": { + "name": "idx_oauth_access_tokens_user_id", + "columns": [ + "user_id" + ], + "isUnique": false + }, + "idx_oauth_access_tokens_client_id": { + "name": "idx_oauth_access_tokens_client_id", + "columns": [ + "client_id" + ], + "isUnique": false + } + }, + "foreignKeys": { + "oauth_access_tokens_user_id_users_id_fk": { + "name": "oauth_access_tokens_user_id_users_id_fk", + "tableFrom": "oauth_access_tokens", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "oauth_applications": { + "name": "oauth_applications", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "client_id": { + "name": "client_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "client_secret": { + "name": "client_secret", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "redirect_urls": { + "name": "redirect_urls", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "metadata": { + "name": "metadata", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "type": { + "name": "type", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "disabled": { + "name": "disabled", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + } + }, + "indexes": { + "oauth_applications_client_id_unique": { + "name": "oauth_applications_client_id_unique", + "columns": [ + "client_id" + ], + "isUnique": true + }, + "idx_oauth_applications_client_id": { + "name": "idx_oauth_applications_client_id", + "columns": [ + "client_id" + ], + "isUnique": false + }, + "idx_oauth_applications_user_id": { + "name": "idx_oauth_applications_user_id", + "columns": [ + "user_id" + ], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "oauth_consent": { + "name": "oauth_consent", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "client_id": { + "name": "client_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "scopes": { + "name": "scopes", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "consent_given": { + "name": "consent_given", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + } + }, + "indexes": { + "idx_oauth_consent_user_id": { + "name": "idx_oauth_consent_user_id", + "columns": [ + "user_id" + ], + "isUnique": false + }, + "idx_oauth_consent_client_id": { + "name": "idx_oauth_consent_client_id", + "columns": [ + "client_id" + ], + "isUnique": false + }, + "idx_oauth_consent_user_client": { + "name": "idx_oauth_consent_user_client", + "columns": [ + "user_id", + "client_id" + ], + "isUnique": false + } + }, + "foreignKeys": { + "oauth_consent_user_id_users_id_fk": { + "name": "oauth_consent_user_id_users_id_fk", + "tableFrom": "oauth_consent", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "organizations": { + "name": "organizations", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "config_id": { + "name": "config_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "avatar_url": { + "name": "avatar_url", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "membership_role": { + "name": "membership_role", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'member'" + }, + "is_included": { + "name": "is_included", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": true + }, + "destination_org": { + "name": "destination_org", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'imported'" + }, + "last_mirrored": { + "name": "last_mirrored", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "error_message": { + "name": "error_message", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "repository_count": { + "name": "repository_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "public_repository_count": { + "name": "public_repository_count", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "private_repository_count": { + "name": "private_repository_count", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "fork_repository_count": { + "name": "fork_repository_count", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + } + }, + "indexes": { + "idx_organizations_user_id": { + "name": "idx_organizations_user_id", + "columns": [ + "user_id" + ], + "isUnique": false + }, + "idx_organizations_config_id": { + "name": "idx_organizations_config_id", + "columns": [ + "config_id" + ], + "isUnique": false + }, + "idx_organizations_status": { + "name": "idx_organizations_status", + "columns": [ + "status" + ], + "isUnique": false + }, + "idx_organizations_is_included": { + "name": "idx_organizations_is_included", + "columns": [ + "is_included" + ], + "isUnique": false + } + }, + "foreignKeys": { + "organizations_user_id_users_id_fk": { + "name": "organizations_user_id_users_id_fk", + "tableFrom": "organizations", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "organizations_config_id_configs_id_fk": { + "name": "organizations_config_id_configs_id_fk", + "tableFrom": "organizations", + "tableTo": "configs", + "columnsFrom": [ + "config_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "rate_limits": { + "name": "rate_limits", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "provider": { + "name": "provider", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'github'" + }, + "limit": { + "name": "limit", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "remaining": { + "name": "remaining", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "used": { + "name": "used", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "reset": { + "name": "reset", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "retry_after": { + "name": "retry_after", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'ok'" + }, + "last_checked": { + "name": "last_checked", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + } + }, + "indexes": { + "idx_rate_limits_user_provider": { + "name": "idx_rate_limits_user_provider", + "columns": [ + "user_id", + "provider" + ], + "isUnique": false + }, + "idx_rate_limits_status": { + "name": "idx_rate_limits_status", + "columns": [ + "status" + ], + "isUnique": false + } + }, + "foreignKeys": { + "rate_limits_user_id_users_id_fk": { + "name": "rate_limits_user_id_users_id_fk", + "tableFrom": "rate_limits", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "repositories": { + "name": "repositories", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "config_id": { + "name": "config_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "full_name": { + "name": "full_name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "url": { + "name": "url", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "clone_url": { + "name": "clone_url", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "owner": { + "name": "owner", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "organization": { + "name": "organization", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "mirrored_location": { + "name": "mirrored_location", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "''" + }, + "is_private": { + "name": "is_private", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "is_fork": { + "name": "is_fork", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "forked_from": { + "name": "forked_from", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "has_issues": { + "name": "has_issues", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "is_starred": { + "name": "is_starred", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "is_archived": { + "name": "is_archived", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "size": { + "name": "size", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "has_lfs": { + "name": "has_lfs", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "has_submodules": { + "name": "has_submodules", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "language": { + "name": "language", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "default_branch": { + "name": "default_branch", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "visibility": { + "name": "visibility", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'public'" + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'imported'" + }, + "last_mirrored": { + "name": "last_mirrored", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "error_message": { + "name": "error_message", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "destination_org": { + "name": "destination_org", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + } + }, + "indexes": { + "idx_repositories_user_id": { + "name": "idx_repositories_user_id", + "columns": [ + "user_id" + ], + "isUnique": false + }, + "idx_repositories_config_id": { + "name": "idx_repositories_config_id", + "columns": [ + "config_id" + ], + "isUnique": false + }, + "idx_repositories_status": { + "name": "idx_repositories_status", + "columns": [ + "status" + ], + "isUnique": false + }, + "idx_repositories_owner": { + "name": "idx_repositories_owner", + "columns": [ + "owner" + ], + "isUnique": false + }, + "idx_repositories_organization": { + "name": "idx_repositories_organization", + "columns": [ + "organization" + ], + "isUnique": false + }, + "idx_repositories_is_fork": { + "name": "idx_repositories_is_fork", + "columns": [ + "is_fork" + ], + "isUnique": false + }, + "idx_repositories_is_starred": { + "name": "idx_repositories_is_starred", + "columns": [ + "is_starred" + ], + "isUnique": false + }, + "uniq_repositories_user_full_name": { + "name": "uniq_repositories_user_full_name", + "columns": [ + "user_id", + "full_name" + ], + "isUnique": true + } + }, + "foreignKeys": { + "repositories_user_id_users_id_fk": { + "name": "repositories_user_id_users_id_fk", + "tableFrom": "repositories", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "repositories_config_id_configs_id_fk": { + "name": "repositories_config_id_configs_id_fk", + "tableFrom": "repositories", + "tableTo": "configs", + "columnsFrom": [ + "config_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "sessions": { + "name": "sessions", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "token": { + "name": "token", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "expires_at": { + "name": "expires_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "ip_address": { + "name": "ip_address", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "user_agent": { + "name": "user_agent", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + } + }, + "indexes": { + "sessions_token_unique": { + "name": "sessions_token_unique", + "columns": [ + "token" + ], + "isUnique": true + }, + "idx_sessions_user_id": { + "name": "idx_sessions_user_id", + "columns": [ + "user_id" + ], + "isUnique": false + }, + "idx_sessions_token": { + "name": "idx_sessions_token", + "columns": [ + "token" + ], + "isUnique": false + }, + "idx_sessions_expires_at": { + "name": "idx_sessions_expires_at", + "columns": [ + "expires_at" + ], + "isUnique": false + } + }, + "foreignKeys": { + "sessions_user_id_users_id_fk": { + "name": "sessions_user_id_users_id_fk", + "tableFrom": "sessions", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "sso_providers": { + "name": "sso_providers", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "issuer": { + "name": "issuer", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "domain": { + "name": "domain", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "oidc_config": { + "name": "oidc_config", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "provider_id": { + "name": "provider_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + } + }, + "indexes": { + "sso_providers_provider_id_unique": { + "name": "sso_providers_provider_id_unique", + "columns": [ + "provider_id" + ], + "isUnique": true + }, + "idx_sso_providers_provider_id": { + "name": "idx_sso_providers_provider_id", + "columns": [ + "provider_id" + ], + "isUnique": false + }, + "idx_sso_providers_domain": { + "name": "idx_sso_providers_domain", + "columns": [ + "domain" + ], + "isUnique": false + }, + "idx_sso_providers_issuer": { + "name": "idx_sso_providers_issuer", + "columns": [ + "issuer" + ], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "users": { + "name": "users", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "email_verified": { + "name": "email_verified", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "image": { + "name": "image", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + }, + "username": { + "name": "username", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": { + "users_email_unique": { + "name": "users_email_unique", + "columns": [ + "email" + ], + "isUnique": true + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "verification_tokens": { + "name": "verification_tokens", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "token": { + "name": "token", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "identifier": { + "name": "identifier", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "type": { + "name": "type", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "expires_at": { + "name": "expires_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + } + }, + "indexes": { + "verification_tokens_token_unique": { + "name": "verification_tokens_token_unique", + "columns": [ + "token" + ], + "isUnique": true + }, + "idx_verification_tokens_token": { + "name": "idx_verification_tokens_token", + "columns": [ + "token" + ], + "isUnique": false + }, + "idx_verification_tokens_identifier": { + "name": "idx_verification_tokens_identifier", + "columns": [ + "identifier" + ], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "verifications": { + "name": "verifications", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "identifier": { + "name": "identifier", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "value": { + "name": "value", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "expires_at": { + "name": "expires_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + } + }, + "indexes": { + "idx_verifications_identifier": { + "name": "idx_verifications_identifier", + "columns": [ + "identifier" + ], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + } + }, + "views": {}, + "enums": {}, + "_meta": { + "schemas": {}, + "tables": {}, + "columns": {} + }, + "internal": { + "indexes": {} + } +} \ No newline at end of file diff --git a/Divers/gitea-mirror/drizzle/meta/0006_snapshot.json b/Divers/gitea-mirror/drizzle/meta/0006_snapshot.json new file mode 100644 index 0000000..e46faf9 --- /dev/null +++ b/Divers/gitea-mirror/drizzle/meta/0006_snapshot.json @@ -0,0 +1,1969 @@ +{ + "version": "6", + "dialect": "sqlite", + "id": "d27b6aa9-14ae-4271-b9c5-6976efbf70bd", + "prevId": "4b92c9e0-363d-4da1-8d8d-c2e6c31cbb70", + "tables": { + "accounts": { + "name": "accounts", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "account_id": { + "name": "account_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "provider_id": { + "name": "provider_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "provider_user_id": { + "name": "provider_user_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "access_token": { + "name": "access_token", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "refresh_token": { + "name": "refresh_token", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "id_token": { + "name": "id_token", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "access_token_expires_at": { + "name": "access_token_expires_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "refresh_token_expires_at": { + "name": "refresh_token_expires_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "scope": { + "name": "scope", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "expires_at": { + "name": "expires_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "password": { + "name": "password", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + } + }, + "indexes": { + "idx_accounts_account_id": { + "name": "idx_accounts_account_id", + "columns": [ + "account_id" + ], + "isUnique": false + }, + "idx_accounts_user_id": { + "name": "idx_accounts_user_id", + "columns": [ + "user_id" + ], + "isUnique": false + }, + "idx_accounts_provider": { + "name": "idx_accounts_provider", + "columns": [ + "provider_id", + "provider_user_id" + ], + "isUnique": false + } + }, + "foreignKeys": { + "accounts_user_id_users_id_fk": { + "name": "accounts_user_id_users_id_fk", + "tableFrom": "accounts", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "configs": { + "name": "configs", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "is_active": { + "name": "is_active", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": true + }, + "github_config": { + "name": "github_config", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "gitea_config": { + "name": "gitea_config", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "include": { + "name": "include", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'[\"*\"]'" + }, + "exclude": { + "name": "exclude", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'[]'" + }, + "schedule_config": { + "name": "schedule_config", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "cleanup_config": { + "name": "cleanup_config", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + } + }, + "indexes": {}, + "foreignKeys": { + "configs_user_id_users_id_fk": { + "name": "configs_user_id_users_id_fk", + "tableFrom": "configs", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "events": { + "name": "events", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "channel": { + "name": "channel", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "payload": { + "name": "payload", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "read": { + "name": "read", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + } + }, + "indexes": { + "idx_events_user_channel": { + "name": "idx_events_user_channel", + "columns": [ + "user_id", + "channel" + ], + "isUnique": false + }, + "idx_events_created_at": { + "name": "idx_events_created_at", + "columns": [ + "created_at" + ], + "isUnique": false + }, + "idx_events_read": { + "name": "idx_events_read", + "columns": [ + "read" + ], + "isUnique": false + } + }, + "foreignKeys": { + "events_user_id_users_id_fk": { + "name": "events_user_id_users_id_fk", + "tableFrom": "events", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "mirror_jobs": { + "name": "mirror_jobs", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "repository_id": { + "name": "repository_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "repository_name": { + "name": "repository_name", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "organization_name": { + "name": "organization_name", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "details": { + "name": "details", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'imported'" + }, + "message": { + "name": "message", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "timestamp": { + "name": "timestamp", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + }, + "job_type": { + "name": "job_type", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'mirror'" + }, + "batch_id": { + "name": "batch_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "total_items": { + "name": "total_items", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "completed_items": { + "name": "completed_items", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": 0 + }, + "item_ids": { + "name": "item_ids", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "completed_item_ids": { + "name": "completed_item_ids", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'[]'" + }, + "in_progress": { + "name": "in_progress", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "started_at": { + "name": "started_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "completed_at": { + "name": "completed_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "last_checkpoint": { + "name": "last_checkpoint", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": { + "idx_mirror_jobs_user_id": { + "name": "idx_mirror_jobs_user_id", + "columns": [ + "user_id" + ], + "isUnique": false + }, + "idx_mirror_jobs_batch_id": { + "name": "idx_mirror_jobs_batch_id", + "columns": [ + "batch_id" + ], + "isUnique": false + }, + "idx_mirror_jobs_in_progress": { + "name": "idx_mirror_jobs_in_progress", + "columns": [ + "in_progress" + ], + "isUnique": false + }, + "idx_mirror_jobs_job_type": { + "name": "idx_mirror_jobs_job_type", + "columns": [ + "job_type" + ], + "isUnique": false + }, + "idx_mirror_jobs_timestamp": { + "name": "idx_mirror_jobs_timestamp", + "columns": [ + "timestamp" + ], + "isUnique": false + } + }, + "foreignKeys": { + "mirror_jobs_user_id_users_id_fk": { + "name": "mirror_jobs_user_id_users_id_fk", + "tableFrom": "mirror_jobs", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "oauth_access_tokens": { + "name": "oauth_access_tokens", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "access_token": { + "name": "access_token", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "refresh_token": { + "name": "refresh_token", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "access_token_expires_at": { + "name": "access_token_expires_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "refresh_token_expires_at": { + "name": "refresh_token_expires_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "client_id": { + "name": "client_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "scopes": { + "name": "scopes", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + } + }, + "indexes": { + "idx_oauth_access_tokens_access_token": { + "name": "idx_oauth_access_tokens_access_token", + "columns": [ + "access_token" + ], + "isUnique": false + }, + "idx_oauth_access_tokens_user_id": { + "name": "idx_oauth_access_tokens_user_id", + "columns": [ + "user_id" + ], + "isUnique": false + }, + "idx_oauth_access_tokens_client_id": { + "name": "idx_oauth_access_tokens_client_id", + "columns": [ + "client_id" + ], + "isUnique": false + } + }, + "foreignKeys": { + "oauth_access_tokens_user_id_users_id_fk": { + "name": "oauth_access_tokens_user_id_users_id_fk", + "tableFrom": "oauth_access_tokens", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "oauth_applications": { + "name": "oauth_applications", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "client_id": { + "name": "client_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "client_secret": { + "name": "client_secret", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "redirect_urls": { + "name": "redirect_urls", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "metadata": { + "name": "metadata", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "type": { + "name": "type", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "disabled": { + "name": "disabled", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + } + }, + "indexes": { + "oauth_applications_client_id_unique": { + "name": "oauth_applications_client_id_unique", + "columns": [ + "client_id" + ], + "isUnique": true + }, + "idx_oauth_applications_client_id": { + "name": "idx_oauth_applications_client_id", + "columns": [ + "client_id" + ], + "isUnique": false + }, + "idx_oauth_applications_user_id": { + "name": "idx_oauth_applications_user_id", + "columns": [ + "user_id" + ], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "oauth_consent": { + "name": "oauth_consent", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "client_id": { + "name": "client_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "scopes": { + "name": "scopes", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "consent_given": { + "name": "consent_given", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + } + }, + "indexes": { + "idx_oauth_consent_user_id": { + "name": "idx_oauth_consent_user_id", + "columns": [ + "user_id" + ], + "isUnique": false + }, + "idx_oauth_consent_client_id": { + "name": "idx_oauth_consent_client_id", + "columns": [ + "client_id" + ], + "isUnique": false + }, + "idx_oauth_consent_user_client": { + "name": "idx_oauth_consent_user_client", + "columns": [ + "user_id", + "client_id" + ], + "isUnique": false + } + }, + "foreignKeys": { + "oauth_consent_user_id_users_id_fk": { + "name": "oauth_consent_user_id_users_id_fk", + "tableFrom": "oauth_consent", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "organizations": { + "name": "organizations", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "config_id": { + "name": "config_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "avatar_url": { + "name": "avatar_url", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "membership_role": { + "name": "membership_role", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'member'" + }, + "is_included": { + "name": "is_included", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": true + }, + "destination_org": { + "name": "destination_org", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'imported'" + }, + "last_mirrored": { + "name": "last_mirrored", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "error_message": { + "name": "error_message", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "repository_count": { + "name": "repository_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "public_repository_count": { + "name": "public_repository_count", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "private_repository_count": { + "name": "private_repository_count", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "fork_repository_count": { + "name": "fork_repository_count", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + } + }, + "indexes": { + "idx_organizations_user_id": { + "name": "idx_organizations_user_id", + "columns": [ + "user_id" + ], + "isUnique": false + }, + "idx_organizations_config_id": { + "name": "idx_organizations_config_id", + "columns": [ + "config_id" + ], + "isUnique": false + }, + "idx_organizations_status": { + "name": "idx_organizations_status", + "columns": [ + "status" + ], + "isUnique": false + }, + "idx_organizations_is_included": { + "name": "idx_organizations_is_included", + "columns": [ + "is_included" + ], + "isUnique": false + } + }, + "foreignKeys": { + "organizations_user_id_users_id_fk": { + "name": "organizations_user_id_users_id_fk", + "tableFrom": "organizations", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "organizations_config_id_configs_id_fk": { + "name": "organizations_config_id_configs_id_fk", + "tableFrom": "organizations", + "tableTo": "configs", + "columnsFrom": [ + "config_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "rate_limits": { + "name": "rate_limits", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "provider": { + "name": "provider", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'github'" + }, + "limit": { + "name": "limit", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "remaining": { + "name": "remaining", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "used": { + "name": "used", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "reset": { + "name": "reset", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "retry_after": { + "name": "retry_after", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'ok'" + }, + "last_checked": { + "name": "last_checked", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + } + }, + "indexes": { + "idx_rate_limits_user_provider": { + "name": "idx_rate_limits_user_provider", + "columns": [ + "user_id", + "provider" + ], + "isUnique": false + }, + "idx_rate_limits_status": { + "name": "idx_rate_limits_status", + "columns": [ + "status" + ], + "isUnique": false + } + }, + "foreignKeys": { + "rate_limits_user_id_users_id_fk": { + "name": "rate_limits_user_id_users_id_fk", + "tableFrom": "rate_limits", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "repositories": { + "name": "repositories", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "config_id": { + "name": "config_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "full_name": { + "name": "full_name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "url": { + "name": "url", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "clone_url": { + "name": "clone_url", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "owner": { + "name": "owner", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "organization": { + "name": "organization", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "mirrored_location": { + "name": "mirrored_location", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "''" + }, + "is_private": { + "name": "is_private", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "is_fork": { + "name": "is_fork", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "forked_from": { + "name": "forked_from", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "has_issues": { + "name": "has_issues", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "is_starred": { + "name": "is_starred", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "is_archived": { + "name": "is_archived", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "size": { + "name": "size", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "has_lfs": { + "name": "has_lfs", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "has_submodules": { + "name": "has_submodules", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "language": { + "name": "language", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "default_branch": { + "name": "default_branch", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "visibility": { + "name": "visibility", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'public'" + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'imported'" + }, + "last_mirrored": { + "name": "last_mirrored", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "error_message": { + "name": "error_message", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "destination_org": { + "name": "destination_org", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + } + }, + "indexes": { + "idx_repositories_user_id": { + "name": "idx_repositories_user_id", + "columns": [ + "user_id" + ], + "isUnique": false + }, + "idx_repositories_config_id": { + "name": "idx_repositories_config_id", + "columns": [ + "config_id" + ], + "isUnique": false + }, + "idx_repositories_status": { + "name": "idx_repositories_status", + "columns": [ + "status" + ], + "isUnique": false + }, + "idx_repositories_owner": { + "name": "idx_repositories_owner", + "columns": [ + "owner" + ], + "isUnique": false + }, + "idx_repositories_organization": { + "name": "idx_repositories_organization", + "columns": [ + "organization" + ], + "isUnique": false + }, + "idx_repositories_is_fork": { + "name": "idx_repositories_is_fork", + "columns": [ + "is_fork" + ], + "isUnique": false + }, + "idx_repositories_is_starred": { + "name": "idx_repositories_is_starred", + "columns": [ + "is_starred" + ], + "isUnique": false + }, + "uniq_repositories_user_full_name": { + "name": "uniq_repositories_user_full_name", + "columns": [ + "user_id", + "full_name" + ], + "isUnique": true + } + }, + "foreignKeys": { + "repositories_user_id_users_id_fk": { + "name": "repositories_user_id_users_id_fk", + "tableFrom": "repositories", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "repositories_config_id_configs_id_fk": { + "name": "repositories_config_id_configs_id_fk", + "tableFrom": "repositories", + "tableTo": "configs", + "columnsFrom": [ + "config_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "sessions": { + "name": "sessions", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "token": { + "name": "token", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "expires_at": { + "name": "expires_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "ip_address": { + "name": "ip_address", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "user_agent": { + "name": "user_agent", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + } + }, + "indexes": { + "sessions_token_unique": { + "name": "sessions_token_unique", + "columns": [ + "token" + ], + "isUnique": true + }, + "idx_sessions_user_id": { + "name": "idx_sessions_user_id", + "columns": [ + "user_id" + ], + "isUnique": false + }, + "idx_sessions_token": { + "name": "idx_sessions_token", + "columns": [ + "token" + ], + "isUnique": false + }, + "idx_sessions_expires_at": { + "name": "idx_sessions_expires_at", + "columns": [ + "expires_at" + ], + "isUnique": false + } + }, + "foreignKeys": { + "sessions_user_id_users_id_fk": { + "name": "sessions_user_id_users_id_fk", + "tableFrom": "sessions", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "sso_providers": { + "name": "sso_providers", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "issuer": { + "name": "issuer", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "domain": { + "name": "domain", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "oidc_config": { + "name": "oidc_config", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "provider_id": { + "name": "provider_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + } + }, + "indexes": { + "sso_providers_provider_id_unique": { + "name": "sso_providers_provider_id_unique", + "columns": [ + "provider_id" + ], + "isUnique": true + }, + "idx_sso_providers_provider_id": { + "name": "idx_sso_providers_provider_id", + "columns": [ + "provider_id" + ], + "isUnique": false + }, + "idx_sso_providers_domain": { + "name": "idx_sso_providers_domain", + "columns": [ + "domain" + ], + "isUnique": false + }, + "idx_sso_providers_issuer": { + "name": "idx_sso_providers_issuer", + "columns": [ + "issuer" + ], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "users": { + "name": "users", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "email_verified": { + "name": "email_verified", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "image": { + "name": "image", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + }, + "username": { + "name": "username", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": { + "users_email_unique": { + "name": "users_email_unique", + "columns": [ + "email" + ], + "isUnique": true + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "verification_tokens": { + "name": "verification_tokens", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "token": { + "name": "token", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "identifier": { + "name": "identifier", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "type": { + "name": "type", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "expires_at": { + "name": "expires_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + } + }, + "indexes": { + "verification_tokens_token_unique": { + "name": "verification_tokens_token_unique", + "columns": [ + "token" + ], + "isUnique": true + }, + "idx_verification_tokens_token": { + "name": "idx_verification_tokens_token", + "columns": [ + "token" + ], + "isUnique": false + }, + "idx_verification_tokens_identifier": { + "name": "idx_verification_tokens_identifier", + "columns": [ + "identifier" + ], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "verifications": { + "name": "verifications", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "identifier": { + "name": "identifier", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "value": { + "name": "value", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "expires_at": { + "name": "expires_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + } + }, + "indexes": { + "idx_verifications_identifier": { + "name": "idx_verifications_identifier", + "columns": [ + "identifier" + ], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + } + }, + "views": {}, + "enums": {}, + "_meta": { + "schemas": {}, + "tables": {}, + "columns": {} + }, + "internal": { + "indexes": {} + } +} \ No newline at end of file diff --git a/Divers/gitea-mirror/drizzle/meta/0007_snapshot.json b/Divers/gitea-mirror/drizzle/meta/0007_snapshot.json new file mode 100644 index 0000000..5f31cf3 --- /dev/null +++ b/Divers/gitea-mirror/drizzle/meta/0007_snapshot.json @@ -0,0 +1,1999 @@ +{ + "version": "6", + "dialect": "sqlite", + "id": "e4d9f5ae-4b18-4529-89fd-b7720822af07", + "prevId": "d27b6aa9-14ae-4271-b9c5-6976efbf70bd", + "tables": { + "accounts": { + "name": "accounts", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "account_id": { + "name": "account_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "provider_id": { + "name": "provider_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "provider_user_id": { + "name": "provider_user_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "access_token": { + "name": "access_token", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "refresh_token": { + "name": "refresh_token", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "id_token": { + "name": "id_token", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "access_token_expires_at": { + "name": "access_token_expires_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "refresh_token_expires_at": { + "name": "refresh_token_expires_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "scope": { + "name": "scope", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "expires_at": { + "name": "expires_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "password": { + "name": "password", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + } + }, + "indexes": { + "idx_accounts_account_id": { + "name": "idx_accounts_account_id", + "columns": [ + "account_id" + ], + "isUnique": false + }, + "idx_accounts_user_id": { + "name": "idx_accounts_user_id", + "columns": [ + "user_id" + ], + "isUnique": false + }, + "idx_accounts_provider": { + "name": "idx_accounts_provider", + "columns": [ + "provider_id", + "provider_user_id" + ], + "isUnique": false + } + }, + "foreignKeys": { + "accounts_user_id_users_id_fk": { + "name": "accounts_user_id_users_id_fk", + "tableFrom": "accounts", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "configs": { + "name": "configs", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "is_active": { + "name": "is_active", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": true + }, + "github_config": { + "name": "github_config", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "gitea_config": { + "name": "gitea_config", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "include": { + "name": "include", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'[\"*\"]'" + }, + "exclude": { + "name": "exclude", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'[]'" + }, + "schedule_config": { + "name": "schedule_config", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "cleanup_config": { + "name": "cleanup_config", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + } + }, + "indexes": {}, + "foreignKeys": { + "configs_user_id_users_id_fk": { + "name": "configs_user_id_users_id_fk", + "tableFrom": "configs", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "events": { + "name": "events", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "channel": { + "name": "channel", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "payload": { + "name": "payload", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "read": { + "name": "read", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + } + }, + "indexes": { + "idx_events_user_channel": { + "name": "idx_events_user_channel", + "columns": [ + "user_id", + "channel" + ], + "isUnique": false + }, + "idx_events_created_at": { + "name": "idx_events_created_at", + "columns": [ + "created_at" + ], + "isUnique": false + }, + "idx_events_read": { + "name": "idx_events_read", + "columns": [ + "read" + ], + "isUnique": false + } + }, + "foreignKeys": { + "events_user_id_users_id_fk": { + "name": "events_user_id_users_id_fk", + "tableFrom": "events", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "mirror_jobs": { + "name": "mirror_jobs", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "repository_id": { + "name": "repository_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "repository_name": { + "name": "repository_name", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "organization_name": { + "name": "organization_name", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "details": { + "name": "details", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'imported'" + }, + "message": { + "name": "message", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "timestamp": { + "name": "timestamp", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + }, + "job_type": { + "name": "job_type", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'mirror'" + }, + "batch_id": { + "name": "batch_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "total_items": { + "name": "total_items", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "completed_items": { + "name": "completed_items", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": 0 + }, + "item_ids": { + "name": "item_ids", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "completed_item_ids": { + "name": "completed_item_ids", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'[]'" + }, + "in_progress": { + "name": "in_progress", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "started_at": { + "name": "started_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "completed_at": { + "name": "completed_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "last_checkpoint": { + "name": "last_checkpoint", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": { + "idx_mirror_jobs_user_id": { + "name": "idx_mirror_jobs_user_id", + "columns": [ + "user_id" + ], + "isUnique": false + }, + "idx_mirror_jobs_batch_id": { + "name": "idx_mirror_jobs_batch_id", + "columns": [ + "batch_id" + ], + "isUnique": false + }, + "idx_mirror_jobs_in_progress": { + "name": "idx_mirror_jobs_in_progress", + "columns": [ + "in_progress" + ], + "isUnique": false + }, + "idx_mirror_jobs_job_type": { + "name": "idx_mirror_jobs_job_type", + "columns": [ + "job_type" + ], + "isUnique": false + }, + "idx_mirror_jobs_timestamp": { + "name": "idx_mirror_jobs_timestamp", + "columns": [ + "timestamp" + ], + "isUnique": false + } + }, + "foreignKeys": { + "mirror_jobs_user_id_users_id_fk": { + "name": "mirror_jobs_user_id_users_id_fk", + "tableFrom": "mirror_jobs", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "oauth_access_tokens": { + "name": "oauth_access_tokens", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "access_token": { + "name": "access_token", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "refresh_token": { + "name": "refresh_token", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "access_token_expires_at": { + "name": "access_token_expires_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "refresh_token_expires_at": { + "name": "refresh_token_expires_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "client_id": { + "name": "client_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "scopes": { + "name": "scopes", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + } + }, + "indexes": { + "idx_oauth_access_tokens_access_token": { + "name": "idx_oauth_access_tokens_access_token", + "columns": [ + "access_token" + ], + "isUnique": false + }, + "idx_oauth_access_tokens_user_id": { + "name": "idx_oauth_access_tokens_user_id", + "columns": [ + "user_id" + ], + "isUnique": false + }, + "idx_oauth_access_tokens_client_id": { + "name": "idx_oauth_access_tokens_client_id", + "columns": [ + "client_id" + ], + "isUnique": false + } + }, + "foreignKeys": { + "oauth_access_tokens_user_id_users_id_fk": { + "name": "oauth_access_tokens_user_id_users_id_fk", + "tableFrom": "oauth_access_tokens", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "oauth_applications": { + "name": "oauth_applications", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "client_id": { + "name": "client_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "client_secret": { + "name": "client_secret", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "redirect_urls": { + "name": "redirect_urls", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "metadata": { + "name": "metadata", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "type": { + "name": "type", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "disabled": { + "name": "disabled", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + } + }, + "indexes": { + "oauth_applications_client_id_unique": { + "name": "oauth_applications_client_id_unique", + "columns": [ + "client_id" + ], + "isUnique": true + }, + "idx_oauth_applications_client_id": { + "name": "idx_oauth_applications_client_id", + "columns": [ + "client_id" + ], + "isUnique": false + }, + "idx_oauth_applications_user_id": { + "name": "idx_oauth_applications_user_id", + "columns": [ + "user_id" + ], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "oauth_consent": { + "name": "oauth_consent", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "client_id": { + "name": "client_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "scopes": { + "name": "scopes", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "consent_given": { + "name": "consent_given", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + } + }, + "indexes": { + "idx_oauth_consent_user_id": { + "name": "idx_oauth_consent_user_id", + "columns": [ + "user_id" + ], + "isUnique": false + }, + "idx_oauth_consent_client_id": { + "name": "idx_oauth_consent_client_id", + "columns": [ + "client_id" + ], + "isUnique": false + }, + "idx_oauth_consent_user_client": { + "name": "idx_oauth_consent_user_client", + "columns": [ + "user_id", + "client_id" + ], + "isUnique": false + } + }, + "foreignKeys": { + "oauth_consent_user_id_users_id_fk": { + "name": "oauth_consent_user_id_users_id_fk", + "tableFrom": "oauth_consent", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "organizations": { + "name": "organizations", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "config_id": { + "name": "config_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "normalized_name": { + "name": "normalized_name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "avatar_url": { + "name": "avatar_url", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "membership_role": { + "name": "membership_role", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'member'" + }, + "is_included": { + "name": "is_included", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": true + }, + "destination_org": { + "name": "destination_org", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'imported'" + }, + "last_mirrored": { + "name": "last_mirrored", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "error_message": { + "name": "error_message", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "repository_count": { + "name": "repository_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "public_repository_count": { + "name": "public_repository_count", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "private_repository_count": { + "name": "private_repository_count", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "fork_repository_count": { + "name": "fork_repository_count", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + } + }, + "indexes": { + "idx_organizations_user_id": { + "name": "idx_organizations_user_id", + "columns": [ + "user_id" + ], + "isUnique": false + }, + "idx_organizations_config_id": { + "name": "idx_organizations_config_id", + "columns": [ + "config_id" + ], + "isUnique": false + }, + "idx_organizations_status": { + "name": "idx_organizations_status", + "columns": [ + "status" + ], + "isUnique": false + }, + "idx_organizations_is_included": { + "name": "idx_organizations_is_included", + "columns": [ + "is_included" + ], + "isUnique": false + }, + "uniq_organizations_user_normalized_name": { + "name": "uniq_organizations_user_normalized_name", + "columns": [ + "user_id", + "normalized_name" + ], + "isUnique": true + } + }, + "foreignKeys": { + "organizations_user_id_users_id_fk": { + "name": "organizations_user_id_users_id_fk", + "tableFrom": "organizations", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "organizations_config_id_configs_id_fk": { + "name": "organizations_config_id_configs_id_fk", + "tableFrom": "organizations", + "tableTo": "configs", + "columnsFrom": [ + "config_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "rate_limits": { + "name": "rate_limits", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "provider": { + "name": "provider", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'github'" + }, + "limit": { + "name": "limit", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "remaining": { + "name": "remaining", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "used": { + "name": "used", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "reset": { + "name": "reset", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "retry_after": { + "name": "retry_after", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'ok'" + }, + "last_checked": { + "name": "last_checked", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + } + }, + "indexes": { + "idx_rate_limits_user_provider": { + "name": "idx_rate_limits_user_provider", + "columns": [ + "user_id", + "provider" + ], + "isUnique": false + }, + "idx_rate_limits_status": { + "name": "idx_rate_limits_status", + "columns": [ + "status" + ], + "isUnique": false + } + }, + "foreignKeys": { + "rate_limits_user_id_users_id_fk": { + "name": "rate_limits_user_id_users_id_fk", + "tableFrom": "rate_limits", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "repositories": { + "name": "repositories", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "config_id": { + "name": "config_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "full_name": { + "name": "full_name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "normalized_full_name": { + "name": "normalized_full_name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "url": { + "name": "url", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "clone_url": { + "name": "clone_url", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "owner": { + "name": "owner", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "organization": { + "name": "organization", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "mirrored_location": { + "name": "mirrored_location", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "''" + }, + "is_private": { + "name": "is_private", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "is_fork": { + "name": "is_fork", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "forked_from": { + "name": "forked_from", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "has_issues": { + "name": "has_issues", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "is_starred": { + "name": "is_starred", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "is_archived": { + "name": "is_archived", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "size": { + "name": "size", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "has_lfs": { + "name": "has_lfs", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "has_submodules": { + "name": "has_submodules", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "language": { + "name": "language", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "default_branch": { + "name": "default_branch", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "visibility": { + "name": "visibility", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'public'" + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'imported'" + }, + "last_mirrored": { + "name": "last_mirrored", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "error_message": { + "name": "error_message", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "destination_org": { + "name": "destination_org", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + } + }, + "indexes": { + "idx_repositories_user_id": { + "name": "idx_repositories_user_id", + "columns": [ + "user_id" + ], + "isUnique": false + }, + "idx_repositories_config_id": { + "name": "idx_repositories_config_id", + "columns": [ + "config_id" + ], + "isUnique": false + }, + "idx_repositories_status": { + "name": "idx_repositories_status", + "columns": [ + "status" + ], + "isUnique": false + }, + "idx_repositories_owner": { + "name": "idx_repositories_owner", + "columns": [ + "owner" + ], + "isUnique": false + }, + "idx_repositories_organization": { + "name": "idx_repositories_organization", + "columns": [ + "organization" + ], + "isUnique": false + }, + "idx_repositories_is_fork": { + "name": "idx_repositories_is_fork", + "columns": [ + "is_fork" + ], + "isUnique": false + }, + "idx_repositories_is_starred": { + "name": "idx_repositories_is_starred", + "columns": [ + "is_starred" + ], + "isUnique": false + }, + "uniq_repositories_user_full_name": { + "name": "uniq_repositories_user_full_name", + "columns": [ + "user_id", + "full_name" + ], + "isUnique": true + }, + "uniq_repositories_user_normalized_full_name": { + "name": "uniq_repositories_user_normalized_full_name", + "columns": [ + "user_id", + "normalized_full_name" + ], + "isUnique": true + } + }, + "foreignKeys": { + "repositories_user_id_users_id_fk": { + "name": "repositories_user_id_users_id_fk", + "tableFrom": "repositories", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "repositories_config_id_configs_id_fk": { + "name": "repositories_config_id_configs_id_fk", + "tableFrom": "repositories", + "tableTo": "configs", + "columnsFrom": [ + "config_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "sessions": { + "name": "sessions", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "token": { + "name": "token", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "expires_at": { + "name": "expires_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "ip_address": { + "name": "ip_address", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "user_agent": { + "name": "user_agent", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + } + }, + "indexes": { + "sessions_token_unique": { + "name": "sessions_token_unique", + "columns": [ + "token" + ], + "isUnique": true + }, + "idx_sessions_user_id": { + "name": "idx_sessions_user_id", + "columns": [ + "user_id" + ], + "isUnique": false + }, + "idx_sessions_token": { + "name": "idx_sessions_token", + "columns": [ + "token" + ], + "isUnique": false + }, + "idx_sessions_expires_at": { + "name": "idx_sessions_expires_at", + "columns": [ + "expires_at" + ], + "isUnique": false + } + }, + "foreignKeys": { + "sessions_user_id_users_id_fk": { + "name": "sessions_user_id_users_id_fk", + "tableFrom": "sessions", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "sso_providers": { + "name": "sso_providers", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "issuer": { + "name": "issuer", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "domain": { + "name": "domain", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "oidc_config": { + "name": "oidc_config", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "provider_id": { + "name": "provider_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + } + }, + "indexes": { + "sso_providers_provider_id_unique": { + "name": "sso_providers_provider_id_unique", + "columns": [ + "provider_id" + ], + "isUnique": true + }, + "idx_sso_providers_provider_id": { + "name": "idx_sso_providers_provider_id", + "columns": [ + "provider_id" + ], + "isUnique": false + }, + "idx_sso_providers_domain": { + "name": "idx_sso_providers_domain", + "columns": [ + "domain" + ], + "isUnique": false + }, + "idx_sso_providers_issuer": { + "name": "idx_sso_providers_issuer", + "columns": [ + "issuer" + ], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "users": { + "name": "users", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "email_verified": { + "name": "email_verified", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "image": { + "name": "image", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + }, + "username": { + "name": "username", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": { + "users_email_unique": { + "name": "users_email_unique", + "columns": [ + "email" + ], + "isUnique": true + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "verification_tokens": { + "name": "verification_tokens", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "token": { + "name": "token", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "identifier": { + "name": "identifier", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "type": { + "name": "type", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "expires_at": { + "name": "expires_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + } + }, + "indexes": { + "verification_tokens_token_unique": { + "name": "verification_tokens_token_unique", + "columns": [ + "token" + ], + "isUnique": true + }, + "idx_verification_tokens_token": { + "name": "idx_verification_tokens_token", + "columns": [ + "token" + ], + "isUnique": false + }, + "idx_verification_tokens_identifier": { + "name": "idx_verification_tokens_identifier", + "columns": [ + "identifier" + ], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "verifications": { + "name": "verifications", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "identifier": { + "name": "identifier", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "value": { + "name": "value", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "expires_at": { + "name": "expires_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + } + }, + "indexes": { + "idx_verifications_identifier": { + "name": "idx_verifications_identifier", + "columns": [ + "identifier" + ], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + } + }, + "views": {}, + "enums": {}, + "_meta": { + "schemas": {}, + "tables": {}, + "columns": {} + }, + "internal": { + "indexes": {} + } +} \ No newline at end of file diff --git a/Divers/gitea-mirror/drizzle/meta/0008_snapshot.json b/Divers/gitea-mirror/drizzle/meta/0008_snapshot.json new file mode 100644 index 0000000..bb906fa --- /dev/null +++ b/Divers/gitea-mirror/drizzle/meta/0008_snapshot.json @@ -0,0 +1,2006 @@ +{ + "version": "6", + "dialect": "sqlite", + "id": "fa3adb7a-7422-49bb-a3b0-ad216bda216a", + "prevId": "e4d9f5ae-4b18-4529-89fd-b7720822af07", + "tables": { + "accounts": { + "name": "accounts", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "account_id": { + "name": "account_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "provider_id": { + "name": "provider_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "provider_user_id": { + "name": "provider_user_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "access_token": { + "name": "access_token", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "refresh_token": { + "name": "refresh_token", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "id_token": { + "name": "id_token", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "access_token_expires_at": { + "name": "access_token_expires_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "refresh_token_expires_at": { + "name": "refresh_token_expires_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "scope": { + "name": "scope", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "expires_at": { + "name": "expires_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "password": { + "name": "password", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + } + }, + "indexes": { + "idx_accounts_account_id": { + "name": "idx_accounts_account_id", + "columns": [ + "account_id" + ], + "isUnique": false + }, + "idx_accounts_user_id": { + "name": "idx_accounts_user_id", + "columns": [ + "user_id" + ], + "isUnique": false + }, + "idx_accounts_provider": { + "name": "idx_accounts_provider", + "columns": [ + "provider_id", + "provider_user_id" + ], + "isUnique": false + } + }, + "foreignKeys": { + "accounts_user_id_users_id_fk": { + "name": "accounts_user_id_users_id_fk", + "tableFrom": "accounts", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "configs": { + "name": "configs", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "is_active": { + "name": "is_active", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": true + }, + "github_config": { + "name": "github_config", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "gitea_config": { + "name": "gitea_config", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "include": { + "name": "include", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'[\"*\"]'" + }, + "exclude": { + "name": "exclude", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'[]'" + }, + "schedule_config": { + "name": "schedule_config", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "cleanup_config": { + "name": "cleanup_config", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + } + }, + "indexes": {}, + "foreignKeys": { + "configs_user_id_users_id_fk": { + "name": "configs_user_id_users_id_fk", + "tableFrom": "configs", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "events": { + "name": "events", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "channel": { + "name": "channel", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "payload": { + "name": "payload", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "read": { + "name": "read", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + } + }, + "indexes": { + "idx_events_user_channel": { + "name": "idx_events_user_channel", + "columns": [ + "user_id", + "channel" + ], + "isUnique": false + }, + "idx_events_created_at": { + "name": "idx_events_created_at", + "columns": [ + "created_at" + ], + "isUnique": false + }, + "idx_events_read": { + "name": "idx_events_read", + "columns": [ + "read" + ], + "isUnique": false + } + }, + "foreignKeys": { + "events_user_id_users_id_fk": { + "name": "events_user_id_users_id_fk", + "tableFrom": "events", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "mirror_jobs": { + "name": "mirror_jobs", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "repository_id": { + "name": "repository_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "repository_name": { + "name": "repository_name", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "organization_name": { + "name": "organization_name", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "details": { + "name": "details", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'imported'" + }, + "message": { + "name": "message", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "timestamp": { + "name": "timestamp", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + }, + "job_type": { + "name": "job_type", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'mirror'" + }, + "batch_id": { + "name": "batch_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "total_items": { + "name": "total_items", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "completed_items": { + "name": "completed_items", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": 0 + }, + "item_ids": { + "name": "item_ids", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "completed_item_ids": { + "name": "completed_item_ids", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'[]'" + }, + "in_progress": { + "name": "in_progress", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "started_at": { + "name": "started_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "completed_at": { + "name": "completed_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "last_checkpoint": { + "name": "last_checkpoint", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": { + "idx_mirror_jobs_user_id": { + "name": "idx_mirror_jobs_user_id", + "columns": [ + "user_id" + ], + "isUnique": false + }, + "idx_mirror_jobs_batch_id": { + "name": "idx_mirror_jobs_batch_id", + "columns": [ + "batch_id" + ], + "isUnique": false + }, + "idx_mirror_jobs_in_progress": { + "name": "idx_mirror_jobs_in_progress", + "columns": [ + "in_progress" + ], + "isUnique": false + }, + "idx_mirror_jobs_job_type": { + "name": "idx_mirror_jobs_job_type", + "columns": [ + "job_type" + ], + "isUnique": false + }, + "idx_mirror_jobs_timestamp": { + "name": "idx_mirror_jobs_timestamp", + "columns": [ + "timestamp" + ], + "isUnique": false + } + }, + "foreignKeys": { + "mirror_jobs_user_id_users_id_fk": { + "name": "mirror_jobs_user_id_users_id_fk", + "tableFrom": "mirror_jobs", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "oauth_access_tokens": { + "name": "oauth_access_tokens", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "access_token": { + "name": "access_token", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "refresh_token": { + "name": "refresh_token", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "access_token_expires_at": { + "name": "access_token_expires_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "refresh_token_expires_at": { + "name": "refresh_token_expires_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "client_id": { + "name": "client_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "scopes": { + "name": "scopes", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + } + }, + "indexes": { + "idx_oauth_access_tokens_access_token": { + "name": "idx_oauth_access_tokens_access_token", + "columns": [ + "access_token" + ], + "isUnique": false + }, + "idx_oauth_access_tokens_user_id": { + "name": "idx_oauth_access_tokens_user_id", + "columns": [ + "user_id" + ], + "isUnique": false + }, + "idx_oauth_access_tokens_client_id": { + "name": "idx_oauth_access_tokens_client_id", + "columns": [ + "client_id" + ], + "isUnique": false + } + }, + "foreignKeys": { + "oauth_access_tokens_user_id_users_id_fk": { + "name": "oauth_access_tokens_user_id_users_id_fk", + "tableFrom": "oauth_access_tokens", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "oauth_applications": { + "name": "oauth_applications", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "client_id": { + "name": "client_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "client_secret": { + "name": "client_secret", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "redirect_urls": { + "name": "redirect_urls", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "metadata": { + "name": "metadata", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "type": { + "name": "type", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "disabled": { + "name": "disabled", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + } + }, + "indexes": { + "oauth_applications_client_id_unique": { + "name": "oauth_applications_client_id_unique", + "columns": [ + "client_id" + ], + "isUnique": true + }, + "idx_oauth_applications_client_id": { + "name": "idx_oauth_applications_client_id", + "columns": [ + "client_id" + ], + "isUnique": false + }, + "idx_oauth_applications_user_id": { + "name": "idx_oauth_applications_user_id", + "columns": [ + "user_id" + ], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "oauth_consent": { + "name": "oauth_consent", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "client_id": { + "name": "client_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "scopes": { + "name": "scopes", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "consent_given": { + "name": "consent_given", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + } + }, + "indexes": { + "idx_oauth_consent_user_id": { + "name": "idx_oauth_consent_user_id", + "columns": [ + "user_id" + ], + "isUnique": false + }, + "idx_oauth_consent_client_id": { + "name": "idx_oauth_consent_client_id", + "columns": [ + "client_id" + ], + "isUnique": false + }, + "idx_oauth_consent_user_client": { + "name": "idx_oauth_consent_user_client", + "columns": [ + "user_id", + "client_id" + ], + "isUnique": false + } + }, + "foreignKeys": { + "oauth_consent_user_id_users_id_fk": { + "name": "oauth_consent_user_id_users_id_fk", + "tableFrom": "oauth_consent", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "organizations": { + "name": "organizations", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "config_id": { + "name": "config_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "normalized_name": { + "name": "normalized_name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "avatar_url": { + "name": "avatar_url", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "membership_role": { + "name": "membership_role", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'member'" + }, + "is_included": { + "name": "is_included", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": true + }, + "destination_org": { + "name": "destination_org", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'imported'" + }, + "last_mirrored": { + "name": "last_mirrored", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "error_message": { + "name": "error_message", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "repository_count": { + "name": "repository_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "public_repository_count": { + "name": "public_repository_count", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "private_repository_count": { + "name": "private_repository_count", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "fork_repository_count": { + "name": "fork_repository_count", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + } + }, + "indexes": { + "idx_organizations_user_id": { + "name": "idx_organizations_user_id", + "columns": [ + "user_id" + ], + "isUnique": false + }, + "idx_organizations_config_id": { + "name": "idx_organizations_config_id", + "columns": [ + "config_id" + ], + "isUnique": false + }, + "idx_organizations_status": { + "name": "idx_organizations_status", + "columns": [ + "status" + ], + "isUnique": false + }, + "idx_organizations_is_included": { + "name": "idx_organizations_is_included", + "columns": [ + "is_included" + ], + "isUnique": false + }, + "uniq_organizations_user_normalized_name": { + "name": "uniq_organizations_user_normalized_name", + "columns": [ + "user_id", + "normalized_name" + ], + "isUnique": true + } + }, + "foreignKeys": { + "organizations_user_id_users_id_fk": { + "name": "organizations_user_id_users_id_fk", + "tableFrom": "organizations", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "organizations_config_id_configs_id_fk": { + "name": "organizations_config_id_configs_id_fk", + "tableFrom": "organizations", + "tableTo": "configs", + "columnsFrom": [ + "config_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "rate_limits": { + "name": "rate_limits", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "provider": { + "name": "provider", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'github'" + }, + "limit": { + "name": "limit", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "remaining": { + "name": "remaining", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "used": { + "name": "used", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "reset": { + "name": "reset", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "retry_after": { + "name": "retry_after", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'ok'" + }, + "last_checked": { + "name": "last_checked", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + } + }, + "indexes": { + "idx_rate_limits_user_provider": { + "name": "idx_rate_limits_user_provider", + "columns": [ + "user_id", + "provider" + ], + "isUnique": false + }, + "idx_rate_limits_status": { + "name": "idx_rate_limits_status", + "columns": [ + "status" + ], + "isUnique": false + } + }, + "foreignKeys": { + "rate_limits_user_id_users_id_fk": { + "name": "rate_limits_user_id_users_id_fk", + "tableFrom": "rate_limits", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "repositories": { + "name": "repositories", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "config_id": { + "name": "config_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "full_name": { + "name": "full_name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "normalized_full_name": { + "name": "normalized_full_name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "url": { + "name": "url", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "clone_url": { + "name": "clone_url", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "owner": { + "name": "owner", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "organization": { + "name": "organization", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "mirrored_location": { + "name": "mirrored_location", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "''" + }, + "is_private": { + "name": "is_private", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "is_fork": { + "name": "is_fork", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "forked_from": { + "name": "forked_from", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "has_issues": { + "name": "has_issues", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "is_starred": { + "name": "is_starred", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "is_archived": { + "name": "is_archived", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "size": { + "name": "size", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "has_lfs": { + "name": "has_lfs", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "has_submodules": { + "name": "has_submodules", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "language": { + "name": "language", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "default_branch": { + "name": "default_branch", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "visibility": { + "name": "visibility", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'public'" + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'imported'" + }, + "last_mirrored": { + "name": "last_mirrored", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "error_message": { + "name": "error_message", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "destination_org": { + "name": "destination_org", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "metadata": { + "name": "metadata", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + } + }, + "indexes": { + "idx_repositories_user_id": { + "name": "idx_repositories_user_id", + "columns": [ + "user_id" + ], + "isUnique": false + }, + "idx_repositories_config_id": { + "name": "idx_repositories_config_id", + "columns": [ + "config_id" + ], + "isUnique": false + }, + "idx_repositories_status": { + "name": "idx_repositories_status", + "columns": [ + "status" + ], + "isUnique": false + }, + "idx_repositories_owner": { + "name": "idx_repositories_owner", + "columns": [ + "owner" + ], + "isUnique": false + }, + "idx_repositories_organization": { + "name": "idx_repositories_organization", + "columns": [ + "organization" + ], + "isUnique": false + }, + "idx_repositories_is_fork": { + "name": "idx_repositories_is_fork", + "columns": [ + "is_fork" + ], + "isUnique": false + }, + "idx_repositories_is_starred": { + "name": "idx_repositories_is_starred", + "columns": [ + "is_starred" + ], + "isUnique": false + }, + "uniq_repositories_user_full_name": { + "name": "uniq_repositories_user_full_name", + "columns": [ + "user_id", + "full_name" + ], + "isUnique": true + }, + "uniq_repositories_user_normalized_full_name": { + "name": "uniq_repositories_user_normalized_full_name", + "columns": [ + "user_id", + "normalized_full_name" + ], + "isUnique": true + } + }, + "foreignKeys": { + "repositories_user_id_users_id_fk": { + "name": "repositories_user_id_users_id_fk", + "tableFrom": "repositories", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "repositories_config_id_configs_id_fk": { + "name": "repositories_config_id_configs_id_fk", + "tableFrom": "repositories", + "tableTo": "configs", + "columnsFrom": [ + "config_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "sessions": { + "name": "sessions", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "token": { + "name": "token", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "expires_at": { + "name": "expires_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "ip_address": { + "name": "ip_address", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "user_agent": { + "name": "user_agent", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + } + }, + "indexes": { + "sessions_token_unique": { + "name": "sessions_token_unique", + "columns": [ + "token" + ], + "isUnique": true + }, + "idx_sessions_user_id": { + "name": "idx_sessions_user_id", + "columns": [ + "user_id" + ], + "isUnique": false + }, + "idx_sessions_token": { + "name": "idx_sessions_token", + "columns": [ + "token" + ], + "isUnique": false + }, + "idx_sessions_expires_at": { + "name": "idx_sessions_expires_at", + "columns": [ + "expires_at" + ], + "isUnique": false + } + }, + "foreignKeys": { + "sessions_user_id_users_id_fk": { + "name": "sessions_user_id_users_id_fk", + "tableFrom": "sessions", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "sso_providers": { + "name": "sso_providers", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "issuer": { + "name": "issuer", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "domain": { + "name": "domain", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "oidc_config": { + "name": "oidc_config", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "provider_id": { + "name": "provider_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + } + }, + "indexes": { + "sso_providers_provider_id_unique": { + "name": "sso_providers_provider_id_unique", + "columns": [ + "provider_id" + ], + "isUnique": true + }, + "idx_sso_providers_provider_id": { + "name": "idx_sso_providers_provider_id", + "columns": [ + "provider_id" + ], + "isUnique": false + }, + "idx_sso_providers_domain": { + "name": "idx_sso_providers_domain", + "columns": [ + "domain" + ], + "isUnique": false + }, + "idx_sso_providers_issuer": { + "name": "idx_sso_providers_issuer", + "columns": [ + "issuer" + ], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "users": { + "name": "users", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "email_verified": { + "name": "email_verified", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "image": { + "name": "image", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + }, + "username": { + "name": "username", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": { + "users_email_unique": { + "name": "users_email_unique", + "columns": [ + "email" + ], + "isUnique": true + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "verification_tokens": { + "name": "verification_tokens", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "token": { + "name": "token", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "identifier": { + "name": "identifier", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "type": { + "name": "type", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "expires_at": { + "name": "expires_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + } + }, + "indexes": { + "verification_tokens_token_unique": { + "name": "verification_tokens_token_unique", + "columns": [ + "token" + ], + "isUnique": true + }, + "idx_verification_tokens_token": { + "name": "idx_verification_tokens_token", + "columns": [ + "token" + ], + "isUnique": false + }, + "idx_verification_tokens_identifier": { + "name": "idx_verification_tokens_identifier", + "columns": [ + "identifier" + ], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "verifications": { + "name": "verifications", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "identifier": { + "name": "identifier", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "value": { + "name": "value", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "expires_at": { + "name": "expires_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + } + }, + "indexes": { + "idx_verifications_identifier": { + "name": "idx_verifications_identifier", + "columns": [ + "identifier" + ], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + } + }, + "views": {}, + "enums": {}, + "_meta": { + "schemas": {}, + "tables": {}, + "columns": {} + }, + "internal": { + "indexes": {} + } +} \ No newline at end of file diff --git a/Divers/gitea-mirror/drizzle/meta/_journal.json b/Divers/gitea-mirror/drizzle/meta/_journal.json new file mode 100644 index 0000000..5127d6c --- /dev/null +++ b/Divers/gitea-mirror/drizzle/meta/_journal.json @@ -0,0 +1,69 @@ +{ + "version": "7", + "dialect": "sqlite", + "entries": [ + { + "idx": 0, + "version": "6", + "when": 1752171873627, + "tag": "0000_init", + "breakpoints": true + }, + { + "idx": 1, + "version": "6", + "when": 1752173351102, + "tag": "0001_polite_exodus", + "breakpoints": true + }, + { + "idx": 2, + "version": "6", + "when": 1753539600567, + "tag": "0002_bored_captain_cross", + "breakpoints": true + }, + { + "idx": 3, + "version": "6", + "when": 1757390828679, + "tag": "0003_open_spacker_dave", + "breakpoints": true + }, + { + "idx": 4, + "version": "6", + "when": 1757392620734, + "tag": "0004_grey_butterfly", + "breakpoints": true + }, + { + "idx": 5, + "version": "6", + "when": 1757786449446, + "tag": "0005_polite_preak", + "breakpoints": true + }, + { + "idx": 6, + "version": "6", + "when": 1761483928546, + "tag": "0006_military_la_nuit", + "breakpoints": true + }, + { + "idx": 7, + "version": "6", + "when": 1761534391115, + "tag": "0007_whole_hellion", + "breakpoints": true + }, + { + "idx": 8, + "version": "6", + "when": 1761802056073, + "tag": "0008_serious_thena", + "breakpoints": true + } + ] +} \ No newline at end of file diff --git a/Divers/gitea-mirror/env.d.ts b/Divers/gitea-mirror/env.d.ts new file mode 100644 index 0000000..1369e80 --- /dev/null +++ b/Divers/gitea-mirror/env.d.ts @@ -0,0 +1,9 @@ +/// +/// + +declare namespace App { + interface Locals { + user: import("better-auth").User | null; + session: import("better-auth").Session | null; + } +} \ No newline at end of file diff --git a/Divers/gitea-mirror/flake.lock b/Divers/gitea-mirror/flake.lock new file mode 100644 index 0000000..725c6d3 --- /dev/null +++ b/Divers/gitea-mirror/flake.lock @@ -0,0 +1,61 @@ +{ + "nodes": { + "flake-utils": { + "inputs": { + "systems": "systems" + }, + "locked": { + "lastModified": 1731533236, + "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1761672384, + "narHash": "sha256-o9KF3DJL7g7iYMZq9SWgfS1BFlNbsm6xplRjVlOCkXI=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "08dacfca559e1d7da38f3cf05f1f45ee9bfd213c", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "flake-utils": "flake-utils", + "nixpkgs": "nixpkgs" + } + }, + "systems": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/Divers/gitea-mirror/flake.nix b/Divers/gitea-mirror/flake.nix new file mode 100644 index 0000000..fca131c --- /dev/null +++ b/Divers/gitea-mirror/flake.nix @@ -0,0 +1,395 @@ +{ + description = "Gitea Mirror - Self-hosted GitHub to Gitea mirroring service"; + + inputs = { + nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; + flake-utils.url = "github:numtide/flake-utils"; + }; + + outputs = { self, nixpkgs, flake-utils }: + flake-utils.lib.eachDefaultSystem (system: + let + pkgs = nixpkgs.legacyPackages.${system}; + + # Build the application + gitea-mirror = pkgs.stdenv.mkDerivation { + pname = "gitea-mirror"; + version = "3.8.11"; + + src = ./.; + + nativeBuildInputs = with pkgs; [ + bun + ]; + + buildInputs = with pkgs; [ + sqlite + openssl + ]; + + configurePhase = '' + export HOME=$TMPDIR + export BUN_INSTALL=$TMPDIR/.bun + export PATH=$BUN_INSTALL/bin:$PATH + ''; + + buildPhase = '' + # Install dependencies + bun install --frozen-lockfile --no-progress + + # Build the application + bun run build + ''; + + installPhase = '' + mkdir -p $out/lib/gitea-mirror + mkdir -p $out/bin + + # Copy the built application + cp -r dist $out/lib/gitea-mirror/ + cp -r node_modules $out/lib/gitea-mirror/ + cp -r scripts $out/lib/gitea-mirror/ + cp package.json $out/lib/gitea-mirror/ + + # Create entrypoint script that matches Docker behavior + cat > $out/bin/gitea-mirror <<'EOF' +#!/usr/bin/env bash +set -e + +# === DEFAULT CONFIGURATION === +# These match docker-compose.alt.yml defaults +export DATA_DIR=''${DATA_DIR:-"$HOME/.local/share/gitea-mirror"} +export DATABASE_URL=''${DATABASE_URL:-"file:$DATA_DIR/gitea-mirror.db"} +export HOST=''${HOST:-"0.0.0.0"} +export PORT=''${PORT:-"4321"} +export NODE_ENV=''${NODE_ENV:-"production"} + +# Better Auth configuration +export BETTER_AUTH_URL=''${BETTER_AUTH_URL:-"http://localhost:4321"} +export BETTER_AUTH_TRUSTED_ORIGINS=''${BETTER_AUTH_TRUSTED_ORIGINS:-"http://localhost:4321"} +export PUBLIC_BETTER_AUTH_URL=''${PUBLIC_BETTER_AUTH_URL:-"http://localhost:4321"} + +# Concurrency settings (match docker-compose.alt.yml) +export MIRROR_ISSUE_CONCURRENCY=''${MIRROR_ISSUE_CONCURRENCY:-3} +export MIRROR_PULL_REQUEST_CONCURRENCY=''${MIRROR_PULL_REQUEST_CONCURRENCY:-5} + +# Create data directory +mkdir -p "$DATA_DIR" +cd $out/lib/gitea-mirror + +# === AUTO-GENERATE SECRETS === +BETTER_AUTH_SECRET_FILE="$DATA_DIR/.better_auth_secret" +ENCRYPTION_SECRET_FILE="$DATA_DIR/.encryption_secret" + +# Generate BETTER_AUTH_SECRET if not provided +if [ -z "$BETTER_AUTH_SECRET" ]; then + if [ -f "$BETTER_AUTH_SECRET_FILE" ]; then + echo "Using previously generated BETTER_AUTH_SECRET" + export BETTER_AUTH_SECRET=$(cat "$BETTER_AUTH_SECRET_FILE") + else + echo "Generating a secure random BETTER_AUTH_SECRET" + GENERATED_SECRET=$(${pkgs.openssl}/bin/openssl rand -hex 32) + export BETTER_AUTH_SECRET="$GENERATED_SECRET" + echo "$GENERATED_SECRET" > "$BETTER_AUTH_SECRET_FILE" + chmod 600 "$BETTER_AUTH_SECRET_FILE" + echo "✅ BETTER_AUTH_SECRET generated and saved to $BETTER_AUTH_SECRET_FILE" + fi +fi + +# Generate ENCRYPTION_SECRET if not provided +if [ -z "$ENCRYPTION_SECRET" ]; then + if [ -f "$ENCRYPTION_SECRET_FILE" ]; then + echo "Using previously generated ENCRYPTION_SECRET" + export ENCRYPTION_SECRET=$(cat "$ENCRYPTION_SECRET_FILE") + else + echo "Generating a secure random ENCRYPTION_SECRET" + GENERATED_ENCRYPTION_SECRET=$(${pkgs.openssl}/bin/openssl rand -base64 36) + export ENCRYPTION_SECRET="$GENERATED_ENCRYPTION_SECRET" + echo "$GENERATED_ENCRYPTION_SECRET" > "$ENCRYPTION_SECRET_FILE" + chmod 600 "$ENCRYPTION_SECRET_FILE" + echo "✅ ENCRYPTION_SECRET generated and saved to $ENCRYPTION_SECRET_FILE" + fi +fi + +# === DATABASE INITIALIZATION === +DB_PATH=$(echo "$DATABASE_URL" | sed 's|^file:||') +if [ ! -f "$DB_PATH" ]; then + echo "Database not found. It will be created and initialized via Drizzle migrations on first app startup..." + touch "$DB_PATH" +else + echo "Database already exists, Drizzle will check for pending migrations on startup..." +fi + +# === STARTUP SCRIPTS === +# Initialize configuration from environment variables +echo "Checking for environment configuration..." +if [ -f "dist/scripts/startup-env-config.js" ]; then + echo "Loading configuration from environment variables..." + ${pkgs.bun}/bin/bun dist/scripts/startup-env-config.js && \ + echo "✅ Environment configuration loaded successfully" || \ + echo "⚠️ Environment configuration loading completed with warnings" +fi + +# Run startup recovery +echo "Running startup recovery..." +if [ -f "dist/scripts/startup-recovery.js" ]; then + ${pkgs.bun}/bin/bun dist/scripts/startup-recovery.js --timeout=30000 && \ + echo "✅ Startup recovery completed successfully" || \ + echo "⚠️ Startup recovery completed with warnings" +fi + +# Run repository status repair +echo "Running repository status repair..." +if [ -f "dist/scripts/repair-mirrored-repos.js" ]; then + ${pkgs.bun}/bin/bun dist/scripts/repair-mirrored-repos.js --startup && \ + echo "✅ Repository status repair completed successfully" || \ + echo "⚠️ Repository status repair completed with warnings" +fi + +# === SIGNAL HANDLING === +shutdown_handler() { + echo "🛑 Received shutdown signal, forwarding to application..." + if [ ! -z "$APP_PID" ]; then + kill -TERM "$APP_PID" 2>/dev/null || true + wait "$APP_PID" 2>/dev/null || true + fi + exit 0 +} + +trap 'shutdown_handler' TERM INT HUP + +# === START APPLICATION === +echo "Starting Gitea Mirror..." +echo "Access the web interface at $BETTER_AUTH_URL" +${pkgs.bun}/bin/bun dist/server/entry.mjs & +APP_PID=$! + +wait "$APP_PID" +EOF + chmod +x $out/bin/gitea-mirror + + # Create database management helper + cat > $out/bin/gitea-mirror-db <<'EOF' +#!/usr/bin/env bash +export DATA_DIR=''${DATA_DIR:-"$HOME/.local/share/gitea-mirror"} +mkdir -p "$DATA_DIR" +cd $out/lib/gitea-mirror +exec ${pkgs.bun}/bin/bun scripts/manage-db.ts "$@" +EOF + chmod +x $out/bin/gitea-mirror-db + ''; + + meta = with pkgs.lib; { + description = "Self-hosted GitHub to Gitea mirroring service"; + homepage = "https://github.com/RayLabsHQ/gitea-mirror"; + license = licenses.mit; + maintainers = [ ]; + platforms = platforms.linux ++ platforms.darwin; + }; + }; + + in + { + packages = { + default = gitea-mirror; + gitea-mirror = gitea-mirror; + }; + + # Development shell + devShells.default = pkgs.mkShell { + buildInputs = with pkgs; [ + bun + sqlite + openssl + ]; + + shellHook = '' + echo "🚀 Gitea Mirror development environment" + echo "" + echo "Quick start:" + echo " bun install # Install dependencies" + echo " bun run dev # Start development server" + echo " bun run build # Build for production" + echo "" + echo "Database:" + echo " bun run manage-db init # Initialize database" + echo " bun run db:studio # Open Drizzle Studio" + ''; + }; + + # NixOS module + nixosModules.default = { config, lib, pkgs, ... }: + with lib; + let + cfg = config.services.gitea-mirror; + in { + options.services.gitea-mirror = { + enable = mkEnableOption "Gitea Mirror service"; + + package = mkOption { + type = types.package; + default = self.packages.${system}.default; + description = "The Gitea Mirror package to use"; + }; + + dataDir = mkOption { + type = types.path; + default = "/var/lib/gitea-mirror"; + description = "Directory to store data and database"; + }; + + user = mkOption { + type = types.str; + default = "gitea-mirror"; + description = "User account under which Gitea Mirror runs"; + }; + + group = mkOption { + type = types.str; + default = "gitea-mirror"; + description = "Group under which Gitea Mirror runs"; + }; + + host = mkOption { + type = types.str; + default = "0.0.0.0"; + description = "Host to bind to"; + }; + + port = mkOption { + type = types.port; + default = 4321; + description = "Port to listen on"; + }; + + betterAuthUrl = mkOption { + type = types.str; + default = "http://localhost:4321"; + description = "Better Auth URL (external URL of the service)"; + }; + + betterAuthTrustedOrigins = mkOption { + type = types.str; + default = "http://localhost:4321"; + description = "Comma-separated list of trusted origins for Better Auth"; + }; + + mirrorIssueConcurrency = mkOption { + type = types.int; + default = 3; + description = "Number of concurrent issue mirror operations (set to 1 for perfect ordering)"; + }; + + mirrorPullRequestConcurrency = mkOption { + type = types.int; + default = 5; + description = "Number of concurrent PR mirror operations (set to 1 for perfect ordering)"; + }; + + environmentFile = mkOption { + type = types.nullOr types.path; + default = null; + description = '' + Path to file containing environment variables. + Only needed if you want to set BETTER_AUTH_SECRET or ENCRYPTION_SECRET manually. + Otherwise, secrets will be auto-generated and stored in the data directory. + + Example: + BETTER_AUTH_SECRET=your-32-character-secret-here + ENCRYPTION_SECRET=your-encryption-secret-here + ''; + }; + + openFirewall = mkOption { + type = types.bool; + default = false; + description = "Open the firewall for the specified port"; + }; + }; + + config = mkIf cfg.enable { + users.users.${cfg.user} = { + isSystemUser = true; + group = cfg.group; + home = cfg.dataDir; + createHome = true; + }; + + users.groups.${cfg.group} = {}; + + systemd.services.gitea-mirror = { + description = "Gitea Mirror - GitHub to Gitea mirroring service"; + after = [ "network.target" ]; + wantedBy = [ "multi-user.target" ]; + + environment = { + DATA_DIR = cfg.dataDir; + DATABASE_URL = "file:${cfg.dataDir}/gitea-mirror.db"; + HOST = cfg.host; + PORT = toString cfg.port; + NODE_ENV = "production"; + BETTER_AUTH_URL = cfg.betterAuthUrl; + BETTER_AUTH_TRUSTED_ORIGINS = cfg.betterAuthTrustedOrigins; + PUBLIC_BETTER_AUTH_URL = cfg.betterAuthUrl; + MIRROR_ISSUE_CONCURRENCY = toString cfg.mirrorIssueConcurrency; + MIRROR_PULL_REQUEST_CONCURRENCY = toString cfg.mirrorPullRequestConcurrency; + }; + + serviceConfig = { + Type = "simple"; + User = cfg.user; + Group = cfg.group; + ExecStart = "${cfg.package}/bin/gitea-mirror"; + Restart = "always"; + RestartSec = "10s"; + + # Security hardening + NoNewPrivileges = true; + PrivateTmp = true; + ProtectSystem = "strict"; + ProtectHome = true; + ReadWritePaths = [ cfg.dataDir ]; + + # Load environment file if specified (optional) + EnvironmentFile = mkIf (cfg.environmentFile != null) cfg.environmentFile; + + # Graceful shutdown + TimeoutStopSec = "30s"; + KillMode = "mixed"; + KillSignal = "SIGTERM"; + }; + }; + + # Health check timer (optional monitoring) + systemd.timers.gitea-mirror-healthcheck = mkIf cfg.enable { + description = "Gitea Mirror health check timer"; + wantedBy = [ "timers.target" ]; + timerConfig = { + OnBootSec = "5min"; + OnUnitActiveSec = "5min"; + }; + }; + + systemd.services.gitea-mirror-healthcheck = mkIf cfg.enable { + description = "Gitea Mirror health check"; + after = [ "gitea-mirror.service" ]; + serviceConfig = { + Type = "oneshot"; + ExecStart = "${pkgs.curl}/bin/curl -f http://${cfg.host}:${toString cfg.port}/api/health || true"; + User = "nobody"; + }; + }; + + networking.firewall = mkIf cfg.openFirewall { + allowedTCPPorts = [ cfg.port ]; + }; + }; + }; + } + ) // { + # Overlay for adding to nixpkgs + overlays.default = final: prev: { + gitea-mirror = self.packages.${final.system}.default; + }; + }; +} diff --git a/Divers/gitea-mirror/helm/gitea-mirror/.yamllint b/Divers/gitea-mirror/helm/gitea-mirror/.yamllint new file mode 100644 index 0000000..111146a --- /dev/null +++ b/Divers/gitea-mirror/helm/gitea-mirror/.yamllint @@ -0,0 +1,21 @@ +--- +extends: default + +ignore: | + .yamllint + node_modules + templates + unittests/bash + +rules: + truthy: + allowed-values: ['true', 'false'] + check-keys: False + level: error + line-length: disable + document-start: disable + comments: + min-spaces-from-content: 1 + braces: + max-spaces-inside: 2 + diff --git a/Divers/gitea-mirror/helm/gitea-mirror/Chart.yaml b/Divers/gitea-mirror/helm/gitea-mirror/Chart.yaml new file mode 100644 index 0000000..042a837 --- /dev/null +++ b/Divers/gitea-mirror/helm/gitea-mirror/Chart.yaml @@ -0,0 +1,12 @@ +apiVersion: v2 +name: gitea-mirror +description: Kubernetes helm chart for gitea-mirror +type: application +version: 0.0.1 +appVersion: 3.7.2 +icon: https://github.com/RayLabsHQ/gitea-mirror/blob/main/.github/assets/logo.png +keywords: + - git + - gitea +sources: + - https://github.com/RayLabsHQ/gitea-mirror diff --git a/Divers/gitea-mirror/helm/gitea-mirror/README.md b/Divers/gitea-mirror/helm/gitea-mirror/README.md new file mode 100644 index 0000000..c682fcb --- /dev/null +++ b/Divers/gitea-mirror/helm/gitea-mirror/README.md @@ -0,0 +1,307 @@ +# gitea-mirror (Helm Chart) + +Deploy **gitea-mirror** to Kubernetes using Helm. The chart packages a Deployment, Service, optional Ingress or Gateway API HTTPRoutes, ConfigMap and Secret, a PVC (optional), and an optional ServiceAccount. + +- **Chart name:** `gitea-mirror` +- **Type:** `application` +- **App version:** `3.7.2` (default image tag, can be overridden) + +--- + +## Prerequisites + +- Kubernetes 1.23+ +- Helm 3.8+ +- (Optional) Gateway API (v1) if you plan to use `route.*` HTTPRoutes, see https://github.com/kubernetes-sigs/gateway-api/ +- (Optional) An Ingress controller if you plan to use `ingress.*` + +--- + +## Quick start + +From the repo root (chart path: `helm/gitea-mirror`): + +```bash +# Create a namespace (optional) +kubectl create namespace gitea-mirror + +# Install with minimal required secrets/values +helm upgrade --install gitea-mirror ./helm/gitea-mirror --namespace gitea-mirror --set "gitea-mirror.github.username=" --set "gitea-mirror.github.token=" --set "gitea-mirror.gitea.url=https://gitea.example.com" --set "gitea-mirror.gitea.token=" +``` + +The default Service is `ClusterIP` on port `4321`. You can expose it via Ingress or Gateway API; see below. + +--- + +## Upgrading + +Standard Helm upgrade: + +```bash +helm upgrade gitea-mirror ./helm/gitea-mirror -n gitea-mirror +``` + +If you change persistence settings or storage class, a rollout may require PVC recreation. + +--- + +## Uninstalling + +```bash +helm uninstall gitea-mirror -n gitea-mirror +``` + +If you enabled persistence with a PVC the data may persist; delete the PVC manually if you want a clean slate. + +--- + +## Configuration + +### Global image & pod settings + +| Key | Type | Default | Description | +| --- | --- | --- | --- | +| `image.registry` | string | `ghcr.io` | Container registry. | +| `image.repository` | string | `raylabshq/gitea-mirror` | Image repository. | +| `image.tag` | string | `""` | Image tag; when empty, uses the chart `appVersion` (`3.7.2`). | +| `image.pullPolicy` | string | `IfNotPresent` | K8s image pull policy. | +| `imagePullSecrets` | list | `[]` | Image pull secrets. | +| `podSecurityContext.runAsUser` | int | `1001` | UID. | +| `podSecurityContext.runAsGroup` | int | `1001` | GID. | +| `podSecurityContext.fsGroup` | int | `1001` | FS group. | +| `podSecurityContext.fsGroupChangePolicy` | string | `OnRootMismatch` | FS group change policy. | +| `nodeSelector` / `tolerations` / `affinity` / `topologySpreadConstraints` | — | — | Standard scheduling knobs. | +| `extraVolumes` / `extraVolumeMounts` | list | `[]` | Append custom volumes/mounts. | +| `priorityClassName` | string | `""` | Optional Pod priority class. | + +### Deployment + +| Key | Type | Default | Description | +| --- | --- | --- | --- | +| `deployment.port` | int | `4321` | Container port & named `http` port. | +| `deployment.strategy.type` | string | `Recreate` | Update strategy (`Recreate` or `RollingUpdate`). | +| `deployment.strategy.rollingUpdate.maxUnavailable/maxSurge` | string/int | — | Used when `type=RollingUpdate`. | +| `deployment.env` | list | `[]` | Extra environment variables. | +| `deployment.resources` | map | `{}` | CPU/memory requests & limits. | +| `deployment.terminationGracePeriodSeconds` | int | `60` | Grace period. | +| `livenessProbe.*` | — | enabled, `/api/health` | Liveness probe (HTTP GET to `/api/health`). | +| `readinessProbe.*` | — | enabled, `/api/health` | Readiness probe. | +| `startupProbe.*` | — | enabled, `/api/health` | Startup probe. | + +> The Pod mounts a volume at `/app/data` (PVC or `emptyDir` depending on `persistence.enabled`). + +### Service + +| Key | Type | Default | Description | +| --- | --- | --- | --- | +| `service.type` | string | `ClusterIP` | Service type. | +| `service.port` | int | `4321` | Service port. | +| `service.clusterIP` | string | `None` | ClusterIP (only when `type=ClusterIP`). | +| `service.externalTrafficPolicy` | string | `""` | External traffic policy (LB). | +| `service.loadBalancerIP` | string | `""` | LoadBalancer IP. | +| `service.loadBalancerClass` | string | `""` | LoadBalancer class. | +| `service.annotations` / `service.labels` | map | `{}` | Extra metadata. | + +### Ingress (optional) + +| Key | Type | Default | Description | +| --- | --- | --- | --- | +| `ingress.enabled` | bool | `false` | Enable Ingress. | +| `ingress.className` | string | `""` | IngressClass name. | +| `ingress.hosts[0].host` | string | `mirror.example.com` | Hostname. | +| `ingress.tls` | list | `[]` | TLS blocks (secret name etc.). | +| `ingress.annotations` | map | `{}` | Controller-specific annotations. | + +> The Ingress exposes `/` to the chart’s Service. + +### Gateway API HTTPRoutes (optional) + +| Key | Type | Default | Description | +| --- | --- | --- | --- | +| `route.enabled` | bool | `false` | Enable Gateway API HTTPRoutes. | +| `route.forceHTTPS` | bool | `true` | If true, create an HTTP route that redirects to HTTPS (301). | +| `route.domain` | list | `["mirror.example.com"]` | Hostnames. | +| `route.gateway` | string | `""` | Gateway name. | +| `route.gatewayNamespace` | string | `""` | Gateway namespace. | +| `route.http.gatewaySection` | string | `""` | SectionName for HTTP listener. | +| `route.https.gatewaySection` | string | `""` | SectionName for HTTPS listener. | +| `route.http.filters` / `route.https.filters` | list | `[]` | Additional filters. (Defaults add HSTS header on HTTPS.) | + +### Persistence + +| Key | Type | Default | Description | +| --- | --- | --- | --- | +| `persistence.enabled` | bool | `true` | Enable persistent storage. | +| `persistence.create` | bool | `true` | Create a PVC from the chart. | +| `persistence.claimName` | string | `gitea-mirror-storage` | PVC name. | +| `persistence.storageClass` | string | `""` | StorageClass to use. | +| `persistence.accessModes` | list | `["ReadWriteOnce"]` | Access modes. | +| `persistence.size` | string | `1Gi` | Requested size. | +| `persistence.volumeName` | string | `""` | Bind to existing PV by name (optional). | +| `persistence.annotations` | map | `{}` | PVC annotations. | + +### ServiceAccount (optional) + +| Key | Type | Default | Description | +| --- | --- | --- | --- | +| `serviceAccount.create` | bool | `false` | Create a ServiceAccount. | +| `serviceAccount.name` | string | `""` | SA name (defaults to release fullname). | +| `serviceAccount.automountServiceAccountToken` | bool | `false` | Automount token. | +| `serviceAccount.annotations` / `labels` | map | `{}` | Extra metadata. | + +--- + +## Application configuration (`gitea-mirror.*`) + +These values populate a **ConfigMap** (non-secret) and a **Secret** (for tokens and sensitive fields). Environment variables from both are consumed by the container. + +### Core + +| Key | Default | Mapped env | +| --- | --- | --- | +| `gitea-mirror.nodeEnv` | `production` | `NODE_ENV` | +| `gitea-mirror.core.databaseUrl` | `file:data/gitea-mirror.db` | `DATABASE_URL` | +| `gitea-mirror.core.encryptionSecret` | `""` | `ENCRYPTION_SECRET` (Secret) | +| `gitea-mirror.core.betterAuthSecret` | `""` | `BETTER_AUTH_SECRET` | +| `gitea-mirror.core.betterAuthUrl` | `http://localhost:4321` | `BETTER_AUTH_URL` | +| `gitea-mirror.core.betterAuthTrustedOrigins` | `http://localhost:4321` | `BETTER_AUTH_TRUSTED_ORIGINS` | + +### GitHub + +| Key | Default | Mapped env | +| --- | --- | --- | +| `gitea-mirror.github.username` | `""` | `GITHUB_USERNAME` | +| `gitea-mirror.github.token` | `""` | `GITHUB_TOKEN` (Secret) | +| `gitea-mirror.github.type` | `personal` | `GITHUB_TYPE` | +| `gitea-mirror.github.privateRepositories` | `true` | `PRIVATE_REPOSITORIES` | +| `gitea-mirror.github.skipForks` | `false` | `SKIP_FORKS` | +| `gitea-mirror.github.starredCodeOnly` | `false` | `SKIP_STARRED_ISSUES` | +| `gitea-mirror.github.mirrorStarred` | `false` | `MIRROR_STARRED` | + +### Gitea + +| Key | Default | Mapped env | +| --- | --- | --- | +| `gitea-mirror.gitea.url` | `""` | `GITEA_URL` | +| `gitea-mirror.gitea.token` | `""` | `GITEA_TOKEN` (Secret) | +| `gitea-mirror.gitea.username` | `""` | `GITEA_USERNAME` | +| `gitea-mirror.gitea.organization` | `github-mirrors` | `GITEA_ORGANIZATION` | +| `gitea-mirror.gitea.visibility` | `public` | `GITEA_ORG_VISIBILITY` | + +### Mirror options + +| Key | Default | Mapped env | +| --- | --- | --- | +| `gitea-mirror.mirror.releases` | `true` | `MIRROR_RELEASES` | +| `gitea-mirror.mirror.wiki` | `true` | `MIRROR_WIKI` | +| `gitea-mirror.mirror.metadata` | `true` | `MIRROR_METADATA` | +| `gitea-mirror.mirror.issues` | `true` | `MIRROR_ISSUES` | +| `gitea-mirror.mirror.pullRequests` | `true` | `MIRROR_PULL_REQUESTS` | +| `gitea-mirror.mirror.starred` | _(see note above)_ | `MIRROR_STARRED` | + +### Automation & cleanup + +| Key | Default | Mapped env | +| --- | --- | --- | +| `gitea-mirror.automation.schedule_enabled` | `true` | `SCHEDULE_ENABLED` | +| `gitea-mirror.automation.schedule_interval` | `3600` | `SCHEDULE_INTERVAL` (seconds) | +| `gitea-mirror.cleanup.enabled` | `true` | `CLEANUP_ENABLED` | +| `gitea-mirror.cleanup.retentionDays` | `30` | `CLEANUP_RETENTION_DAYS` | + +> **Secrets:** If you set `gitea-mirror.existingSecret` (name of an existing Secret), the chart will **not** create its own Secret and will reference yours instead. Otherwise it creates a Secret with `GITHUB_TOKEN`, `GITEA_TOKEN`, `ENCRYPTION_SECRET`. + +--- + +## Exposing the service + +### Using Ingress + +```yaml +ingress: + enabled: true + className: "nginx" + hosts: + - host: mirror.example.com + tls: + - secretName: mirror-tls + hosts: + - mirror.example.com +``` + +This creates an Ingress routing `/` to the service on port `4321`. + +### Using Gateway API (HTTPRoute) + +```yaml +route: + enabled: true + domain: ["mirror.example.com"] + gateway: "my-gateway" + gatewayNamespace: "gateway-system" + http: + gatewaySection: "http" + https: + gatewaySection: "https" + # Example extra filter already included by default: add HSTS header +``` + +If `forceHTTPS: true`, the chart emits an HTTP route that redirects to HTTPS with 301. An HTTPS route is always created when `route.enabled=true`. + +--- + +## Persistence & data + +By default, the chart provisions a PVC named `gitea-mirror-storage` with `1Gi` and mounts it at `/app/data`. To use an existing PV or tune storage, adjust `persistence.*` in `values.yaml`. If you disable persistence, an `emptyDir` will be used instead. + +--- + +## Environment & health endpoints + +The container listens on `PORT` (defaults to `deployment.port` = `4321`) and exposes `GET /api/health` for liveness/readiness/startup probes. + +--- + +## Examples + +### Minimal (tokens via chart-managed Secret) + +```yaml +gitea-mirror: + github: + username: "gitea-mirror" + token: "" + gitea: + url: "https://gitea.company.tld" + token: "" +``` + +### Bring your own Secret + +```yaml +gitea-mirror: + existingSecret: "gitea-mirror-secrets" + github: + username: "gitea-mirror" + gitea: + url: "https://gitea.company.tld" +``` + +Where `gitea-mirror-secrets` contains keys `GITHUB_TOKEN`, `GITEA_TOKEN`, `ENCRYPTION_SECRET`. + +--- + +## Development + +Lint the chart: + +```bash +yamllint -c helm/gitea-mirror/.yamllint helm/gitea-mirror +``` + +Tweak probes, resources, and scheduling as needed; see `values.yaml`. + +--- + +## License + +This chart is part of the `RayLabsHQ/gitea-mirror` repository. See the repository for licensing details. diff --git a/Divers/gitea-mirror/helm/gitea-mirror/templates/_helpers.tpl b/Divers/gitea-mirror/helm/gitea-mirror/templates/_helpers.tpl new file mode 100644 index 0000000..e01bff9 --- /dev/null +++ b/Divers/gitea-mirror/helm/gitea-mirror/templates/_helpers.tpl @@ -0,0 +1,59 @@ +{{/* +Expand the name of the chart. +*/}} + +{{- define "gitea-mirror.name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}} +{{- end -}} + +{{/* +Create a default fully qualified app name. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +If release name contains chart name it will be used as a full name. +*/}} +{{- define "gitea-mirror.fullname" -}} +{{- if .Values.fullnameOverride -}} +{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}} +{{- else -}} +{{- $name := default .Chart.Name .Values.nameOverride -}} +{{- if contains $name .Release.Name -}} +{{- .Release.Name | trunc 63 | trimSuffix "-" -}} +{{- else -}} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}} +{{- end -}} +{{- end -}} +{{- end -}} + +{{/* +Create chart name and version as used by the chart label. +*/}} +{{- define "gitea-mirror.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}} +{{- end -}} + +{{/* +Common labels +*/}} +{{- define "gitea-mirror.labels" -}} +helm.sh/chart: {{ include "gitea-mirror.chart" . }} +app: {{ include "gitea-mirror.name" . }} +{{ include "gitea-mirror.selectorLabels" . }} +app.kubernetes.io/version: {{ .Values.image.tag | default .Chart.AppVersion | quote }} +version: {{ .Values.image.tag | default .Chart.AppVersion | quote }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +{{- end -}} + +{{/* +Selector labels +*/}} +{{- define "gitea-mirror.selectorLabels" -}} +app.kubernetes.io/name: {{ include "gitea-mirror.name" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +{{- end -}} + +{{/* +ServiceAccount name +*/}} +{{- define "gitea-mirror.serviceAccountName" -}} +{{ .Values.serviceAccount.name | default (include "gitea-mirror.fullname" .) }} +{{- end -}} diff --git a/Divers/gitea-mirror/helm/gitea-mirror/templates/configmap.yaml b/Divers/gitea-mirror/helm/gitea-mirror/templates/configmap.yaml new file mode 100644 index 0000000..9b149e0 --- /dev/null +++ b/Divers/gitea-mirror/helm/gitea-mirror/templates/configmap.yaml @@ -0,0 +1,38 @@ +{{- $gm := index .Values "gitea-mirror" -}} +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ include "gitea-mirror.fullname" . }} + labels: + {{- include "gitea-mirror.labels" . | nindent 4 }} +data: + NODE_ENV: {{ $gm.nodeEnv | quote }} + # Core configuration + DATABASE_URL: {{ $gm.core.databaseUrl | quote }} + BETTER_AUTH_SECRET: {{ $gm.core.betterAuthSecret | quote }} + BETTER_AUTH_URL: {{ $gm.core.betterAuthUrl | quote }} + BETTER_AUTH_TRUSTED_ORIGINS: {{ $gm.core.betterAuthTrustedOrigins | quote }} + # GitHub Config + GITHUB_USERNAME: {{ $gm.github.username | quote }} + GITHUB_TYPE: {{ $gm.github.type | quote }} + PRIVATE_REPOSITORIES: {{ $gm.github.privateRepositories | quote }} + MIRROR_STARRED: {{ $gm.github.mirrorStarred | quote }} + SKIP_FORKS: {{ $gm.github.skipForks | quote }} + SKIP_STARRED_ISSUES: {{ $gm.github.starredCodeOnly | quote }} + # Gitea Config + GITEA_URL: {{ $gm.gitea.url | quote }} + GITEA_USERNAME: {{ $gm.gitea.username | quote }} + GITEA_ORGANIZATION: {{ $gm.gitea.organization | quote }} + GITEA_ORG_VISIBILITY: {{ $gm.gitea.visibility | quote }} + # Mirror Options + MIRROR_RELEASES: {{ $gm.mirror.releases | quote }} + MIRROR_WIKI: {{ $gm.mirror.wiki | quote }} + MIRROR_METADATA: {{ $gm.mirror.metadata | quote }} + MIRROR_ISSUES: {{ $gm.mirror.issues | quote }} + MIRROR_PULL_REQUESTS: {{ $gm.mirror.pullRequests | quote }} + # Automation + SCHEDULE_ENABLED: {{ $gm.automation.schedule_enabled| quote }} + SCHEDULE_INTERVAL: {{ $gm.automation.schedule_interval | quote }} + # Cleanup + CLEANUP_ENABLED: {{ $gm.cleanup.enabled | quote }} + CLEANUP_RETENTION_DAYS: {{ $gm.cleanup.retentionDays | quote }} diff --git a/Divers/gitea-mirror/helm/gitea-mirror/templates/deployment.yaml b/Divers/gitea-mirror/helm/gitea-mirror/templates/deployment.yaml new file mode 100644 index 0000000..2209a71 --- /dev/null +++ b/Divers/gitea-mirror/helm/gitea-mirror/templates/deployment.yaml @@ -0,0 +1,143 @@ +{{- $gm := index .Values "gitea-mirror" -}} +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ include "gitea-mirror.fullname" . }} + {{- with .Values.deployment.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} + labels: + {{- include "gitea-mirror.labels" . | nindent 4 }} + {{- if .Values.deployment.labels }} + {{- toYaml .Values.deployment.labels | nindent 4 }} + {{- end }} +spec: + replicas: 1 + strategy: + type: {{ .Values.deployment.strategy.type }} + {{- if eq .Values.deployment.strategy.type "RollingUpdate" }} + rollingUpdate: + maxUnavailable: {{ .Values.deployment.strategy.rollingUpdate.maxUnavailable }} + maxSurge: {{ .Values.deployment.strategy.rollingUpdate.maxSurge }} + {{- end }} + selector: + matchLabels: + {{- include "gitea-mirror.selectorLabels" . | nindent 6 }} + template: + metadata: + labels: + {{- include "gitea-mirror.labels" . | nindent 8 }} + {{- if .Values.deployment.labels }} + {{- toYaml .Values.deployment.labels | nindent 8 }} + {{- end }} + spec: + {{- if (or .Values.serviceAccount.create .Values.serviceAccount.name) }} + serviceAccountName: {{ include "gitea-mirror.serviceAccountName" . }} + {{- end }} + {{- if .Values.priorityClassName }} + priorityClassName: "{{ .Values.priorityClassName }}" + {{- end }} + {{- with .Values.imagePullSecrets }} + imagePullSecrets: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.podSecurityContext }} + securityContext: + {{- toYaml . | nindent 8 }} + {{- end }} + terminationGracePeriodSeconds: {{ .Values.deployment.terminationGracePeriodSeconds }} + containers: + - name: gitea-mirror + image: {{ .Values.image.registry }}/{{ .Values.image.repository }}:{{ .Values.image.tag | default (printf "v%s" .Chart.AppVersion) }} + imagePullPolicy: {{ .Values.image.pullPolicy }} + envFrom: + - configMapRef: + name: {{ include "gitea-mirror.fullname" . }} + {{- if $gm.existingSecret }} + - secretRef: + name: {{ $gm.existingSecret }} + {{- else }} + - secretRef: + name: {{ include "gitea-mirror.fullname" . }} + {{- end }} + env: + - name: PORT + value: "{{ .Values.deployment.port }}" + {{- if .Values.deployment.env }} + {{- toYaml .Values.deployment.env | nindent 12 }} + {{- end }} + ports: + - name: http + containerPort: {{ .Values.deployment.port }} + {{- if .Values.livenessProbe.enabled }} + livenessProbe: + httpGet: + path: /api/health + port: "http" + initialDelaySeconds: {{ .Values.livenessProbe.initialDelaySeconds }} + periodSeconds: {{ .Values.livenessProbe.periodSeconds }} + timeoutSeconds: {{ .Values.livenessProbe.timeoutSeconds }} + failureThreshold: {{ .Values.livenessProbe.failureThreshold }} + successThreshold: {{ .Values.livenessProbe.successThreshold }} + {{- end }} + {{- if .Values.readinessProbe.enabled }} + readinessProbe: + httpGet: + path: /api/health + port: "http" + initialDelaySeconds: {{ .Values.readinessProbe.initialDelaySeconds }} + periodSeconds: {{ .Values.readinessProbe.periodSeconds }} + timeoutSeconds: {{ .Values.readinessProbe.timeoutSeconds }} + failureThreshold: {{ .Values.readinessProbe.failureThreshold }} + successThreshold: {{ .Values.readinessProbe.successThreshold }} + {{- end }} + {{- if .Values.startupProbe.enabled }} + startupProbe: + httpGet: + path: /api/health + port: "http" + initialDelaySeconds: {{ .Values.startupProbe.initialDelaySeconds }} + periodSeconds: {{ .Values.startupProbe.periodSeconds }} + timeoutSeconds: {{ .Values.startupProbe.timeoutSeconds }} + failureThreshold: {{ .Values.startupProbe.failureThreshold }} + successThreshold: {{ .Values.startupProbe.successThreshold }} + {{- end }} + volumeMounts: + - name: data + mountPath: /app/data + {{- if .Values.extraVolumeMounts }} + {{- toYaml .Values.extraVolumeMounts | nindent 12 }} + {{- end }} + {{- with .Values.deployment.resources }} + resources: + {{- toYaml .Values.deployment.resources | nindent 12 }} + {{- end }} + {{- with .Values.affinity }} + affinity: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.nodeSelector }} + nodeSelector: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.tolerations }} + tolerations: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.topologySpreadConstraints }} + topologySpreadConstraints: + {{- toYaml . | nindent 8 }} + {{- end }} + volumes: + {{- if .Values.persistence.enabled }} + - name: data + persistentVolumeClaim: + claimName: {{ .Values.persistence.claimName }} + {{- else if not .Values.persistence.enabled }} + - name: data + emptyDir: {} + {{- end }} + {{- if .Values.extraVolumes }} + {{- toYaml .Values.extraVolumes | nindent 8 }} + {{- end }} diff --git a/Divers/gitea-mirror/helm/gitea-mirror/templates/httproute.yaml b/Divers/gitea-mirror/helm/gitea-mirror/templates/httproute.yaml new file mode 100644 index 0000000..d9a8bc1 --- /dev/null +++ b/Divers/gitea-mirror/helm/gitea-mirror/templates/httproute.yaml @@ -0,0 +1,77 @@ +{{- if .Values.route.enabled }} +{{- if .Values.route.forceHTTPS }} +--- +apiVersion: gateway.networking.k8s.io/v1 +kind: HTTPRoute +metadata: + name: {{ include "gitea-mirror.fullname" . }}-http + labels: + {{- include "gitea-mirror.labels" . | nindent 4 }} +spec: + parentRefs: + - name: {{ .Values.route.gateway }} + sectionName: {{ .Values.route.http.gatewaySection }} + namespace: {{ .Values.route.gatewayNamespace }} + hostnames: {{ .Values.route.domain }} + rules: + - filters: + - type: RequestRedirect + requestRedirect: + scheme: https + statusCode: 301 + {{- with .Values.route.http.filters }} + {{ toYaml . | nindent 4 }} + {{- end }} +{{- else }} +--- +apiVersion: gateway.networking.k8s.io/v1 +kind: HTTPRoute +metadata: + name: {{ include "gitea-mirror.fullname" . }}-http + labels: + {{- include "gitea-mirror.labels" . | nindent 4 }} +spec: + parentRefs: + - name: {{ .Values.route.gateway }} + sectionName: {{ .Values.route.http.gatewaySection }} + namespace: {{ .Values.route.gatewayNamespace }} + hostnames: {{ .Values.route.domain }} + rules: + - matches: + - path: + type: PathPrefix + value: / + backendRefs: + - name: {{ include "gitea-mirror.fullname" . }} + port: {{ .Values.service.port }} + {{- with .Values.route.http.filters }} + filters: + {{- toYaml . | nindent 8 }} + {{- end }} +{{- end }} +--- +apiVersion: gateway.networking.k8s.io/v1 +kind: HTTPRoute +metadata: + name: {{ include "gitea-mirror.fullname" . }}-https + labels: + {{- include "gitea-mirror.labels" . | nindent 4 }} +spec: + parentRefs: + - name: {{ .Values.route.gateway }} + sectionName: {{ .Values.route.https.gatewaySection }} + namespace: {{ .Values.route.gatewayNamespace }} + hostnames: {{ .Values.route.domain }} + rules: + - matches: + - path: + type: PathPrefix + value: / + backendRefs: + - name: {{ include "gitea-mirror.fullname" . }} + port: {{ .Values.service.port }} + {{- with .Values.route.https.filters }} + filters: + {{- toYaml . | nindent 8 }} + {{- end }} +{{- end }} diff --git a/Divers/gitea-mirror/helm/gitea-mirror/templates/ingress.yaml b/Divers/gitea-mirror/helm/gitea-mirror/templates/ingress.yaml new file mode 100644 index 0000000..6458752 --- /dev/null +++ b/Divers/gitea-mirror/helm/gitea-mirror/templates/ingress.yaml @@ -0,0 +1,40 @@ +{{- if .Values.ingress.enabled -}} +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: {{ include "gitea-mirror.fullname" . }} + labels: + {{- include "gitea-mirror.labels" . | nindent 4 }} + {{- with .Values.ingress.annotations }} + annotations: + {{- . | toYaml | nindent 4 }} + {{- end }} +spec: +{{- if .Values.ingress.className }} + ingressClassName: {{ .Values.ingress.className }} +{{- end }} +{{- if .Values.ingress.tls }} + tls: + {{- range .Values.ingress.tls }} + - hosts: + {{- range .hosts }} + - {{ tpl . $ | quote }} + {{- end }} + secretName: {{ .secretName }} + {{- end }} +{{- end }} + rules: + {{- range .Values.ingress.hosts }} + - host: {{ tpl .host $ | quote }} + http: + paths: + - path: {{ .path | default "/" }} + pathType: {{ .pathType | default "Prefix" }} + backend: + service: + name: {{ include "gitea-mirror.fullname" $ }} + port: + number: {{ $.Values.service.port }} + {{- end }} +{{- end }} + diff --git a/Divers/gitea-mirror/helm/gitea-mirror/templates/pvc.yaml b/Divers/gitea-mirror/helm/gitea-mirror/templates/pvc.yaml new file mode 100644 index 0000000..81f9ce9 --- /dev/null +++ b/Divers/gitea-mirror/helm/gitea-mirror/templates/pvc.yaml @@ -0,0 +1,26 @@ +{{- if and .Values.persistence.enabled .Values.persistence.create }} +{{- $gm := index .Values "gitea-mirror" -}} +kind: PersistentVolumeClaim +apiVersion: v1 +metadata: + name: {{ .Values.persistence.claimName }} + labels: + {{- include "gitea-mirror.labels" . | nindent 4 }} + {{- with .Values.persistence.annotations }} + annotations: + {{ . | toYaml | indent 4}} + {{- end }} +spec: + accessModes: + {{- toYaml .Values.persistence.accessModes | nindent 4 }} + {{- with .Values.persistence.storageClass }} + storageClassName: {{ . }} + {{- end }} + volumeMode: Filesystem + {{- with .Values.persistence.volumeName }} + volumeName: {{ . }} + {{- end }} + resources: + requests: + storage: {{ .Values.persistence.size }} +{{- end }} diff --git a/Divers/gitea-mirror/helm/gitea-mirror/templates/secret.yaml b/Divers/gitea-mirror/helm/gitea-mirror/templates/secret.yaml new file mode 100644 index 0000000..0cae2e2 --- /dev/null +++ b/Divers/gitea-mirror/helm/gitea-mirror/templates/secret.yaml @@ -0,0 +1,14 @@ +{{- $gm := index .Values "gitea-mirror" -}} +{{- if (empty $gm.existingSecret) }} +apiVersion: v1 +kind: Secret +metadata: + name: {{ include "gitea-mirror.fullname" . }} + labels: + {{- include "gitea-mirror.labels" . | nindent 4 }} +type: Opaque +stringData: + GITHUB_TOKEN: {{ $gm.github.token | quote }} + GITEA_TOKEN: {{ $gm.gitea.token | quote }} + ENCRYPTION_SECRET: {{ $gm.core.encryptionSecret | quote }} +{{- end }} diff --git a/Divers/gitea-mirror/helm/gitea-mirror/templates/service.yaml b/Divers/gitea-mirror/helm/gitea-mirror/templates/service.yaml new file mode 100644 index 0000000..00c4e73 --- /dev/null +++ b/Divers/gitea-mirror/helm/gitea-mirror/templates/service.yaml @@ -0,0 +1,34 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{ include "gitea-mirror.fullname" . }} + labels: + {{- include "gitea-mirror.labels" . | nindent 4 }} + {{- if .Values.service.labels }} + {{- toYaml .Values.service.labels | nindent 4 }} + {{- end }} + annotations: + {{- toYaml .Values.service.annotations | nindent 4 }} +spec: + type: {{ .Values.service.type }} + {{- if eq .Values.service.type "LoadBalancer" }} + {{- if .Values.service.loadBalancerClass }} + loadBalancerClass: {{ .Values.service.loadBalancerClass }} + {{- end }} + {{- if and .Values.service.loadBalancerIP }} + loadBalancerIP: {{ .Values.service.loadBalancerIP }} + {{- end }} + {{- end }} + {{- if .Values.service.externalTrafficPolicy }} + externalTrafficPolicy: {{ .Values.service.externalTrafficPolicy }} + {{- end }} + {{- if and .Values.service.clusterIP (eq .Values.service.type "ClusterIP") }} + clusterIP: {{ .Values.service.clusterIP }} + {{- end }} + ports: + - name: http + port: {{ .Values.service.port }} + protocol: TCP + targetPort: http + selector: + {{- include "gitea-mirror.selectorLabels" . | nindent 4 }} diff --git a/Divers/gitea-mirror/helm/gitea-mirror/templates/serviceaccount.yaml b/Divers/gitea-mirror/helm/gitea-mirror/templates/serviceaccount.yaml new file mode 100644 index 0000000..34249c9 --- /dev/null +++ b/Divers/gitea-mirror/helm/gitea-mirror/templates/serviceaccount.yaml @@ -0,0 +1,17 @@ +{{- if .Values.serviceAccount.create }} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ include "gitea-mirror.serviceAccountName" . }} + labels: + {{- include "gitea-mirror.labels" . | nindent 4 }} + {{- with .Values.serviceAccount.labels }} + {{- . | toYaml | nindent 4 }} + {{- end }} + {{- with .Values.serviceAccount.annotations }} + annotations: + {{- . | toYaml | nindent 4 }} + {{- end }} +automountServiceAccountToken: {{ .Values.serviceAccount.automountServiceAccountToken }} +{{- end }} + diff --git a/Divers/gitea-mirror/helm/gitea-mirror/values.yaml b/Divers/gitea-mirror/helm/gitea-mirror/values.yaml new file mode 100644 index 0000000..deb702f --- /dev/null +++ b/Divers/gitea-mirror/helm/gitea-mirror/values.yaml @@ -0,0 +1,151 @@ +image: + registry: ghcr.io + repository: raylabshq/gitea-mirror + # Leave blank to use the Appversion tag + tag: "" + pullPolicy: IfNotPresent + +imagePullSecrets: [] +podSecurityContext: + runAsUser: 1001 + runAsGroup: 1001 + fsGroup: 1001 + fsGroupChangePolicy: OnRootMismatch + +ingress: + enabled: false + className: "" + annotations: {} + hosts: + - host: mirror.example.com + paths: + - path: / + pathType: Prefix + tls: [] + # - secretName: chart-example-tls + # hosts: + # - mirror.example.com + +route: + enabled: false + forceHTTPS: true + domain: ["mirror.example.com"] + gateway: "" + gatewayNamespace: "" + http: + gatewaySection: "" + filters: [] + https: + gatewaySection: "" + filters: + - type: ResponseHeaderModifier + responseHeaderModifier: + add: + - name: Strict-Transport-Security + value: "max-age=31536000; includeSubDomains; preload" + +service: + type: ClusterIP + port: 4321 + clusterIP: None + annotations: {} + externalTrafficPolicy: + labels: {} + loadBalancerIP: + loadBalancerClass: + +deployment: + port: 4321 + strategy: + type: Recreate + env: [] + terminationGracePeriodSeconds: 60 + labels: {} + annotations: {} + resources: {} + +livenessProbe: + enabled: true + initialDelaySeconds: 60 + periodSeconds: 10 + timeoutSeconds: 5 + failureThreshold: 6 + successThreshold: 1 +readinessProbe: + enabled: true + initialDelaySeconds: 60 + periodSeconds: 10 + timeoutSeconds: 5 + failureThreshold: 6 + successThreshold: 1 +startupProbe: + enabled: true + initialDelaySeconds: 60 + periodSeconds: 10 + timeoutSeconds: 5 + failureThreshold: 6 + successThreshold: 1 + +persistence: + enabled: true + create: true + claimName: gitea-mirror-storage + storageClass: "" + accessModes: + - ReadWriteOnce + size: 1Gi + +affinity: {} +nodeSelector: {} +tolerations: [] +topologySpreadConstraints: [] +extraVolumes: [] +extraVolumeMounts: [] + +serviceAccount: + create: false + name: "" + annotations: {} + labels: {} + automountServiceAccountToken: false + +gitea-mirror: + existingSecret: "" + nodeEnv: production + core: + databaseUrl: file:data/gitea-mirror.db + encryptionSecret: "" + betterAuthSecret: "" + betterAuthUrl: "http://localhost:4321" + betterAuthTrustedOrigins: "http://localhost:4321" + + github: + username: "" + token: "" + type: personal + privateRepositories: true + mirrorStarred: false + skipForks: false + starredCodeOnly: false + + gitea: + url: "" + token: "" + username: "" + organization: "github-mirrors" + visibility: "public" + + mirror: + releases: true + wiki: true + metadata: true + issues: true + pullRequests: true + + automation: + schedule_enabled: true + schedule_interval: 3600 + + cleanup: + enabled: true + retentionDays: 30 diff --git a/Divers/gitea-mirror/package.json b/Divers/gitea-mirror/package.json new file mode 100644 index 0000000..dc68391 --- /dev/null +++ b/Divers/gitea-mirror/package.json @@ -0,0 +1,115 @@ +{ + "name": "gitea-mirror", + "type": "module", + "version": "3.9.2", + "engines": { + "bun": ">=1.2.9" + }, + "scripts": { + "setup": "bun install && bun run manage-db init", + "dev": "bunx --bun astro dev", + "dev:clean": "bun run cleanup-db && bun run manage-db init && bunx --bun astro dev", + "build": "bunx --bun astro build", + "cleanup-db": "rm -f gitea-mirror.db data/gitea-mirror.db", + "manage-db": "bun scripts/manage-db.ts", + "init-db": "bun scripts/manage-db.ts init", + "check-db": "bun scripts/manage-db.ts check", + "fix-db": "bun scripts/manage-db.ts fix", + "reset-users": "bun scripts/manage-db.ts reset-users", + "db:generate": "bun drizzle-kit generate", + "db:migrate": "bun drizzle-kit migrate", + "db:push": "bun drizzle-kit push", + "db:pull": "bun drizzle-kit pull", + "db:check": "bun drizzle-kit check", + "db:studio": "bun drizzle-kit studio", + "startup-recovery": "bun scripts/startup-recovery.ts", + "startup-recovery-force": "bun scripts/startup-recovery.ts --force", + "startup-env-config": "bun scripts/startup-env-config.ts", + "test-recovery": "bun scripts/test-recovery.ts", + "test-recovery-cleanup": "bun scripts/test-recovery.ts --cleanup", + "test-shutdown": "bun scripts/test-graceful-shutdown.ts", + "test-shutdown-cleanup": "bun scripts/test-graceful-shutdown.ts --cleanup", + "preview": "bunx --bun astro preview", + "start": "bun dist/server/entry.mjs", + "start:fresh": "bun run cleanup-db && bun run manage-db init && bun dist/server/entry.mjs", + "test": "bun test", + "test:watch": "bun test --watch", + "test:coverage": "bun test --coverage", + "astro": "bunx --bun astro" + }, + "overrides": { + "@esbuild-kit/esm-loader": "npm:tsx@^4.21.0", + "devalue": "^5.5.0" + }, + "dependencies": { + "@astrojs/check": "^0.9.6", + "@astrojs/mdx": "4.3.12", + "@astrojs/node": "9.5.1", + "@astrojs/react": "^4.4.2", + "@better-auth/sso": "1.4.5", + "@octokit/plugin-throttling": "^11.0.3", + "@octokit/rest": "^22.0.1", + "@radix-ui/react-accordion": "^1.2.12", + "@radix-ui/react-avatar": "^1.1.11", + "@radix-ui/react-checkbox": "^1.3.3", + "@radix-ui/react-collapsible": "^1.1.12", + "@radix-ui/react-dialog": "^1.1.15", + "@radix-ui/react-dropdown-menu": "^2.1.16", + "@radix-ui/react-hover-card": "^1.1.15", + "@radix-ui/react-label": "^2.1.8", + "@radix-ui/react-popover": "^1.1.15", + "@radix-ui/react-progress": "^1.1.8", + "@radix-ui/react-radio-group": "^1.3.8", + "@radix-ui/react-scroll-area": "^1.2.10", + "@radix-ui/react-select": "^2.2.6", + "@radix-ui/react-separator": "^1.1.8", + "@radix-ui/react-slot": "^1.2.4", + "@radix-ui/react-switch": "^1.2.6", + "@radix-ui/react-tabs": "^1.1.13", + "@radix-ui/react-tooltip": "^1.2.8", + "@tailwindcss/vite": "^4.1.17", + "@tanstack/react-virtual": "^3.13.12", + "@types/canvas-confetti": "^1.9.0", + "@types/react": "^19.2.7", + "@types/react-dom": "^19.2.3", + "astro": "^5.16.4", + "bcryptjs": "^3.0.3", + "buffer": "^6.0.3", + "better-auth": "1.4.5", + "canvas-confetti": "^1.9.4", + "class-variance-authority": "^0.7.1", + "clsx": "^2.1.1", + "cmdk": "^1.1.1", + "dotenv": "^17.2.3", + "drizzle-orm": "^0.44.7", + "fuse.js": "^7.1.0", + "jsonwebtoken": "^9.0.3", + "lucide-react": "^0.555.0", + "next-themes": "^0.4.6", + "react": "^19.2.1", + "react-dom": "^19.2.1", + "react-icons": "^5.5.0", + "sonner": "^2.0.7", + "tailwind-merge": "^3.4.0", + "tailwindcss": "^4.1.17", + "tw-animate-css": "^1.4.0", + "typescript": "^5.9.3", + "uuid": "^13.0.0", + "vaul": "^1.1.2", + "zod": "^4.1.13" + }, + "devDependencies": { + "@testing-library/jest-dom": "^6.9.1", + "@testing-library/react": "^16.3.0", + "@types/bcryptjs": "^3.0.0", + "@types/bun": "^1.3.3", + "@types/jsonwebtoken": "^9.0.10", + "@types/uuid": "^10.0.0", + "@vitejs/plugin-react": "^5.1.1", + "drizzle-kit": "^0.31.7", + "jsdom": "^27.2.0", + "tsx": "^4.21.0", + "vitest": "^4.0.15" + }, + "packageManager": "bun@1.3.3" +} diff --git a/Divers/gitea-mirror/public/apple-touch-icon.png b/Divers/gitea-mirror/public/apple-touch-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..c1335a4330d35aa37bc3980bbc25c37b34d21f48 GIT binary patch literal 31669 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 literal 0 HcmV?d00001 diff --git a/Divers/gitea-mirror/public/favicon.ico b/Divers/gitea-mirror/public/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..039619799170aa7b442a68b5de68761ca9185a50 GIT binary patch literal 15086 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 rHZ4dL^Q8Fg?G(nPcyBAkXj`cTdtI1wiu< \ No newline at end of file diff --git a/Divers/gitea-mirror/public/logo.png b/Divers/gitea-mirror/public/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..7d037428872922a77566203e52916a4b15991356 GIT binary patch literal 24463 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:4321 +``` + +### Additional Information: +* **Script Source**: [Community Scripts for Proxmox VE](https://github.com/community-scripts/ProxmoxVE) +* **Documentation**: [Gitea Mirror Script Documentation](https://community-scripts.github.io/ProxmoxVE/scripts?id=gitea-mirror) +* **Support**: [Community Scripts Discord](https://discord.gg/fiXVvSHnBU) + +--- + +## 2. Local testing (LXD on a workstation, works offline) + +### Prerequisites + +* `lxd` installed (`sudo apt install lxd`; `lxd init --auto`) +* Your repo cloned locally – e.g. `~/Development/gitea-mirror` +* Bun ZIP downloaded once: + `https://github.com/oven-sh/bun/releases/latest/download/bun-linux-x64.zip` + +### Offline installer script + +```bash +git clone https://github.com/RayLabsHQ/gitea-mirror.git # if not already +curl -fsSL https://raw.githubusercontent.com/raylabshq/gitea-mirror:/main/scripts/gitea-mirror-lxc-local.sh -o gitea-mirror-lxc-local.sh +chmod +x gitea-mirror-lxc-local.sh + +sudo LOCAL_REPO_DIR=~/Development/gitea-mirror \ + ./gitea-mirror-lxc-local.sh +``` + +What it does: + +* Launches privileged LXC `gitea-test` (`lxc launch ubuntu:22.04 ...`) +* Pushes **Bun ZIP** + tarred **local repo** into `/opt` +* Unpacks, builds, initializes DB +* Symlinks both `bun` and `bunx` → `/usr/local/bin` +* Creates a root systemd unit and starts it + +Access from host: + +``` +http://$(lxc exec gitea-test -- hostname -I | awk '{print $1}'):4321 +``` + +(Optional) forward to host localhost: + +```bash +sudo lxc config device add gitea-test mirror proxy \ + listen=tcp:0.0.0.0:4321 connect=tcp:127.0.0.1:4321 +``` + +--- + +## Health-check endpoint + +Gitea Mirror includes a built-in health check endpoint at `/api/health` that provides: + +- System status and uptime +- Database connectivity check +- Memory usage statistics +- Environment information + +You can use this endpoint for monitoring your deployment: + +```bash +# Basic check (returns 200 OK if healthy) +curl -I http://:4321/api/health + +# Detailed health information (JSON) +curl http://:4321/api/health +``` + +--- + +## Troubleshooting + +| Check | Command | +| -------------- | ----------------------------------------------------- | +| Service status | `systemctl status gitea-mirror` | +| Live logs | `journalctl -u gitea-mirror -f` | +| Verify Bun | `bun --version && bunx --version` | +| DB perms | `chown -R root:root /opt/gitea-mirror/data` (Proxmox) | + +--- + +## Connecting LXC and Docker Containers + +If you need your LXC container to communicate with Docker containers: + +1. On your host machine, create a bridge network: + ```bash + docker network create gitea-network + ``` + +2. Find the bridge interface created by Docker: + ```bash + ip a | grep docker + # Look for something like docker0 or br-xxxxxxxx + ``` + +3. In Proxmox, edit the LXC container's network configuration to use this bridge. diff --git a/Divers/gitea-mirror/scripts/README.md b/Divers/gitea-mirror/scripts/README.md new file mode 100644 index 0000000..acd9869 --- /dev/null +++ b/Divers/gitea-mirror/scripts/README.md @@ -0,0 +1,140 @@ +# Scripts Directory + +This folder contains utility scripts for database management, event management, Docker builds, and LXC container deployment. + +## Database Management + +### Database Management Tool (manage-db.ts) + +This is a consolidated database management tool that handles all database-related operations. It combines the functionality of the previous separate scripts into a single, more intelligent script that can check, fix, and initialize the database as needed. + +#### Features + +- **Check Mode**: Validates the existence and integrity of the database +- **Init Mode**: Creates the database only if it doesn't already exist +- **Fix Mode**: Corrects database file location issues +- **Reset Users Mode**: Removes all users and their data +- **Auto Mode**: Automatically checks, fixes, and initializes the database if needed + +#### Running the Database Management Tool + +You can execute the database management tool using your package manager with various commands: + +```bash +# Checks database status (default action if no command is specified) +bun run manage-db + +# Check database status +bun run check-db + +# Initialize the database (only if it doesn't exist) +bun run init-db + +# Fix database location issues +bun run fix-db + +# Automatic check, fix, and initialize if needed +bun run db-auto + +# Reset all users (for testing signup flow) +bun run reset-users + +# Remove database files completely +bun run cleanup-db + +# Complete setup (install dependencies and initialize database) +bun run setup + +# Start development server with a fresh database +bun run dev:clean + +# Start production server with a fresh database +bun run start:fresh +``` + +#### Database File Location + +The database file should be located in the `./data/gitea-mirror.db` directory. If the file is found in the root directory, the fix mode will move it to the correct location. + +## Event Management + +The following scripts help manage events in the SQLite database: + +> **Note**: For a more user-friendly approach, you can use the cleanup button in the Activity Log page of the web interface to delete all activities with a single click. + + + + + +### Remove Duplicate Events (remove-duplicate-events.ts) + +Specifically removes duplicate events based on deduplication keys without affecting old events. + +```bash +# Remove duplicate events for all users +bun scripts/remove-duplicate-events.ts + +# Remove duplicate events for a specific user +bun scripts/remove-duplicate-events.ts +``` + + + +### Fix Interrupted Jobs (fix-interrupted-jobs.ts) + +Fixes interrupted jobs that might be preventing cleanup by marking them as failed. + +```bash +# Fix all interrupted jobs +bun scripts/fix-interrupted-jobs.ts + +# Fix interrupted jobs for a specific user +bun scripts/fix-interrupted-jobs.ts +``` + +Use this script if you're having trouble cleaning up activities due to "interrupted" jobs that won't delete. + +### Startup Recovery (startup-recovery.ts) + +Runs job recovery during application startup to handle any interrupted jobs from previous runs. + +```bash +# Run startup recovery (normal mode) +bun scripts/startup-recovery.ts + +# Force recovery even if recent attempt was made +bun scripts/startup-recovery.ts --force + +# Set custom timeout (default: 30000ms) +bun scripts/startup-recovery.ts --timeout=60000 + +# Using npm scripts +bun run startup-recovery +bun run startup-recovery-force +``` + +This script is automatically run by the Docker entrypoint during container startup. It ensures that any jobs interrupted by container restarts or application crashes are properly recovered or marked as failed. + +## Deployment Scripts + +### Docker Deployment + +- **build-docker.sh**: Builds the Docker image for the application +- **docker-diagnostics.sh**: Provides diagnostic information for Docker deployments + +### LXC Container Deployment + +Two deployment options are available for LXC containers: + +1. **Proxmox VE (online)**: Using the community-maintained script by Tobias ([CrazyWolf13](https://github.com/CrazyWolf13)) + - Author: Tobias ([CrazyWolf13](https://github.com/CrazyWolf13)) + - Available at: [community-scripts/ProxmoxVED](https://github.com/community-scripts/ProxmoxVED/blob/main/install/gitea-mirror-install.sh) + - Pulls everything from GitHub + - Creates a privileged container with the application + - Sets up systemd service + +2. **gitea-mirror-lxc-local.sh**: For offline/LAN-only deployment on a developer laptop + - Pushes your local checkout + Bun ZIP to the container + - Useful for testing without internet access + +For detailed instructions on LXC deployment, see [README-lxc.md](./README-lxc.md). diff --git a/Divers/gitea-mirror/scripts/build-docker.sh b/Divers/gitea-mirror/scripts/build-docker.sh new file mode 100755 index 0000000..26e0061 --- /dev/null +++ b/Divers/gitea-mirror/scripts/build-docker.sh @@ -0,0 +1,104 @@ +#!/bin/bash +# Build and push the Gitea Mirror docker image for multiple architectures + +set -e # Exit on any error + +# Load environment variables if .env file exists +if [ -f .env ]; then + echo "Loading environment variables from .env" + export $(grep -v '^#' .env | xargs) +fi + +# Set default values if not set in environment +DOCKER_REGISTRY=${DOCKER_REGISTRY:-ghcr.io} +DOCKER_IMAGE=${DOCKER_IMAGE:-gitea-mirror} +DOCKER_TAG=${DOCKER_TAG:-latest} + +FULL_IMAGE_NAME="$DOCKER_REGISTRY/$DOCKER_IMAGE:$DOCKER_TAG" +echo "Building image: $FULL_IMAGE_NAME" + +# Parse command line arguments +LOAD=false +PUSH=false + +while [[ $# -gt 0 ]]; do + key="$1" + case $key in + --load) + LOAD=true + shift + ;; + --push) + PUSH=true + shift + ;; + *) + echo "Unknown option: $key" + echo "Usage: $0 [--load] [--push]" + echo " --load Load the image into Docker after build" + echo " --push Push the image to the registry after build" + exit 1 + ;; + esac +done + +# Build command construction +BUILD_CMD="docker buildx build --platform linux/amd64,linux/arm64 -t $FULL_IMAGE_NAME" + +# Add load or push flag if specified +if [ "$LOAD" = true ]; then + BUILD_CMD="$BUILD_CMD --load" +fi + +if [ "$PUSH" = true ]; then + BUILD_CMD="$BUILD_CMD --push" +fi + +# Add context directory +BUILD_CMD="$BUILD_CMD ." + +# Execute the build command +echo "Executing: $BUILD_CMD" + +# Function to execute with retries +execute_with_retry() { + local cmd="$1" + local max_attempts=${2:-3} + local attempt=1 + local delay=5 + + while [ $attempt -le $max_attempts ]; do + echo "Attempt $attempt of $max_attempts..." + if eval "$cmd"; then + echo "Command succeeded!" + return 0 + else + echo "Command failed, waiting $delay seconds before retry..." + sleep $delay + attempt=$((attempt + 1)) + delay=$((delay * 2)) # Exponential backoff + fi + done + + echo "All attempts failed!" + return 1 +} + +# Execute with retry +execute_with_retry "$BUILD_CMD" +BUILD_RESULT=$? + +if [ $BUILD_RESULT -eq 0 ]; then + echo "✅ Build successful!" +else + echo "❌ Build failed after multiple attempts." + exit 1 +fi + +# Print help message if neither --load nor --push was specified +if [ "$LOAD" = false ] && [ "$PUSH" = false ]; then + echo + echo "NOTE: Image was built but not loaded or pushed. To use this image, run again with:" + echo " $0 --load # to load into local Docker" + echo " $0 --push # to push to registry $DOCKER_REGISTRY" +fi diff --git a/Divers/gitea-mirror/scripts/cleanup-duplicate-repos.ts b/Divers/gitea-mirror/scripts/cleanup-duplicate-repos.ts new file mode 100644 index 0000000..471bd0c --- /dev/null +++ b/Divers/gitea-mirror/scripts/cleanup-duplicate-repos.ts @@ -0,0 +1,129 @@ +#!/usr/bin/env bun + +/** + * Script to find and clean up duplicate repositories in the database + * Keeps the most recent entry and removes older duplicates + * + * Usage: bun scripts/cleanup-duplicate-repos.ts [--dry-run] [--repo-name=] + */ + +import { db, repositories, mirrorJobs } from "@/lib/db"; +import { eq, and, desc } from "drizzle-orm"; + +const isDryRun = process.argv.includes("--dry-run"); +const specificRepo = process.argv.find(arg => arg.startsWith("--repo-name="))?.split("=")[1]; + +async function findDuplicateRepositories() { + console.log("🔍 Finding duplicate repositories"); + console.log("=" .repeat(40)); + + if (isDryRun) { + console.log("🔍 DRY RUN MODE - No changes will be made"); + console.log(""); + } + + if (specificRepo) { + console.log(`🎯 Targeting specific repository: ${specificRepo}`); + console.log(""); + } + + try { + // Find all repositories, grouped by name and fullName + let allRepos = await db.select().from(repositories); + + if (specificRepo) { + allRepos = allRepos.filter(repo => repo.name === specificRepo); + } + + // Group repositories by name and fullName + const repoGroups = new Map(); + + for (const repo of allRepos) { + const key = `${repo.name}|${repo.fullName}`; + if (!repoGroups.has(key)) { + repoGroups.set(key, []); + } + repoGroups.get(key)!.push(repo); + } + + // Find groups with duplicates + const duplicateGroups = Array.from(repoGroups.entries()) + .filter(([_, repos]) => repos.length > 1); + + if (duplicateGroups.length === 0) { + console.log("✅ No duplicate repositories found"); + return; + } + + console.log(`📋 Found ${duplicateGroups.length} sets of duplicate repositories:`); + console.log(""); + + let totalDuplicates = 0; + let totalRemoved = 0; + + for (const [key, repos] of duplicateGroups) { + const [name, fullName] = key.split("|"); + console.log(`🔄 Processing duplicates for: ${name} (${fullName})`); + console.log(` Found ${repos.length} entries:`); + + // Sort by updatedAt descending to keep the most recent + repos.sort((a, b) => new Date(b.updatedAt).getTime() - new Date(a.updatedAt).getTime()); + + const keepRepo = repos[0]; + const removeRepos = repos.slice(1); + + console.log(` ✅ Keeping: ID ${keepRepo.id} (Status: ${keepRepo.status}, Updated: ${new Date(keepRepo.updatedAt).toISOString()})`); + + for (const repo of removeRepos) { + console.log(` ❌ Removing: ID ${repo.id} (Status: ${repo.status}, Updated: ${new Date(repo.updatedAt).toISOString()})`); + + if (!isDryRun) { + try { + // First, delete related mirror jobs + await db + .delete(mirrorJobs) + .where(eq(mirrorJobs.repositoryId, repo.id!)); + + // Then delete the repository + await db + .delete(repositories) + .where(eq(repositories.id, repo.id!)); + + console.log(` 🗑️ Deleted repository and related mirror jobs`); + totalRemoved++; + } catch (error) { + console.log(` ❌ Error deleting repository: ${error instanceof Error ? error.message : String(error)}`); + } + } else { + console.log(` 🗑️ Would delete repository and related mirror jobs`); + totalRemoved++; + } + } + + totalDuplicates += removeRepos.length; + console.log(""); + } + + console.log("📊 Cleanup Summary:"); + console.log(` Duplicate sets found: ${duplicateGroups.length}`); + console.log(` Total duplicates: ${totalDuplicates}`); + console.log(` ${isDryRun ? 'Would remove' : 'Removed'}: ${totalRemoved}`); + + if (isDryRun && totalRemoved > 0) { + console.log(""); + console.log("💡 To apply these changes, run the script without --dry-run"); + } + + } catch (error) { + console.error("❌ Error during cleanup process:", error); + } +} + +// Run the cleanup +findDuplicateRepositories().then(() => { + console.log("Cleanup process complete."); + process.exit(0); +}).catch((error) => { + console.error("Fatal error:", error); + process.exit(1); +}); diff --git a/Divers/gitea-mirror/scripts/docker-diagnostics.sh b/Divers/gitea-mirror/scripts/docker-diagnostics.sh new file mode 100644 index 0000000..3972a91 --- /dev/null +++ b/Divers/gitea-mirror/scripts/docker-diagnostics.sh @@ -0,0 +1,125 @@ +#!/bin/bash +# Docker setup diagnostics tool for Gitea Mirror + +# ANSI color codes +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[0;33m' +BLUE='\033[0;34m' +NC='\033[0m' # No Color + +echo -e "${BLUE}=====================================================${NC}" +echo -e "${BLUE} Gitea Mirror Docker Setup Diagnostics ${NC}" +echo -e "${BLUE}=====================================================${NC}" + +# Check if Docker is installed and running +echo -e "\n${YELLOW}Checking Docker...${NC}" +if command -v docker &> /dev/null; then + echo -e "${GREEN}✓ Docker is installed${NC}" + if docker info &> /dev/null; then + echo -e "${GREEN}✓ Docker daemon is running${NC}" + + # Get Docker version + DOCKER_VERSION=$(docker version --format '{{.Server.Version}}') + echo -e "${GREEN}✓ Docker version: $DOCKER_VERSION${NC}" + else + echo -e "${RED}✗ Docker daemon is not running${NC}" + echo -e " Run: ${YELLOW}open -a Docker${NC}" + fi +else + echo -e "${RED}✗ Docker is not installed${NC}" + echo -e " Visit: ${BLUE}https://www.docker.com/products/docker-desktop${NC}" +fi + +# Check for Docker Compose +echo -e "\n${YELLOW}Checking Docker Compose...${NC}" +if docker compose version &> /dev/null; then + COMPOSE_VERSION=$(docker compose version --short) + echo -e "${GREEN}✓ Docker Compose is installed (v$COMPOSE_VERSION)${NC}" +elif command -v docker-compose &> /dev/null; then + COMPOSE_VERSION=$(docker-compose --version | awk '{print $3}' | sed 's/,//') + echo -e "${GREEN}✓ Docker Compose is installed (v$COMPOSE_VERSION)${NC}" + echo -e "${YELLOW}⚠ Using legacy docker-compose - consider upgrading${NC}" +else + echo -e "${RED}✗ Docker Compose is not installed${NC}" +fi + +# Check for Docker Buildx +echo -e "\n${YELLOW}Checking Docker Buildx...${NC}" +if docker buildx version &> /dev/null; then + BUILDX_VERSION=$(docker buildx version | head -n1 | awk '{print $2}') + echo -e "${GREEN}✓ Docker Buildx is installed (v$BUILDX_VERSION)${NC}" + + # List available builders + echo -e "\n${YELLOW}Available builders:${NC}" + docker buildx ls +else + echo -e "${RED}✗ Docker Buildx is not installed or not activated${NC}" +fi + +# Check for QEMU +echo -e "\n${YELLOW}Checking QEMU for multi-platform builds...${NC}" +if docker run --rm --privileged multiarch/qemu-user-static --reset -p yes &> /dev/null; then + echo -e "${GREEN}✓ QEMU is available for multi-architecture builds${NC}" +else + echo -e "${RED}✗ QEMU setup issue - multi-platform builds may fail${NC}" + echo -e " Run: ${YELLOW}docker run --rm --privileged multiarch/qemu-user-static --reset -p yes${NC}" +fi + +# Check Docker resources +echo -e "\n${YELLOW}Checking Docker resources...${NC}" +if [ "$(uname)" == "Darwin" ]; then + # macOS + if command -v osascript &> /dev/null; then + SYS_MEM=$(( $(sysctl -n hw.memsize) / 1024 / 1024 / 1024 )) + echo -e "System memory: ${GREEN}$SYS_MEM GB${NC}" + echo -e "NOTE: Check Docker Desktop settings to see allocated resources" + echo -e "Recommended: At least 4GB RAM and 2 CPUs for multi-platform builds" + fi +fi + +# Check environment file +echo -e "\n${YELLOW}Checking environment configuration...${NC}" +if [ -f .env ]; then + echo -e "${GREEN}✓ .env file exists${NC}" + + # Parse .env file safely + if [ -f .env ]; then + REGISTRY=$(grep DOCKER_REGISTRY .env | cut -d= -f2) + IMAGE=$(grep DOCKER_IMAGE .env | cut -d= -f2) + TAG=$(grep DOCKER_TAG .env | cut -d= -f2) + + echo -e "Docker image configuration:" + echo -e " Registry: ${BLUE}${REGISTRY:-"Not set (will use default)"}${NC}" + echo -e " Image: ${BLUE}${IMAGE:-"Not set (will use default)"}${NC}" + echo -e " Tag: ${BLUE}${TAG:-"Not set (will use default)"}${NC}" + fi +else + echo -e "${YELLOW}⚠ .env file not found${NC}" + echo -e " Run: ${YELLOW}cp .env.example .env${NC}" +fi + +# Conclusion and recommendations +echo -e "\n${BLUE}=====================================================${NC}" +echo -e "${BLUE} Recommendations ${NC}" +echo -e "${BLUE}=====================================================${NC}" + +echo -e "\n${YELLOW}For local development:${NC}" +echo -e "1. ${GREEN}bun run setup${NC} (initialize database and install dependencies)" +echo -e "2. ${GREEN}./scripts/build-docker.sh --load${NC} (build and load into Docker)" +echo -e "3. ${GREEN}docker-compose -f docker-compose.dev.yml up -d${NC} (start the development container)" + +echo -e "\n${YELLOW}For production deployment (using Docker Compose):${NC}" +echo -e "1. ${GREEN}bun run setup${NC} (if not already done, to ensure database schema is ready)" +echo -e "2. ${GREEN}docker-compose --profile production up -d${NC} (start the production container)" + +echo -e "\n${YELLOW}For CI/CD builds:${NC}" +echo -e "1. Use GitHub Actions workflow with retry mechanism" +echo -e "2. If build fails, try running with: ${GREEN}DOCKER_BUILDKIT=1${NC}" +echo -e "3. Consider breaking the build into multiple steps for better reliability" + +echo -e "\n${YELLOW}For troubleshooting:${NC}" +echo -e "1. Check container logs: ${GREEN}docker logs gitea-mirror-dev${NC} (for development) or ${GREEN}docker logs gitea-mirror${NC} (for production)" +echo -e "2. Check health status: ${GREEN}docker inspect --format='{{.State.Health.Status}}' gitea-mirror-dev${NC} (for development) or ${GREEN}docker inspect --format='{{.State.Health.Status}}' gitea-mirror${NC} (for production)" +echo -e "3. See full documentation: ${BLUE}.github/workflows/TROUBLESHOOTING.md${NC}" +echo -e "" diff --git a/Divers/gitea-mirror/scripts/fix-interrupted-jobs.ts b/Divers/gitea-mirror/scripts/fix-interrupted-jobs.ts new file mode 100644 index 0000000..21d7860 --- /dev/null +++ b/Divers/gitea-mirror/scripts/fix-interrupted-jobs.ts @@ -0,0 +1,69 @@ +#!/usr/bin/env bun +/** + * Script to fix interrupted jobs that might be preventing cleanup + * This script marks all in-progress jobs as failed to allow them to be deleted + * + * Usage: + * bun scripts/fix-interrupted-jobs.ts [userId] + * + * Where [userId] is optional - if provided, only fixes jobs for that user + */ + +import { db, mirrorJobs } from "../src/lib/db"; +import { eq, and } from "drizzle-orm"; + +// Parse command line arguments +const args = process.argv.slice(2); +const userId = args.length > 0 ? args[0] : undefined; + +async function fixInterruptedJobs() { + try { + console.log("Checking for interrupted jobs..."); + + // Build the query + const whereConditions = userId + ? and(eq(mirrorJobs.inProgress, true), eq(mirrorJobs.userId, userId)) + : eq(mirrorJobs.inProgress, true); + + if (userId) { + console.log(`Filtering for user: ${userId}`); + } + + // Find all in-progress jobs + const inProgressJobs = await db + .select() + .from(mirrorJobs) + .where(whereConditions); + + if (inProgressJobs.length === 0) { + console.log("No interrupted jobs found."); + return; + } + + console.log(`Found ${inProgressJobs.length} interrupted jobs:`); + inProgressJobs.forEach(job => { + console.log(`- Job ${job.id}: ${job.message} (${job.repositoryName || job.organizationName || 'Unknown'})`); + }); + + // Mark all in-progress jobs as failed + await db + .update(mirrorJobs) + .set({ + inProgress: false, + completedAt: new Date(), + status: "failed", + message: "Job interrupted and marked as failed by cleanup script" + }) + .where(whereConditions); + + console.log(`✅ Successfully marked ${inProgressJobs.length} interrupted jobs as failed.`); + console.log("These jobs can now be deleted through the normal cleanup process."); + + } catch (error) { + console.error("Error fixing interrupted jobs:", error); + process.exit(1); + } +} + +// Run the fix +fixInterruptedJobs(); diff --git a/Divers/gitea-mirror/scripts/generate-better-auth-schema.ts b/Divers/gitea-mirror/scripts/generate-better-auth-schema.ts new file mode 100644 index 0000000..5f5cef1 --- /dev/null +++ b/Divers/gitea-mirror/scripts/generate-better-auth-schema.ts @@ -0,0 +1,110 @@ +#!/usr/bin/env bun + +import { betterAuth } from "better-auth"; +import { drizzleAdapter } from "better-auth/adapters/drizzle"; +import Database from "bun:sqlite"; +import { drizzle } from "drizzle-orm/bun-sqlite"; +import { sqliteTable, text, integer } from "drizzle-orm/sqlite-core"; + +// Create a minimal auth instance just for schema generation +const tempDb = new Database(":memory:"); +const db = drizzle({ client: tempDb }); + +// Minimal auth config for schema generation +const auth = betterAuth({ + database: drizzleAdapter(db, { + provider: "sqlite", + usePlural: true, + }), + emailAndPassword: { + enabled: true, + }, +}); + +// Generate the schema +// Note: $internal API is not available in current better-auth version +// const schema = auth.$internal.schema; + +console.log("Better Auth Tables Required:"); +console.log("============================"); + +// Convert Better Auth schema to Drizzle schema definitions +const drizzleSchemaCode = `// Better Auth Tables - Generated Schema +import { sqliteTable, text, integer, index } from "drizzle-orm/sqlite-core"; +import { sql } from "drizzle-orm"; + +// Sessions table +export const sessions = sqliteTable("sessions", { + id: text("id").primaryKey(), + token: text("token").notNull().unique(), + userId: text("user_id").notNull().references(() => users.id), + expiresAt: integer("expires_at", { mode: "timestamp" }).notNull(), + ipAddress: text("ip_address"), + userAgent: text("user_agent"), + createdAt: integer("created_at", { mode: "timestamp" }) + .notNull() + .default(sql\`(unixepoch())\`), + updatedAt: integer("updated_at", { mode: "timestamp" }) + .notNull() + .default(sql\`(unixepoch())\`), +}, (table) => { + return { + userIdIdx: index("idx_sessions_user_id").on(table.userId), + tokenIdx: index("idx_sessions_token").on(table.token), + expiresAtIdx: index("idx_sessions_expires_at").on(table.expiresAt), + }; +}); + +// Accounts table (for OAuth providers and credentials) +export const accounts = sqliteTable("accounts", { + id: text("id").primaryKey(), + userId: text("user_id").notNull().references(() => users.id), + providerId: text("provider_id").notNull(), + providerUserId: text("provider_user_id").notNull(), + accessToken: text("access_token"), + refreshToken: text("refresh_token"), + expiresAt: integer("expires_at", { mode: "timestamp" }), + password: text("password"), // For credential provider + createdAt: integer("created_at", { mode: "timestamp" }) + .notNull() + .default(sql\`(unixepoch())\`), + updatedAt: integer("updated_at", { mode: "timestamp" }) + .notNull() + .default(sql\`(unixepoch())\`), +}, (table) => { + return { + userIdIdx: index("idx_accounts_user_id").on(table.userId), + providerIdx: index("idx_accounts_provider").on(table.providerId, table.providerUserId), + }; +}); + +// Verification tokens table +export const verificationTokens = sqliteTable("verification_tokens", { + id: text("id").primaryKey(), + token: text("token").notNull().unique(), + identifier: text("identifier").notNull(), + type: text("type").notNull(), // email, password-reset, etc + expiresAt: integer("expires_at", { mode: "timestamp" }).notNull(), + createdAt: integer("created_at", { mode: "timestamp" }) + .notNull() + .default(sql\`(unixepoch())\`), +}, (table) => { + return { + tokenIdx: index("idx_verification_tokens_token").on(table.token), + identifierIdx: index("idx_verification_tokens_identifier").on(table.identifier), + }; +}); + +// Future: SSO and OIDC Provider tables will be added when we enable those plugins +`; + +console.log(drizzleSchemaCode); + +// Output information about the schema +console.log("\n\nSummary:"); +console.log("========="); +console.log("- Better Auth will modify the existing 'users' table"); +console.log("- New tables required: sessions, accounts, verification_tokens"); +console.log("\nNote: The 'users' table needs emailVerified field added"); + +tempDb.close(); \ No newline at end of file diff --git a/Divers/gitea-mirror/scripts/gitea-app.ini b/Divers/gitea-mirror/scripts/gitea-app.ini new file mode 100644 index 0000000..d76d266 --- /dev/null +++ b/Divers/gitea-mirror/scripts/gitea-app.ini @@ -0,0 +1,68 @@ +APP_NAME = Gitea: Git with a cup of tea +RUN_MODE = prod + +[database] +DB_TYPE = sqlite3 +PATH = /data/gitea/gitea.db + +[repository] +ROOT = /data/git/repositories + +[server] +SSH_DOMAIN = localhost +DOMAIN = localhost +HTTP_PORT = 3000 +ROOT_URL = http://localhost:3001/ +DISABLE_SSH = false +SSH_PORT = 22 +LFS_START_SERVER = true +LFS_JWT_SECRET = _oaWNP5sCH5cSECa-K_HvCXeXhg-zN5H0cU5vVQAZr4 +OFFLINE_MODE = false + +[security] +INSTALL_LOCK = true +SECRET_KEY = vLu5OuX0EweZjDNxKPQ5V9DXXXX8cJiKpJyQylKkMVTrNdFAzlUlNdYLYfiCybu +INTERNAL_TOKEN = eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYmYiOjE3MjgzMTk1MDB9.Lz0cJB_DCLmJFh8FqDX0z9IUcxfY9jPftHEGvz_WeHo +PASSWORD_HASH_ALGO = pbkdf2 + +[service] +DISABLE_REGISTRATION = false +REQUIRE_SIGNIN_VIEW = false +REGISTER_EMAIL_CONFIRM = false +ENABLE_NOTIFY_MAIL = false +ALLOW_ONLY_EXTERNAL_REGISTRATION = false +ENABLE_CAPTCHA = false +DEFAULT_KEEP_EMAIL_PRIVATE = false +DEFAULT_ALLOW_CREATE_ORGANIZATION = true +DEFAULT_ENABLE_TIMETRACKING = true +NO_REPLY_ADDRESS = noreply.localhost + +[oauth2] +JWT_SECRET = gQXt_D8B-VJGCvFfJ9xEj5yp8mOd6fAza8TKc9rJJYw + +[lfs] +PATH = /data/git/lfs + +[mailer] +ENABLED = false + +[openid] +ENABLE_OPENID_SIGNIN = true +ENABLE_OPENID_SIGNUP = true + +[session] +PROVIDER = file + +[log] +MODE = console +LEVEL = Info +ROOT_PATH = /data/gitea/log + +[repository.pull-request] +DEFAULT_MERGE_STYLE = merge + +[repository.signing] +DEFAULT_TRUST_MODEL = committer + +[actions] +ENABLED = false \ No newline at end of file diff --git a/Divers/gitea-mirror/scripts/gitea-create-admin.sh b/Divers/gitea-mirror/scripts/gitea-create-admin.sh new file mode 100755 index 0000000..a8d91bc --- /dev/null +++ b/Divers/gitea-mirror/scripts/gitea-create-admin.sh @@ -0,0 +1,14 @@ +#!/bin/sh +# Create admin user for Gitea development instance + +echo "Creating admin user for Gitea..." +docker exec -u git gitea gitea admin user create \ + --username admin \ + --password admin123 \ + --email admin@localhost \ + --admin \ + --must-change-password=false + +echo "Admin user created!" +echo "Username: admin" +echo "Password: admin123" \ No newline at end of file diff --git a/Divers/gitea-mirror/scripts/gitea-dev-init.sh b/Divers/gitea-mirror/scripts/gitea-dev-init.sh new file mode 100755 index 0000000..a49abdc --- /dev/null +++ b/Divers/gitea-mirror/scripts/gitea-dev-init.sh @@ -0,0 +1,32 @@ +#!/bin/sh +# Initialize Gitea for development with pre-configured settings + +# Create necessary directories +mkdir -p /data/gitea/conf + +# Copy pre-configured app.ini if it doesn't exist +if [ ! -f /data/gitea/conf/app.ini ]; then + echo "Initializing Gitea with development configuration..." + cp /tmp/app.ini /data/gitea/conf/app.ini + chown 1000:1000 /data/gitea/conf/app.ini +fi + +# Start Gitea in background +/usr/bin/entrypoint "$@" & +GITEA_PID=$! + +# Wait for Gitea to be ready +echo "Waiting for Gitea to start..." +until wget --no-verbose --tries=1 --spider http://localhost:3000/ 2>/dev/null; do + sleep 2 +done + +# Create admin user if it doesn't exist +if [ ! -f /data/.admin_created ]; then + echo "Creating default admin user..." + su git -c "gitea admin user create --username admin --password admin123 --email admin@localhost --admin --must-change-password=false" && \ + touch /data/.admin_created +fi + +# Keep Gitea running in foreground +wait $GITEA_PID \ No newline at end of file diff --git a/Divers/gitea-mirror/scripts/gitea-mirror-lxc-local.sh b/Divers/gitea-mirror/scripts/gitea-mirror-lxc-local.sh new file mode 100755 index 0000000..2168c1b --- /dev/null +++ b/Divers/gitea-mirror/scripts/gitea-mirror-lxc-local.sh @@ -0,0 +1,86 @@ +#!/usr/bin/env bash +# gitea-mirror-lxc-local.sh (offline, local repo, verbose) + +set -euo pipefail + +CONTAINER="gitea-test" +IMAGE="ubuntu:22.04" +INSTALL_DIR="/opt/gitea-mirror" +PORT=4321 +BETTER_AUTH_SECRET="$(openssl rand -hex 32)" + +BUN_ZIP="/tmp/bun-linux-x64.zip" +BUN_URL="https://github.com/oven-sh/bun/releases/latest/download/bun-linux-x64.zip" + +LOCAL_REPO_DIR="${LOCAL_REPO_DIR:-./gitea-mirror}" +REPO_TAR="/tmp/gitea-mirror-local.tar.gz" + +need() { command -v "$1" >/dev/null || { echo "Missing $1"; exit 1; }; } +need curl; need lxc; need tar; need unzip + +# ── build host artefacts ──────────────────────────────────────────────── +[[ -d $LOCAL_REPO_DIR ]] || { echo "❌ LOCAL_REPO_DIR not found"; exit 1; } +[[ -f $LOCAL_REPO_DIR/package.json ]] || { echo "❌ package.json missing"; exit 1; } +[[ -f $BUN_ZIP ]] || curl -L --retry 5 --retry-delay 5 -o "$BUN_ZIP" "$BUN_URL" +tar -czf "$REPO_TAR" -C "$(dirname "$LOCAL_REPO_DIR")" "$(basename "$LOCAL_REPO_DIR")" + +# ── ensure container exists ───────────────────────────────────────────── +lxd init --auto >/dev/null 2>&1 || true +lxc info "$CONTAINER" >/dev/null 2>&1 || lxc launch "$IMAGE" "$CONTAINER" + +echo "🔧 installing base packages…" +sudo lxc exec "$CONTAINER" -- bash -c 'set -ex; apt update; apt install -y unzip tar openssl sqlite3' + +echo "⬆️ pushing artefacts…" +sudo lxc file push "$BUN_ZIP" "$CONTAINER/opt/" +sudo lxc file push "$REPO_TAR" "$CONTAINER/opt/" + +echo "📦 unpacking Bun + repo…" +sudo lxc exec "$CONTAINER" -- bash -ex <<'IN' +cd /opt +# Bun +unzip -oq bun-linux-x64.zip -d bun +BIN=$(find /opt/bun -type f -name bun -perm -111 | head -n1) +ln -sf "$BIN" /usr/local/bin/bun # bun +ln -sf "$BIN" /usr/local/bin/bunx # bunx shim +# Repo +rm -rf /opt/gitea-mirror +mkdir -p /opt/gitea-mirror +tar -xzf gitea-mirror-local.tar.gz --strip-components=1 -C /opt/gitea-mirror +IN + +echo "🏗️ bun install / build…" +sudo lxc exec "$CONTAINER" -- bash -ex <<'IN' +cd /opt/gitea-mirror +bun install +bun run build +bun run manage-db init +IN + +echo "📝 systemd unit…" +sudo lxc exec "$CONTAINER" -- bash -ex </etc/systemd/system/gitea-mirror.service < { + console.log(` ${index + 1}. ${new Date(job.timestamp).toISOString()}`); + console.log(` Status: ${job.status}`); + console.log(` Message: ${job.message}`); + if (job.details) { + console.log(` Details: ${job.details}`); + } + console.log(""); + }); + } + + // Get user configuration + console.log("⚙️ User Configuration:"); + console.log("-".repeat(20)); + + const config = await db + .select() + .from(configs) + .where(eq(configs.id, repo.configId)) + .limit(1); + + if (config.length > 0) { + const userConfig = config[0]; + console.log(` User ID: ${userConfig.userId}`); + console.log(` GitHub Owner: ${userConfig.githubConfig?.owner || "Not set"}`); + console.log(` Gitea URL: ${userConfig.giteaConfig?.url || "Not set"}`); + console.log(` Gitea Default Owner: ${userConfig.giteaConfig?.defaultOwner || "Not set"}`); + console.log(` Mirror Strategy: ${userConfig.githubConfig?.mirrorStrategy || "preserve"}`); + console.log(` Include Starred: ${userConfig.githubConfig?.includeStarred || false}`); + } + + // Check for any active jobs + console.log("\n🔄 Active Jobs:"); + console.log("-".repeat(15)); + + const activeJobs = await db + .select() + .from(mirrorJobs) + .where( + and( + eq(mirrorJobs.repositoryId, repo.id), + eq(mirrorJobs.inProgress, true) + ) + ); + + if (activeJobs.length === 0) { + console.log(" No active jobs found"); + } else { + activeJobs.forEach((job, index) => { + console.log(` ${index + 1}. Job ID: ${job.id}`); + console.log(` Type: ${job.jobType || "mirror"}`); + console.log(` Batch ID: ${job.batchId || "None"}`); + console.log(` Started: ${job.startedAt ? new Date(job.startedAt).toISOString() : "Unknown"}`); + console.log(` Last Checkpoint: ${job.lastCheckpoint ? new Date(job.lastCheckpoint).toISOString() : "None"}`); + console.log(` Progress: ${job.completedItems || 0}/${job.totalItems || 0}`); + console.log(""); + }); + } + + // Check if repository exists in Gitea + if (config.length > 0) { + const userConfig = config[0]; + console.log("\n🔗 Gitea Repository Check:"); + console.log("-".repeat(25)); + + try { + const giteaUrl = userConfig.giteaConfig?.url; + const giteaToken = userConfig.giteaConfig?.token; + const giteaUsername = userConfig.giteaConfig?.defaultOwner; + + if (giteaUrl && giteaToken && giteaUsername) { + const checkUrl = `${giteaUrl}/api/v1/repos/${giteaUsername}/${repo.name}`; + console.log(` Checking: ${checkUrl}`); + + const response = await fetch(checkUrl, { + headers: { + Authorization: `token ${giteaToken}`, + }, + }); + + console.log(` Response Status: ${response.status} ${response.statusText}`); + + if (response.ok) { + const repoData = await response.json(); + console.log(` ✅ Repository exists in Gitea`); + console.log(` Name: ${repoData.name}`); + console.log(` Full Name: ${repoData.full_name}`); + console.log(` Private: ${repoData.private}`); + console.log(` Mirror: ${repoData.mirror}`); + console.log(` Clone URL: ${repoData.clone_url}`); + console.log(` Created: ${new Date(repoData.created_at).toISOString()}`); + console.log(` Updated: ${new Date(repoData.updated_at).toISOString()}`); + if (repoData.mirror_updated) { + console.log(` Mirror Updated: ${new Date(repoData.mirror_updated).toISOString()}`); + } + } else { + console.log(` ❌ Repository not found in Gitea`); + const errorText = await response.text(); + console.log(` Error: ${errorText}`); + } + } else { + console.log(" ⚠️ Missing Gitea configuration"); + } + } catch (error) { + console.log(` ❌ Error checking Gitea: ${error instanceof Error ? error.message : String(error)}`); + } + } + + } catch (error) { + console.error("❌ Error investigating repository:", error); + } +} + +// Run the investigation +investigateRepository().then(() => { + console.log("Investigation complete."); + process.exit(0); +}).catch((error) => { + console.error("Fatal error:", error); + process.exit(1); +}); diff --git a/Divers/gitea-mirror/scripts/manage-db.ts b/Divers/gitea-mirror/scripts/manage-db.ts new file mode 100644 index 0000000..ae858de --- /dev/null +++ b/Divers/gitea-mirror/scripts/manage-db.ts @@ -0,0 +1,239 @@ +import fs from "fs"; +import path from "path"; +import { Database } from "bun:sqlite"; +import { drizzle } from "drizzle-orm/bun-sqlite"; +import { migrate } from "drizzle-orm/bun-sqlite/migrator"; +import { v4 as uuidv4 } from "uuid"; +import { users, configs, repositories, organizations, mirrorJobs, events } from "../src/lib/db/schema"; +import bcrypt from "bcryptjs"; +import { eq } from "drizzle-orm"; + +// Command line arguments +const args = process.argv.slice(2); +const command = args[0] || "check"; + +// Ensure data directory exists +const dataDir = path.join(process.cwd(), "data"); +if (!fs.existsSync(dataDir)) { + fs.mkdirSync(dataDir, { recursive: true }); +} + +// Database path - ensure we use absolute path +const dbPath = path.join(dataDir, "gitea-mirror.db"); + +/** + * Initialize database with migrations + */ +async function initDatabase() { + console.log("📦 Initializing database..."); + + // Create an empty database file if it doesn't exist + if (!fs.existsSync(dbPath)) { + fs.writeFileSync(dbPath, ""); + } + + // Create SQLite instance + const sqlite = new Database(dbPath); + const db = drizzle({ client: sqlite }); + + // Run migrations + console.log("🔄 Running migrations..."); + try { + migrate(db, { migrationsFolder: "./drizzle" }); + console.log("✅ Migrations completed successfully"); + } catch (error) { + console.error("❌ Error running migrations:", error); + throw error; + } + + sqlite.close(); + console.log("✅ Database initialized successfully"); +} + +/** + * Check database status + */ +async function checkDatabase() { + console.log("🔍 Checking database status..."); + + if (!fs.existsSync(dbPath)) { + console.log("❌ Database does not exist at:", dbPath); + console.log("💡 Run 'bun run init-db' to create the database"); + process.exit(1); + } + + const sqlite = new Database(dbPath); + const db = drizzle({ client: sqlite }); + + try { + // Check tables + const tables = sqlite.query( + "SELECT name FROM sqlite_master WHERE type='table' ORDER BY name" + ).all() as Array<{name: string}>; + + console.log("\n📊 Tables found:"); + for (const table of tables) { + const count = sqlite.query(`SELECT COUNT(*) as count FROM ${table.name}`).get() as {count: number}; + console.log(` - ${table.name}: ${count.count} records`); + } + + // Check migrations + const migrations = sqlite.query( + "SELECT * FROM __drizzle_migrations ORDER BY created_at DESC LIMIT 5" + ).all() as Array<{hash: string, created_at: number}>; + + if (migrations.length > 0) { + console.log("\n📋 Recent migrations:"); + for (const migration of migrations) { + const date = new Date(migration.created_at); + console.log(` - ${migration.hash} (${date.toLocaleString()})`); + } + } + + sqlite.close(); + console.log("\n✅ Database check complete"); + } catch (error) { + console.error("❌ Error checking database:", error); + sqlite.close(); + process.exit(1); + } +} + +/** + * Reset user accounts (development only) + */ +async function resetUsers() { + console.log("🗑️ Resetting all user accounts..."); + + if (!fs.existsSync(dbPath)) { + console.log("❌ Database does not exist"); + process.exit(1); + } + + const sqlite = new Database(dbPath); + const db = drizzle({ client: sqlite }); + + try { + // Delete all data in order of foreign key dependencies + await db.delete(events); + await db.delete(mirrorJobs); + await db.delete(repositories); + await db.delete(organizations); + await db.delete(configs); + await db.delete(users); + + console.log("✅ All user accounts and related data have been removed"); + + sqlite.close(); + } catch (error) { + console.error("❌ Error resetting users:", error); + sqlite.close(); + process.exit(1); + } +} + +/** + * Clean up database files + */ +async function cleanupDatabase() { + console.log("🧹 Cleaning up database files..."); + + const filesToRemove = [ + dbPath, + path.join(dataDir, "gitea-mirror-dev.db"), + path.join(process.cwd(), "gitea-mirror.db"), + path.join(process.cwd(), "gitea-mirror-dev.db"), + ]; + + for (const file of filesToRemove) { + if (fs.existsSync(file)) { + fs.unlinkSync(file); + console.log(` - Removed: ${file}`); + } + } + + console.log("✅ Database cleanup complete"); +} + +/** + * Fix database location issues + */ +async function fixDatabase() { + console.log("🔧 Fixing database location issues..."); + + // Legacy database paths + const rootDbFile = path.join(process.cwd(), "gitea-mirror.db"); + const rootDevDbFile = path.join(process.cwd(), "gitea-mirror-dev.db"); + const dataDevDbFile = path.join(dataDir, "gitea-mirror-dev.db"); + + // Check for databases in wrong locations + if (fs.existsSync(rootDbFile)) { + console.log("📁 Found database in root directory"); + if (!fs.existsSync(dbPath)) { + console.log(" → Moving to data directory..."); + fs.renameSync(rootDbFile, dbPath); + console.log("✅ Database moved successfully"); + } else { + console.log(" ⚠️ Database already exists in data directory"); + console.log(" → Keeping existing data directory database"); + fs.unlinkSync(rootDbFile); + console.log(" → Removed root directory database"); + } + } + + // Clean up dev databases + if (fs.existsSync(rootDevDbFile)) { + fs.unlinkSync(rootDevDbFile); + console.log(" → Removed root dev database"); + } + if (fs.existsSync(dataDevDbFile)) { + fs.unlinkSync(dataDevDbFile); + console.log(" → Removed data dev database"); + } + + console.log("✅ Database location fixed"); +} + +/** + * Auto mode - check and initialize if needed + */ +async function autoMode() { + if (!fs.existsSync(dbPath)) { + console.log("📦 Database not found, initializing..."); + await initDatabase(); + } else { + console.log("✅ Database already exists"); + await checkDatabase(); + } +} + +// Execute command +switch (command) { + case "init": + await initDatabase(); + break; + case "check": + await checkDatabase(); + break; + case "fix": + await fixDatabase(); + break; + case "reset-users": + await resetUsers(); + break; + case "cleanup": + await cleanupDatabase(); + break; + case "auto": + await autoMode(); + break; + default: + console.log("Available commands:"); + console.log(" init - Initialize database with migrations"); + console.log(" check - Check database status"); + console.log(" fix - Fix database location issues"); + console.log(" reset-users - Remove all users and related data"); + console.log(" cleanup - Remove all database files"); + console.log(" auto - Auto initialize if needed"); + process.exit(1); +} \ No newline at end of file diff --git a/Divers/gitea-mirror/scripts/remove-duplicate-events.ts b/Divers/gitea-mirror/scripts/remove-duplicate-events.ts new file mode 100644 index 0000000..d0333d0 --- /dev/null +++ b/Divers/gitea-mirror/scripts/remove-duplicate-events.ts @@ -0,0 +1,44 @@ +#!/usr/bin/env bun +/** + * Script to remove duplicate events from the database + * This script identifies and removes events with duplicate deduplication keys + * + * Usage: + * bun scripts/remove-duplicate-events.ts [userId] + * + * Where [userId] is optional - if provided, only removes duplicates for that user + */ + +import { removeDuplicateEvents } from "../src/lib/events"; + +// Parse command line arguments +const args = process.argv.slice(2); +const userId = args.length > 0 ? args[0] : undefined; + +async function runDuplicateRemoval() { + try { + if (userId) { + console.log(`Starting duplicate event removal for user: ${userId}...`); + } else { + console.log("Starting duplicate event removal for all users..."); + } + + // Call the removeDuplicateEvents function + const result = await removeDuplicateEvents(userId); + + console.log(`Duplicate removal summary:`); + console.log(`- Duplicate events removed: ${result.duplicatesRemoved}`); + + if (result.duplicatesRemoved > 0) { + console.log("Duplicate event removal completed successfully"); + } else { + console.log("No duplicate events found"); + } + } catch (error) { + console.error("Error running duplicate event removal:", error); + process.exit(1); + } +} + +// Run the duplicate removal +runDuplicateRemoval(); diff --git a/Divers/gitea-mirror/scripts/repair-mirrored-repos.ts b/Divers/gitea-mirror/scripts/repair-mirrored-repos.ts new file mode 100644 index 0000000..1a01b3f --- /dev/null +++ b/Divers/gitea-mirror/scripts/repair-mirrored-repos.ts @@ -0,0 +1,279 @@ +#!/usr/bin/env bun + +/** + * Script to repair repositories that exist in Gitea but have incorrect status in the database + * This fixes the issue where repositories show as "imported" but are actually mirrored in Gitea + * + * Usage: bun scripts/repair-mirrored-repos.ts [--dry-run] [--repo-name=] + */ + +import { db, repositories, configs } from "@/lib/db"; +import { eq, and, or } from "drizzle-orm"; +import { createMirrorJob } from "@/lib/helpers"; +import { repoStatusEnum } from "@/types/Repository"; + +const isDryRun = process.argv.includes("--dry-run"); +const specificRepo = process.argv.find(arg => arg.startsWith("--repo-name="))?.split("=")[1]; +const isStartupMode = process.argv.includes("--startup"); + +async function checkRepoInGitea(config: any, owner: string, repoName: string): Promise { + try { + if (!config.giteaConfig?.url || !config.giteaConfig?.token) { + return false; + } + + const response = await fetch( + `${config.giteaConfig.url}/api/v1/repos/${owner}/${repoName}`, + { + headers: { + Authorization: `token ${config.giteaConfig.token}`, + }, + } + ); + + return response.ok; + } catch (error) { + console.error(`Error checking repo ${owner}/${repoName} in Gitea:`, error); + return false; + } +} + +async function getRepoDetailsFromGitea(config: any, owner: string, repoName: string): Promise { + try { + if (!config.giteaConfig?.url || !config.giteaConfig?.token) { + return null; + } + + const response = await fetch( + `${config.giteaConfig.url}/api/v1/repos/${owner}/${repoName}`, + { + headers: { + Authorization: `token ${config.giteaConfig.token}`, + }, + } + ); + + if (response.ok) { + return await response.json(); + } + return null; + } catch (error) { + console.error(`Error getting repo details for ${owner}/${repoName}:`, error); + return null; + } +} + +async function repairMirroredRepositories() { + if (!isStartupMode) { + console.log("🔧 Repairing mirrored repositories database status"); + console.log("=" .repeat(60)); + + if (isDryRun) { + console.log("🔍 DRY RUN MODE - No changes will be made"); + console.log(""); + } + + if (specificRepo) { + console.log(`🎯 Targeting specific repository: ${specificRepo}`); + console.log(""); + } + } + + try { + // Find repositories that might need repair + const whereConditions = specificRepo + ? and( + or( + eq(repositories.status, "imported"), + eq(repositories.status, "failed") + ), + eq(repositories.name, specificRepo) + ) + : or( + eq(repositories.status, "imported"), + eq(repositories.status, "failed") + ); + + const repos = await db + .select() + .from(repositories) + .where(whereConditions); + + if (repos.length === 0) { + if (!isStartupMode) { + console.log("✅ No repositories found that need repair"); + } + return; + } + + if (!isStartupMode) { + console.log(`📋 Found ${repos.length} repositories to check:`); + console.log(""); + } + + let repairedCount = 0; + let skippedCount = 0; + let errorCount = 0; + + for (const repo of repos) { + if (!isStartupMode) { + console.log(`🔍 Checking repository: ${repo.name}`); + console.log(` Current status: ${repo.status}`); + console.log(` Mirrored location: ${repo.mirroredLocation || "Not set"}`); + } + + try { + // Get user configuration + const config = await db + .select() + .from(configs) + .where(eq(configs.id, repo.configId)) + .limit(1); + + if (config.length === 0) { + if (!isStartupMode) { + console.log(` ❌ No configuration found for repository`); + } + errorCount++; + continue; + } + + const userConfig = config[0]; + const giteaUsername = userConfig.giteaConfig?.defaultOwner; + + if (!giteaUsername) { + if (!isStartupMode) { + console.log(` ❌ No Gitea username in configuration`); + } + errorCount++; + continue; + } + + // Check if repository exists in Gitea (try both user and organization) + let existsInGitea = false; + let actualOwner = giteaUsername; + let giteaRepoDetails = null; + + // First check user location + existsInGitea = await checkRepoInGitea(userConfig, giteaUsername, repo.name); + if (existsInGitea) { + giteaRepoDetails = await getRepoDetailsFromGitea(userConfig, giteaUsername, repo.name); + } + + // If not found in user location and repo has organization, check organization + if (!existsInGitea && repo.organization) { + existsInGitea = await checkRepoInGitea(userConfig, repo.organization, repo.name); + if (existsInGitea) { + actualOwner = repo.organization; + giteaRepoDetails = await getRepoDetailsFromGitea(userConfig, repo.organization, repo.name); + } + } + + if (!existsInGitea) { + if (!isStartupMode) { + console.log(` ⏭️ Repository not found in Gitea - skipping`); + } + skippedCount++; + continue; + } + + if (!isStartupMode) { + console.log(` ✅ Repository found in Gitea at: ${actualOwner}/${repo.name}`); + + if (giteaRepoDetails) { + console.log(` 📊 Gitea details:`); + console.log(` Mirror: ${giteaRepoDetails.mirror}`); + console.log(` Created: ${new Date(giteaRepoDetails.created_at).toISOString()}`); + console.log(` Updated: ${new Date(giteaRepoDetails.updated_at).toISOString()}`); + if (giteaRepoDetails.mirror_updated) { + console.log(` Mirror Updated: ${new Date(giteaRepoDetails.mirror_updated).toISOString()}`); + } + } + } else if (repairedCount === 0) { + // In startup mode, only log the first repair to indicate activity + console.log(`Repairing repository status inconsistencies...`); + } + + if (!isDryRun) { + // Update repository status in database + const mirrorUpdated = giteaRepoDetails?.mirror_updated + ? new Date(giteaRepoDetails.mirror_updated) + : new Date(); + + await db + .update(repositories) + .set({ + status: repoStatusEnum.parse("mirrored"), + updatedAt: new Date(), + lastMirrored: mirrorUpdated, + errorMessage: null, + mirroredLocation: `${actualOwner}/${repo.name}`, + }) + .where(eq(repositories.id, repo.id!)); + + // Create a mirror job log entry + await createMirrorJob({ + userId: userConfig.userId || "", + repositoryId: repo.id, + repositoryName: repo.name, + message: `Repository status repaired - found existing mirror in Gitea`, + details: `Repository ${repo.name} was found to already exist in Gitea at ${actualOwner}/${repo.name} and database status was updated from ${repo.status} to mirrored.`, + status: "mirrored", + }); + + if (!isStartupMode) { + console.log(` 🔧 Repaired: Updated status to 'mirrored'`); + } + } else { + if (!isStartupMode) { + console.log(` 🔧 Would repair: Update status from '${repo.status}' to 'mirrored'`); + } + } + + repairedCount++; + + } catch (error) { + if (!isStartupMode) { + console.log(` ❌ Error processing repository: ${error instanceof Error ? error.message : String(error)}`); + } + errorCount++; + } + + if (!isStartupMode) { + console.log(""); + } + } + + if (isStartupMode) { + // In startup mode, only log if there were repairs or errors + if (repairedCount > 0) { + console.log(`Repaired ${repairedCount} repository status inconsistencies`); + } + if (errorCount > 0) { + console.log(`Warning: ${errorCount} repositories had errors during repair`); + } + } else { + console.log("📊 Repair Summary:"); + console.log(` Repaired: ${repairedCount}`); + console.log(` Skipped: ${skippedCount}`); + console.log(` Errors: ${errorCount}`); + + if (isDryRun && repairedCount > 0) { + console.log(""); + console.log("💡 To apply these changes, run the script without --dry-run"); + } + } + + } catch (error) { + console.error("❌ Error during repair process:", error); + } +} + +// Run the repair +repairMirroredRepositories().then(() => { + console.log("Repair process complete."); + process.exit(0); +}).catch((error) => { + console.error("Fatal error:", error); + process.exit(1); +}); diff --git a/Divers/gitea-mirror/scripts/run-migration.ts b/Divers/gitea-mirror/scripts/run-migration.ts new file mode 100644 index 0000000..48a7c04 --- /dev/null +++ b/Divers/gitea-mirror/scripts/run-migration.ts @@ -0,0 +1,31 @@ +import { Database } from "bun:sqlite"; +import { readFileSync } from "fs"; +import path from "path"; + +const dbPath = path.join(process.cwd(), "data/gitea-mirror.db"); +const db = new Database(dbPath); + +// Read the migration file +const migrationPath = path.join(process.cwd(), "drizzle/0001_polite_exodus.sql"); +const migration = readFileSync(migrationPath, "utf-8"); + +// Split by statement-breakpoint and execute each statement +const statements = migration.split("--> statement-breakpoint").map(s => s.trim()).filter(s => s); + +try { + db.run("BEGIN TRANSACTION"); + + for (const statement of statements) { + console.log(`Executing: ${statement.substring(0, 50)}...`); + db.run(statement); + } + + db.run("COMMIT"); + console.log("Migration completed successfully!"); +} catch (error) { + db.run("ROLLBACK"); + console.error("Migration failed:", error); + process.exit(1); +} finally { + db.close(); +} \ No newline at end of file diff --git a/Divers/gitea-mirror/scripts/setup-authentik-test.sh b/Divers/gitea-mirror/scripts/setup-authentik-test.sh new file mode 100755 index 0000000..0aec993 --- /dev/null +++ b/Divers/gitea-mirror/scripts/setup-authentik-test.sh @@ -0,0 +1,184 @@ +#!/bin/bash + +# Setup script for testing Authentik SSO with Gitea Mirror +# This script helps configure Authentik for testing SSO integration + +set -e + +echo "======================================" +echo "Authentik SSO Test Environment Setup" +echo "======================================" +echo "" + +# Colors for output +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +NC='\033[0m' # No Color + +# Check if docker and docker-compose are installed +if ! command -v docker &> /dev/null; then + echo -e "${RED}Docker is not installed. Please install Docker first.${NC}" + exit 1 +fi + +if ! command -v docker-compose &> /dev/null && ! docker compose version &> /dev/null; then + echo -e "${RED}Docker Compose is not installed. Please install Docker Compose first.${NC}" + exit 1 +fi + +# Function to generate random secret +generate_secret() { + openssl rand -base64 32 | tr -d '\n' | tr -d '=' | tr -d '/' | tr -d '+' +} + +# Function to wait for service +wait_for_service() { + local service=$1 + local port=$2 + local max_attempts=30 + local attempt=1 + + echo -n "Waiting for $service to be ready" + while ! nc -z localhost $port 2>/dev/null; do + if [ $attempt -eq $max_attempts ]; then + echo -e "\n${RED}Timeout waiting for $service${NC}" + return 1 + fi + echo -n "." + sleep 2 + ((attempt++)) + done + echo -e " ${GREEN}Ready!${NC}" + return 0 +} + +# Parse command line arguments +ACTION=${1:-start} + +case $ACTION in + start) + echo "Starting Authentik test environment..." + echo "" + + # Check if .env.authentik exists, if not create it + if [ ! -f .env.authentik ]; then + echo "Creating .env.authentik with secure defaults..." + cat > .env.authentik << EOF +# Authentik Configuration +AUTHENTIK_SECRET_KEY=$(generate_secret) +AUTHENTIK_DB_PASSWORD=$(generate_secret) +AUTHENTIK_BOOTSTRAP_PASSWORD=admin-password +AUTHENTIK_BOOTSTRAP_EMAIL=admin@example.com + +# Gitea Mirror Configuration +BETTER_AUTH_SECRET=$(generate_secret) +BETTER_AUTH_URL=http://localhost:4321 +BETTER_AUTH_TRUSTED_ORIGINS=http://localhost:4321,http://localhost:9000 + +# URLs for testing +AUTHENTIK_URL=http://localhost:9000 +GITEA_MIRROR_URL=http://localhost:4321 +EOF + echo -e "${GREEN}Created .env.authentik with secure secrets${NC}" + echo "" + fi + + # Load environment variables + source .env.authentik + + # Start Authentik services + echo "Starting Authentik services..." + docker-compose -f docker-compose.authentik.yml --env-file .env.authentik up -d + + # Wait for Authentik to be ready + echo "" + wait_for_service "Authentik" 9000 + + # Wait a bit more for initialization + echo "Waiting for Authentik to initialize..." + sleep 10 + + echo "" + echo -e "${GREEN}✓ Authentik is running!${NC}" + echo "" + echo "======================================" + echo "Authentik Access Information:" + echo "======================================" + echo "URL: http://localhost:9000" + echo "Admin Username: akadmin" + echo "Admin Password: admin-password" + echo "" + echo "======================================" + echo "Next Steps:" + echo "======================================" + echo "1. Access Authentik at http://localhost:9000" + echo "2. Login with akadmin / admin-password" + echo "3. Create an Authentik OIDC Provider for Gitea Mirror:" + echo " - Name: gitea-mirror" + echo " - Redirect URI:" + echo " http://localhost:4321/api/auth/sso/callback/authentik" + echo " - Scopes: openid, profile, email" + echo "" + echo "4. Create Application:" + echo " - Name: Gitea Mirror" + echo " - Slug: gitea-mirror" + echo " - Provider: gitea-mirror (created above)" + echo "" + echo "5. Start Gitea Mirror with:" + echo " bun run dev" + echo "" + echo "6. Configure SSO in Gitea Mirror:" + echo " - Go to Settings → Authentication & SSO" + echo " - Add provider with:" + echo " - Provider ID: authentik" + echo " - Issuer URL: http://localhost:9000/application/o/gitea-mirror/" + echo " - Click Discover to pull Authentik endpoints" + echo " - Client ID: (from Authentik provider)" + echo " - Client Secret: (from Authentik provider)" + echo "" + echo "If you previously registered this provider on a version earlier than v3.8.10, delete it and re-add it after upgrading to avoid missing endpoint data." + echo "" + ;; + + stop) + echo "Stopping Authentik test environment..." + docker-compose -f docker-compose.authentik.yml down + echo -e "${GREEN}✓ Authentik stopped${NC}" + ;; + + clean) + echo "Cleaning up Authentik test environment..." + docker-compose -f docker-compose.authentik.yml down -v + echo -e "${GREEN}✓ Authentik data cleaned${NC}" + + read -p "Remove .env.authentik file? (y/N) " -n 1 -r + echo + if [[ $REPLY =~ ^[Yy]$ ]]; then + rm -f .env.authentik + echo -e "${GREEN}✓ Configuration file removed${NC}" + fi + ;; + + logs) + docker-compose -f docker-compose.authentik.yml logs -f + ;; + + status) + echo "Authentik Service Status:" + echo "=========================" + docker-compose -f docker-compose.authentik.yml ps + ;; + + *) + echo "Usage: $0 {start|stop|clean|logs|status}" + echo "" + echo "Commands:" + echo " start - Start Authentik test environment" + echo " stop - Stop Authentik services" + echo " clean - Stop and remove all data" + echo " logs - Show Authentik logs" + echo " status - Show service status" + exit 1 + ;; +esac diff --git a/Divers/gitea-mirror/scripts/startup-env-config.ts b/Divers/gitea-mirror/scripts/startup-env-config.ts new file mode 100644 index 0000000..b9b1636 --- /dev/null +++ b/Divers/gitea-mirror/scripts/startup-env-config.ts @@ -0,0 +1,52 @@ +#!/usr/bin/env bun +/** + * Startup environment configuration script + * This script loads configuration from environment variables before the application starts + * It ensures that Docker environment variables are properly populated in the database + * + * Usage: + * bun scripts/startup-env-config.ts + */ + +import { initializeConfigFromEnv } from "../src/lib/env-config-loader"; + +async function runEnvConfigInitialization() { + console.log('=== Gitea Mirror Environment Configuration ==='); + console.log('Loading configuration from environment variables...'); + console.log(''); + + const startTime = Date.now(); + + try { + await initializeConfigFromEnv(); + + const endTime = Date.now(); + const duration = endTime - startTime; + + console.log(`✅ Environment configuration loaded successfully in ${duration}ms`); + process.exit(0); + } catch (error) { + const endTime = Date.now(); + const duration = endTime - startTime; + + console.error(`❌ Failed to load environment configuration after ${duration}ms:`, error); + console.error('Application will start anyway, but environment configuration was not loaded.'); + + // Exit with error code but allow startup to continue + process.exit(1); + } +} + +// Handle process signals gracefully +process.on('SIGINT', () => { + console.log('\n⚠️ Configuration loading interrupted by SIGINT'); + process.exit(130); +}); + +process.on('SIGTERM', () => { + console.log('\n⚠️ Configuration loading interrupted by SIGTERM'); + process.exit(143); +}); + +// Run the environment configuration initialization +runEnvConfigInitialization(); \ No newline at end of file diff --git a/Divers/gitea-mirror/scripts/startup-recovery.ts b/Divers/gitea-mirror/scripts/startup-recovery.ts new file mode 100644 index 0000000..8d7b594 --- /dev/null +++ b/Divers/gitea-mirror/scripts/startup-recovery.ts @@ -0,0 +1,113 @@ +#!/usr/bin/env bun +/** + * Startup recovery script + * This script runs job recovery before the application starts serving requests + * It ensures that any interrupted jobs from previous runs are properly handled + * + * Usage: + * bun scripts/startup-recovery.ts [--force] [--timeout=30000] + * + * Options: + * --force: Force recovery even if a recent attempt was made + * --timeout: Maximum time to wait for recovery (in milliseconds, default: 30000) + */ + +import { initializeRecovery, hasJobsNeedingRecovery, getRecoveryStatus } from "../src/lib/recovery"; + +// Parse command line arguments +const args = process.argv.slice(2); +const forceRecovery = args.includes('--force'); +const timeoutArg = args.find(arg => arg.startsWith('--timeout=')); +const timeout = timeoutArg ? parseInt(timeoutArg.split('=')[1], 10) : 30000; + +if (isNaN(timeout) || timeout < 1000) { + console.error("Error: Timeout must be at least 1000ms"); + process.exit(1); +} + +async function runStartupRecovery() { + console.log('=== Gitea Mirror Startup Recovery ==='); + console.log(`Timeout: ${timeout}ms`); + console.log(`Force recovery: ${forceRecovery}`); + console.log(''); + + const startTime = Date.now(); + + try { + // Set up timeout + const timeoutPromise = new Promise((_, reject) => { + setTimeout(() => { + reject(new Error(`Recovery timeout after ${timeout}ms`)); + }, timeout); + }); + + // Check if recovery is needed first + console.log('Checking if recovery is needed...'); + const needsRecovery = await hasJobsNeedingRecovery(); + + if (!needsRecovery) { + console.log('✅ No jobs need recovery. Startup can proceed.'); + process.exit(0); + } + + console.log('⚠️ Jobs found that need recovery. Starting recovery process...'); + + // Run recovery with timeout + const recoveryPromise = initializeRecovery({ + skipIfRecentAttempt: !forceRecovery, + maxRetries: 3, + retryDelay: 5000, + }); + + const recoveryResult = await Promise.race([recoveryPromise, timeoutPromise]); + + const endTime = Date.now(); + const duration = endTime - startTime; + + if (recoveryResult) { + console.log(`✅ Recovery completed successfully in ${duration}ms`); + console.log('Application startup can proceed.'); + process.exit(0); + } else { + console.log(`⚠️ Recovery completed with some failures in ${duration}ms`); + console.log('Application startup can proceed, but some jobs may have failed.'); + process.exit(0); + } + + } catch (error) { + const endTime = Date.now(); + const duration = endTime - startTime; + + if (error instanceof Error && error.message.includes('timeout')) { + console.error(`❌ Recovery timed out after ${duration}ms`); + console.error('Application will start anyway, but some jobs may remain interrupted.'); + + // Get current recovery status + const status = getRecoveryStatus(); + console.log('Recovery status:', status); + + // Exit with warning code but allow startup to continue + process.exit(1); + } else { + console.error(`❌ Recovery failed after ${duration}ms:`, error); + console.error('Application will start anyway, but recovery was unsuccessful.'); + + // Exit with error code but allow startup to continue + process.exit(1); + } + } +} + +// Handle process signals gracefully +process.on('SIGINT', () => { + console.log('\n⚠️ Recovery interrupted by SIGINT'); + process.exit(130); +}); + +process.on('SIGTERM', () => { + console.log('\n⚠️ Recovery interrupted by SIGTERM'); + process.exit(143); +}); + +// Run the startup recovery +runStartupRecovery(); diff --git a/Divers/gitea-mirror/scripts/test-graceful-shutdown.ts b/Divers/gitea-mirror/scripts/test-graceful-shutdown.ts new file mode 100644 index 0000000..e79ff75 --- /dev/null +++ b/Divers/gitea-mirror/scripts/test-graceful-shutdown.ts @@ -0,0 +1,237 @@ +#!/usr/bin/env bun +/** + * Integration test for graceful shutdown functionality + * + * This script tests the complete graceful shutdown flow: + * 1. Starts a mock job + * 2. Initiates shutdown + * 3. Verifies job state is saved correctly + * 4. Tests recovery after restart + * + * Usage: + * bun scripts/test-graceful-shutdown.ts [--cleanup] + */ + +import { db, mirrorJobs } from "../src/lib/db"; +import { eq } from "drizzle-orm"; +import { + initializeShutdownManager, + registerActiveJob, + unregisterActiveJob, + gracefulShutdown, + getShutdownStatus, + registerShutdownCallback +} from "../src/lib/shutdown-manager"; +import { setupSignalHandlers, removeSignalHandlers } from "../src/lib/signal-handlers"; +import { createMirrorJob } from "../src/lib/helpers"; + +// Test configuration +const TEST_USER_ID = "test-user-shutdown"; +const TEST_JOB_PREFIX = "test-shutdown-job"; + +// Parse command line arguments +const args = process.argv.slice(2); +const shouldCleanup = args.includes('--cleanup'); + +/** + * Create a test job for shutdown testing + */ +async function createTestJob(): Promise { + console.log('📝 Creating test job...'); + + const jobId = await createMirrorJob({ + userId: TEST_USER_ID, + message: 'Test job for graceful shutdown testing', + details: 'This job simulates a long-running mirroring operation', + status: "mirroring", + jobType: "mirror", + totalItems: 10, + itemIds: ['item-1', 'item-2', 'item-3', 'item-4', 'item-5'], + inProgress: true, + }); + + console.log(`✅ Created test job: ${jobId}`); + return jobId; +} + +/** + * Verify that job state was saved correctly during shutdown + */ +async function verifyJobState(jobId: string): Promise { + console.log(`🔍 Verifying job state for ${jobId}...`); + + const jobs = await db + .select() + .from(mirrorJobs) + .where(eq(mirrorJobs.id, jobId)); + + if (jobs.length === 0) { + console.error(`❌ Job ${jobId} not found in database`); + return false; + } + + const job = jobs[0]; + + // Check that the job was marked as interrupted + if (job.inProgress) { + console.error(`❌ Job ${jobId} is still marked as in progress`); + return false; + } + + if (!job.message?.includes('interrupted by application shutdown')) { + console.error(`❌ Job ${jobId} does not have shutdown message. Message: ${job.message}`); + return false; + } + + if (!job.lastCheckpoint) { + console.error(`❌ Job ${jobId} does not have a checkpoint timestamp`); + return false; + } + + console.log(`✅ Job ${jobId} state verified correctly`); + console.log(` - In Progress: ${job.inProgress}`); + console.log(` - Message: ${job.message}`); + console.log(` - Last Checkpoint: ${job.lastCheckpoint}`); + + return true; +} + +/** + * Test the graceful shutdown process + */ +async function testGracefulShutdown(): Promise { + console.log('\n🧪 Testing Graceful Shutdown Process'); + console.log('=====================================\n'); + + try { + // Step 1: Initialize shutdown manager + console.log('Step 1: Initializing shutdown manager...'); + initializeShutdownManager(); + setupSignalHandlers(); + + // Step 2: Create and register a test job + console.log('\nStep 2: Creating and registering test job...'); + const jobId = await createTestJob(); + registerActiveJob(jobId); + + // Step 3: Register a test shutdown callback + console.log('\nStep 3: Registering shutdown callback...'); + let callbackExecuted = false; + registerShutdownCallback(async () => { + console.log('🔧 Test shutdown callback executed'); + callbackExecuted = true; + }); + + // Step 4: Check initial status + console.log('\nStep 4: Checking initial status...'); + const initialStatus = getShutdownStatus(); + console.log(` - Active jobs: ${initialStatus.activeJobs.length}`); + console.log(` - Registered callbacks: ${initialStatus.registeredCallbacks}`); + console.log(` - Shutdown in progress: ${initialStatus.inProgress}`); + + // Step 5: Simulate graceful shutdown + console.log('\nStep 5: Simulating graceful shutdown...'); + + // Override process.exit to prevent actual exit during test + const originalExit = process.exit; + let exitCode: number | undefined; + process.exit = ((code?: number) => { + exitCode = code; + console.log(`🚪 Process.exit called with code: ${code}`); + // Don't actually exit during test + }) as any; + + try { + // This should save job state and execute callbacks + await gracefulShutdown('TEST_SIGNAL'); + } catch (error) { + // Expected since we're not actually exiting + console.log(`⚠️ Graceful shutdown completed (exit intercepted)`); + } + + // Restore original process.exit + process.exit = originalExit; + + // Step 6: Verify job state was saved + console.log('\nStep 6: Verifying job state was saved...'); + const jobStateValid = await verifyJobState(jobId); + + // Step 7: Verify callback was executed + console.log('\nStep 7: Verifying callback execution...'); + if (callbackExecuted) { + console.log('✅ Shutdown callback was executed'); + } else { + console.error('❌ Shutdown callback was not executed'); + } + + // Step 8: Test results + console.log('\n📊 Test Results:'); + console.log(` - Job state saved correctly: ${jobStateValid ? '✅' : '❌'}`); + console.log(` - Shutdown callback executed: ${callbackExecuted ? '✅' : '❌'}`); + console.log(` - Exit code: ${exitCode}`); + + if (jobStateValid && callbackExecuted) { + console.log('\n🎉 All tests passed! Graceful shutdown is working correctly.'); + } else { + console.error('\n❌ Some tests failed. Please check the implementation.'); + process.exit(1); + } + + } catch (error) { + console.error('\n💥 Test failed with error:', error); + process.exit(1); + } finally { + // Clean up signal handlers + removeSignalHandlers(); + } +} + +/** + * Clean up test data + */ +async function cleanupTestData(): Promise { + console.log('🧹 Cleaning up test data...'); + + const result = await db + .delete(mirrorJobs) + .where(eq(mirrorJobs.userId, TEST_USER_ID)); + + console.log('✅ Test data cleaned up'); +} + +/** + * Main test runner + */ +async function runTest(): Promise { + console.log('🧪 Graceful Shutdown Integration Test'); + console.log('====================================\n'); + + if (shouldCleanup) { + await cleanupTestData(); + console.log('✅ Cleanup completed'); + return; + } + + try { + await testGracefulShutdown(); + } finally { + // Always clean up test data + await cleanupTestData(); + } +} + +// Handle process signals gracefully during testing +process.on('SIGINT', async () => { + console.log('\n⚠️ Test interrupted by SIGINT'); + await cleanupTestData(); + process.exit(130); +}); + +process.on('SIGTERM', async () => { + console.log('\n⚠️ Test interrupted by SIGTERM'); + await cleanupTestData(); + process.exit(143); +}); + +// Run the test +runTest(); diff --git a/Divers/gitea-mirror/scripts/test-recovery.ts b/Divers/gitea-mirror/scripts/test-recovery.ts new file mode 100644 index 0000000..66b1d7d --- /dev/null +++ b/Divers/gitea-mirror/scripts/test-recovery.ts @@ -0,0 +1,183 @@ +#!/usr/bin/env bun +/** + * Test script for the recovery system + * This script creates test jobs and verifies that the recovery system can handle them + * + * Usage: + * bun scripts/test-recovery.ts [--cleanup] + * + * Options: + * --cleanup: Clean up test jobs after testing + */ + +import { db, mirrorJobs } from "../src/lib/db"; +import { createMirrorJob } from "../src/lib/helpers"; +import { initializeRecovery, hasJobsNeedingRecovery, getRecoveryStatus } from "../src/lib/recovery"; +import { eq } from "drizzle-orm"; +import { v4 as uuidv4 } from "uuid"; + +// Parse command line arguments +const args = process.argv.slice(2); +const cleanup = args.includes('--cleanup'); + +// Test configuration +const TEST_USER_ID = "test-user-recovery"; +const TEST_BATCH_ID = "test-batch-recovery"; + +async function runRecoveryTest() { + console.log('=== Recovery System Test ==='); + console.log(`Cleanup mode: ${cleanup}`); + console.log(''); + + try { + if (cleanup) { + await cleanupTestJobs(); + return; + } + + // Step 1: Create test jobs that simulate interrupted state + console.log('Step 1: Creating test interrupted jobs...'); + await createTestInterruptedJobs(); + + // Step 2: Check if recovery system detects them + console.log('Step 2: Checking if recovery system detects interrupted jobs...'); + const needsRecovery = await hasJobsNeedingRecovery(); + console.log(`Jobs needing recovery: ${needsRecovery}`); + + if (!needsRecovery) { + console.log('❌ Recovery system did not detect interrupted jobs'); + return; + } + + // Step 3: Get recovery status + console.log('Step 3: Getting recovery status...'); + const status = getRecoveryStatus(); + console.log('Recovery status:', status); + + // Step 4: Run recovery + console.log('Step 4: Running recovery...'); + const recoveryResult = await initializeRecovery({ + skipIfRecentAttempt: false, + maxRetries: 2, + retryDelay: 2000, + }); + + console.log(`Recovery result: ${recoveryResult}`); + + // Step 5: Verify recovery completed + console.log('Step 5: Verifying recovery completed...'); + const stillNeedsRecovery = await hasJobsNeedingRecovery(); + console.log(`Jobs still needing recovery: ${stillNeedsRecovery}`); + + // Step 6: Check final job states + console.log('Step 6: Checking final job states...'); + await checkTestJobStates(); + + console.log(''); + console.log('✅ Recovery test completed successfully!'); + console.log('Run with --cleanup to remove test jobs'); + + } catch (error) { + console.error('❌ Recovery test failed:', error); + process.exit(1); + } +} + +/** + * Create test jobs that simulate interrupted state + */ +async function createTestInterruptedJobs() { + const testJobs = [ + { + repositoryId: uuidv4(), + repositoryName: "test-repo-1", + message: "Test mirror job 1", + status: "mirroring" as const, + jobType: "mirror" as const, + }, + { + repositoryId: uuidv4(), + repositoryName: "test-repo-2", + message: "Test sync job 2", + status: "syncing" as const, + jobType: "sync" as const, + }, + ]; + + for (const job of testJobs) { + const jobId = await createMirrorJob({ + userId: TEST_USER_ID, + repositoryId: job.repositoryId, + repositoryName: job.repositoryName, + message: job.message, + status: job.status, + jobType: job.jobType, + batchId: TEST_BATCH_ID, + totalItems: 5, + itemIds: [job.repositoryId, uuidv4(), uuidv4(), uuidv4(), uuidv4()], + inProgress: true, + skipDuplicateEvent: true, + }); + + // Manually set the job to look interrupted (old timestamp) + const oldTimestamp = new Date(); + oldTimestamp.setMinutes(oldTimestamp.getMinutes() - 15); // 15 minutes ago + + await db + .update(mirrorJobs) + .set({ + startedAt: oldTimestamp, + lastCheckpoint: oldTimestamp, + }) + .where(eq(mirrorJobs.id, jobId)); + + console.log(`Created test job: ${jobId} (${job.repositoryName})`); + } +} + +/** + * Check the final states of test jobs + */ +async function checkTestJobStates() { + const testJobs = await db + .select() + .from(mirrorJobs) + .where(eq(mirrorJobs.userId, TEST_USER_ID)); + + console.log(`Found ${testJobs.length} test jobs:`); + + for (const job of testJobs) { + console.log(`- Job ${job.id}: ${job.status} (inProgress: ${job.inProgress})`); + console.log(` Message: ${job.message}`); + console.log(` Started: ${job.startedAt ? new Date(job.startedAt).toISOString() : 'never'}`); + console.log(` Completed: ${job.completedAt ? new Date(job.completedAt).toISOString() : 'never'}`); + console.log(''); + } +} + +/** + * Clean up test jobs + */ +async function cleanupTestJobs() { + console.log('Cleaning up test jobs...'); + + const result = await db + .delete(mirrorJobs) + .where(eq(mirrorJobs.userId, TEST_USER_ID)); + + console.log('✅ Test jobs cleaned up successfully'); +} + +// Handle process signals gracefully +process.on('SIGINT', () => { + console.log('\n⚠️ Test interrupted by SIGINT'); + process.exit(130); +}); + +process.on('SIGTERM', () => { + console.log('\n⚠️ Test interrupted by SIGTERM'); + process.exit(143); +}); + +// Run the test +runRecoveryTest(); diff --git a/Divers/gitea-mirror/src/components/NotFound.tsx b/Divers/gitea-mirror/src/components/NotFound.tsx new file mode 100644 index 0000000..8dee1cb --- /dev/null +++ b/Divers/gitea-mirror/src/components/NotFound.tsx @@ -0,0 +1,80 @@ +import { Button } from "@/components/ui/button"; +import { Card, CardContent, CardHeader } from "@/components/ui/card"; +import { Home, ArrowLeft, GitBranch, BookOpen, Settings, FileQuestion } from "lucide-react"; + +export function NotFound() { + return ( +

+ + +
+ +
+

404

+

Page Not Found

+

+ The page you're looking for doesn't exist or has been moved. +

+
+ + + {/* Action Buttons */} +
+ + +
+ + {/* Divider */} +
+
+ +
+
+ or visit +
+
+ + {/* Quick Links */} + + + {/* Error Code */} +
+

+ Error Code: 404_NOT_FOUND +

+
+
+
+
+ ); +} \ No newline at end of file diff --git a/Divers/gitea-mirror/src/components/activity/ActivityList.tsx b/Divers/gitea-mirror/src/components/activity/ActivityList.tsx new file mode 100644 index 0000000..64741fb --- /dev/null +++ b/Divers/gitea-mirror/src/components/activity/ActivityList.tsx @@ -0,0 +1,352 @@ +import { useEffect, useMemo, useRef, useState } from 'react'; +import { useVirtualizer } from '@tanstack/react-virtual'; +import type { MirrorJob } from '@/lib/db/schema'; +import Fuse from 'fuse.js'; +import { Button } from '../ui/button'; +import { RefreshCw, Check, X, Loader2, Import } from 'lucide-react'; +import { Card } from '../ui/card'; +import { formatDate, getStatusColor } from '@/lib/utils'; +import { Skeleton } from '../ui/skeleton'; +import type { FilterParams } from '@/types/filter'; +import { + Tooltip, + TooltipContent, + TooltipProvider, + TooltipTrigger, +} from '../ui/tooltip'; + +type MirrorJobWithKey = MirrorJob & { _rowKey: string }; + +interface ActivityListProps { + activities: MirrorJobWithKey[]; + isLoading: boolean; + isLiveActive?: boolean; + filter: FilterParams; + setFilter: (filter: FilterParams) => void; +} + +export default function ActivityList({ + activities, + isLoading, + isLiveActive = false, + filter, + setFilter, +}: ActivityListProps) { + const [expandedItems, setExpandedItems] = useState>( + () => new Set(), + ); + + const parentRef = useRef(null); + // We keep the ref only for possible future scroll-to-row logic. + const rowRefs = useRef>(new Map()); // eslint-disable-line @typescript-eslint/no-unused-vars + + const filteredActivities = useMemo(() => { + let result = activities; + + if (filter.status) { + result = result.filter((a) => a.status === filter.status); + } + + if (filter.type) { + result = + filter.type === 'repository' + ? result.filter((a) => !!a.repositoryId) + : filter.type === 'organization' + ? result.filter((a) => !!a.organizationId) + : result; + } + + if (filter.name) { + result = result.filter( + (a) => + a.repositoryName === filter.name || + a.organizationName === filter.name, + ); + } + + if (filter.searchTerm) { + const fuse = new Fuse(result, { + keys: ['message', 'details', 'organizationName', 'repositoryName'], + threshold: 0.3, + }); + result = fuse.search(filter.searchTerm).map((r) => r.item); + } + + return result; + }, [activities, filter]); + + const virtualizer = useVirtualizer({ + count: filteredActivities.length, + getScrollElement: () => parentRef.current, + estimateSize: (idx) => + expandedItems.has(filteredActivities[idx]._rowKey) ? 217 : 100, + overscan: 5, + measureElement: (el) => el.getBoundingClientRect().height + 8, + }); + + useEffect(() => { + virtualizer.measure(); + }, [expandedItems, virtualizer]); + + /* ------------------------------ render ------------------------------ */ + + if (isLoading) { + return ( +
+ {Array.from({ length: 5 }, (_, i) => ( + + ))} +
+ ); + } + + if (filteredActivities.length === 0) { + const hasFilter = + filter.searchTerm || filter.status || filter.type || filter.name; + + return ( +
+ +

No activities found

+

+ {hasFilter + ? 'Try adjusting your search or filter criteria.' + : 'No mirroring activities have been recorded yet.'} +

+ {hasFilter && ( + + )} +
+ ); + } + + return ( +
+ +
+ {virtualizer.getVirtualItems().map((vRow) => { + const activity = filteredActivities[vRow.index]; + const isExpanded = expandedItems.has(activity._rowKey); + + return ( +
{ + rowRefs.current.set(activity._rowKey, node); + if (node) virtualizer.measureElement(node); + }} + style={{ + position: 'absolute', + top: 0, + left: 0, + width: '100%', + transform: `translateY(${vRow.start}px)`, + paddingBottom: '8px', + }} + className='border-b px-4 pt-4' + > +
+
+
+
+ +
+
+
+ {/* Mobile: Show simplified status-based message */} +
+

+ {activity.status === 'synced' ? ( + <> + + Sync successful + + ) : activity.status === 'mirrored' ? ( + <> + + Mirror successful + + ) : activity.status === 'failed' ? ( + <> + + Operation failed + + ) : activity.status === 'syncing' ? ( + <> + + Syncing in progress + + ) : activity.status === 'mirroring' ? ( + <> + + Mirroring in progress + + ) : activity.status === 'imported' ? ( + <> + + Imported + + ) : ( + {activity.message} + )} +

+
+ {/* Desktop: Show status with icon and full message in tooltip */} +
+ + + +

+ {activity.status === 'synced' ? ( + <> + + Sync successful + + ) : activity.status === 'mirrored' ? ( + <> + + Mirror successful + + ) : activity.status === 'failed' ? ( + <> + + Operation failed + + ) : activity.status === 'syncing' ? ( + <> + + Syncing in progress + + ) : activity.status === 'mirroring' ? ( + <> + + Mirroring in progress + + ) : activity.status === 'imported' ? ( + <> + + Imported + + ) : ( + {activity.message} + )} +

+
+ +

{activity.message}

+
+
+
+
+
+

+ {formatDate(activity.timestamp)} +

+
+ +
+ {activity.repositoryName && ( +

+ Repo: {activity.repositoryName} +

+ )} + {activity.organizationName && ( +

+ Org: {activity.organizationName} +

+ )} +
+ + {activity.details && ( +
+ + + {isExpanded && ( +
+                          {activity.details}
+                        
+ )} +
+ )} +
+
+
+ ); + })} +
+ + + {/* Status Bar */} +
+
+
+ + {filteredActivities.length} {filteredActivities.length === 1 ? 'activity' : 'activities'} total + +
+ + {/* Center - Live active indicator */} + {isLiveActive && ( +
+
+ + Live active + +
+
+ )} + + {(filter.searchTerm || filter.status || filter.type || filter.name) && ( + + Filters applied + + )} +
+
+ ); +} diff --git a/Divers/gitea-mirror/src/components/activity/ActivityLog.tsx b/Divers/gitea-mirror/src/components/activity/ActivityLog.tsx new file mode 100644 index 0000000..d650d14 --- /dev/null +++ b/Divers/gitea-mirror/src/components/activity/ActivityLog.tsx @@ -0,0 +1,752 @@ +import { useCallback, useEffect, useState, useRef } from 'react'; +import { Button } from '@/components/ui/button'; +import { ChevronDown, Download, RefreshCw, Search, Trash2, Filter } from 'lucide-react'; +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuTrigger, +} from '../ui/dropdown-menu'; +import { + Dialog, + DialogContent, + DialogDescription, + DialogFooter, + DialogHeader, + DialogTitle, + DialogTrigger, +} from '../ui/dialog'; +import { apiRequest, formatDate, showErrorToast } from '@/lib/utils'; +import { useAuth } from '@/hooks/useAuth'; +import type { MirrorJob } from '@/lib/db/schema'; +import type { ActivityApiResponse } from '@/types/activities'; +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from '../ui/select'; +import { repoStatusEnum, type RepoStatus } from '@/types/Repository'; +import ActivityList from './ActivityList'; +import { ActivityNameCombobox } from './ActivityNameCombobox'; +import { useSSE } from '@/hooks/useSEE'; +import { useFilterParams } from '@/hooks/useFilterParams'; +import { toast } from 'sonner'; +import { useLiveRefresh } from '@/hooks/useLiveRefresh'; +import { useConfigStatus } from '@/hooks/useConfigStatus'; +import { useNavigation } from '@/components/layout/MainLayout'; +import { + Drawer, + DrawerClose, + DrawerContent, + DrawerDescription, + DrawerFooter, + DrawerHeader, + DrawerTitle, + DrawerTrigger, +} from '@/components/ui/drawer'; + +type MirrorJobWithKey = MirrorJob & { _rowKey: string }; + +// Maximum number of activities to keep in memory to prevent performance issues +const MAX_ACTIVITIES = 1000; + +// More robust key generation to prevent collisions +function genKey(job: MirrorJob, index?: number): string { + const baseId = job.id || `temp-${Date.now()}-${Math.random().toString(36).slice(2)}`; + const timestamp = job.timestamp instanceof Date ? job.timestamp.getTime() : new Date(job.timestamp).getTime(); + const indexSuffix = index !== undefined ? `-${index}` : ''; + return `${baseId}-${timestamp}${indexSuffix}`; +} + +// Create a deep clone without structuredClone for better browser compatibility +function deepClone(obj: T): T { + if (obj === null || typeof obj !== 'object') return obj; + if (obj instanceof Date) return new Date(obj.getTime()) as T; + if (Array.isArray(obj)) return obj.map(item => deepClone(item)) as T; + + const cloned = {} as T; + for (const key in obj) { + if (Object.prototype.hasOwnProperty.call(obj, key)) { + cloned[key] = deepClone(obj[key]); + } + } + return cloned; +} + +export function ActivityLog() { + const { user } = useAuth(); + const { registerRefreshCallback, isLiveEnabled } = useLiveRefresh(); + const { isFullyConfigured } = useConfigStatus(); + const { navigationKey } = useNavigation(); + + const [activities, setActivities] = useState([]); + const [isInitialLoading, setIsInitialLoading] = useState(false); + const [showCleanupDialog, setShowCleanupDialog] = useState(false); + + // Ref to track if component is mounted to prevent state updates after unmount + const isMountedRef = useRef(true); + + useEffect(() => { + return () => { + isMountedRef.current = false; + }; + }, []); + + const { filter, setFilter } = useFilterParams({ + searchTerm: '', + status: '', + type: '', + name: '', + }); + + /* ----------------------------- SSE hook ----------------------------- */ + + const handleNewMessage = useCallback((data: MirrorJob) => { + if (!isMountedRef.current) return; + + setActivities((prev) => { + // Create a deep clone of the new activity + const clonedData = deepClone(data); + + // Check if this activity already exists to prevent duplicates + const existingIndex = prev.findIndex(activity => + activity.id === clonedData.id || + (activity.repositoryId === clonedData.repositoryId && + activity.organizationId === clonedData.organizationId && + activity.message === clonedData.message && + Math.abs(new Date(activity.timestamp).getTime() - new Date(clonedData.timestamp).getTime()) < 1000) + ); + + if (existingIndex !== -1) { + // Update existing activity instead of adding duplicate + const updated = [...prev]; + updated[existingIndex] = { + ...clonedData, + _rowKey: prev[existingIndex]._rowKey, // Keep the same key + }; + return updated; + } + + // Add new activity with unique key + const withKey: MirrorJobWithKey = { + ...clonedData, + _rowKey: genKey(clonedData, prev.length), + }; + + // Limit the number of activities to prevent memory issues + const newActivities = [withKey, ...prev]; + return newActivities.slice(0, MAX_ACTIVITIES); + }); + }, []); + + const { connected } = useSSE({ + userId: user?.id, + onMessage: handleNewMessage, + }); + + /* ------------------------- initial fetch --------------------------- */ + + const fetchActivities = useCallback(async (isLiveRefresh = false) => { + if (!user?.id) return false; + + try { + // Set appropriate loading state based on refresh type + if (!isLiveRefresh) { + setIsInitialLoading(true); + } + + const res = await apiRequest( + `/activities?userId=${user.id}`, + { method: 'GET' }, + ); + + if (!res.success) { + // Only show error toast for manual refreshes to avoid spam during live updates + if (!isLiveRefresh) { + showErrorToast(res.message ?? 'Failed to fetch activities.', toast); + } + return false; + } + + // Process activities with robust cloning and unique keys + const data: MirrorJobWithKey[] = res.activities.map((activity, index) => { + const clonedActivity = deepClone(activity); + return { + ...clonedActivity, + _rowKey: genKey(clonedActivity, index), + }; + }); + + // Sort by timestamp (newest first) to ensure consistent ordering + data.sort((a, b) => { + const timeA = new Date(a.timestamp).getTime(); + const timeB = new Date(b.timestamp).getTime(); + return timeB - timeA; + }); + + if (isMountedRef.current) { + setActivities(data); + } + return true; + } catch (err) { + if (isMountedRef.current) { + // Only show error toast for manual refreshes to avoid spam during live updates + if (!isLiveRefresh) { + showErrorToast(err, toast); + } + } + return false; + } finally { + if (isMountedRef.current && !isLiveRefresh) { + setIsInitialLoading(false); + } + } + }, [user?.id]); // Only depend on user.id, not entire user object + + useEffect(() => { + // Reset loading state when component becomes active + setIsInitialLoading(true); + fetchActivities(false); // Manual refresh, not live + }, [fetchActivities, navigationKey]); // Include navigationKey to trigger on navigation + + // Register with global live refresh system + useEffect(() => { + // Only register for live refresh if configuration is complete + // Activity logs can exist from previous runs, but new activities won't be generated without config + if (!isFullyConfigured) { + return; + } + + const unregister = registerRefreshCallback(() => { + fetchActivities(true); // Live refresh + }); + + return unregister; + }, [registerRefreshCallback, fetchActivities, isFullyConfigured]); + + /* ---------------------- filtering + exporting ---------------------- */ + + const applyLightFilter = (list: MirrorJobWithKey[]) => { + return list.filter((a) => { + if (filter.status && a.status !== filter.status) return false; + + if (filter.type === 'repository' && !a.repositoryId) return false; + if (filter.type === 'organization' && !a.organizationId) return false; + + if ( + filter.name && + a.repositoryName !== filter.name && + a.organizationName !== filter.name + ) { + return false; + } + + return true; + }); + }; + + const exportAsCSV = () => { + const rows = applyLightFilter(activities); + if (!rows.length) return toast.error('No activities to export.'); + + const headers = [ + 'Timestamp', + 'Message', + 'Status', + 'Repository', + 'Organization', + 'Details', + ]; + + const escape = (v: string | null | undefined) => + v && /[,\"\n]/.test(v) ? `"${v.replace(/"/g, '""')}"` : v ?? ''; + + const csv = [ + headers.join(','), + ...rows.map((a) => + [ + formatDate(a.timestamp), + escape(a.message), + a.status, + escape(a.repositoryName), + escape(a.organizationName), + escape(a.details), + ].join(','), + ), + ].join('\n'); + + downloadFile(csv, 'text/csv;charset=utf-8;', 'activity_log_export.csv'); + toast.success('CSV exported.'); + }; + + const exportAsJSON = () => { + const rows = applyLightFilter(activities); + if (!rows.length) return toast.error('No activities to export.'); + + const json = JSON.stringify( + rows.map((a) => ({ + ...a, + formattedTime: formatDate(a.timestamp), + })), + null, + 2, + ); + + downloadFile(json, 'application/json', 'activity_log_export.json'); + toast.success('JSON exported.'); + }; + + const downloadFile = ( + content: string, + mime: string, + filename: string, + ): void => { + const date = new Date().toISOString().slice(0, 10); // yyyy-mm-dd + const link = document.createElement('a'); + link.href = URL.createObjectURL(new Blob([content], { type: mime })); + link.download = filename.replace('.', `_${date}.`); + link.click(); + }; + + const handleCleanupClick = () => { + setShowCleanupDialog(true); + }; + + const confirmCleanup = async () => { + if (!user?.id) return; + + try { + setIsInitialLoading(true); + setShowCleanupDialog(false); + + const response = await fetch('/api/activities/cleanup', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ userId: user.id }), + }); + + if (!response.ok) { + const errorData = await response.json().catch(() => ({ error: 'Unknown error occurred' })); + throw new Error(errorData.error || `HTTP ${response.status}: ${response.statusText}`); + } + + const res = await response.json(); + + if (res.success) { + // Clear the activities from the UI + setActivities([]); + toast.success(`All activities cleaned up successfully. Deleted ${res.result.mirrorJobsDeleted} mirror jobs and ${res.result.eventsDeleted} events.`); + } else { + showErrorToast(res.error || 'Failed to cleanup activities.', toast); + } + } catch (error) { + console.error('Error cleaning up activities:', error); + showErrorToast(error, toast); + } finally { + setIsInitialLoading(false); + } + }; + + const cancelCleanup = () => { + setShowCleanupDialog(false); + }; + + // Check if any filters are active + const hasActiveFilters = !!(filter.status || filter.type || filter.name); + const activeFilterCount = [filter.status, filter.type, filter.name].filter(Boolean).length; + + // Clear all filters + const clearFilters = () => { + setFilter({ + searchTerm: filter.searchTerm, + status: '', + type: '', + name: '', + }); + }; + + /* ------------------------------ UI ------------------------------ */ + + return ( +
+ {/* Mobile: Search bar with filter and action buttons */} +
+
+
+ + + setFilter((prev) => ({ ...prev, searchTerm: e.target.value })) + } + /> +
+ + {/* Mobile Filter Drawer */} + + + + + + + Filter Activities + + Narrow down your activity log + + + +
+ {/* Active filters summary */} + {hasActiveFilters && ( +
+ + {activeFilterCount} filter{activeFilterCount > 1 ? 's' : ''} active + + +
+ )} + + {/* Status Filter */} +
+ + +
+ + {/* Type Filter */} +
+ + +
+ + {/* Name Filter */} +
+ + setFilter((p) => ({ ...p, name }))} + /> +
+
+ + + + + + + + + +
+
+ + + + + +
+
+ + {/* Desktop: Original layout */} +
+ {/* search input */} +
+ + + setFilter((prev) => ({ + ...prev, + searchTerm: e.target.value, + })) + } + /> +
+ + {/* Filter controls */} +
+ {/* status select */} + + + {/* type select */} + +
+ + {/* repo/org name combobox */} + setFilter((p) => ({ ...p, name }))} + /> + + {/* Action buttons */} +
+ {/* export dropdown */} + + + + + + + Export as CSV + + + Export as JSON + + + + + {/* refresh */} + + + {/* cleanup all activities */} + +
+
+ + {/* activity list */} + + + {/* cleanup confirmation dialog */} + + + + Delete All Activities + + Are you sure you want to delete ALL activities? This action cannot be undone and will remove all mirror jobs and events from the database. + + + + + + + + + + {/* Mobile FAB for Export - only visible on mobile */} + + + + + + + Export as CSV + + + Export as JSON + + + +
+ ); +} diff --git a/Divers/gitea-mirror/src/components/activity/ActivityNameCombobox.tsx b/Divers/gitea-mirror/src/components/activity/ActivityNameCombobox.tsx new file mode 100644 index 0000000..77d24f2 --- /dev/null +++ b/Divers/gitea-mirror/src/components/activity/ActivityNameCombobox.tsx @@ -0,0 +1,91 @@ +import * as React from "react"; +import { ChevronsUpDown, Check } from "lucide-react"; +import { Button } from "@/components/ui/button"; +import { + Command, + CommandEmpty, + CommandGroup, + CommandInput, + CommandItem, + CommandList, +} from "@/components/ui/command"; +import { + Popover, + PopoverContent, + PopoverTrigger, +} from "@/components/ui/popover"; +import { cn } from "@/lib/utils"; + +type ActivityNameComboboxProps = { + activities: any[]; + value: string; + onChange: (value: string) => void; +}; + +export function ActivityNameCombobox({ activities, value, onChange }: ActivityNameComboboxProps) { + // Collect unique names from repositoryName and organizationName + const names = React.useMemo(() => { + const set = new Set(); + activities.forEach((a) => { + if (a.repositoryName) set.add(a.repositoryName); + if (a.organizationName) set.add(a.organizationName); + }); + return Array.from(set).sort(); + }, [activities]); + + const [open, setOpen] = React.useState(false); + return ( + + + + + + + + + No name found. + + { + onChange(""); + setOpen(false); + }} + > + + All names + + {names.map((name) => ( + { + onChange(name); + setOpen(false); + }} + > + + {name} + + ))} + + + + + + ); +} diff --git a/Divers/gitea-mirror/src/components/auth/LoginForm.tsx b/Divers/gitea-mirror/src/components/auth/LoginForm.tsx new file mode 100644 index 0000000..3a25054 --- /dev/null +++ b/Divers/gitea-mirror/src/components/auth/LoginForm.tsx @@ -0,0 +1,297 @@ +'use client'; + +import * as React from 'react'; +import { useState } from 'react'; +import { Button } from '@/components/ui/button'; +import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from '@/components/ui/card'; +import { useAuth } from '@/hooks/useAuth'; +import { useAuthMethods } from '@/hooks/useAuthMethods'; +import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'; +import { authClient } from '@/lib/auth-client'; +import { Separator } from '@/components/ui/separator'; +import { toast, Toaster } from 'sonner'; +import { showErrorToast } from '@/lib/utils'; +import { Loader2, Mail, Globe, Eye, EyeOff } from 'lucide-react'; + + +export function LoginForm() { + const [isLoading, setIsLoading] = useState(false); + const [showPassword, setShowPassword] = useState(false); + const [ssoEmail, setSsoEmail] = useState(''); + const { login } = useAuth(); + const { authMethods, isLoading: isLoadingMethods } = useAuthMethods(); + + // Determine which tab to show by default + const getDefaultTab = () => { + if (authMethods.emailPassword) return 'email'; + if (authMethods.sso.enabled) return 'sso'; + return 'email'; // fallback + }; + + async function handleLogin(e: React.FormEvent) { + e.preventDefault(); + setIsLoading(true); + const form = e.currentTarget; + const formData = new FormData(form); + const email = formData.get('email') as string | null; + const password = formData.get('password') as string | null; + + if (!email || !password) { + toast.error('Please enter both email and password'); + setIsLoading(false); + return; + } + + try { + await login(email, password); + toast.success('Login successful!'); + // Small delay before redirecting to see the success message + setTimeout(() => { + window.location.href = '/'; + }, 1000); + } catch (error) { + showErrorToast(error, toast); + } finally { + setIsLoading(false); + } + } + + async function handleSSOLogin(domain?: string, providerId?: string) { + setIsLoading(true); + try { + if (!domain && !ssoEmail) { + toast.error('Please enter your email or select a provider'); + return; + } + + const baseURL = typeof window !== 'undefined' ? window.location.origin : 'http://localhost:4321'; + await authClient.signIn.sso({ + email: ssoEmail || undefined, + domain: domain, + providerId: providerId, + callbackURL: `${baseURL}/`, + scopes: ['openid', 'email', 'profile'], // TODO: This is not being respected by the SSO plugin. + }); + } catch (error) { + showErrorToast(error, toast); + } finally { + setIsLoading(false); + } + } + + return ( + <> + + +
+ Gitea Mirror Logo +
+ Gitea Mirror + + Log in to manage your GitHub to Gitea mirroring + +
+ + {isLoadingMethods ? ( + +
+ +
+
+ ) : ( + <> + {/* Show tabs only if multiple auth methods are available */} + {authMethods.sso.enabled && authMethods.emailPassword ? ( + + + + + Email + + + + SSO + + + + + +
+
+
+ + +
+
+ +
+ + +
+
+
+
+
+ + + +
+ + + +
+ {authMethods.sso.providers.length > 0 && ( + <> +
+

+ Sign in with your organization account +

+ {authMethods.sso.providers.map(provider => ( + + ))} +
+ +
+
+ +
+
+ Or +
+
+ + )} + +
+ + setSsoEmail(e.target.value)} + className="w-full rounded-md border border-input bg-background px-3 py-2 text-sm shadow-sm transition-colors placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring" + placeholder="Enter your work email" + disabled={isLoading} + /> +

+ We'll redirect you to your organization's SSO provider +

+
+
+
+ + + +
+
+ ) : ( + // Single auth method - show email/password only + <> + +
+
+
+ + +
+
+ + +
+
+
+
+ + + + + )} + + )} + +
+

+ Don't have an account? Contact your administrator. +

+
+
+ + + ); +} diff --git a/Divers/gitea-mirror/src/components/auth/LoginPage.tsx b/Divers/gitea-mirror/src/components/auth/LoginPage.tsx new file mode 100644 index 0000000..abd8937 --- /dev/null +++ b/Divers/gitea-mirror/src/components/auth/LoginPage.tsx @@ -0,0 +1,10 @@ +import { LoginForm } from './LoginForm'; +import Providers from '@/components/layout/Providers'; + +export function LoginPage() { + return ( + + + + ); +} \ No newline at end of file diff --git a/Divers/gitea-mirror/src/components/auth/SignupForm.tsx b/Divers/gitea-mirror/src/components/auth/SignupForm.tsx new file mode 100644 index 0000000..a2fdcf9 --- /dev/null +++ b/Divers/gitea-mirror/src/components/auth/SignupForm.tsx @@ -0,0 +1,156 @@ +'use client'; + +import { useState } from 'react'; +import { Button } from '@/components/ui/button'; +import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from '@/components/ui/card'; +import { toast, Toaster } from 'sonner'; +import { showErrorToast } from '@/lib/utils'; +import { useAuth } from '@/hooks/useAuth'; +import { Eye, EyeOff } from 'lucide-react'; + +export function SignupForm() { + const [isLoading, setIsLoading] = useState(false); + const [showPassword, setShowPassword] = useState(false); + const [showConfirmPassword, setShowConfirmPassword] = useState(false); + const { register } = useAuth(); + + async function handleSignup(e: React.FormEvent) { + e.preventDefault(); + setIsLoading(true); + const form = e.currentTarget; + const formData = new FormData(form); + const email = formData.get('email') as string | null; + const password = formData.get('password') as string | null; + const confirmPassword = formData.get('confirmPassword') as string | null; + + if (!email || !password || !confirmPassword) { + toast.error('Please fill in all fields'); + setIsLoading(false); + return; + } + + if (password !== confirmPassword) { + toast.error('Passwords do not match'); + setIsLoading(false); + return; + } + + try { + // Derive username from email (part before @) + const username = email.split('@')[0]; + await register(username, email, password); + toast.success('Account created successfully! Redirecting to dashboard...'); + // Small delay before redirecting to see the success message + setTimeout(() => { + window.location.href = '/'; + }, 1500); + } catch (error) { + showErrorToast(error, toast); + } finally { + setIsLoading(false); + } + } + + return ( + <> + + +
+ Gitea Mirror Logo +
+ Create Admin Account + + Set up your administrator account for Gitea Mirror + +
+ +
+
+
+ + +
+
+ +
+ + +
+
+
+ +
+ + +
+
+
+
+
+ + + +
+ + + ); +} diff --git a/Divers/gitea-mirror/src/components/auth/SignupPage.tsx b/Divers/gitea-mirror/src/components/auth/SignupPage.tsx new file mode 100644 index 0000000..c864ea2 --- /dev/null +++ b/Divers/gitea-mirror/src/components/auth/SignupPage.tsx @@ -0,0 +1,10 @@ +import { SignupForm } from './SignupForm'; +import Providers from '@/components/layout/Providers'; + +export function SignupPage() { + return ( + + + + ); +} \ No newline at end of file diff --git a/Divers/gitea-mirror/src/components/config/AdvancedOptionsForm.tsx b/Divers/gitea-mirror/src/components/config/AdvancedOptionsForm.tsx new file mode 100644 index 0000000..c319e17 --- /dev/null +++ b/Divers/gitea-mirror/src/components/config/AdvancedOptionsForm.tsx @@ -0,0 +1,90 @@ +import React from "react"; +import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; +import { Checkbox } from "../ui/checkbox"; +import type { AdvancedOptions } from "@/types/config"; +import { RefreshCw } from "lucide-react"; + +interface AdvancedOptionsFormProps { + config: AdvancedOptions; + setConfig: React.Dispatch>; + onAutoSave?: (config: AdvancedOptions) => Promise; + isAutoSaving?: boolean; +} + +export function AdvancedOptionsForm({ + config, + setConfig, + onAutoSave, + isAutoSaving = false, +}: AdvancedOptionsFormProps) { + const handleChange = (name: string, checked: boolean) => { + const newConfig = { + ...config, + [name]: checked, + }; + + setConfig(newConfig); + + // Auto-save + if (onAutoSave) { + onAutoSave(newConfig); + } + }; + + return ( + + + + Advanced Options + {isAutoSaving && ( +
+ + Auto-saving... +
+ )} +
+
+ +
+
+ + handleChange("skipForks", Boolean(checked)) + } + /> + +
+

+ Don't mirror repositories that are forks of other repositories +

+ +
+ + handleChange("starredCodeOnly", Boolean(checked)) + } + /> + +
+

+ Mirror only source code for starred repositories, skipping all metadata (issues, PRs, labels, milestones, wiki, releases) +

+
+
+
+ ); +} diff --git a/Divers/gitea-mirror/src/components/config/AutomationSettings.tsx b/Divers/gitea-mirror/src/components/config/AutomationSettings.tsx new file mode 100644 index 0000000..547627f --- /dev/null +++ b/Divers/gitea-mirror/src/components/config/AutomationSettings.tsx @@ -0,0 +1,446 @@ +import { useEffect } from "react"; +import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; +import { Checkbox } from "@/components/ui/checkbox"; +import { Label } from "@/components/ui/label"; +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from "@/components/ui/select"; +import { Switch } from "@/components/ui/switch"; +import { + Clock, + Database, + RefreshCw, + Calendar, + Activity, + Zap, + Info, + Archive, +} from "lucide-react"; +import { + Tooltip, + TooltipContent, + TooltipProvider, + TooltipTrigger, +} from "@/components/ui/tooltip"; +import type { ScheduleConfig, DatabaseCleanupConfig } from "@/types/config"; +import { formatDate } from "@/lib/utils"; + +interface AutomationSettingsProps { + scheduleConfig: ScheduleConfig; + cleanupConfig: DatabaseCleanupConfig; + onScheduleChange: (config: ScheduleConfig) => void; + onCleanupChange: (config: DatabaseCleanupConfig) => void; + isAutoSavingSchedule?: boolean; + isAutoSavingCleanup?: boolean; +} + +const scheduleIntervals = [ + { label: "Every hour", value: 3600 }, + { label: "Every 2 hours", value: 7200 }, + { label: "Every 4 hours", value: 14400 }, + { label: "Every 8 hours", value: 28800 }, + { label: "Every 12 hours", value: 43200 }, + { label: "Daily", value: 86400 }, + { label: "Every 2 days", value: 172800 }, + { label: "Weekly", value: 604800 }, +]; + +const retentionPeriods = [ + { label: "1 day", value: 86400 }, + { label: "3 days", value: 259200 }, + { label: "1 week", value: 604800 }, + { label: "2 weeks", value: 1209600 }, + { label: "1 month", value: 2592000 }, + { label: "2 months", value: 5184000 }, + { label: "3 months", value: 7776000 }, +]; + +function getCleanupInterval(retentionSeconds: number): number { + const days = retentionSeconds / 86400; + if (days <= 1) return 21600; // 6 hours + if (days <= 3) return 43200; // 12 hours + if (days <= 7) return 86400; // 24 hours + if (days <= 30) return 172800; // 48 hours + return 604800; // 1 week +} + +function getCleanupFrequencyText(retentionSeconds: number): string { + const days = retentionSeconds / 86400; + if (days <= 1) return "every 6 hours"; + if (days <= 3) return "every 12 hours"; + if (days <= 7) return "daily"; + if (days <= 30) return "every 2 days"; + return "weekly"; +} + +export function AutomationSettings({ + scheduleConfig, + cleanupConfig, + onScheduleChange, + onCleanupChange, + isAutoSavingSchedule, + isAutoSavingCleanup, +}: AutomationSettingsProps) { + // Update nextRun for cleanup when settings change + useEffect(() => { + if (cleanupConfig.enabled && !cleanupConfig.nextRun) { + const cleanupInterval = getCleanupInterval(cleanupConfig.retentionDays); + const nextRun = new Date(Date.now() + cleanupInterval * 1000); + onCleanupChange({ ...cleanupConfig, nextRun }); + } + }, [cleanupConfig.enabled, cleanupConfig.retentionDays]); + + return ( + + + + + Automation & Maintenance + + + + + + +
+

Background Operations

+

+ These automated tasks run in the background to keep your mirrors up-to-date and maintain optimal database performance. + Choose intervals that match your workflow and repository update frequency. +

+
+
+
+
+
+
+ + +
+ {/* Automatic Syncing Section */} +
+
+

+ + Automatic Syncing +

+ {isAutoSavingSchedule && ( + + )} +
+ +
+
+ + onScheduleChange({ ...scheduleConfig, enabled: !!checked }) + } + /> +
+ +

+ Periodically check GitHub for changes and mirror them to Gitea +

+
+
+ + {scheduleConfig.enabled && ( +
+
+ + +
+
+ )} + +
+
+ + + Last sync + + + {scheduleConfig.lastRun + ? formatDate(scheduleConfig.lastRun) + : "Never"} + +
+ {scheduleConfig.enabled ? ( + scheduleConfig.nextRun && ( +
+ + + Next sync + + + {formatDate(scheduleConfig.nextRun)} + +
+ ) + ) : ( +
+ Enable automatic syncing to schedule periodic repository updates +
+ )} +
+
+
+ + {/* Database Cleanup Section */} +
+
+

+ + Database Maintenance +

+ {isAutoSavingCleanup && ( + + )} +
+ +
+
+ + onCleanupChange({ ...cleanupConfig, enabled: !!checked }) + } + /> +
+ +

+ Remove old activity logs and events to optimize storage +

+
+
+ + {cleanupConfig.enabled && ( +
+
+ +
+ +

+ Cleanup runs {getCleanupFrequencyText(cleanupConfig.retentionDays)} +

+
+
+ +
+ )} + +
+
+ + + Last cleanup + + + {cleanupConfig.lastRun + ? formatDate(cleanupConfig.lastRun) + : "Never"} + +
+ {cleanupConfig.enabled ? ( + cleanupConfig.nextRun && ( +
+ + + Next cleanup + + + {formatDate(cleanupConfig.nextRun)} + +
+ ) + ) : ( +
+ Enable automatic cleanup to optimize database storage +
+ )} +
+
+
+ + {/* Repository Cleanup Section */} +
+
+

+ + Repository Cleanup (orphaned mirrors) +

+ {isAutoSavingCleanup && ( + + )} +
+ +
+
+ + onCleanupChange({ + ...cleanupConfig, + deleteIfNotInGitHub: Boolean(checked), + }) + } + /> +
+ +

+ Keep your Gitea backups when GitHub repos disappear. Archive is the safest option—it preserves data and disables automatic syncs. +

+
+
+ + {cleanupConfig.deleteIfNotInGitHub && ( +
+
+ + +

+ Archive renames mirror backups with an archived- prefix and disables automatic syncs—use Manual Sync when you want to refresh. +

+
+ +
+
+ +

+ When enabled, cleanup logs the planned action without modifying repositories. +

+
+ + onCleanupChange({ + ...cleanupConfig, + dryRun: Boolean(checked), + }) + } + /> +
+
+ )} +
+
+
+
+
+ ); +} diff --git a/Divers/gitea-mirror/src/components/config/ConfigTabs.tsx b/Divers/gitea-mirror/src/components/config/ConfigTabs.tsx new file mode 100644 index 0000000..6a05fb8 --- /dev/null +++ b/Divers/gitea-mirror/src/components/config/ConfigTabs.tsx @@ -0,0 +1,703 @@ +import { useEffect, useState, useCallback, useRef } from 'react'; +import { GitHubConfigForm } from './GitHubConfigForm'; +import { GiteaConfigForm } from './GiteaConfigForm'; +import { AutomationSettings } from './AutomationSettings'; +import { SSOSettings } from './SSOSettings'; +import type { + ConfigApiResponse, + GiteaConfig, + GitHubConfig, + SaveConfigApiRequest, + SaveConfigApiResponse, + ScheduleConfig, + DatabaseCleanupConfig, + MirrorOptions, + AdvancedOptions, +} from '@/types/config'; +import { Button } from '../ui/button'; +import { useAuth } from '@/hooks/useAuth'; +import { apiRequest, showErrorToast } from '@/lib/utils'; +import { RefreshCw } from 'lucide-react'; +import { toast } from 'sonner'; +import { Skeleton } from '@/components/ui/skeleton'; +import { invalidateConfigCache } from '@/hooks/useConfigStatus'; +import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'; + +type ConfigState = { + githubConfig: GitHubConfig; + giteaConfig: GiteaConfig; + scheduleConfig: ScheduleConfig; + cleanupConfig: DatabaseCleanupConfig; + mirrorOptions: MirrorOptions; + advancedOptions: AdvancedOptions; +}; + +export function ConfigTabs() { + const [config, setConfig] = useState({ + githubConfig: { + username: '', + token: '', + privateRepositories: false, + mirrorStarred: false, + }, + giteaConfig: { + url: '', + username: '', + token: '', + organization: 'github-mirrors', + visibility: 'public', + starredReposOrg: 'starred', + preserveOrgStructure: false, + }, + scheduleConfig: { + enabled: false, // Don't set defaults here - will be loaded from API + interval: 0, // Will be replaced with actual value from API + }, + cleanupConfig: { + enabled: false, // Don't set defaults here - will be loaded from API + retentionDays: 0, // Will be replaced with actual value from API + deleteIfNotInGitHub: true, + orphanedRepoAction: "archive", + dryRun: false, + deleteFromGitea: false, + protectedRepos: [], + }, + mirrorOptions: { + mirrorReleases: false, + mirrorLFS: false, + mirrorMetadata: false, + metadataComponents: { + issues: false, + pullRequests: false, + labels: false, + milestones: false, + wiki: false, + }, + }, + advancedOptions: { + skipForks: false, + starredCodeOnly: false, + }, + }); + const { user } = useAuth(); + const [isLoading, setIsLoading] = useState(true); + const [isSyncing, setIsSyncing] = useState(false); + + const [isAutoSavingSchedule, setIsAutoSavingSchedule] = useState(false); + const [isAutoSavingCleanup, setIsAutoSavingCleanup] = useState(false); + const [isAutoSavingGitHub, setIsAutoSavingGitHub] = useState(false); + const [isAutoSavingGitea, setIsAutoSavingGitea] = useState(false); + const autoSaveScheduleTimeoutRef = useRef(null); + const autoSaveCleanupTimeoutRef = useRef(null); + const autoSaveGitHubTimeoutRef = useRef(null); + const autoSaveGiteaTimeoutRef = useRef(null); + + const isConfigFormValid = (): boolean => { + const { githubConfig, giteaConfig } = config; + const isGitHubValid = !!( + githubConfig.username.trim() && githubConfig.token.trim() + ); + const isGiteaValid = !!( + giteaConfig.url.trim() && + giteaConfig.username.trim() && + giteaConfig.token.trim() + ); + return isGitHubValid && isGiteaValid; + }; + + const isGitHubConfigValid = (): boolean => { + const { githubConfig } = config; + return !!(githubConfig.username.trim() && githubConfig.token.trim()); + }; + + // Removed the problematic useEffect that was causing circular dependencies + // The lastRun and nextRun should be managed by the backend and fetched via API + + const handleImportGitHubData = async () => { + if (!user?.id) return; + setIsSyncing(true); + try { + const result = await apiRequest<{ success: boolean; message?: string }>( + `/sync?userId=${user.id}`, + { method: 'POST' }, + ); + result.success + ? toast.success( + 'GitHub data imported successfully! Head to the Repositories page to start mirroring.', + ) + : toast.error( + `Failed to import GitHub data: ${ + result.message || 'Unknown error' + }`, + ); + } catch (error) { + toast.error( + `Error importing GitHub data: ${ + error instanceof Error ? error.message : String(error) + }`, + ); + } finally { + setIsSyncing(false); + } + }; + + // Auto-save function specifically for schedule config changes + const autoSaveScheduleConfig = useCallback(async (scheduleConfig: ScheduleConfig) => { + if (!user?.id) return; + + // Clear any existing timeout + if (autoSaveScheduleTimeoutRef.current) { + clearTimeout(autoSaveScheduleTimeoutRef.current); + } + + // Debounce the auto-save to prevent excessive API calls + autoSaveScheduleTimeoutRef.current = setTimeout(async () => { + setIsAutoSavingSchedule(true); + + const reqPayload: SaveConfigApiRequest = { + userId: user.id!, + githubConfig: config.githubConfig, + giteaConfig: config.giteaConfig, + scheduleConfig: scheduleConfig, + cleanupConfig: config.cleanupConfig, + mirrorOptions: config.mirrorOptions, + advancedOptions: config.advancedOptions, + }; + + try { + const response = await fetch('/api/config', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(reqPayload), + }); + const result: SaveConfigApiResponse = await response.json(); + + if (result.success) { + // Silent success - no toast for auto-save + // Removed refreshUser() call to prevent page reload + // Invalidate config cache so other components get fresh data + invalidateConfigCache(); + + // Fetch updated config to get the recalculated nextRun time + try { + const updatedResponse = await apiRequest( + `/config?userId=${user.id}`, + { method: 'GET' }, + ); + if (updatedResponse && !updatedResponse.error) { + setConfig(prev => ({ + ...prev, + scheduleConfig: updatedResponse.scheduleConfig || prev.scheduleConfig, + })); + } + } catch (fetchError) { + console.warn('Failed to fetch updated config after auto-save:', fetchError); + } + } else { + showErrorToast( + `Auto-save failed: ${result.message || 'Unknown error'}`, + toast + ); + } + } catch (error) { + showErrorToast(error, toast); + } finally { + setIsAutoSavingSchedule(false); + } + }, 500); // 500ms debounce + }, [user?.id, config.githubConfig, config.giteaConfig, config.cleanupConfig]); + + // Auto-save function specifically for cleanup config changes + const autoSaveCleanupConfig = useCallback(async (cleanupConfig: DatabaseCleanupConfig) => { + if (!user?.id) return; + + // Clear any existing timeout + if (autoSaveCleanupTimeoutRef.current) { + clearTimeout(autoSaveCleanupTimeoutRef.current); + } + + // Debounce the auto-save to prevent excessive API calls + autoSaveCleanupTimeoutRef.current = setTimeout(async () => { + setIsAutoSavingCleanup(true); + + const reqPayload: SaveConfigApiRequest = { + userId: user.id!, + githubConfig: config.githubConfig, + giteaConfig: config.giteaConfig, + scheduleConfig: config.scheduleConfig, + cleanupConfig: cleanupConfig, + mirrorOptions: config.mirrorOptions, + advancedOptions: config.advancedOptions, + }; + + try { + const response = await fetch('/api/config', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(reqPayload), + }); + const result: SaveConfigApiResponse = await response.json(); + + if (result.success) { + // Silent success - no toast for auto-save + // Invalidate config cache so other components get fresh data + invalidateConfigCache(); + + // Fetch updated config to get the recalculated nextRun time + try { + const updatedResponse = await apiRequest( + `/config?userId=${user.id}`, + { method: 'GET' }, + ); + if (updatedResponse && !updatedResponse.error) { + setConfig(prev => ({ + ...prev, + cleanupConfig: updatedResponse.cleanupConfig || prev.cleanupConfig, + })); + } + } catch (fetchError) { + console.warn('Failed to fetch updated config after auto-save:', fetchError); + } + } else { + showErrorToast( + `Auto-save failed: ${result.message || 'Unknown error'}`, + toast + ); + } + } catch (error) { + showErrorToast(error, toast); + } finally { + setIsAutoSavingCleanup(false); + } + }, 500); // 500ms debounce + }, [user?.id, config.githubConfig, config.giteaConfig, config.scheduleConfig]); + + // Auto-save function specifically for GitHub config changes + const autoSaveGitHubConfig = useCallback(async (githubConfig: GitHubConfig) => { + if (!user?.id) return; + + // Clear any existing timeout + if (autoSaveGitHubTimeoutRef.current) { + clearTimeout(autoSaveGitHubTimeoutRef.current); + } + + // Debounce the auto-save to prevent excessive API calls + autoSaveGitHubTimeoutRef.current = setTimeout(async () => { + setIsAutoSavingGitHub(true); + + const reqPayload: SaveConfigApiRequest = { + userId: user.id!, + githubConfig: githubConfig, + giteaConfig: config.giteaConfig, + scheduleConfig: config.scheduleConfig, + cleanupConfig: config.cleanupConfig, + mirrorOptions: config.mirrorOptions, + advancedOptions: config.advancedOptions, + }; + + try { + const response = await fetch('/api/config', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(reqPayload), + }); + const result: SaveConfigApiResponse = await response.json(); + + if (result.success) { + // Silent success - no toast for auto-save + // Invalidate config cache so other components get fresh data + invalidateConfigCache(); + } else { + showErrorToast( + `Auto-save failed: ${result.message || 'Unknown error'}`, + toast + ); + } + } catch (error) { + showErrorToast(error, toast); + } finally { + setIsAutoSavingGitHub(false); + } + }, 500); // 500ms debounce + }, [user?.id, config.giteaConfig, config.scheduleConfig, config.cleanupConfig]); + + // Auto-save function specifically for Gitea config changes + const autoSaveGiteaConfig = useCallback(async (giteaConfig: GiteaConfig) => { + if (!user?.id) return; + + // Clear any existing timeout + if (autoSaveGiteaTimeoutRef.current) { + clearTimeout(autoSaveGiteaTimeoutRef.current); + } + + // Debounce the auto-save to prevent excessive API calls + autoSaveGiteaTimeoutRef.current = setTimeout(async () => { + setIsAutoSavingGitea(true); + + const reqPayload: SaveConfigApiRequest = { + userId: user.id!, + githubConfig: config.githubConfig, + giteaConfig: giteaConfig, + scheduleConfig: config.scheduleConfig, + cleanupConfig: config.cleanupConfig, + mirrorOptions: config.mirrorOptions, + advancedOptions: config.advancedOptions, + }; + + try { + const response = await fetch('/api/config', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(reqPayload), + }); + const result: SaveConfigApiResponse = await response.json(); + + if (result.success) { + // Silent success - no toast for auto-save + // Invalidate config cache so other components get fresh data + invalidateConfigCache(); + } else { + showErrorToast( + `Auto-save failed: ${result.message || 'Unknown error'}`, + toast + ); + } + } catch (error) { + showErrorToast(error, toast); + } finally { + setIsAutoSavingGitea(false); + } + }, 500); // 500ms debounce + }, [user?.id, config.githubConfig, config.scheduleConfig, config.cleanupConfig]); + + // Auto-save function for mirror options (handled within GitHub config) + const autoSaveMirrorOptions = useCallback(async (mirrorOptions: MirrorOptions) => { + if (!user?.id) return; + + const reqPayload: SaveConfigApiRequest = { + userId: user.id!, + githubConfig: config.githubConfig, + giteaConfig: config.giteaConfig, + scheduleConfig: config.scheduleConfig, + cleanupConfig: config.cleanupConfig, + mirrorOptions: mirrorOptions, + advancedOptions: config.advancedOptions, + }; + + try { + const response = await fetch('/api/config', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(reqPayload), + }); + const result: SaveConfigApiResponse = await response.json(); + + if (result.success) { + invalidateConfigCache(); + } else { + showErrorToast( + `Auto-save failed: ${result.message || 'Unknown error'}`, + toast + ); + } + } catch (error) { + showErrorToast(error, toast); + } + }, [user?.id, config.githubConfig, config.giteaConfig, config.scheduleConfig, config.cleanupConfig, config.advancedOptions]); + + // Auto-save function for advanced options (handled within GitHub config) + const autoSaveAdvancedOptions = useCallback(async (advancedOptions: AdvancedOptions) => { + if (!user?.id) return; + + const reqPayload: SaveConfigApiRequest = { + userId: user.id!, + githubConfig: config.githubConfig, + giteaConfig: config.giteaConfig, + scheduleConfig: config.scheduleConfig, + cleanupConfig: config.cleanupConfig, + mirrorOptions: config.mirrorOptions, + advancedOptions: advancedOptions, + }; + + try { + const response = await fetch('/api/config', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(reqPayload), + }); + const result: SaveConfigApiResponse = await response.json(); + + if (result.success) { + invalidateConfigCache(); + } else { + showErrorToast( + `Auto-save failed: ${result.message || 'Unknown error'}`, + toast + ); + } + } catch (error) { + showErrorToast(error, toast); + } + }, [user?.id, config.githubConfig, config.giteaConfig, config.scheduleConfig, config.cleanupConfig, config.mirrorOptions]); + + // Cleanup timeouts on unmount + useEffect(() => { + return () => { + if (autoSaveScheduleTimeoutRef.current) { + clearTimeout(autoSaveScheduleTimeoutRef.current); + } + if (autoSaveCleanupTimeoutRef.current) { + clearTimeout(autoSaveCleanupTimeoutRef.current); + } + if (autoSaveGitHubTimeoutRef.current) { + clearTimeout(autoSaveGitHubTimeoutRef.current); + } + if (autoSaveGiteaTimeoutRef.current) { + clearTimeout(autoSaveGiteaTimeoutRef.current); + } + }; + }, []); + + useEffect(() => { + if (!user?.id) return; + + const fetchConfig = async () => { + setIsLoading(true); + try { + const response = await apiRequest( + `/config?userId=${user.id}`, + { method: 'GET' }, + ); + if (response && !response.error) { + setConfig({ + githubConfig: + response.githubConfig || config.githubConfig, + giteaConfig: + response.giteaConfig || config.giteaConfig, + scheduleConfig: + response.scheduleConfig || config.scheduleConfig, + cleanupConfig: { + ...config.cleanupConfig, + ...response.cleanupConfig, // Merge to preserve all fields + }, + mirrorOptions: { + ...config.mirrorOptions, + ...response.mirrorOptions, // Merge to preserve all fields including new mirrorLFS + }, + advancedOptions: + response.advancedOptions || config.advancedOptions, + }); + + } + } catch (error) { + console.warn( + 'Could not fetch configuration, using defaults:', + error, + ); + } + setIsLoading(false); + }; + + fetchConfig(); + }, [user?.id]); // Only depend on user.id, not the entire user object + + function ConfigCardSkeleton() { + return ( +
+ {/* Header section */} +
+
+ + +
+
+ +
+
+ + {/* Content section - Grid layout */} +
+ {/* GitHub & Gitea connections - Side by side */} +
+
+
+ + +
+
+ + + + + + +
+
+
+
+ + +
+
+ + + + +
+
+
+ + {/* Automation & Maintenance - Full width */} +
+ +
+
+ + + +
+
+ + + +
+
+
+
+
+ ); + } + + return isLoading ? ( +
+ +
+ ) : ( +
+ {/* Header section */} +
+
+

+ Configuration +

+

+ Configure your GitHub and Gitea connections, and set up automatic + mirroring. +

+
+
+ +
+
+ + {/* Content section - Tabs layout */} + + + Connections + Automation + Authentication + + + +
+ + setConfig(prev => ({ + ...prev, + githubConfig: + typeof update === 'function' + ? update(prev.githubConfig) + : update, + })) + } + mirrorOptions={config.mirrorOptions} + setMirrorOptions={update => + setConfig(prev => ({ + ...prev, + mirrorOptions: + typeof update === 'function' + ? update(prev.mirrorOptions) + : update, + })) + } + advancedOptions={config.advancedOptions} + setAdvancedOptions={update => + setConfig(prev => ({ + ...prev, + advancedOptions: + typeof update === 'function' + ? update(prev.advancedOptions) + : update, + })) + } + onAutoSave={autoSaveGitHubConfig} + onMirrorOptionsAutoSave={autoSaveMirrorOptions} + onAdvancedOptionsAutoSave={autoSaveAdvancedOptions} + isAutoSaving={isAutoSavingGitHub} + /> + + setConfig(prev => ({ + ...prev, + giteaConfig: + typeof update === 'function' + ? update(prev.giteaConfig) + : update, + })) + } + onAutoSave={autoSaveGiteaConfig} + isAutoSaving={isAutoSavingGitea} + githubUsername={config.githubConfig.username} + /> +
+
+ + + { + setConfig(prev => ({ ...prev, scheduleConfig: newConfig })); + autoSaveScheduleConfig(newConfig); + }} + onCleanupChange={(newConfig) => { + setConfig(prev => ({ ...prev, cleanupConfig: newConfig })); + autoSaveCleanupConfig(newConfig); + }} + isAutoSavingSchedule={isAutoSavingSchedule} + isAutoSavingCleanup={isAutoSavingCleanup} + /> + + + + + +
+
+ ); +} diff --git a/Divers/gitea-mirror/src/components/config/GitHubConfigForm.tsx b/Divers/gitea-mirror/src/components/config/GitHubConfigForm.tsx new file mode 100644 index 0000000..cfcbd34 --- /dev/null +++ b/Divers/gitea-mirror/src/components/config/GitHubConfigForm.tsx @@ -0,0 +1,220 @@ +import React, { useState } from "react"; +import { Button } from "@/components/ui/button"; +import { + Card, + CardContent, + CardHeader, + CardTitle, +} from "@/components/ui/card"; +import { githubApi } from "@/lib/api"; +import type { GitHubConfig, MirrorOptions, AdvancedOptions } from "@/types/config"; +import { Input } from "../ui/input"; +import { toast } from "sonner"; +import { Info } from "lucide-react"; +import { GitHubMirrorSettings } from "./GitHubMirrorSettings"; +import { Separator } from "../ui/separator"; +import { + HoverCard, + HoverCardContent, + HoverCardTrigger, +} from "@/components/ui/hover-card"; + +interface GitHubConfigFormProps { + config: GitHubConfig; + setConfig: React.Dispatch>; + mirrorOptions: MirrorOptions; + setMirrorOptions: React.Dispatch>; + advancedOptions: AdvancedOptions; + setAdvancedOptions: React.Dispatch>; + onAutoSave?: (githubConfig: GitHubConfig) => Promise; + onMirrorOptionsAutoSave?: (mirrorOptions: MirrorOptions) => Promise; + onAdvancedOptionsAutoSave?: (advancedOptions: AdvancedOptions) => Promise; + isAutoSaving?: boolean; +} + +export function GitHubConfigForm({ + config, + setConfig, + mirrorOptions, + setMirrorOptions, + advancedOptions, + setAdvancedOptions, + onAutoSave, + onMirrorOptionsAutoSave, + onAdvancedOptionsAutoSave, + isAutoSaving +}: GitHubConfigFormProps) { + const [isLoading, setIsLoading] = useState(false); + + const handleChange = (e: React.ChangeEvent) => { + const { name, value, type, checked } = e.target; + + const newConfig = { + ...config, + [name]: type === "checkbox" ? checked : value, + }; + + setConfig(newConfig); + + // Auto-save for all field changes + if (onAutoSave) { + onAutoSave(newConfig); + } + }; + + const testConnection = async () => { + if (!config.token) { + toast.error("GitHub token is required to test the connection"); + return; + } + + setIsLoading(true); + + try { + const result = await githubApi.testConnection(config.token); + if (result.success) { + toast.success("Successfully connected to GitHub!"); + } else { + toast.error("Failed to connect to GitHub. Please check your token."); + } + } catch (error) { + toast.error( + error instanceof Error ? error.message : "An unknown error occurred" + ); + } finally { + setIsLoading(false); + } + }; + + return ( + + + + GitHub Configuration + + {/* Desktop: Show button in header */} + + + + +
+ + +
+ +
+
+ + + + + + + + +
+

GitHub Token Requirements

+
+

+ You need to create a Classic GitHub PAT Token with the following scopes: +

+
    +
  • repo
  • +
  • admin:org
  • +
+

+ The organization access is required for mirroring organization repositories. +

+

+ Generate tokens at{" "} + + github.com/settings/tokens + +

+
+
+
+
+
+ +

+ Required for private repositories, organizations, and starred + repositories. +

+
+ + + + { + setConfig(newConfig); + if (onAutoSave) onAutoSave(newConfig); + }} + onMirrorOptionsChange={(newOptions) => { + setMirrorOptions(newOptions); + if (onMirrorOptionsAutoSave) onMirrorOptionsAutoSave(newOptions); + }} + onAdvancedOptionsChange={(newOptions) => { + setAdvancedOptions(newOptions); + if (onAdvancedOptionsAutoSave) onAdvancedOptionsAutoSave(newOptions); + }} + /> + + {/* Mobile: Show button at bottom */} + +
+ +
+ ); +} diff --git a/Divers/gitea-mirror/src/components/config/GitHubMirrorSettings.tsx b/Divers/gitea-mirror/src/components/config/GitHubMirrorSettings.tsx new file mode 100644 index 0000000..9c1821f --- /dev/null +++ b/Divers/gitea-mirror/src/components/config/GitHubMirrorSettings.tsx @@ -0,0 +1,641 @@ +import React from "react"; +import { Checkbox } from "@/components/ui/checkbox"; +import { Label } from "@/components/ui/label"; +import { Separator } from "@/components/ui/separator"; +import { Badge } from "@/components/ui/badge"; +import { Button } from "@/components/ui/button"; +import { + Tooltip, + TooltipContent, + TooltipProvider, + TooltipTrigger, +} from "@/components/ui/tooltip"; +import { + Popover, + PopoverContent, + PopoverTrigger, +} from "@/components/ui/popover"; +import { + Info, + GitBranch, + Star, + Lock, + Archive, + GitPullRequest, + Tag, + FileText, + MessageSquare, + Target, + BookOpen, + GitFork, + ChevronDown, + Funnel, + HardDrive, + FileCode2 +} from "lucide-react"; +import type { GitHubConfig, MirrorOptions, AdvancedOptions, DuplicateNameStrategy } from "@/types/config"; +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from "@/components/ui/select"; +import { cn } from "@/lib/utils"; + +interface GitHubMirrorSettingsProps { + githubConfig: GitHubConfig; + mirrorOptions: MirrorOptions; + advancedOptions: AdvancedOptions; + onGitHubConfigChange: (config: GitHubConfig) => void; + onMirrorOptionsChange: (options: MirrorOptions) => void; + onAdvancedOptionsChange: (options: AdvancedOptions) => void; +} + +export function GitHubMirrorSettings({ + githubConfig, + mirrorOptions, + advancedOptions, + onGitHubConfigChange, + onMirrorOptionsChange, + onAdvancedOptionsChange, +}: GitHubMirrorSettingsProps) { + + const handleGitHubChange = (field: keyof GitHubConfig, value: boolean | string) => { + onGitHubConfigChange({ ...githubConfig, [field]: value }); + }; + + const handleMirrorChange = (field: keyof MirrorOptions, value: boolean | number) => { + onMirrorOptionsChange({ ...mirrorOptions, [field]: value }); + }; + + const handleMetadataComponentChange = (component: keyof MirrorOptions['metadataComponents'], value: boolean) => { + onMirrorOptionsChange({ + ...mirrorOptions, + metadataComponents: { + ...mirrorOptions.metadataComponents, + [component]: value, + }, + }); + }; + + const handleAdvancedChange = (field: keyof AdvancedOptions, value: boolean) => { + onAdvancedOptionsChange({ ...advancedOptions, [field]: value }); + }; + + // When metadata is disabled, all components should be disabled + const isMetadataEnabled = mirrorOptions.mirrorMetadata; + + // Calculate what content is included for starred repos + const starredRepoContent = { + code: true, // Always included + releases: !advancedOptions.starredCodeOnly && mirrorOptions.mirrorReleases, + issues: !advancedOptions.starredCodeOnly && mirrorOptions.mirrorMetadata && mirrorOptions.metadataComponents.issues, + pullRequests: !advancedOptions.starredCodeOnly && mirrorOptions.mirrorMetadata && mirrorOptions.metadataComponents.pullRequests, + wiki: !advancedOptions.starredCodeOnly && mirrorOptions.mirrorMetadata && mirrorOptions.metadataComponents.wiki, + }; + + const starredContentCount = Object.entries(starredRepoContent).filter(([key, value]) => key !== 'code' && value).length; + const totalStarredOptions = 4; // releases, issues, PRs, wiki + + return ( +
+ {/* Repository Selection Section */} +
+
+

+ + Repository Selection +

+

+ Choose which repositories to include in mirroring +

+
+ +
+
+ handleGitHubChange('privateRepositories', !!checked)} + /> +
+ +

+ Mirror your private repositories +

+
+
+ +
+
+ handleGitHubChange('mirrorStarred', !!checked)} + /> +
+ +

+ Include repositories you've starred on GitHub +

+
+
+ + {/* Starred repos content selection - responsive layout */} + +
+ + {/* Duplicate name handling for starred repos */} + {githubConfig.mirrorStarred && ( +
+ +
+ +
+

Name collision strategy

+

+ How to handle repos with the same name from different owners +

+
+ +
+
+ )} +
+
+ + + + {/* Content & Data Section */} +
+
+

+ + Content & Data +

+

+ Select what content to mirror from each repository +

+
+ +
+ {/* Code is always mirrored - shown as info */} +
+ +
+

Source code & branches

+

Always included

+
+ Default +
+ +
+ handleMirrorChange('mirrorReleases', !!checked)} + /> +
+
+
+ +

+ Include GitHub releases, tags, and associated assets +

+
+ {mirrorOptions.mirrorReleases && ( +
+ + { + const value = parseInt(e.target.value) || 10; + const clampedValue = Math.min(100, Math.max(1, value)); + handleMirrorChange('releaseLimit', clampedValue); + }} + className="w-16 px-2 py-1 text-xs border border-input rounded bg-background text-foreground" + /> + releases +
+ )} +
+
+
+ +
+ handleMirrorChange('mirrorLFS', !!checked)} + /> +
+ +

+ Mirror Git LFS objects. Requires LFS to be enabled on your Gitea server and Git v2.1.2+ +

+
+
+ +
+
+ handleMirrorChange('mirrorMetadata', !!checked)} + /> +
+ +

+ Mirror issues, pull requests, and other repository data +

+
+
+ + {/* Metadata multi-select - responsive layout */} + +
+
+
+ + + + {/* Filtering & Behavior Section */} +
+
+

+ + Filtering & Behavior +

+

+ Fine-tune what gets excluded from mirroring +

+
+ +
+
+ handleAdvancedChange('skipForks', !!checked)} + /> +
+ +

+ Exclude repositories that are forks of other projects +

+
+
+
+
+
+ ); +} diff --git a/Divers/gitea-mirror/src/components/config/GiteaConfigForm.tsx b/Divers/gitea-mirror/src/components/config/GiteaConfigForm.tsx new file mode 100644 index 0000000..08f542d --- /dev/null +++ b/Divers/gitea-mirror/src/components/config/GiteaConfigForm.tsx @@ -0,0 +1,275 @@ +import React, { useState, useEffect } from "react"; +import { Button } from "@/components/ui/button"; +import { + Card, + CardContent, + CardHeader, + CardTitle, +} from "@/components/ui/card"; +import { giteaApi } from "@/lib/api"; +import type { GiteaConfig, MirrorStrategy } from "@/types/config"; +import { toast } from "sonner"; +import { OrganizationStrategy } from "./OrganizationStrategy"; +import { OrganizationConfiguration } from "./OrganizationConfiguration"; +import { Separator } from "../ui/separator"; + +interface GiteaConfigFormProps { + config: GiteaConfig; + setConfig: React.Dispatch>; + onAutoSave?: (giteaConfig: GiteaConfig) => Promise; + isAutoSaving?: boolean; + githubUsername?: string; +} + +export function GiteaConfigForm({ config, setConfig, onAutoSave, isAutoSaving, githubUsername }: GiteaConfigFormProps) { + const [isLoading, setIsLoading] = useState(false); + + // Derive the mirror strategy from existing config for backward compatibility + const getMirrorStrategy = (): MirrorStrategy => { + if (config.mirrorStrategy) return config.mirrorStrategy; + // Check for mixed mode: when we have both organization and personalReposOrg defined + if (config.organization && config.personalReposOrg && !config.preserveOrgStructure) return "mixed"; + if (config.preserveOrgStructure) return "preserve"; + if (config.organization && config.organization !== config.username) return "single-org"; + return "flat-user"; + }; + + const [mirrorStrategy, setMirrorStrategy] = useState(getMirrorStrategy()); + + // Update config when strategy changes + useEffect(() => { + const newConfig = { ...config }; + + switch (mirrorStrategy) { + case "preserve": + newConfig.preserveOrgStructure = true; + newConfig.mirrorStrategy = "preserve"; + newConfig.personalReposOrg = undefined; // Clear personal repos org in preserve mode + break; + case "single-org": + newConfig.preserveOrgStructure = false; + newConfig.mirrorStrategy = "single-org"; + // Reset to default if coming from mixed mode where it was personal repos org + if (config.mirrorStrategy === "mixed" || !newConfig.organization || newConfig.organization === "github-personal") { + newConfig.organization = "github-mirrors"; + } + break; + case "flat-user": + newConfig.preserveOrgStructure = false; + newConfig.mirrorStrategy = "flat-user"; + newConfig.organization = ""; + break; + case "mixed": + newConfig.preserveOrgStructure = false; + newConfig.mirrorStrategy = "mixed"; + // In mixed mode, organization field represents personal repos org + // Reset it to default if coming from single-org mode + if (config.mirrorStrategy === "single-org" || !newConfig.organization || newConfig.organization === "github-mirrors") { + newConfig.organization = "github-personal"; + } + if (!newConfig.personalReposOrg) { + newConfig.personalReposOrg = "github-personal"; + } + break; + } + + setConfig(newConfig); + if (onAutoSave) { + onAutoSave(newConfig); + } + }, [mirrorStrategy]); + + const handleChange = ( + e: React.ChangeEvent + ) => { + const { name, value, type } = e.target; + const checked = type === "checkbox" ? (e.target as HTMLInputElement).checked : undefined; + + // Special handling for preserveOrgStructure changes + if ( + name === "preserveOrgStructure" && + config.preserveOrgStructure !== checked + ) { + toast.info( + "Changing this setting may affect how repositories are accessed in Gitea. " + + "Existing mirrored repositories will still be accessible during sync operations.", + { + duration: 6000, + position: "top-center", + } + ); + } + + const newConfig = { + ...config, + [name]: type === "checkbox" ? checked : value, + }; + setConfig(newConfig); + + // Auto-save for all field changes + if (onAutoSave) { + onAutoSave(newConfig); + } + }; + + const testConnection = async () => { + if (!config.url || !config.token) { + toast.error("Gitea URL and token are required to test the connection"); + return; + } + + setIsLoading(true); + + try { + const result = await giteaApi.testConnection(config.url, config.token); + if (result.success) { + toast.success("Successfully connected to Gitea!"); + } else { + toast.error( + "Failed to connect to Gitea. Please check your URL and token." + ); + } + } catch (error) { + toast.error( + error instanceof Error ? error.message : "An unknown error occurred" + ); + } finally { + setIsLoading(false); + } + }; + + return ( + + + + Gitea Configuration + + {/* Desktop: Show button in header */} + + + + +
+ + +
+ +
+ + +
+ +
+ + +

+ Create a token in your Gitea instance under Settings > + Applications. +

+
+ + + + + + + + { + const newConfig = { ...config, organization: org }; + setConfig(newConfig); + if (onAutoSave) onAutoSave(newConfig); + }} + onStarredReposOrgChange={(org) => { + const newConfig = { ...config, starredReposOrg: org }; + setConfig(newConfig); + if (onAutoSave) onAutoSave(newConfig); + }} + onPersonalReposOrgChange={(org) => { + const newConfig = { ...config, personalReposOrg: org }; + setConfig(newConfig); + if (onAutoSave) onAutoSave(newConfig); + }} + onVisibilityChange={(visibility) => { + const newConfig = { ...config, visibility }; + setConfig(newConfig); + if (onAutoSave) onAutoSave(newConfig); + }} + /> + + {/* Mobile: Show button at bottom */} + +
+
+ ); +} diff --git a/Divers/gitea-mirror/src/components/config/OrganizationConfiguration.tsx b/Divers/gitea-mirror/src/components/config/OrganizationConfiguration.tsx new file mode 100644 index 0000000..6e4cf99 --- /dev/null +++ b/Divers/gitea-mirror/src/components/config/OrganizationConfiguration.tsx @@ -0,0 +1,175 @@ +import React from "react"; +import { Input } from "@/components/ui/input"; +import { Label } from "@/components/ui/label"; +import { Star, Globe, Lock, Shield, Info, MonitorCog } from "lucide-react"; +import { + Tooltip, + TooltipContent, + TooltipProvider, + TooltipTrigger, +} from "@/components/ui/tooltip"; +import { cn } from "@/lib/utils"; +import type { MirrorStrategy, GiteaOrgVisibility } from "@/types/config"; + +interface OrganizationConfigurationProps { + strategy: MirrorStrategy; + destinationOrg?: string; + starredReposOrg?: string; + personalReposOrg?: string; + visibility: GiteaOrgVisibility; + onDestinationOrgChange: (org: string) => void; + onStarredReposOrgChange: (org: string) => void; + onPersonalReposOrgChange: (org: string) => void; + onVisibilityChange: (visibility: GiteaOrgVisibility) => void; +} + +const visibilityOptions = [ + { value: "public" as GiteaOrgVisibility, label: "Public", icon: Globe, description: "Visible to everyone" }, + { value: "private" as GiteaOrgVisibility, label: "Private", icon: Lock, description: "Visible to members only" }, + { value: "limited" as GiteaOrgVisibility, label: "Limited", icon: Shield, description: "Visible to logged-in users" }, +]; + +export const OrganizationConfiguration: React.FC = ({ + strategy, + destinationOrg, + starredReposOrg, + personalReposOrg, + visibility, + onDestinationOrgChange, + onStarredReposOrgChange, + onPersonalReposOrgChange, + onVisibilityChange, +}) => { + return ( +
+
+

+ + Organization Configuration +

+
+ + {/* First row - Organization inputs with consistent layout */} +
+ {/* Left column - always shows starred repos org */} +
+ + onStarredReposOrgChange(e.target.value)} + placeholder="starred" + className="" + /> +

+ Keep starred repos organized separately +

+
+ + {/* Right column - shows destination org for single-org/mixed, personal repos org for preserve, empty div for others */} + {strategy === "single-org" || strategy === "mixed" ? ( +
+ + onDestinationOrgChange(e.target.value)} + placeholder={strategy === "mixed" ? "github-personal" : "github-mirrors"} + className="" + /> +

+ {strategy === "mixed" + ? "All personal repos will go to this organization" + : "Organization for consolidated repositories" + } +

+
+ ) : ( +
+ )} +
+ + {/* Second row - Organization Visibility (always shown) */} +
+ +
+ {visibilityOptions.map((option) => { + const Icon = option.icon; + const isSelected = visibility === option.value; + return ( + + + + + + +

{option.description}

+
+
+
+ ); + })} +
+
+
+ ); +}; + diff --git a/Divers/gitea-mirror/src/components/config/OrganizationStrategy.tsx b/Divers/gitea-mirror/src/components/config/OrganizationStrategy.tsx new file mode 100644 index 0000000..c6f0315 --- /dev/null +++ b/Divers/gitea-mirror/src/components/config/OrganizationStrategy.tsx @@ -0,0 +1,437 @@ +import React from "react"; +import { Card } from "@/components/ui/card"; +import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group"; +import { Info, GitBranch, FolderTree, Star, Building2, User, Building } from "lucide-react"; +import { + HoverCard, + HoverCardContent, + HoverCardTrigger, +} from "@/components/ui/hover-card"; +import { cn } from "@/lib/utils"; + +export type MirrorStrategy = "preserve" | "single-org" | "flat-user" | "mixed"; + +interface OrganizationStrategyProps { + strategy: MirrorStrategy; + destinationOrg?: string; + starredReposOrg?: string; + onStrategyChange: (strategy: MirrorStrategy) => void; + githubUsername?: string; + giteaUsername?: string; +} + +const strategyConfig = { + preserve: { + title: "Preserve Structure", + icon: FolderTree, + description: "Keep the exact same org structure as GitHub", + color: "text-blue-600 dark:text-blue-400", + bgColor: "bg-blue-50 dark:bg-blue-950/20", + borderColor: "border-blue-200 dark:border-blue-900", + repoColors: { + bg: "bg-blue-50 dark:bg-blue-950/30", + icon: "text-blue-600 dark:text-blue-400" + } + }, + "single-org": { + title: "Single Organization", + icon: Building2, + description: "Consolidate all repositories into one Gitea organization", + color: "text-purple-600 dark:text-purple-400", + bgColor: "bg-purple-50 dark:bg-purple-950/20", + borderColor: "border-purple-200 dark:border-purple-900", + repoColors: { + bg: "bg-purple-50 dark:bg-purple-950/30", + icon: "text-purple-600 dark:text-purple-400" + } + }, + "flat-user": { + title: "User Repositories", + icon: User, + description: "Place all repositories directly under your user account", + color: "text-green-600 dark:text-green-400", + bgColor: "bg-green-50 dark:bg-green-950/20", + borderColor: "border-green-200 dark:border-green-900", + repoColors: { + bg: "bg-green-50 dark:bg-green-950/30", + icon: "text-green-600 dark:text-green-400" + } + }, + "mixed": { + title: "Mixed Mode", + icon: GitBranch, + description: "Personal repos in single org, org repos preserve structure", + color: "text-orange-600 dark:text-orange-400", + bgColor: "bg-orange-50 dark:bg-orange-950/20", + borderColor: "border-orange-200 dark:border-orange-900", + repoColors: { + bg: "bg-orange-50 dark:bg-orange-950/30", + icon: "text-orange-600 dark:text-orange-400" + } + } +}; + +const MappingPreview: React.FC<{ + strategy: MirrorStrategy; + config: typeof strategyConfig.preserve; + destinationOrg?: string; + starredReposOrg?: string; + githubUsername?: string; + giteaUsername?: string; +}> = ({ strategy, config, destinationOrg, starredReposOrg, githubUsername, giteaUsername }) => { + const displayGithubUsername = githubUsername || ""; + const displayGiteaUsername = giteaUsername || ""; + const isGithubPlaceholder = !githubUsername; + const isGiteaPlaceholder = !giteaUsername; + + if (strategy === "preserve") { + return ( +
+
+
GitHub
+
+
+ + {displayGithubUsername}/my-repo +
+
+ + my-org/team-repo +
+
+ + awesome/starred-repo +
+
+
+ +
+ +
+ +
+
Gitea
+
+
+ + {displayGiteaUsername}/my-repo +
+
+ + my-org/team-repo +
+
+ + {starredReposOrg || "starred"}/starred-repo +
+
+
+
+ ); + } + + if (strategy === "single-org") { + return ( +
+
+
GitHub
+
+
+ + {displayGithubUsername}/my-repo +
+
+ + my-org/team-repo +
+
+ + awesome/starred-repo +
+
+
+ +
+ +
+ +
+
Gitea
+
+
+ + {destinationOrg || "github-mirrors"}/my-repo +
+
+ + {destinationOrg || "github-mirrors"}/team-repo +
+
+ + {starredReposOrg || "starred"}/starred-repo +
+
+
+
+ ); + } + + if (strategy === "flat-user") { + return ( +
+
+
GitHub
+
+
+ + {displayGithubUsername}/my-repo +
+
+ + my-org/team-repo +
+
+ + awesome/starred-repo +
+
+
+ +
+ +
+ +
+
Gitea
+
+
+ + {displayGiteaUsername}/my-repo +
+
+ + {displayGiteaUsername}/team-repo +
+
+ + {starredReposOrg || "starred"}/starred-repo +
+
+
+
+ ); + } + + if (strategy === "mixed") { + return ( +
+
+
GitHub
+
+
+ + {displayGithubUsername}/my-repo +
+
+ + my-org/team-repo +
+
+ + awesome/starred-repo +
+
+
+ +
+ +
+ +
+
Gitea
+
+
+ + {destinationOrg || "github-mirrors"}/my-repo +
+
+ + my-org/team-repo +
+
+ + {starredReposOrg || "starred"}/starred-repo +
+
+
+
+ ); + } + + return null; +}; + +export const OrganizationStrategy: React.FC = ({ + strategy, + destinationOrg, + starredReposOrg, + onStrategyChange, + githubUsername, + giteaUsername, +}) => { + return ( +
+
+
+

+ + Organization Strategy +

+

+ Choose how your repositories will be organized in Gitea +

+
+ +
+ + + + + +
+
+

Fine-tune Your Mirror Destinations

+

+ After selecting a strategy, you can customize destinations for specific organizations and repositories. +

+
+ +
+
+
+ + Organization Overrides +
+

+ Click the edit button on any organization card to redirect all its repositories to a different Gitea organization. +

+
+ +
+
+ + Repository Overrides +
+

+ Use the inline editor in the repository table's "Destination" column to set custom destinations for individual repositories. +

+
+ +
+
+ + Starred Repositories +
+

+ Always go to the configured starred repos organization and cannot be overridden. +

+
+
+ +
+

+ Priority: Repository override → Organization override → Strategy default +

+
+
+
+
+
+
+ + +
+ {(Object.entries(strategyConfig) as [MirrorStrategy, typeof strategyConfig.preserve][]).map(([key, config]) => { + const isSelected = strategy === key; + const Icon = config.icon; + + return ( +
+ +
+ ); + })} +
+
+
+ ); +}; \ No newline at end of file diff --git a/Divers/gitea-mirror/src/components/config/SSOSettings.tsx b/Divers/gitea-mirror/src/components/config/SSOSettings.tsx new file mode 100644 index 0000000..7dd68d2 --- /dev/null +++ b/Divers/gitea-mirror/src/components/config/SSOSettings.tsx @@ -0,0 +1,727 @@ +import { useState, useEffect } from 'react'; +import { Button } from '@/components/ui/button'; +import { Input } from '@/components/ui/input'; +import { Label } from '@/components/ui/label'; +import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'; +import { Switch } from '@/components/ui/switch'; +import { Alert, AlertDescription } from '@/components/ui/alert'; +import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, DialogTrigger } from '@/components/ui/dialog'; +import { apiRequest, showErrorToast } from '@/lib/utils'; +import { toast } from 'sonner'; +import { Plus, Trash2, Loader2, AlertCircle, Shield, Edit2 } from 'lucide-react'; +import { Skeleton } from '../ui/skeleton'; +import { Badge } from '../ui/badge'; +import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'; +import { Textarea } from '@/components/ui/textarea'; +import { MultiSelect } from '@/components/ui/multi-select'; + +function isTrustedIssuer(issuer: string, allowedHosts: string[]): boolean { + try { + const url = new URL(issuer); + return allowedHosts.some(host => url.hostname === host || url.hostname.endsWith(`.${host}`)); + } catch { + return false; // Return false if the URL is invalid + } +} +interface SSOProvider { + id: string; + issuer: string; + domain: string; + providerId: string; + organizationId?: string; + oidcConfig?: { + clientId: string; + clientSecret: string; + authorizationEndpoint: string; + tokenEndpoint: string; + jwksEndpoint?: string; + userInfoEndpoint?: string; + discoveryEndpoint?: string; + scopes?: string[]; + pkce?: boolean; + }; + samlConfig?: { + entryPoint: string; + cert: string; + callbackUrl?: string; + audience?: string; + wantAssertionsSigned?: boolean; + signatureAlgorithm?: string; + digestAlgorithm?: string; + identifierFormat?: string; + }; + mapping?: { + id: string; + email: string; + emailVerified?: string; + name?: string; + image?: string; + firstName?: string; + lastName?: string; + }; + createdAt: string; + updatedAt: string; +} + +export function SSOSettings() { + const [providers, setProviders] = useState([]); + const [isLoading, setIsLoading] = useState(true); + const [showProviderDialog, setShowProviderDialog] = useState(false); + const [addingProvider, setAddingProvider] = useState(false); + const [isDiscovering, setIsDiscovering] = useState(false); + const [headerAuthEnabled, setHeaderAuthEnabled] = useState(false); + const [editingProvider, setEditingProvider] = useState(null); + + // Form states for new provider + const [providerType, setProviderType] = useState<'oidc' | 'saml'>('oidc'); + const [providerForm, setProviderForm] = useState({ + // Common fields + issuer: '', + domain: '', + providerId: '', + organizationId: '', + // OIDC fields + clientId: '', + clientSecret: '', + authorizationEndpoint: '', + tokenEndpoint: '', + jwksEndpoint: '', + userInfoEndpoint: '', + discoveryEndpoint: '', + scopes: ['openid', 'email', 'profile'] as string[], + pkce: true, + // SAML fields + entryPoint: '', + cert: '', + callbackUrl: '', + audience: '', + wantAssertionsSigned: true, + signatureAlgorithm: 'sha256', + digestAlgorithm: 'sha256', + identifierFormat: 'urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress', + }); + + + + useEffect(() => { + loadData(); + }, []); + + const loadData = async () => { + setIsLoading(true); + try { + const [providersRes, headerAuthStatus] = await Promise.all([ + apiRequest('/sso/providers'), + apiRequest<{ enabled: boolean }>('/auth/header-status').catch(() => ({ enabled: false })) + ]); + + setProviders(Array.isArray(providersRes) ? providersRes : providersRes?.providers || []); + setHeaderAuthEnabled(headerAuthStatus.enabled); + } catch (error) { + showErrorToast(error, toast); + } finally { + setIsLoading(false); + } + }; + + const discoverOIDC = async () => { + if (!providerForm.issuer) { + toast.error('Please enter an issuer URL'); + return; + } + + setIsDiscovering(true); + try { + const discovered = await apiRequest('/sso/discover', { + method: 'POST', + data: { issuer: providerForm.issuer }, + }); + + setProviderForm(prev => ({ + ...prev, + authorizationEndpoint: discovered.authorizationEndpoint || '', + tokenEndpoint: discovered.tokenEndpoint || '', + jwksEndpoint: discovered.jwksEndpoint || '', + userInfoEndpoint: discovered.userInfoEndpoint || '', + discoveryEndpoint: discovered.discoveryEndpoint || `${providerForm.issuer}/.well-known/openid-configuration`, + domain: discovered.suggestedDomain || prev.domain, + })); + + toast.success('OIDC configuration discovered successfully'); + } catch (error) { + showErrorToast(error, toast); + } finally { + setIsDiscovering(false); + } + }; + + const createProvider = async () => { + setAddingProvider(true); + try { + const requestData: any = { + providerId: providerForm.providerId, + issuer: providerForm.issuer, + domain: providerForm.domain, + organizationId: providerForm.organizationId || undefined, + providerType, + }; + + if (providerType === 'oidc') { + requestData.clientId = providerForm.clientId; + requestData.clientSecret = providerForm.clientSecret; + requestData.authorizationEndpoint = providerForm.authorizationEndpoint; + requestData.tokenEndpoint = providerForm.tokenEndpoint; + requestData.jwksEndpoint = providerForm.jwksEndpoint; + requestData.userInfoEndpoint = providerForm.userInfoEndpoint; + requestData.discoveryEndpoint = providerForm.discoveryEndpoint; + requestData.scopes = providerForm.scopes; + requestData.pkce = providerForm.pkce; + } else { + requestData.entryPoint = providerForm.entryPoint; + requestData.cert = providerForm.cert; + requestData.callbackUrl = providerForm.callbackUrl || `${window.location.origin}/api/auth/sso/saml2/callback/${providerForm.providerId}`; + requestData.audience = providerForm.audience || window.location.origin; + requestData.wantAssertionsSigned = providerForm.wantAssertionsSigned; + requestData.signatureAlgorithm = providerForm.signatureAlgorithm; + requestData.digestAlgorithm = providerForm.digestAlgorithm; + requestData.identifierFormat = providerForm.identifierFormat; + } + + if (editingProvider) { + // Update existing provider + const updatedProvider = await apiRequest(`/sso/providers?id=${editingProvider.id}`, { + method: 'PUT', + data: requestData, + }); + setProviders(providers.map(p => p.id === editingProvider.id ? updatedProvider : p)); + toast.success('SSO provider updated successfully'); + } else { + // Create new provider + const newProvider = await apiRequest('/sso/providers', { + method: 'POST', + data: requestData, + }); + setProviders([...providers, newProvider]); + toast.success('SSO provider created successfully'); + } + + setShowProviderDialog(false); + setEditingProvider(null); + setProviderForm({ + issuer: '', + domain: '', + providerId: '', + organizationId: '', + clientId: '', + clientSecret: '', + authorizationEndpoint: '', + tokenEndpoint: '', + jwksEndpoint: '', + userInfoEndpoint: '', + discoveryEndpoint: '', + scopes: ['openid', 'email', 'profile'] as string[], + pkce: true, + entryPoint: '', + cert: '', + callbackUrl: '', + audience: '', + wantAssertionsSigned: true, + signatureAlgorithm: 'sha256', + digestAlgorithm: 'sha256', + identifierFormat: 'urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress', + }); + } catch (error) { + showErrorToast(error, toast); + } finally { + setAddingProvider(false); + } + }; + + const startEditProvider = (provider: SSOProvider) => { + setEditingProvider(provider); + setProviderType(provider.samlConfig ? 'saml' : 'oidc'); + + if (provider.oidcConfig) { + setProviderForm({ + ...providerForm, + providerId: provider.providerId, + issuer: provider.issuer, + domain: provider.domain, + organizationId: provider.organizationId || '', + clientId: provider.oidcConfig.clientId || '', + clientSecret: provider.oidcConfig.clientSecret || '', + authorizationEndpoint: provider.oidcConfig.authorizationEndpoint || '', + tokenEndpoint: provider.oidcConfig.tokenEndpoint || '', + jwksEndpoint: provider.oidcConfig.jwksEndpoint || '', + userInfoEndpoint: provider.oidcConfig.userInfoEndpoint || '', + discoveryEndpoint: provider.oidcConfig.discoveryEndpoint || '', + scopes: provider.oidcConfig.scopes || ['openid', 'email', 'profile'], + pkce: provider.oidcConfig.pkce !== false, + }); + } + + setShowProviderDialog(true); + }; + + const deleteProvider = async (id: string) => { + try { + await apiRequest(`/sso/providers?id=${id}`, { method: 'DELETE' }); + setProviders(providers.filter(p => p.id !== id)); + toast.success('Provider deleted successfully'); + } catch (error) { + showErrorToast(error, toast); + } + }; + + + + if (isLoading) { + return ( +
+ + +
+ ); + } + + return ( +
+ {/* Header with status indicators */} +
+
+

Authentication & SSO

+

+ Configure how users authenticate with your application +

+
+
+
0 ? 'bg-green-500' : 'bg-muted'}`} /> + + {providers.length} Provider{providers.length !== 1 ? 's' : ''} configured + +
+
+ + {/* Authentication Methods Overview */} + + + Active Authentication Methods + + +
+ {/* Email & Password - Always enabled */} +
+
+
+ Email & Password + Default +
+ Always enabled +
+ + {/* Header Authentication Status */} + {headerAuthEnabled && ( +
+
+
+ Header Authentication + Auto-login +
+ Via reverse proxy +
+ )} + + {/* SSO Providers Status */} +
+
+
0 ? 'bg-green-500' : 'bg-muted'}`} /> + SSO/OIDC Providers +
+ + {providers.length > 0 ? `${providers.length} provider${providers.length !== 1 ? 's' : ''} configured` : 'Not configured'} + +
+
+ + {/* Header Auth Info */} + {headerAuthEnabled && ( + + + + Header authentication is enabled. Users authenticated by your reverse proxy will be automatically logged in. + + + )} + + + + {/* SSO Providers */} + + +
+
+ External Identity Providers + + Connect external OIDC/OAuth providers (Google, Azure AD, etc.) to allow users to sign in with their existing accounts + +
+ + + + + + + {editingProvider ? 'Edit SSO Provider' : 'Add SSO Provider'} + + {editingProvider + ? 'Update the configuration for this identity provider' + : 'Configure an external identity provider for user authentication'} + + +
+ setProviderType(value as 'oidc' | 'saml')}> + + OIDC / OAuth2 + SAML 2.0 + + + {/* Common Fields */} +
+
+
+ + setProviderForm(prev => ({ ...prev, providerId: e.target.value }))} + placeholder="google-sso" + disabled={!!editingProvider} + /> +
+
+ + setProviderForm(prev => ({ ...prev, domain: e.target.value }))} + placeholder="example.com" + /> +
+
+ +
+ +
+ setProviderForm(prev => ({ ...prev, issuer: e.target.value }))} + placeholder={providerType === 'oidc' ? "https://accounts.google.com" : "https://idp.example.com"} + /> + {providerType === 'oidc' && ( + + )} +
+
+ +
+ + setProviderForm(prev => ({ ...prev, organizationId: e.target.value }))} + placeholder="org_123" + /> +

Link this provider to an organization for automatic user provisioning

+
+
+ + +
+
+ + setProviderForm(prev => ({ ...prev, clientId: e.target.value }))} + /> +
+
+ + setProviderForm(prev => ({ ...prev, clientSecret: e.target.value }))} + /> +
+
+ +
+ + setProviderForm(prev => ({ ...prev, authorizationEndpoint: e.target.value }))} + placeholder="https://accounts.google.com/o/oauth2/auth" + /> +
+ +
+ + setProviderForm(prev => ({ ...prev, tokenEndpoint: e.target.value }))} + placeholder="https://oauth2.googleapis.com/token" + /> +
+ +
+ + setProviderForm(prev => ({ ...prev, scopes }))} + placeholder="Select scopes..." + /> +

+ Select the OAuth scopes to request from the provider +

+
+ +
+ setProviderForm(prev => ({ ...prev, pkce: checked }))} + /> + +
+ + + + +
+

Redirect URL: {window.location.origin}/api/auth/sso/callback/{providerForm.providerId || '{provider-id}'}

+ {isTrustedIssuer(providerForm.issuer, ['google.com']) && ( +

+ Note: Google doesn't support the "offline_access" scope. Make sure to exclude it from the selected scopes. +

+ )} +
+
+
+
+ + +
+ + setProviderForm(prev => ({ ...prev, entryPoint: e.target.value }))} + placeholder="https://idp.example.com/sso" + /> +
+ +
+ +