From df2d36e9d95fe5a1c41735ed4c2c2d1f7015f90b Mon Sep 17 00:00:00 2001 From: "benjamin.747" Date: Wed, 8 Jul 2026 14:36:42 +0800 Subject: [PATCH 1/3] fix(web): use runtime CAMPSITE_API_SESSION_COOKIE for SSR session forwarding Drop NEXT_PUBLIC_CAMPSITE_API_SESSION_COOKIE from the build and entrypoint injection path so mega-ui reads the cookie name at runtime, matching Rails SESSION_COOKIE_KEY in k8s without rebuilding the image. --- moon/apps/web/.env.buck2hub | 8 -------- moon/apps/web/.env.demo | 9 --------- moon/apps/web/.env.gitmono | 8 -------- moon/apps/web/.env.openatom | 8 -------- moon/apps/web/.env.runtime | 7 ++++--- moon/apps/web/.env.staging | 9 --------- moon/apps/web/.env.staging-nju | 8 -------- moon/apps/web/docker-entrypoint.sh | 3 +-- moon/apps/web/turbo.json | 1 + moon/apps/web/utils/apiCookieHeaders.ts | 15 ++++++++++++--- moon/apps/web/utils/queryClient.ts | 15 +++++---------- moon/packages/config/src/index.ts | 5 ++--- 12 files changed, 25 insertions(+), 71 deletions(-) delete mode 100644 moon/apps/web/.env.buck2hub delete mode 100644 moon/apps/web/.env.demo delete mode 100644 moon/apps/web/.env.gitmono delete mode 100644 moon/apps/web/.env.openatom delete mode 100644 moon/apps/web/.env.staging delete mode 100644 moon/apps/web/.env.staging-nju diff --git a/moon/apps/web/.env.buck2hub b/moon/apps/web/.env.buck2hub deleted file mode 100644 index 48d8b4bd1..000000000 --- a/moon/apps/web/.env.buck2hub +++ /dev/null @@ -1,8 +0,0 @@ -NEXT_PUBLIC_API_URL=https://api.buck2hub.com -NEXT_PUBLIC_MONO_API_URL=https://git.buck2hub.com -NEXT_PUBLIC_WEB_URL=https://app.buck2hub.com -NEXT_PUBLIC_SYNC_URL=wss://sync.buck2hub.com -NEXT_PUBLIC_AUTH_URL=https://auth.buck2hub.com -NEXT_PUBLIC_CRATES_PRO_URL= -NEXT_PUBLIC_ORION_API_URL=https://orion.buck2hub.com -NEXT_PUBLIC_INTERNAL_API_URL=https://api.buck2hub.com \ No newline at end of file diff --git a/moon/apps/web/.env.demo b/moon/apps/web/.env.demo deleted file mode 100644 index 92cbfc0a8..000000000 --- a/moon/apps/web/.env.demo +++ /dev/null @@ -1,9 +0,0 @@ -NEXT_PUBLIC_API_URL=http://api.gitmono.local:8080 -NEXT_PUBLIC_MONO_API_URL=http://git.gitmono.local:8000 -NEXT_PUBLIC_WEB_URL=http://app.gitmono.local -NEXT_PUBLIC_SYNC_URL=ws://sync.gitmono.local -NEXT_PUBLIC_AUTH_URL=http://auth.gitmono.local:8080 -NEXT_PUBLIC_CRATES_PRO_URL=http://cratespro.gitmega.nju:8080 -NEXT_PUBLIC_ORION_API_URL=http://orion.gitmono.local:8004 - -NEXT_PUBLIC_INTERNAL_API_URL=http://api.gitmono.local:8080 diff --git a/moon/apps/web/.env.gitmono b/moon/apps/web/.env.gitmono deleted file mode 100644 index d2c230e91..000000000 --- a/moon/apps/web/.env.gitmono +++ /dev/null @@ -1,8 +0,0 @@ -NEXT_PUBLIC_API_URL=https://api.gitmono.com -NEXT_PUBLIC_MONO_API_URL=https://git.gitmono.com -NEXT_PUBLIC_WEB_URL=https://app.gitmono.com -NEXT_PUBLIC_SYNC_URL=wss://sync.gitmono.com -NEXT_PUBLIC_AUTH_URL=https://auth.gitmono.com -NEXT_PUBLIC_CRATES_PRO_URL= -NEXT_PUBLIC_ORION_API_URL=https://orion.gitmono.com -NEXT_PUBLIC_INTERNAL_API_URL=https://api.gitmono.com \ No newline at end of file diff --git a/moon/apps/web/.env.openatom b/moon/apps/web/.env.openatom deleted file mode 100644 index 9f2bc44d1..000000000 --- a/moon/apps/web/.env.openatom +++ /dev/null @@ -1,8 +0,0 @@ -NEXT_PUBLIC_API_URL=https://api.xuanwu.openatom.cn -NEXT_PUBLIC_MONO_API_URL=https://git.xuanwu.openatom.cn -NEXT_PUBLIC_WEB_URL=https://app.xuanwu.openatom.cn -NEXT_PUBLIC_SYNC_URL=wss://sync.xuanwu.openatom.cn -NEXT_PUBLIC_AUTH_URL=https://auth.xuanwu.openatom.cn -NEXT_PUBLIC_CRATES_PRO_URL=https://cratespro.xuanwu.openatom.cn -NEXT_PUBLIC_ORION_API_URL=https://orion.xuanwu.openatom.cn -NEXT_PUBLIC_INTERNAL_API_URL=http://campsite-api.mega-rag.svc.cluster.local \ No newline at end of file diff --git a/moon/apps/web/.env.runtime b/moon/apps/web/.env.runtime index 722421bfc..753962fea 100644 --- a/moon/apps/web/.env.runtime +++ b/moon/apps/web/.env.runtime @@ -12,8 +12,10 @@ # Provide the real values at runtime (ECS task definition / Cloud Run env): # NEXT_PUBLIC_API_URL, NEXT_PUBLIC_INTERNAL_API_URL, NEXT_PUBLIC_MONO_API_URL, # NEXT_PUBLIC_ORION_API_URL, NEXT_PUBLIC_AUTH_URL, NEXT_PUBLIC_WEB_URL, -# NEXT_PUBLIC_SYNC_URL, NEXT_PUBLIC_CRATES_PRO_URL, -# NEXT_PUBLIC_CAMPSITE_API_SESSION_COOKIE +# NEXT_PUBLIC_SYNC_URL, NEXT_PUBLIC_CRATES_PRO_URL +# +# Session cookie name for SSR is configured at runtime via CAMPSITE_API_SESSION_COOKIE +# (default: _campsite_api_session). See apps/web/utils/apiCookieHeaders.ts. # ============================================================================= NEXT_PUBLIC_API_URL=https://rt-api.placeholder.local @@ -24,4 +26,3 @@ NEXT_PUBLIC_AUTH_URL=https://rt-auth.placeholder.local NEXT_PUBLIC_WEB_URL=https://rt-web.placeholder.local NEXT_PUBLIC_SYNC_URL=wss://rt-sync.placeholder.local NEXT_PUBLIC_CRATES_PRO_URL=https://rt-crates-pro.placeholder.local -NEXT_PUBLIC_CAMPSITE_API_SESSION_COOKIE=__rt_campsite_api_session_cookie__ diff --git a/moon/apps/web/.env.staging b/moon/apps/web/.env.staging deleted file mode 100644 index 4edbe5bf6..000000000 --- a/moon/apps/web/.env.staging +++ /dev/null @@ -1,9 +0,0 @@ -NEXT_PUBLIC_API_URL=https://api.gitmega.com -NEXT_PUBLIC_MONO_API_URL=https://git.gitmega.com -NEXT_PUBLIC_WEB_URL=https://app.gitmega.com -NEXT_PUBLIC_SYNC_URL=wss://sync.gitmega.com -NEXT_PUBLIC_AUTH_URL=https://auth.gitmega.com -NEXT_PUBLIC_CRATES_PRO_URL=http://cratespro.gitmega.nju:8080 -NEXT_PUBLIC_ORION_API_URL=https://orion.gitmega.com - -NEXT_PUBLIC_INTERNAL_API_URL=https://api.gitmega.com \ No newline at end of file diff --git a/moon/apps/web/.env.staging-nju b/moon/apps/web/.env.staging-nju deleted file mode 100644 index 631dfe6c5..000000000 --- a/moon/apps/web/.env.staging-nju +++ /dev/null @@ -1,8 +0,0 @@ -NEXT_PUBLIC_API_URL=http://api.gitmega.nju:8080 -NEXT_PUBLIC_MONO_API_URL=http://git.gitmega.nju:8080 -NEXT_PUBLIC_WEB_URL=http://app.gitmega.nju:8080 -NEXT_PUBLIC_SYNC_URL=ws://sync.gitmega.nju:8080 -NEXT_PUBLIC_AUTH_URL=http://auth.gitmega.nju:8080 -NEXT_PUBLIC_CRATES_PRO_URL=http://cratespro.gitmega.nju:8080 - -NEXT_PUBLIC_INTERNAL_API_URL=http://api.gitmega.nju:8080 \ No newline at end of file diff --git a/moon/apps/web/docker-entrypoint.sh b/moon/apps/web/docker-entrypoint.sh index 344301d62..d165a75d5 100755 --- a/moon/apps/web/docker-entrypoint.sh +++ b/moon/apps/web/docker-entrypoint.sh @@ -25,8 +25,7 @@ https://rt-orion-api.placeholder.local${TAB}NEXT_PUBLIC_ORION_API_URL https://rt-auth.placeholder.local${TAB}NEXT_PUBLIC_AUTH_URL https://rt-web.placeholder.local${TAB}NEXT_PUBLIC_WEB_URL wss://rt-sync.placeholder.local${TAB}NEXT_PUBLIC_SYNC_URL -https://rt-crates-pro.placeholder.local${TAB}NEXT_PUBLIC_CRATES_PRO_URL -__rt_campsite_api_session_cookie__${TAB}NEXT_PUBLIC_CAMPSITE_API_SESSION_COOKIE" +https://rt-crates-pro.placeholder.local${TAB}NEXT_PUBLIC_CRATES_PRO_URL" # Escape a string for safe use on the left (regex) side of a sed s||| command. escape_regex() { printf '%s' "$1" | sed -e 's/[.[\*^$()+?{|/\\]/\\&/g'; } diff --git a/moon/apps/web/turbo.json b/moon/apps/web/turbo.json index 7237fb906..d9b520f40 100644 --- a/moon/apps/web/turbo.json +++ b/moon/apps/web/turbo.json @@ -25,6 +25,7 @@ "SENTRY_ORG", "SENTRY_PROJECT", "SSR_SECRET", + "CAMPSITE_API_SESSION_COOKIE", "TIPTAP_PRIVATE_REGISTRY_KEY", "VERCEL_GIT_COMMIT_SHA" ] diff --git a/moon/apps/web/utils/apiCookieHeaders.ts b/moon/apps/web/utils/apiCookieHeaders.ts index 9e6b8601b..b8860cf6d 100644 --- a/moon/apps/web/utils/apiCookieHeaders.ts +++ b/moon/apps/web/utils/apiCookieHeaders.ts @@ -1,17 +1,26 @@ import { NextApiRequestCookies } from 'next/dist/server/api-utils' -import { CAMPSITE_API_SESSION_COOKIE } from '@gitmono/config' +const DEFAULT_CAMPSITE_API_SESSION_COOKIE = '_campsite_api_session' + +/** + * Session cookie name for SSR → Campsite API forwarding. + * Prefer server-only `CAMPSITE_API_SESSION_COOKIE` so dev/k8s can override without rebuilding. + */ +export function getCampsiteApiSessionCookieName(): string { + return process.env.CAMPSITE_API_SESSION_COOKIE || DEFAULT_CAMPSITE_API_SESSION_COOKIE +} export const SsrSecretHeader: Record = { 'x-campsite-ssr-secret': process.env.SSR_SECRET || '' } export function apiCookieHeaders(cookies: NextApiRequestCookies) { let headers: Record = {} + const cookieName = getCampsiteApiSessionCookieName() - const sessionCookie = cookies[CAMPSITE_API_SESSION_COOKIE] + const sessionCookie = cookies[cookieName] if (sessionCookie) { const apiCookie = encodeURIComponent(sessionCookie) - headers['Cookie'] = `${CAMPSITE_API_SESSION_COOKIE}=${apiCookie}` + headers['Cookie'] = `${cookieName}=${apiCookie}` } return { ...headers, ...SsrSecretHeader } diff --git a/moon/apps/web/utils/queryClient.ts b/moon/apps/web/utils/queryClient.ts index b65444f3b..41281d651 100644 --- a/moon/apps/web/utils/queryClient.ts +++ b/moon/apps/web/utils/queryClient.ts @@ -1,15 +1,9 @@ import { InfiniteData, QueryClient, QueryKey } from '@tanstack/react-query' -import { - CAMPSITE_API_SESSION_COOKIE, - MONO_API_URL, - ORION_API_URL, - RAILS_API_URL, - RAILS_AUTH_URL, - RAILS_INTERNAL_API_URL -} from '@gitmono/config' +import { MONO_API_URL, ORION_API_URL, RAILS_API_URL, RAILS_AUTH_URL, RAILS_INTERNAL_API_URL } from '@gitmono/config' import { Api, ApiError, DataTag } from '@gitmono/types' +import { getCampsiteApiSessionCookieName } from './apiCookieHeaders' import { ApiErrorResponse } from './types' interface UrlProps { @@ -83,11 +77,12 @@ export async function fetcher(url: string, { method, body, cookies }: Fetcher let credentials: RequestCredentials | undefined if (cookies) { - const sessionCookie = cookies[CAMPSITE_API_SESSION_COOKIE] + const cookieName = getCampsiteApiSessionCookieName() + const sessionCookie = cookies[cookieName] if (sessionCookie) { const apiCookie = encodeURIComponent(sessionCookie) - headers.append('Cookie', `${CAMPSITE_API_SESSION_COOKIE}=${apiCookie}`) + headers.append('Cookie', `${cookieName}=${apiCookie}`) } } else { credentials = 'include' diff --git a/moon/packages/config/src/index.ts b/moon/packages/config/src/index.ts index e0b906e75..6533a35b9 100644 --- a/moon/packages/config/src/index.ts +++ b/moon/packages/config/src/index.ts @@ -32,9 +32,8 @@ export const ORION_API_URL = process.env.NEXT_PUBLIC_ORION_API_URL || 'https://o // eslint-disable-next-line turbo/no-undeclared-env-vars export const RAILS_AUTH_URL = process.env.NEXT_PUBLIC_AUTH_URL || 'https://auth.gitmega.com' -// eslint-disable-next-line turbo/no-undeclared-env-vars -export const CAMPSITE_API_SESSION_COOKIE = - process.env.NEXT_PUBLIC_CAMPSITE_API_SESSION_COOKIE || '_campsite_api_session' +// Default Campsite API session cookie name (SSR overrides via CAMPSITE_API_SESSION_COOKIE). +export const CAMPSITE_API_SESSION_COOKIE = '_campsite_api_session' /* Not using an env variable because we use this variable in the browser, which From c92be8fcf0fb3af4527390b8aba6170a419ff432 Mon Sep 17 00:00:00 2001 From: "benjamin.747" Date: Tue, 14 Jul 2026 11:19:07 +0800 Subject: [PATCH 2/3] feat: add user account approval with admin review UI Gate new users behind Mono-backed pending/approved/rejected status, and let admins review them from settings with a top-right pending badge. --- Cargo.lock | 638 +++++++++--------- Cargo.toml | 20 +- .../api_service/mono/admin/group.rs | 2 +- ceres/src/application/api_service/mono/mod.rs | 1 + .../api_service/mono/user_approval.rs | 84 +++ ceres/src/model/user.rs | 30 +- ...0714_021900_create_user_approval_status.rs | 95 +++ jupiter-migrate/src/migration/mod.rs | 2 + jupiter/callisto/src/access_token.rs | 2 +- jupiter/callisto/src/artifact_objects.rs | 2 +- jupiter/callisto/src/artifact_set_files.rs | 2 +- jupiter/callisto/src/artifact_sets.rs | 2 +- jupiter/callisto/src/audit_logs.rs | 2 +- jupiter/callisto/src/bot_installations.rs | 2 +- jupiter/callisto/src/bot_keys.rs | 2 +- jupiter/callisto/src/bot_tokens.rs | 2 +- jupiter/callisto/src/bots.rs | 2 +- jupiter/callisto/src/buck_session.rs | 2 +- jupiter/callisto/src/buck_session_file.rs | 2 +- jupiter/callisto/src/build_events.rs | 2 +- jupiter/callisto/src/build_targets.rs | 2 +- jupiter/callisto/src/build_triggers.rs | 2 +- jupiter/callisto/src/check_result.rs | 2 +- jupiter/callisto/src/cla_sign_status.rs | 2 +- jupiter/callisto/src/commit_auths.rs | 2 +- jupiter/callisto/src/dynamic_sidebar.rs | 2 +- jupiter/callisto/src/email_jobs.rs | 2 +- jupiter/callisto/src/git_blob.rs | 2 +- jupiter/callisto/src/git_commit.rs | 2 +- jupiter/callisto/src/git_issue.rs | 2 +- jupiter/callisto/src/git_pr.rs | 2 +- jupiter/callisto/src/git_repo.rs | 2 +- jupiter/callisto/src/git_tag.rs | 2 +- jupiter/callisto/src/git_tree.rs | 2 +- jupiter/callisto/src/gpg_key.rs | 2 +- jupiter/callisto/src/import_refs.rs | 2 +- jupiter/callisto/src/issue_cl_references.rs | 2 +- jupiter/callisto/src/item_assignees.rs | 2 +- jupiter/callisto/src/item_labels.rs | 2 +- jupiter/callisto/src/label.rs | 2 +- jupiter/callisto/src/lfs_locks.rs | 2 +- jupiter/callisto/src/lfs_objects.rs | 2 +- jupiter/callisto/src/mega_blob.rs | 2 +- jupiter/callisto/src/mega_cl.rs | 2 +- jupiter/callisto/src/mega_cl_commits.rs | 2 +- jupiter/callisto/src/mega_cl_reviewer.rs | 2 +- .../callisto/src/mega_code_review_anchor.rs | 2 +- .../callisto/src/mega_code_review_comment.rs | 2 +- .../callisto/src/mega_code_review_position.rs | 2 +- .../callisto/src/mega_code_review_thread.rs | 2 +- jupiter/callisto/src/mega_commit.rs | 2 +- jupiter/callisto/src/mega_conversation.rs | 2 +- jupiter/callisto/src/mega_group.rs | 2 +- jupiter/callisto/src/mega_group_member.rs | 2 +- jupiter/callisto/src/mega_issue.rs | 2 +- jupiter/callisto/src/mega_refs.rs | 2 +- .../callisto/src/mega_resource_permission.rs | 2 +- jupiter/callisto/src/mega_tag.rs | 2 +- jupiter/callisto/src/mega_tree.rs | 2 +- jupiter/callisto/src/mega_webhook.rs | 2 +- jupiter/callisto/src/mega_webhook_delivery.rs | 2 +- .../callisto/src/mega_webhook_event_type.rs | 2 +- jupiter/callisto/src/merge_queue.rs | 2 +- jupiter/callisto/src/mod.rs | 3 +- jupiter/callisto/src/non_member_note_views.rs | 2 +- jupiter/callisto/src/note_views.rs | 2 +- jupiter/callisto/src/notes.rs | 2 +- .../callisto/src/notification_event_types.rs | 2 +- jupiter/callisto/src/orion_tasks.rs | 2 +- jupiter/callisto/src/path_check_configs.rs | 2 +- jupiter/callisto/src/prelude.rs | 3 +- jupiter/callisto/src/reactions.rs | 2 +- jupiter/callisto/src/sea_orm_active_enums.rs | 2 +- jupiter/callisto/src/ssh_keys.rs | 2 +- jupiter/callisto/src/target_build_status.rs | 2 +- .../callisto/src/target_state_histories.rs | 2 +- jupiter/callisto/src/user_approval_status.rs | 24 + .../src/user_notification_preferences.rs | 2 +- .../src/user_notification_settings.rs | 2 +- jupiter/callisto/src/vault.rs | 2 +- jupiter/src/storage/init.rs | 2 +- jupiter/src/storage/mod.rs | 10 + jupiter/src/storage/user_approval_storage.rs | 167 +++++ jupiter/src/tests.rs | 2 + mono/src/api/router/admin_router.rs | 124 +++- mono/src/api/router/user_router.rs | 34 +- .../web/components/AccountApprovalGuard.tsx | 117 ++++ .../PendingAccountReviewButton.tsx | 28 + .../web/components/NavigationBar/index.tsx | 2 + .../NavigationSidebar/ProfileDropdown.tsx | 19 +- .../components/Providers/AuthAppProviders.tsx | 7 +- moon/apps/web/components/Sidebar/index.tsx | 9 +- .../AccountReview/AccountReviewSection.tsx | 189 ++++++ .../components/UserSettings/PageWrapper.tsx | 33 + .../AccountReview/useAdminUserApprovals.ts | 18 + .../web/hooks/AccountReview/useApproveUser.ts | 20 + .../AccountReview/useMyApprovalStatus.ts | 17 + .../web/hooks/AccountReview/useRejectUser.ts | 20 + .../web/hooks/usePendingAccountReviewCount.ts | 18 + moon/apps/web/next.config.js | 1 + .../web/pages/me/settings/account-review.tsx | 57 ++ moon/packages/types/generated.ts | 169 +++++ 102 files changed, 1675 insertions(+), 428 deletions(-) create mode 100644 ceres/src/application/api_service/mono/user_approval.rs create mode 100644 jupiter-migrate/src/migration/m20260714_021900_create_user_approval_status.rs create mode 100644 jupiter/callisto/src/user_approval_status.rs create mode 100644 jupiter/src/storage/user_approval_storage.rs create mode 100644 moon/apps/web/components/AccountApprovalGuard.tsx create mode 100644 moon/apps/web/components/AccountReview/PendingAccountReviewButton.tsx create mode 100644 moon/apps/web/components/UserSettings/AccountReview/AccountReviewSection.tsx create mode 100644 moon/apps/web/hooks/AccountReview/useAdminUserApprovals.ts create mode 100644 moon/apps/web/hooks/AccountReview/useApproveUser.ts create mode 100644 moon/apps/web/hooks/AccountReview/useMyApprovalStatus.ts create mode 100644 moon/apps/web/hooks/AccountReview/useRejectUser.ts create mode 100644 moon/apps/web/hooks/usePendingAccountReviewCount.ts create mode 100644 moon/apps/web/pages/me/settings/account-review.tsx diff --git a/Cargo.lock b/Cargo.lock index dc15ebf0a..751735768 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -201,7 +201,7 @@ version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc" dependencies = [ - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -212,7 +212,7 @@ checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d" dependencies = [ "anstyle", "once_cell_polyfill", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -321,9 +321,9 @@ checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b" [[package]] name = "arrayvec" -version = "0.7.7" +version = "0.7.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f02882884d3e1bc524fb12c79f107f6ad0e1cfd498c536ffb494301740995dfe" +checksum = "d3fb67a6e08acf24fdeccbac2cb6ac4305825bd1f117462e0e6f2f193345ad56" [[package]] name = "as-any" @@ -498,9 +498,9 @@ checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53" [[package]] name = "aws-lc-rs" -version = "1.17.0" +version = "1.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ec2f1fc3ec205783a5da9a7e6c1509cc69dedf09a1949e412c1e18469326d00" +checksum = "4342d8937fc7e5dd9b1c60292261c0670c882a2cd1719cfc11b1af41731e32ad" dependencies = [ "aws-lc-sys", "untrusted 0.7.1", @@ -509,14 +509,15 @@ dependencies = [ [[package]] name = "aws-lc-sys" -version = "0.41.0" +version = "0.42.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a2f9779ce85b93ab6170dd940ad0169b5766ff848247aff13bb788b832fe3f4" +checksum = "6d9ceb1da931507a12f4fccea479dccd00da1943e1b4ae72d8e502d707361444" dependencies = [ "cc", "cmake", "dunce", "fs_extra", + "pkg-config", ] [[package]] @@ -546,10 +547,10 @@ dependencies = [ "serde_json", "serde_path_to_error", "serde_urlencoded", - "sha1 0.10.6", + "sha1 0.10.7", "sync_wrapper", "tokio", - "tokio-tungstenite", + "tokio-tungstenite 0.29.0", "tower", "tower-layer", "tower-service", @@ -873,7 +874,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b79834656f71332577234b50bfc009996f7449e0c056884e6a02492ded0ca2f3" dependencies = [ "arrayref", - "arrayvec 0.7.7", + "arrayvec 0.7.8", "constant_time_eq", ] @@ -884,7 +885,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0aa83c34e62843d924f905e0f5c866eb1dd6545fc4d719e803d9ba6030371fce" dependencies = [ "arrayref", - "arrayvec 0.7.7", + "arrayvec 0.7.8", "cc", "cfg-if", "constant_time_eq", @@ -1106,9 +1107,9 @@ dependencies = [ [[package]] name = "bytemuck" -version = "1.25.0" +version = "1.25.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8efb64bd706a16a1bdde310ae86b351e4d21550d98d056f22f8a7f7a2183fec" +checksum = "d6aedf8ae72766347502cf3cb4f41cf5e9cc37d28bee90f1fdaaae15f9cf9424" [[package]] name = "byteorder" @@ -1118,9 +1119,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.12.0" +version = "1.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ae3f5d315924270530207e2a68396c3cc547f6dca3fbdca317cfb1a51edb593" +checksum = "fc652a48c352aef3ea3aed32080501cf3ef6ed5da78602a020c991775b0aff04" [[package]] name = "bzip2" @@ -1138,7 +1139,7 @@ dependencies = [ "chrono", "hex", "idgenerator", - "rand 0.10.1", + "rand 0.10.2", "sea-orm", "serde", "sha2 0.11.0", @@ -1183,9 +1184,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.2.65" +version = "1.2.67" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e228eec9be7c17ccb640b59b36a5cd805ea2a564a4c5e162c2f659fea30d3b96" +checksum = "e17dd265a7d0f31ef544e1b20e03add05d3b45b491b633b10d67145d2acc1a38" dependencies = [ "find-msvc-tools", "jobserver", @@ -1271,22 +1272,22 @@ dependencies = [ "chrono", "common", "futures", - "git-internal 0.7.6", + "git-internal 0.8.3", "hex", "io-orbit", "jupiter", "jupiter-migrate", "orion-client", "pgp", - "rand 0.10.1", + "rand 0.10.2", "regex", "reqwest 0.13.4", - "rkyv 0.8.16", + "rkyv 0.8.17", "saturn", "serde", "serde_json", "sha1 0.11.0", - "sysinfo 0.39.5", + "sysinfo 0.39.6", "tempfile", "tokio", "tokio-stream", @@ -1475,7 +1476,7 @@ version = "3.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "faf9468729b8cbcea668e36183cb69d317348c2e08e994829fb56ebfdfbaac34" dependencies = [ - "windows-sys 0.48.0", + "windows-sys 0.61.2", ] [[package]] @@ -1502,12 +1503,12 @@ dependencies = [ "config", "directories", "envsubst", - "git-internal 0.7.6", + "git-internal 0.8.3", "idgenerator", "pgp", "redis", "regex", - "rkyv 0.8.16", + "rkyv 0.8.17", "sea-orm", "serde", "serde_json", @@ -1561,15 +1562,15 @@ dependencies = [ "serde_core", "serde_json", "toml 1.1.2+spec-1.1.0", - "winnow 1.0.3", + "winnow 1.0.4", "yaml-rust2", ] [[package]] name = "console" -version = "0.16.3" +version = "0.16.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d64e8af5551369d19cf50138de61f1c42074ab970f74e99be916646777f8fc87" +checksum = "4fe5f465a4f6fee88fad41b85d990f84c835335e85b5d9e6e63e0d06d28cba7c" dependencies = [ "encode_unicode", "libc", @@ -1684,7 +1685,7 @@ checksum = "0940496e5c83c54f3b753d5317daec82e8edac71c33aaa1f666d76f518de2444" dependencies = [ "hax-lib", "pastey", - "rand 0.9.4", + "rand 0.9.5", ] [[package]] @@ -1733,7 +1734,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e75b2483e97a5a7da73ac68a05b629f9c53cff58d8ed1c77866079e18b00dba5" dependencies = [ "digest 0.10.7", - "spin 0.10.0", + "spin 0.10.1", ] [[package]] @@ -1766,18 +1767,18 @@ dependencies = [ [[package]] name = "crossbeam-channel" -version = "0.5.15" +version = "0.5.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82b8f8f868b36967f9606790d1903570de9ceaf870a7bf9fbbd3016d636a2cb2" +checksum = "d85363c37faeca707aef026efa9f3b34d077bce547e48f770770625c6013679e" dependencies = [ "crossbeam-utils", ] [[package]] name = "crossbeam-deque" -version = "0.8.6" +version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51" +checksum = "5181e0de7b61eb03a81e347d6dd8797bae9da5146707b51077e2d71a54ec0ceb" dependencies = [ "crossbeam-epoch", "crossbeam-utils", @@ -1785,27 +1786,27 @@ dependencies = [ [[package]] name = "crossbeam-epoch" -version = "0.9.18" +version = "0.9.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" +checksum = "2d6914041f254d6e9176c01941b21115dcfb7089e55135a35411081bd106ef3f" dependencies = [ "crossbeam-utils", ] [[package]] name = "crossbeam-queue" -version = "0.3.12" +version = "0.3.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f58bbc28f91df819d0aa2a2c00cd19754769c2fad90579b3592b1c9ba7a3115" +checksum = "803d13fb3b09d88be9f4dbc29062c66b19bf7170867ceb746d2a8689bf6c7a26" dependencies = [ "crossbeam-utils", ] [[package]] name = "crossbeam-utils" -version = "0.8.21" +version = "0.8.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" +checksum = "61803da095bee82a81bb1a452ecc25d3b2f1416d1897eb86430c6159ef717c17" [[package]] name = "crunchy" @@ -2096,9 +2097,9 @@ dependencies = [ [[package]] name = "defmt" -version = "1.1.0" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6e524506490a1953d237cb87b1cfc1e46f88c18f10a22dfe0f507dc6bfc7f7f" +checksum = "e2953bfe4f93bbd20cc71198842756f77d161884c99ebbabc41d80231ded88d1" dependencies = [ "bitflags 1.3.2", "defmt-macros", @@ -2106,12 +2107,11 @@ dependencies = [ [[package]] name = "defmt-macros" -version = "1.1.0" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0a27770e9c8f719a79d8b638281f4d828f77d8fd61e0bd94451b9b85e576a0b" +checksum = "bad9c72e7ca2137e0dc3813245a0d282fd6daad32fd800af018306a9169b5fe8" dependencies = [ "defmt-parser", - "proc-macro-error2", "proc-macro2", "quote", "syn 2.0.118", @@ -2150,9 +2150,9 @@ dependencies = [ [[package]] name = "der" -version = "0.8.0" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71fd89660b2dc699704064e59e9dba0147b903e85319429e131620d022be411b" +checksum = "a69dedd701da44b0536442edf09c81a64b0ab97a7a4a5e3d1971f00027cbc63d" dependencies = [ "const-oid 0.10.2", "pem-rfc7468 1.0.0", @@ -2348,7 +2348,7 @@ dependencies = [ "libc", "option-ext", "redox_users", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -2450,7 +2450,7 @@ version = "0.17.0-rc.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "54fb064faabbee66e1fc8e5c5a9458d4269dc2d8b638fe86a425adb2510d1a96" dependencies = [ - "der 0.8.0", + "der 0.8.1", "digest 0.11.3", "elliptic-curve 0.14.0-rc.33", "rfc6979 0.5.0", @@ -2739,7 +2739,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -2863,7 +2863,7 @@ checksum = "da0e4dd2a88388a1f4ccc7c9ce104604dab68d9f408dc34cd45823d5a9069095" dependencies = [ "futures-core", "futures-sink", - "spin 0.9.8", + "spin 0.9.9", ] [[package]] @@ -3060,9 +3060,9 @@ dependencies = [ [[package]] name = "generic-array" -version = "1.4.3" +version = "1.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2e55f16dcf0e9c00efbe2e655ffe45fc98e7066b52bc92f8a79e64060a79351" +checksum = "ab4e5aa225bc56696909483320f0ff9b600f1a971b52e07a17d70f3d9b43254b" dependencies = [ "generic-array 0.14.7", "rustversion", @@ -3089,11 +3089,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" dependencies = [ "cfg-if", - "js-sys", "libc", "r-efi 5.3.0", "wasip2", - "wasm-bindgen", ] [[package]] @@ -3126,7 +3124,7 @@ version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2eecf2d5dc9b66b732b97707a0210906b1d30523eb773193ab777c0c84b3e8d5" dependencies = [ - "polyval 0.7.1", + "polyval 0.7.2", ] [[package]] @@ -3161,9 +3159,9 @@ dependencies = [ "rayon", "sea-orm", "serde", - "sha1 0.10.6", + "sha1 0.10.7", "sha2 0.10.9", - "similar", + "similar 2.7.0", "tempfile", "thiserror 2.0.18", "threadpool", @@ -3177,9 +3175,9 @@ dependencies = [ [[package]] name = "git-internal" -version = "0.7.6" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "514e1c3227f3ade4a4ea2fadcf80936b034cf75ec52e9deabea34594804622c9" +checksum = "4fa539369d3cb64828c397475332b274f4561a2c21cba8efa01e57654e3d5ee2" dependencies = [ "ahash 0.8.12", "async-trait", @@ -3205,13 +3203,13 @@ dependencies = [ "path-absolutize", "rayon", "ring", - "rkyv 0.8.16", + "rkyv 0.8.17", "sea-orm", "serde", "serde_json", - "sha1 0.10.6", - "sha2 0.10.9", - "similar", + "sha1 0.11.0", + "sha2 0.11.0", + "similar 3.1.1", "tempfile", "thiserror 2.0.18", "threadpool", @@ -3454,7 +3452,7 @@ dependencies = [ "http", "httpdate", "mime", - "sha1 0.10.6", + "sha1 0.10.7", ] [[package]] @@ -3496,7 +3494,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fda06d18ac606267c40c04e41b9947729bf8b9efe74bd4e82b61a5f26a510b9f" dependencies = [ - "arrayvec 0.7.7", + "arrayvec 0.7.8", ] [[package]] @@ -3505,7 +3503,7 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "830e599c2904b08f0834ee6337d8fe8f0ed4a63b5d9e7a7f49c0ffa06d08d360" dependencies = [ - "arrayvec 0.7.7", + "arrayvec 0.7.8", ] [[package]] @@ -3577,9 +3575,9 @@ dependencies = [ [[package]] name = "http-body" -version = "1.0.1" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" +checksum = "ca2a8f2913ee65f60facd6a5905613afaa448497a0230cc41ce022d93290bc2c" dependencies = [ "bytes", "http", @@ -3587,9 +3585,9 @@ dependencies = [ [[package]] name = "http-body-util" -version = "0.1.3" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" +checksum = "e9f41fd6a08e4d4ec69df65976da761afd5ad5e58a9d4acb46bd1c953a9e3ff2" dependencies = [ "bytes", "futures-core", @@ -3612,9 +3610,9 @@ checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" [[package]] name = "humantime" -version = "2.3.0" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "135b12329e5e3ce057a9f972339ea52bc954fe1e9358ef27f95e89716fbc5424" +checksum = "15cdd26707701c53297e2fa6afb323d55fbc1d0810c3aec078ae3ef0424c3c15" [[package]] name = "hybrid-array" @@ -3712,7 +3710,7 @@ dependencies = [ "libc", "percent-encoding", "pin-project-lite", - "socket2 0.5.10", + "socket2 0.6.5", "system-configuration", "tokio", "tower-service", @@ -3899,9 +3897,9 @@ dependencies = [ [[package]] name = "indicatif" -version = "0.18.5" +version = "0.18.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "993f007684f2e9727160da8b960ec161264703bfd1af084fd2e34d040c9a0dd4" +checksum = "9433806cd6b4ec1aba79c021c7e4c58fb4c3b9977c085062e611ac929998fb0c" dependencies = [ "console", "portable-atomic", @@ -3970,7 +3968,7 @@ dependencies = [ "rand_core 0.6.4", "rsa 0.9.10", "sec1 0.7.3", - "sha1 0.10.6", + "sha1 0.10.7", "sha2 0.10.9", "signature 2.2.0", "ssh-cipher 0.2.0", @@ -3987,7 +3985,7 @@ checksum = "ae8e22120c32fb4d19ec55fba35015f57095cd95a2e3b732e44457f5915b2ee8" dependencies = [ "num-integer", "num-traits", - "rand 0.10.1", + "rand 0.10.2", "rand_core 0.10.1", ] @@ -4094,9 +4092,9 @@ dependencies = [ [[package]] name = "jiff" -version = "0.2.31" +version = "0.2.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ccfe6121cbe750cf81efa362d85c0bde7ea298ec43092d3a193baca59cdbd634" +checksum = "961d16382652bfdd8c6f68b223b26a8c93e0d475c672f414411db31c6c5c900e" dependencies = [ "defmt", "jiff-static", @@ -4108,9 +4106,9 @@ dependencies = [ [[package]] name = "jiff-static" -version = "0.2.31" +version = "0.2.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e165e897f662d428f3cd3828a919dbe067c2d42bb1031eede74ef9d27ecdedd2" +checksum = "d0879bd39df99c4c5e2c6615ccc026391a423dde10532c573e6086eb94a802cc" dependencies = [ "proc-macro2", "quote", @@ -4168,11 +4166,11 @@ dependencies = [ [[package]] name = "jobserver" -version = "0.1.34" +version = "0.1.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9afb3de4395d6b3e67a780b6de64b51c978ecf11cb9a462c66be7d4ca9039d33" +checksum = "1c00acbd29eabad4a2392fa0e921c874934dbbf4194312ad20f04a0ed67a3cb3" dependencies = [ - "getrandom 0.3.4", + "getrandom 0.4.3", "libc", ] @@ -4210,7 +4208,7 @@ dependencies = [ "chrono", "common", "futures", - "git-internal 0.7.6", + "git-internal 0.8.3", "hex", "hmac 0.13.0", "idgenerator", @@ -4218,7 +4216,7 @@ dependencies = [ "io-orbit", "jupiter-migrate", "pgp", - "rand 0.10.1", + "rand 0.10.2", "redis", "redis-test", "reqwest 0.13.4", @@ -4364,7 +4362,7 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" dependencies = [ - "spin 0.9.8", + "spin 0.9.9", ] [[package]] @@ -4388,7 +4386,7 @@ dependencies = [ "quoted_printable", "rustls", "rustls-platform-verifier", - "socket2 0.6.4", + "socket2 0.6.5", "tokio", "tokio-rustls", "url", @@ -4428,7 +4426,7 @@ dependencies = [ "libcrux-secrets", "libcrux-sha3", "libcrux-traits", - "rand 0.9.4", + "rand 0.9.5", "tls_codec", ] @@ -4469,7 +4467,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9adfd58e79d860f6b9e40e35127bfae9e5bd3ade33201d1347459011a2add034" dependencies = [ "libcrux-secrets", - "rand 0.9.4", + "rand 0.9.5", ] [[package]] @@ -4530,14 +4528,14 @@ dependencies = [ [[package]] name = "libredox" -version = "0.1.17" +version = "0.1.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f02ab6bace2054fb888a3c16f990117b579d14a3088e472d63c6011fa185c9d3" +checksum = "c943259e342f1e06ff2da7a83eabdfe7f92ce10262688dbf1895ff0b3e6e4652" dependencies = [ "bitflags 2.13.0", "libc", "plain", - "redox_syscall 0.8.1", + "redox_syscall 0.9.0", ] [[package]] @@ -4592,7 +4590,7 @@ dependencies = [ "pem", "priority-queue", "radix_trie 0.2.1", - "rand 0.8.6", + "rand 0.8.7", "regex", "reqwest 0.12.28", "rustls", @@ -4806,15 +4804,15 @@ checksum = "490cc448043f947bae3cbee9c203358d62dbee0db12107a74be5c30ccfd09771" [[package]] name = "md5" -version = "0.8.0" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae960838283323069879657ca3de837e9f7bbb4c7bf6ea7f1b290d5e9476d2e0" +checksum = "7ebb8d8732c6a6df3d8f032a82911cfc747e00efb95cc46e8d0acd5b5b88570c" [[package]] name = "memchr" -version = "2.8.2" +version = "2.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88904434abc2901f197fe8cc55f0445e7ded921dba5911dad2e2b39b48e663c4" +checksum = "cf8baf1c55e62ffcace7a9f06f4bd9cd3f0c4beb022d3b367256b91b87513d98" [[package]] name = "memmap2" @@ -4900,9 +4898,9 @@ dependencies = [ [[package]] name = "mio" -version = "1.2.1" +version = "1.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02bd0af71c67b473010cbbc60715ee815645a4dc942899111f494b4b737d6fda" +checksum = "30d65c71f1ce40ab09135ce117d742b9f8a19ff91a41a8b57ed50bc2de59c427" dependencies = [ "libc", "wasi", @@ -4974,7 +4972,7 @@ dependencies = [ "ctrlc", "ed25519-dalek 2.2.0", "futures", - "git-internal 0.7.6", + "git-internal 0.8.3", "http", "jemallocator", "jupiter", @@ -4986,7 +4984,7 @@ dependencies = [ "orion-scheduler-client", "percent-encoding", "qlean", - "rand 0.10.1", + "rand 0.10.2", "regex", "reqwest 0.13.4", "russh 0.61.2", @@ -5045,7 +5043,7 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3ffa00dec017b5b1a8b7cf5e2c008bfda1aa7e0697ac1508b491fdf2622fb4d8" dependencies = [ - "rand 0.8.6", + "rand 0.8.7", ] [[package]] @@ -5155,18 +5153,18 @@ version = "0.50.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" dependencies = [ - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] name = "num-bigint" -version = "0.4.6" +version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" +checksum = "c89e69e7e0f03bea5ef08013795c25018e101932225a656383bd384495ecc367" dependencies = [ "num-integer", "num-traits", - "rand 0.8.6", + "rand 0.8.7", ] [[package]] @@ -5180,7 +5178,7 @@ dependencies = [ "num-integer", "num-iter", "num-traits", - "rand 0.8.6", + "rand 0.8.7", "serde", "smallvec", "zeroize", @@ -5203,11 +5201,10 @@ dependencies = [ [[package]] name = "num-iter" -version = "0.1.45" +version = "0.1.46" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf" +checksum = "c92800bd69a1eac91786bcfe9da64a897eb72911b8dc3095decbd07429e8048b" dependencies = [ - "autocfg", "num-integer", "num-traits", ] @@ -5351,7 +5348,7 @@ dependencies = [ "parking_lot 0.12.5", "percent-encoding", "quick-xml", - "rand 0.10.1", + "rand 0.10.2", "reqwest 0.13.4", "rustls-pki-types", "serde", @@ -5496,11 +5493,11 @@ dependencies = [ "td_util_buck", "tempfile", "tokio", - "tokio-tungstenite", + "tokio-tungstenite 0.30.0", "tokio-util", "tracing", "tracing-subscriber", - "tungstenite", + "tungstenite 0.30.0", "url", "uuid", ] @@ -5563,7 +5560,7 @@ dependencies = [ "http", "io-orbit", "once_cell", - "rand 0.10.1", + "rand 0.10.2", "sea-orm", "serde", "serde_json", @@ -5704,7 +5701,7 @@ dependencies = [ "delegate", "futures", "log", - "rand 0.10.1", + "rand 0.10.2", "sha2 0.11.0", "thiserror 2.0.18", "tokio", @@ -5929,9 +5926,9 @@ checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" [[package]] name = "pest" -version = "2.8.6" +version = "2.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0848c601009d37dfa3430c4666e147e49cdcf1b92ecd3e63657d8a5f19da662" +checksum = "47627dd7305c6a2d6c8c6bcd24c5a4c17dbbf425f4f9c5313e724b38fc9782e9" dependencies = [ "memchr", "ucd-trie", @@ -5939,9 +5936,9 @@ dependencies = [ [[package]] name = "pest_derive" -version = "2.8.6" +version = "2.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11f486f1ea21e6c10ed15d5a7c77165d0ee443402f0780849d1768e7d9d6fe77" +checksum = "4b4254325ecad416ab689e27ba51da03ba01a9632bc6e108f5fe7c3c4ad29d58" dependencies = [ "pest", "pest_generator", @@ -5949,9 +5946,9 @@ dependencies = [ [[package]] name = "pest_generator" -version = "2.8.6" +version = "2.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8040c4647b13b210a963c1ed407c1ff4fdfa01c31d6d2a098218702e6664f94f" +checksum = "6c4c0e91ead7a8f7acecbca6f003fc2e8282b1dbe2dd9c9d2f16aba42995e0a7" dependencies = [ "pest", "pest_meta", @@ -5962,12 +5959,11 @@ dependencies = [ [[package]] name = "pest_meta" -version = "2.8.6" +version = "2.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89815c69d36021a140146f26659a81d6c2afa33d216d736dd4be5381a7362220" +checksum = "f9744bc48116fee06334924bb5f2bad41eed5e89bd26e29b0b799f9a3f82c210" dependencies = [ "pest", - "sha2 0.10.9", ] [[package]] @@ -6033,11 +6029,11 @@ dependencies = [ "p256 0.13.2", "p384 0.13.1", "p521 0.13.3", - "rand 0.8.6", + "rand 0.8.7", "replace_with", "ripemd", "rsa 0.9.10", - "sha1 0.10.6", + "sha1 0.10.7", "sha1-checked", "sha2 0.10.9", "sha3 0.10.9", @@ -6127,7 +6123,7 @@ version = "0.8.0-rc.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "986d2e952779af96ea048f160fd9194e1751b4faea78bcf3ceb456efe008088e" dependencies = [ - "der 0.8.0", + "der 0.8.1", "spki 0.8.0", ] @@ -6155,7 +6151,7 @@ dependencies = [ "aes 0.9.1", "aes-gcm 0.11.0", "cbc 0.2.1", - "der 0.8.0", + "der 0.8.1", "pbkdf2 0.13.0", "rand_core 0.10.1", "scrypt 0.12.0", @@ -6181,7 +6177,7 @@ version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "451913da69c775a56034ea8d9003d27ee8948e12443eae7c038ba100a4f21cb7" dependencies = [ - "der 0.8.0", + "der 0.8.1", "pkcs5 0.8.1", "rand_core 0.10.1", "spki 0.8.0", @@ -6212,9 +6208,9 @@ dependencies = [ [[package]] name = "poly1305" -version = "0.9.0" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a00baa632505d05512f48a963e16051c54fda9a95cc9acea1a4e3c90991c4a2e" +checksum = "6e2d0073b297041425c7c3df6eb4792d598a15323fe63346852b092eca02904c" dependencies = [ "cpufeatures 0.3.0", "universal-hash 0.6.1", @@ -6235,9 +6231,9 @@ dependencies = [ [[package]] name = "polyval" -version = "0.7.1" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7dfc63250416fea14f5749b90725916a6c903f599d51cb635aa7a52bfd03eede" +checksum = "b20f20e954175de5f463f67781b35583397d916b1d148738923711b2ad16bee8" dependencies = [ "cpubits", "cpufeatures 0.3.0", @@ -6582,7 +6578,7 @@ dependencies = [ "kvm-ioctls", "nanoid", "qapi", - "rand 0.9.4", + "rand 0.9.5", "reqwest 0.13.4", "russh 0.55.0", "russh-sftp", @@ -6624,7 +6620,7 @@ dependencies = [ "quinn-udp", "rustc-hash", "rustls", - "socket2 0.5.10", + "socket2 0.6.5", "thiserror 2.0.18", "tokio", "tracing", @@ -6633,15 +6629,16 @@ dependencies = [ [[package]] name = "quinn-proto" -version = "0.11.15" +version = "0.11.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fcb935c5bec503c2f0e306bdd3e58bb9029dcb14fa8d9ac76e3a5256ac0763e" +checksum = "2f4bfc015262b9df63c8845072ce59068853ff5872180c2ce2f13038b970e560" dependencies = [ "aws-lc-rs", "bytes", - "getrandom 0.3.4", + "getrandom 0.4.3", "lru-slab", - "rand 0.9.4", + "rand 0.10.2", + "rand_pcg", "ring", "rustc-hash", "rustls", @@ -6655,16 +6652,16 @@ dependencies = [ [[package]] name = "quinn-udp" -version = "0.5.14" +version = "0.5.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "addec6a0dcad8a8d96a771f815f0eaf55f9d1805756410b39f5fa81332574cbd" +checksum = "35a133f956daabe89a61a685c2649f13d82d5aa4bd5d12d1277e1072a21c0694" dependencies = [ "cfg_aliases", "libc", "once_cell", - "socket2 0.5.10", + "socket2 0.6.5", "tracing", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -6722,18 +6719,18 @@ dependencies = [ [[package]] name = "rancor" -version = "0.1.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a063ea72381527c2a0561da9c80000ef822bdd7c3241b1cc1b12100e3df081ee" +checksum = "daff8b7b3ccf5f7ba270b3e7a0a4d4c701c5797e38dec27c7e2c3dbb830fed1c" dependencies = [ "ptr_meta 0.3.1", ] [[package]] name = "rand" -version = "0.8.6" +version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ca0ecfa931c29007047d1bc58e623ab12e5590e8c7cc53200d5202b69266d8a" +checksum = "22f6172bdec972074665ed81ed53b71da00bfc44b65a753cfde883ec4c702a1a" dependencies = [ "libc", "rand_chacha 0.3.1", @@ -6742,9 +6739,9 @@ dependencies = [ [[package]] name = "rand" -version = "0.9.4" +version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44c5af06bb1b7d3216d91932aed5265164bf384dc89cd6ba05cf59a35f5f76ea" +checksum = "b9ef1d0d795eb7d84685bca4f72f3649f064e6641543d3a8c415898726a57b41" dependencies = [ "rand_chacha 0.9.0", "rand_core 0.9.5", @@ -6752,9 +6749,9 @@ dependencies = [ [[package]] name = "rand" -version = "0.10.1" +version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2e8e8bcc7961af1fdac401278c6a831614941f6164ee3bf4ce61b7edb162207" +checksum = "c7f5fa3a058cd35567ef9bfa5e75732bee0f9e4c55fa90477bef2dfcdbc4be80" dependencies = [ "chacha20 0.10.1", "getrandom 0.4.3", @@ -6805,6 +6802,15 @@ version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "63b8176103e19a2643978565ca18b50549f6101881c443590420e4dc998a3c69" +[[package]] +name = "rand_pcg" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "caa0f4137e1c0a72f4c651489402276c8e8e1cf081f3b0ba156d2cbeef09e86a" +dependencies = [ + "rand_core 0.10.1", +] + [[package]] name = "rayon" version = "1.12.0" @@ -6848,7 +6854,7 @@ dependencies = [ "rustls-native-certs", "ryu", "sha1_smol", - "socket2 0.6.4", + "socket2 0.6.5", "tokio", "tokio-rustls", "tokio-util", @@ -6863,9 +6869,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "804d36862e4323b69f96440cbb13c9894fc90176abdeaf91264e21d5d77f6aca" dependencies = [ "futures", - "rand 0.9.4", + "rand 0.9.5", "redis", - "socket2 0.6.4", + "socket2 0.6.5", "tempfile", ] @@ -6889,9 +6895,9 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.8.1" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b44b894f2a6e36457d665d1e08c3866add6ed5e70050c1b4ba8a8ddedb02ce7" +checksum = "c5102a6aaa05aa011a238e178e6bca86d2cb56fc9f586d37cb80f5bca6e07759" dependencies = [ "bitflags 2.13.0", ] @@ -6929,9 +6935,9 @@ dependencies = [ [[package]] name = "regex" -version = "1.12.4" +version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1292b7759ae1cb9ec195452d1390a074f0cd8541ab7a5a8c31cd6db45d4a6ba" +checksum = "2a0e75113e14dc5acb068cd0786884f214f1312650a3d36d269f5c4f3cdee8a2" dependencies = [ "aho-corasick", "memchr", @@ -6941,9 +6947,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.14" +version = "0.4.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f" +checksum = "1f388202e4b80542a0921078cc23b6333bcf1409c1e3f86404cae4766a6131db" dependencies = [ "aho-corasick", "memchr", @@ -6973,9 +6979,9 @@ dependencies = [ [[package]] name = "rend" -version = "0.5.3" +version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cadadef317c2f20755a64d7fdc48f9e7178ee6b0e1f7fce33fa60f1d68a276e6" +checksum = "663ba70707f96e871406fe10d68128412e619b06d1d47cb91c3a4c6501176240" dependencies = [ "bytecheck 0.8.2", ] @@ -7163,9 +7169,9 @@ dependencies = [ [[package]] name = "rkyv" -version = "0.8.16" +version = "0.8.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73389e0c99e664f919275ab5b5b0471391fe9a8de61e1dff9b1eaf56a90f16e3" +checksum = "815cc8a37159a463064825246cadb07961e25cd9885908606f6d08a98d8f8874" dependencies = [ "bytecheck 0.8.2", "bytes", @@ -7174,8 +7180,8 @@ dependencies = [ "munge", "ptr_meta 0.3.1", "rancor", - "rend 0.5.3", - "rkyv_derive 0.8.16", + "rend 0.5.4", + "rkyv_derive 0.8.17", "tinyvec", "uuid", ] @@ -7193,9 +7199,9 @@ dependencies = [ [[package]] name = "rkyv_derive" -version = "0.8.16" +version = "0.8.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d2ed0b54125315fb36bd021e82d314d1c126548f871634b483f46b31d13cac6" +checksum = "c0ed1a78a1b19d184b0daa629dd9a024573173ec7d485b287cb369fb3607cc1c" dependencies = [ "proc-macro2", "quote", @@ -7281,7 +7287,7 @@ dependencies = [ "enum_dispatch", "flate2", "futures", - "generic-array 1.4.3", + "generic-array 1.4.4", "getrandom 0.2.17", "hex-literal 0.4.1", "hmac 0.12.1", @@ -7300,13 +7306,13 @@ dependencies = [ "pkcs1 0.7.5", "pkcs5 0.7.1", "pkcs8 0.10.2", - "rand 0.8.6", + "rand 0.8.7", "rand_core 0.6.4", "rsa 0.9.10", "russh-cryptovec 0.52.0", "russh-util", "sec1 0.7.3", - "sha1 0.10.6", + "sha1 0.10.7", "sha2 0.10.9", "signature 2.2.0", "spki 0.7.3", @@ -7337,7 +7343,7 @@ dependencies = [ "curve25519-dalek 5.0.0-rc.0", "data-encoding", "delegate", - "der 0.8.0", + "der 0.8.1", "digest 0.11.3", "ecdsa 0.17.0-rc.18", "ed25519-dalek 3.0.0-rc.0", @@ -7345,7 +7351,7 @@ dependencies = [ "enum_dispatch", "flate2", "futures", - "generic-array 1.4.3", + "generic-array 1.4.4", "getrandom 0.4.3", "ghash 0.6.0", "hex-literal 1.1.0", @@ -7354,7 +7360,7 @@ dependencies = [ "internal-russh-num-bigint", "keccak 0.2.0", "log", - "md5 0.8.0", + "md5 0.8.1", "ml-kem", "module-lattice", "num-bigint", @@ -7366,8 +7372,8 @@ dependencies = [ "pkcs1 0.8.0-rc.4", "pkcs5 0.8.1", "pkcs8 0.11.0", - "polyval 0.7.1", - "rand 0.10.1", + "polyval 0.7.2", + "rand 0.10.2", "rand_core 0.10.1", "rsa 0.10.0-rc.18", "russh-cryptovec 0.61.0", @@ -7449,9 +7455,9 @@ dependencies = [ [[package]] name = "rust-embed" -version = "8.11.0" +version = "8.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04113cb9355a377d83f06ef1f0a45b8ab8cd7d8b1288160717d66df5c7988d27" +checksum = "e9e7760e252aaba7b09f4be00e36476cf585bdb68a53552ac954cdf504ab4bc9" dependencies = [ "rust-embed-impl", "rust-embed-utils", @@ -7460,10 +7466,11 @@ dependencies = [ [[package]] name = "rust-embed-impl" -version = "8.11.0" +version = "8.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da0902e4c7c8e997159ab384e6d0fc91c221375f6894346ae107f47dd0f3ccaa" +checksum = "3bcfc4d6f53af43755f7a723e4b6b8794fcce052a178dd8c6c1dadc5f5343097" dependencies = [ + "mime_guess", "proc-macro2", "quote", "rust-embed-utils", @@ -7473,11 +7480,11 @@ dependencies = [ [[package]] name = "rust-embed-utils" -version = "8.11.0" +version = "8.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5bcdef0be6fe7f6fa333b1073c949729274b05f123a0ad7efcb8efd878e5c3b1" +checksum = "42ffa149f6aa81b58a5b3011d01a857c4ed12c7a732d2c51947a4c7c692185f0" dependencies = [ - "sha2 0.10.9", + "sha2 0.11.0", "walkdir", ] @@ -7497,11 +7504,11 @@ version = "1.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "be2a24f50780bc85f09cc6ac299bdf1424302742d77221106859c9d8b102126a" dependencies = [ - "arrayvec 0.7.7", + "arrayvec 0.7.8", "borsh", "bytes", "num-traits", - "rand 0.8.6", + "rand 0.8.7", "rkyv 0.7.46", "serde", "serde_json", @@ -7510,9 +7517,9 @@ dependencies = [ [[package]] name = "rustc-hash" -version = "2.1.2" +version = "2.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94300abf3f1ae2e2b8ffb7b58043de3d399c73fa6f4b73826402a5c457614dbe" +checksum = "6b1e7f9a428571be2dc5bc0505c13fb6bf936822b894ec87abf8a08a4e51742d" [[package]] name = "rustc-literal-escaper" @@ -7561,14 +7568,14 @@ dependencies = [ "errno", "libc", "linux-raw-sys 0.12.1", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] name = "rustls" -version = "0.23.41" +version = "0.23.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b92b125634d9b795e7beca796cc790df15a7fb38323bf3196fda83292d06b1f" +checksum = "3c54fcab019b409d04215d3a17cb438fd7fbf192ee61461f20f4fe18704bc138" dependencies = [ "aws-lc-rs", "log", @@ -7603,9 +7610,9 @@ dependencies = [ [[package]] name = "rustls-pki-types" -version = "1.14.1" +version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30a7197ae7eb376e574fe940d068c30fe0462554a3ddbe4eca7838e049c937a9" +checksum = "764899a24af3980067ee14bc143654f297b22eaebfe3c7b6b211920a5a59b046" dependencies = [ "web-time", "zeroize", @@ -7629,7 +7636,7 @@ dependencies = [ "security-framework", "security-framework-sys", "webpki-root-certs", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -7652,9 +7659,9 @@ dependencies = [ [[package]] name = "rustversion" -version = "1.0.22" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" +checksum = "cf54715a573b99ac80df0bc206da022bcd442c974952c7b9720069370852e21f" [[package]] name = "ryu" @@ -7994,7 +8001,7 @@ checksum = "d56d437c2f19203ce5f7122e507831de96f3d2d4d3be5af44a0b0a09d8a80e4d" dependencies = [ "base16ct 1.0.0", "ctutils", - "der 0.8.0", + "der 0.8.1", "hybrid-array", "subtle", "zeroize", @@ -8007,7 +8014,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2c3c81b43dc2d8877c216a3fccf76677ee1ebccd429566d3e67447290d0c42b2" dependencies = [ "bitcoin_hashes", - "rand 0.9.4", + "rand 0.9.5", "secp256k1-sys", "serde", ] @@ -8274,9 +8281,9 @@ dependencies = [ [[package]] name = "sha1" -version = "0.10.6" +version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" +checksum = "a978451301f4db1d02937a4ab3ccce137717b81826e79b7d49ffe3244a13c3b8" dependencies = [ "cfg-if", "cpufeatures 0.2.17", @@ -8301,7 +8308,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "89f599ac0c323ebb1c6082821a54962b839832b03984598375bff3975b804423" dependencies = [ "digest 0.10.7", - "sha1 0.10.6", + "sha1 0.10.7", "zeroize", ] @@ -8418,9 +8425,9 @@ checksum = "703d5c7ef118737c72f1af64ad2f6f8c5e1921f818cdcb97b8fe6fc69bf66214" [[package]] name = "simd_cesu8" -version = "1.1.1" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94f90157bb87cddf702797c5dadfa0be7d266cdf49e22da2fcaa32eff75b2c33" +checksum = "11031e251abf8611c80f460e19dbdeb54a66db918e49c65a7065b46ac7aec520" dependencies = [ "rustc_version", "simdutf8", @@ -8438,6 +8445,15 @@ version = "2.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbbb5d9659141646ae647b42fe094daf6c6192d1620870b449d9557f748b2daa" +[[package]] +name = "similar" +version = "3.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6505efef05804732ed8a3f2d4f279429eb485bd69d5b0cc6b19cc02005cda16" +dependencies = [ + "bstr", +] + [[package]] name = "siphasher" version = "1.0.3" @@ -8518,28 +8534,28 @@ dependencies = [ [[package]] name = "socket2" -version = "0.6.4" +version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52d1cfed4120b4d927bf7c0f86d2087a4a7d6027c906d9f9d525a80573b9be51" +checksum = "c3d1e2c7f27f8d4cb10542a02c49005dbd6e93095799d6f3be745fae9f8fedd4" dependencies = [ "libc", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] name = "spin" -version = "0.9.8" +version = "0.9.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" +checksum = "3763264f6b73151db08c50ff20d7d8a0b8796e021cdea7ceedad07b80155fa0e" dependencies = [ "lock_api", ] [[package]] name = "spin" -version = "0.10.0" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5fe4ccb98d9c292d56fec89a5e07da7fc4cf0dc11e156b41793132775d3e591" +checksum = "023a211cb3138dbc438680b32560ad89f699977624c9f8dbb95a47d5b4c07dd3" [[package]] name = "spki" @@ -8558,7 +8574,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1d9efca8738c78ee9484207732f728b1ef517bbb1833d6fc0879ca898a522f6f" dependencies = [ "base64ct", - "der 0.8.0", + "der 0.8.1", ] [[package]] @@ -8684,11 +8700,11 @@ dependencies = [ "memchr", "once_cell", "percent-encoding", - "rand 0.8.6", + "rand 0.8.7", "rsa 0.9.10", "rust_decimal", "serde", - "sha1 0.10.6", + "sha1 0.10.7", "sha2 0.10.9", "smallvec", "sqlx-core", @@ -8728,7 +8744,7 @@ dependencies = [ "memchr", "num-bigint", "once_cell", - "rand 0.8.6", + "rand 0.8.7", "rust_decimal", "serde", "serde_json", @@ -8802,7 +8818,7 @@ dependencies = [ "ctr 0.10.1", "ctutils", "des 0.9.0", - "poly1305 0.9.0", + "poly1305 0.9.1", "ssh-encoding 0.3.0-rc.9", "zeroize", ] @@ -8876,7 +8892,7 @@ dependencies = [ "cfg-if", "libc", "psm", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -8895,7 +8911,7 @@ dependencies = [ "crossbeam-channel", "getrandom 0.2.17", "parking_lot 0.12.5", - "rand 0.8.6", + "rand 0.8.7", "seahash", "thiserror 1.0.69", "tracing", @@ -9076,9 +9092,9 @@ dependencies = [ [[package]] name = "sysinfo" -version = "0.39.5" +version = "0.39.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c8bd2130a9b60bee2581bf82cfe89ee836424d1f37dcfa4ce21509611684673" +checksum = "d2071df9448915b71c4fe6d25deaf1c22f12bd234f01540b77312bb8e41361e6" dependencies = [ "libc", "memchr", @@ -9179,7 +9195,7 @@ dependencies = [ "getrandom 0.4.3", "once_cell", "rustix 1.1.4", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -9188,7 +9204,7 @@ version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d8c27177b12a6399ffc08b98f76f7c9a1f4fe9fc967c784c5a071fa8d93cf7e1" dependencies = [ - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -9243,9 +9259,9 @@ dependencies = [ [[package]] name = "thread_local" -version = "1.1.9" +version = "1.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f60246a4944f24f6e018aa17cdeffb7818b76356965d03b07d6a9886e8962185" +checksum = "1ad99c4c6d32803332c548b1af0540b357b3f5fc0be8f6c6bfe8b2e6ae784070" dependencies = [ "cfg-if", ] @@ -9261,9 +9277,9 @@ dependencies = [ [[package]] name = "time" -version = "0.3.51" +version = "0.3.53" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85c17d80feb7334b40c484e45ed1a5273dfd8bfda537c3be2e74a06a6686f327" +checksum = "18dfaaeddcb932337b5e7866ee7d0ce9b76d2fd092997146f187ec09b4558a50" dependencies = [ "deranged", "num-conv", @@ -9281,9 +9297,9 @@ checksum = "9e1c906769ad99c88eaa54e728060edef082f8e358ff32030cb7c7d315e81109" [[package]] name = "time-macros" -version = "0.2.30" +version = "0.2.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcef1a61bdb119096e153208ec5cbec23944ce8bca13be5c7f60c634f7403935" +checksum = "c431b87111666e491a90baa837f914fb45cd5dc3c268591b0220ff5057f2085f" dependencies = [ "num-conv", "time-core", @@ -9310,9 +9326,9 @@ dependencies = [ [[package]] name = "tinyvec" -version = "1.11.0" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e61e67053d25a4e82c844e8424039d9745781b3fc4f32b8d55ed50f5f667ef3" +checksum = "bb4ebadaa0af04fab11ae01eb5f9fdb5f9c5b875506e210e71c07873528baa7f" dependencies = [ "tinyvec_macros", ] @@ -9356,7 +9372,7 @@ dependencies = [ "parking_lot 0.12.5", "pin-project-lite", "signal-hook-registry", - "socket2 0.6.4", + "socket2 0.6.5", "tokio-macros", "windows-sys 0.61.2", ] @@ -9419,13 +9435,25 @@ name = "tokio-tungstenite" version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f72a05e828585856dacd553fba484c242c46e391fb0e58917c942ee9202915c" +dependencies = [ + "futures-util", + "log", + "tokio", + "tungstenite 0.29.0", +] + +[[package]] +name = "tokio-tungstenite" +version = "0.30.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17a073bfed563fa236697a068031408a93cd9522e08abf9933ead3e73411bd71" dependencies = [ "futures-util", "log", "native-tls", "tokio", "tokio-native-tls", - "tungstenite", + "tungstenite 0.30.0", ] [[package]] @@ -9494,7 +9522,7 @@ dependencies = [ "toml_datetime 1.1.1+spec-1.1.0", "toml_parser", "toml_writer", - "winnow 1.0.3", + "winnow 1.0.4", ] [[package]] @@ -9547,7 +9575,7 @@ dependencies = [ "indexmap 2.14.0", "toml_datetime 1.1.1+spec-1.1.0", "toml_parser", - "winnow 1.0.3", + "winnow 1.0.4", ] [[package]] @@ -9556,7 +9584,7 @@ version = "1.1.2+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2abe9b86193656635d2411dc43050282ca48aa31c2451210f4202550afb7526" dependencies = [ - "winnow 1.0.3", + "winnow 1.0.4", ] [[package]] @@ -9732,7 +9760,7 @@ dependencies = [ "futures", "http", "parking_lot 0.12.5", - "rand 0.9.4", + "rand 0.9.5", "serde", "serde_json", "thiserror 2.0.18", @@ -9885,6 +9913,22 @@ name = "tungstenite" version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c01152af293afb9c7c2a57e4b559c5620b421f6d133261c60dd2d0cdb38e6b8" +dependencies = [ + "bytes", + "data-encoding", + "http", + "httparse", + "log", + "rand 0.9.5", + "sha1 0.10.7", + "thiserror 2.0.18", +] + +[[package]] +name = "tungstenite" +version = "0.30.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e48ac77174b19c110a50ab2128b24215ac9cb40e0e12e093fb602d175c569d22" dependencies = [ "bytes", "data-encoding", @@ -9892,8 +9936,8 @@ dependencies = [ "httparse", "log", "native-tls", - "rand 0.9.4", - "sha1 0.10.6", + "rand 0.10.2", + "sha1 0.11.0", "thiserror 2.0.18", ] @@ -10152,13 +10196,13 @@ dependencies = [ [[package]] name = "uuid" -version = "1.23.4" +version = "1.23.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf80a72845275afea99e7f2b434723d3bc7e38470fcd1c7ed39a599c73319a53" +checksum = "ea5fab0d6c3c01ae70085a09cb03d4c7a1d6314e2b3e075392783396d724ca0a" dependencies = [ "getrandom 0.4.3", "js-sys", - "rand 0.10.1", + "rand 0.10.2", "serde_core", "wasm-bindgen", ] @@ -10181,7 +10225,7 @@ dependencies = [ "libvault-core", "openssl", "pgp", - "rand 0.8.6", + "rand 0.8.7", "secp256k1", "serde", "serde_json", @@ -10199,9 +10243,9 @@ checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" [[package]] name = "vecmap-rs" -version = "0.2.4" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9758649b51083aa8008666f41c23f05abca1766aad4cc447b195dd83ef1297b" +checksum = "58c1dc449b873236909c7f325adf395071c209047737d831183e95c692413adf" dependencies = [ "serde", ] @@ -10276,7 +10320,7 @@ version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a5924018406ce0063cd67f8e008104968b74b563ee1b85dde3ed1f7cb87d3dbd" dependencies = [ - "arrayvec 0.7.7", + "arrayvec 0.7.8", "memchr", ] @@ -10493,7 +10537,7 @@ version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ - "windows-sys 0.48.0", + "windows-sys 0.61.2", ] [[package]] @@ -10721,15 +10765,6 @@ dependencies = [ "windows-targets 0.52.6", ] -[[package]] -name = "windows-sys" -version = "0.60.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" -dependencies = [ - "windows-targets 0.53.5", -] - [[package]] name = "windows-sys" version = "0.61.2" @@ -10763,30 +10798,13 @@ dependencies = [ "windows_aarch64_gnullvm 0.52.6", "windows_aarch64_msvc 0.52.6", "windows_i686_gnu 0.52.6", - "windows_i686_gnullvm 0.52.6", + "windows_i686_gnullvm", "windows_i686_msvc 0.52.6", "windows_x86_64_gnu 0.52.6", "windows_x86_64_gnullvm 0.52.6", "windows_x86_64_msvc 0.52.6", ] -[[package]] -name = "windows-targets" -version = "0.53.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3" -dependencies = [ - "windows-link 0.2.1", - "windows_aarch64_gnullvm 0.53.1", - "windows_aarch64_msvc 0.53.1", - "windows_i686_gnu 0.53.1", - "windows_i686_gnullvm 0.53.1", - "windows_i686_msvc 0.53.1", - "windows_x86_64_gnu 0.53.1", - "windows_x86_64_gnullvm 0.53.1", - "windows_x86_64_msvc 0.53.1", -] - [[package]] name = "windows-threading" version = "0.1.0" @@ -10817,12 +10835,6 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53" - [[package]] name = "windows_aarch64_msvc" version = "0.48.5" @@ -10835,12 +10847,6 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" -[[package]] -name = "windows_aarch64_msvc" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006" - [[package]] name = "windows_i686_gnu" version = "0.48.5" @@ -10853,24 +10859,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" -[[package]] -name = "windows_i686_gnu" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3" - [[package]] name = "windows_i686_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" -[[package]] -name = "windows_i686_gnullvm" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c" - [[package]] name = "windows_i686_msvc" version = "0.48.5" @@ -10883,12 +10877,6 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" -[[package]] -name = "windows_i686_msvc" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2" - [[package]] name = "windows_x86_64_gnu" version = "0.48.5" @@ -10901,12 +10889,6 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" -[[package]] -name = "windows_x86_64_gnu" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499" - [[package]] name = "windows_x86_64_gnullvm" version = "0.48.5" @@ -10919,12 +10901,6 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1" - [[package]] name = "windows_x86_64_msvc" version = "0.48.5" @@ -10937,12 +10913,6 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" -[[package]] -name = "windows_x86_64_msvc" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" - [[package]] name = "winnow" version = "0.7.15" @@ -10954,9 +10924,9 @@ dependencies = [ [[package]] name = "winnow" -version = "1.0.3" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0592e1c9d151f854e6fd382574c3a0855250e1d9b2f99d9281c6e6391af352f1" +checksum = "23b97319f7b8343df12cc98938e5c3eb436064524c8d2b4e30a1d3a36eecdf81" dependencies = [ "memchr", ] @@ -11013,9 +10983,9 @@ dependencies = [ [[package]] name = "xxhash-rust" -version = "0.8.15" +version = "0.8.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fdd20c5420375476fbd4394763288da7eb0cc0b8c11deed431a91562af7335d3" +checksum = "4d93c89cdc2d3a63c3ec48ffe926931bdc069eafa8e4402fe6d8f790c9d1e576" [[package]] name = "yaml-rust2" @@ -11059,18 +11029,18 @@ dependencies = [ [[package]] name = "zerocopy" -version = "0.8.52" +version = "0.8.54" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce1022995ff5ff5d841ad7d994facc23098cd40152f2c1d11cd607c6f530653f" +checksum = "b7cbbc0a705a0fd05cc3676525980d2bf5a9bc4adac6d6475209a7887cf59d19" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.8.52" +version = "0.8.54" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ae7f38b72ec2a254e2b87ef277cf2cd4fb97cbebf944faa6f33354da0867930" +checksum = "e2e817b7b52d0c7358d3246da9d69935ebb18116b2b102b4230dac079b4862f5" dependencies = [ "proc-macro2", "quote", @@ -11167,15 +11137,15 @@ dependencies = [ [[package]] name = "zlib-rs" -version = "0.6.4" +version = "0.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "977347db8caa080403f6b6b7c1cda9479a8e869316f7e13a59b19076a40f94e3" +checksum = "b142a20ec14a91d5bc708c1dc21b080c550113d8aa77afa29635673a65dd02c5" [[package]] name = "zmij" -version = "1.0.21" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" +checksum = "29666d0abbfad1e3dc4dcf6144730dd3a3ab225bbbdac83319345b1b44ccfc1b" [[package]] name = "zopfli" diff --git a/Cargo.toml b/Cargo.toml index 8abc7d29d..9bc3c120b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -38,7 +38,7 @@ orion = { path = "orion" } orion-client = { path = "clients/orion-client" } orion-scheduler-client = { path = "clients/orion-scheduler-client" } -git-internal = "0.7.6" +git-internal = "0.8.3" libvault-core = "0.1.0" #==== @@ -81,9 +81,9 @@ sea-orm = "1.1.20" sea-orm-migration = "1.1.20" #==== -rand = "0.10.1" +rand = "0.10.2" smallvec = "1.15.2" -bytes = "1.12.0" +bytes = "1.12.1" chrono = { version = "0.4.45", features = ["serde"] } hex = "0.4.3" sha1 = "0.11" @@ -95,8 +95,8 @@ idgenerator = "2.0.0" config = "0.15.25" reqwest = "0.13.4" lazy_static = "1.5.0" -uuid = "1.23.4" -regex = "1.12.4" +uuid = "1.23.5" +regex = "1.13.0" ed25519-dalek = "2.2.0" ctrlc = "3.5.2" cedar-policy = "4.11.2" @@ -111,14 +111,14 @@ tempfile = "3.27.0" dashmap = "6.2.1" once_cell = "1.21.4" serial_test = "3.5.0" -sysinfo = "0.39.5" +sysinfo = "0.39.6" http = "1.4.2" url = "2.5.8" jemallocator = "0.5.4" mimalloc = "0.1.52" dotenvy = "0.15.7" -tokio-tungstenite = "0.29" -tungstenite = "0.29" +tokio-tungstenite = "0.30" +tungstenite = "0.30" itertools = "0.15.0" bs58 = "0.5.1" indexmap = "2.14" @@ -126,12 +126,12 @@ envsubst = "0.2.1" directories = "6.0.0" redis = "1.3.0" redis-test = "1.0.4" -rustls = "0.23.41" +rustls = "0.23.42" object_store = "0.14.0" parse-display = "0.11.0" qlean = "0.3.0" toml = "1.1.2" -rkyv = "0.8.16" +rkyv = "0.8.17" percent-encoding = "2.3" diff --git a/ceres/src/application/api_service/mono/admin/group.rs b/ceres/src/application/api_service/mono/admin/group.rs index 905f303c5..6d6e8857c 100644 --- a/ceres/src/application/api_service/mono/admin/group.rs +++ b/ceres/src/application/api_service/mono/admin/group.rs @@ -17,7 +17,7 @@ use crate::{ fn normalize_optional_description(description: Option) -> Option { description .map(|item| item.trim().to_string()) - .and_then(|item| if item.is_empty() { None } else { Some(item) }) + .filter(|item| !item.is_empty()) } fn validate_group_name(name: &str) -> Result { diff --git a/ceres/src/application/api_service/mono/mod.rs b/ceres/src/application/api_service/mono/mod.rs index f830894d5..3e73ad3d4 100644 --- a/ceres/src/application/api_service/mono/mod.rs +++ b/ceres/src/application/api_service/mono/mod.rs @@ -27,6 +27,7 @@ pub mod stack; pub mod sync; pub mod tag; pub mod user; +pub mod user_approval; pub use admin::{ADMIN_FILE, EffectiveResourcePermission}; pub use app_services::MonoAppServices; diff --git a/ceres/src/application/api_service/mono/user_approval.rs b/ceres/src/application/api_service/mono/user_approval.rs new file mode 100644 index 000000000..d00372514 --- /dev/null +++ b/ceres/src/application/api_service/mono/user_approval.rs @@ -0,0 +1,84 @@ +//! User approval status operations for [`UserApplicationService`]. + +use callisto::user_approval_status; +use common::errors::MegaError; +use jupiter::storage::user_approval_storage::{ + APPROVAL_STATUS_APPROVED, APPROVAL_STATUS_REJECTED, UserApprovalProfile, +}; + +use super::context::UserApplicationService; +use crate::model::user::UserApprovalStatusRes; + +impl UserApplicationService { + pub async fn get_or_init_user_approval_status( + &self, + username: &str, + campsite_user_id: &str, + display_name: &str, + email: &str, + ) -> Result { + self.ctx + .storage() + .user_approval_storage() + .get_or_create( + username, + UserApprovalProfile { + campsite_user_id: campsite_user_id.to_string(), + display_name: display_name.to_string(), + email: email.to_string(), + }, + ) + .await + } + + pub async fn list_user_approvals( + &self, + status: Option<&str>, + limit: u64, + ) -> Result, MegaError> { + self.ctx + .storage() + .user_approval_storage() + .list_by_status(status, limit) + .await + } + + pub async fn approve_user( + &self, + username: &str, + reviewed_by: &str, + ) -> Result { + self.ctx + .storage() + .user_approval_storage() + .set_status(username, APPROVAL_STATUS_APPROVED, reviewed_by) + .await + } + + pub async fn reject_user( + &self, + username: &str, + reviewed_by: &str, + ) -> Result { + self.ctx + .storage() + .user_approval_storage() + .set_status(username, APPROVAL_STATUS_REJECTED, reviewed_by) + .await + } +} + +impl From for UserApprovalStatusRes { + fn from(value: user_approval_status::Model) -> Self { + Self { + username: value.username, + campsite_user_id: value.campsite_user_id, + display_name: value.display_name, + email: value.email, + status: value.status, + reviewed_by: value.reviewed_by, + reviewed_at: value.reviewed_at.map(|dt| dt.and_utc().timestamp()), + registered_at: value.created_at.and_utc().timestamp(), + } + } +} diff --git a/ceres/src/model/user.rs b/ceres/src/model/user.rs index e76c1ecd9..85c79bfd4 100644 --- a/ceres/src/model/user.rs +++ b/ceres/src/model/user.rs @@ -1,6 +1,6 @@ use callisto::{access_token, ssh_keys}; use serde::{Deserialize, Serialize}; -use utoipa::ToSchema; +use utoipa::{IntoParams, ToSchema}; #[derive(Debug, Deserialize, ToSchema)] pub struct AddSSHKey { @@ -71,3 +71,31 @@ pub struct UpdateClaContentPayload { pub struct ClaContentRes { pub content: String, } + +#[derive(Debug, Serialize, Deserialize, ToSchema)] +pub struct UserApprovalStatusRes { + pub username: String, + pub campsite_user_id: String, + pub display_name: String, + pub email: String, + /// One of: pending, approved, rejected + pub status: String, + pub reviewed_by: Option, + /// Unix timestamp + pub reviewed_at: Option, + /// Unix timestamp + pub registered_at: i64, +} + +#[derive(Debug, Deserialize, IntoParams, ToSchema)] +pub struct ListUserApprovalsQuery { + /// Filter by status: pending, approved, rejected, or all (default: pending) + pub status: Option, + /// Max rows to return (default 100, max 500) + pub limit: Option, +} + +#[derive(Debug, Serialize, Deserialize, ToSchema)] +pub struct UserApprovalListRes { + pub items: Vec, +} diff --git a/jupiter-migrate/src/migration/m20260714_021900_create_user_approval_status.rs b/jupiter-migrate/src/migration/m20260714_021900_create_user_approval_status.rs new file mode 100644 index 000000000..bb2c89c3d --- /dev/null +++ b/jupiter-migrate/src/migration/m20260714_021900_create_user_approval_status.rs @@ -0,0 +1,95 @@ +use sea_orm_migration::{prelude::*, schema::*}; + +#[derive(DeriveMigrationName)] +pub struct Migration; + +#[async_trait::async_trait] +impl MigrationTrait for Migration { + async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> { + manager + .create_table( + Table::create() + .table(UserApprovalStatus::Table) + .if_not_exists() + .col( + ColumnDef::new(UserApprovalStatus::Username) + .string() + .not_null() + .primary_key(), + ) + .col( + ColumnDef::new(UserApprovalStatus::CampsiteUserId) + .string() + .not_null() + .default(""), + ) + .col( + ColumnDef::new(UserApprovalStatus::DisplayName) + .string() + .not_null() + .default(""), + ) + .col( + ColumnDef::new(UserApprovalStatus::Email) + .string() + .not_null() + .default(""), + ) + .col( + ColumnDef::new(UserApprovalStatus::Status) + .string() + .not_null() + .default("pending"), + ) + .col(string_null(UserApprovalStatus::ReviewedBy)) + .col(date_time_null(UserApprovalStatus::ReviewedAt)) + .col(date_time(UserApprovalStatus::CreatedAt)) + .col(date_time(UserApprovalStatus::UpdatedAt)) + .to_owned(), + ) + .await?; + + manager + .create_index( + Index::create() + .if_not_exists() + .name("idx_user_approval_status_status_created_at") + .table(UserApprovalStatus::Table) + .col(UserApprovalStatus::Status) + .col(UserApprovalStatus::CreatedAt) + .to_owned(), + ) + .await?; + + Ok(()) + } + + async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> { + manager + .drop_index( + Index::drop() + .name("idx_user_approval_status_status_created_at") + .table(UserApprovalStatus::Table) + .to_owned(), + ) + .await?; + + manager + .drop_table(Table::drop().table(UserApprovalStatus::Table).to_owned()) + .await + } +} + +#[derive(DeriveIden)] +enum UserApprovalStatus { + Table, + Username, + CampsiteUserId, + DisplayName, + Email, + Status, + ReviewedBy, + ReviewedAt, + CreatedAt, + UpdatedAt, +} diff --git a/jupiter-migrate/src/migration/mod.rs b/jupiter-migrate/src/migration/mod.rs index 01b0760b9..2a3170d8e 100644 --- a/jupiter-migrate/src/migration/mod.rs +++ b/jupiter-migrate/src/migration/mod.rs @@ -98,6 +98,7 @@ mod m20260324_033322_fix_migration; mod m20260327_034553_drop_legacy_tasks; mod m20260413_033315_create_artifact_tables; mod m20260612_011232_drop_build_events_log; +mod m20260714_021900_create_user_approval_status; mod runner; pub use runner::apply_migrations; @@ -183,6 +184,7 @@ impl MigratorTrait for Migrator { Box::new(m20260327_034553_drop_legacy_tasks::Migration), Box::new(m20260413_033315_create_artifact_tables::Migration), Box::new(m20260612_011232_drop_build_events_log::Migration), + Box::new(m20260714_021900_create_user_approval_status::Migration), ] } } diff --git a/jupiter/callisto/src/access_token.rs b/jupiter/callisto/src/access_token.rs index 3f49a41a4..3bc44b213 100644 --- a/jupiter/callisto/src/access_token.rs +++ b/jupiter/callisto/src/access_token.rs @@ -1,4 +1,4 @@ -//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.19 +//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.20 use sea_orm::entity::prelude::*; use serde::{Deserialize, Serialize}; diff --git a/jupiter/callisto/src/artifact_objects.rs b/jupiter/callisto/src/artifact_objects.rs index 7dcfd794f..edd330536 100644 --- a/jupiter/callisto/src/artifact_objects.rs +++ b/jupiter/callisto/src/artifact_objects.rs @@ -1,4 +1,4 @@ -//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.19 +//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.20 use sea_orm::entity::prelude::*; use serde::{Deserialize, Serialize}; diff --git a/jupiter/callisto/src/artifact_set_files.rs b/jupiter/callisto/src/artifact_set_files.rs index 6cedf5291..105bf25f9 100644 --- a/jupiter/callisto/src/artifact_set_files.rs +++ b/jupiter/callisto/src/artifact_set_files.rs @@ -1,4 +1,4 @@ -//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.19 +//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.20 use sea_orm::entity::prelude::*; use serde::{Deserialize, Serialize}; diff --git a/jupiter/callisto/src/artifact_sets.rs b/jupiter/callisto/src/artifact_sets.rs index a65d1fa3b..345a08dbc 100644 --- a/jupiter/callisto/src/artifact_sets.rs +++ b/jupiter/callisto/src/artifact_sets.rs @@ -1,4 +1,4 @@ -//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.19 +//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.20 use sea_orm::entity::prelude::*; use serde::{Deserialize, Serialize}; diff --git a/jupiter/callisto/src/audit_logs.rs b/jupiter/callisto/src/audit_logs.rs index 3c8ab5a62..4fed92f56 100644 --- a/jupiter/callisto/src/audit_logs.rs +++ b/jupiter/callisto/src/audit_logs.rs @@ -1,4 +1,4 @@ -//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.19 +//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.20 use sea_orm::entity::prelude::*; use serde::{Deserialize, Serialize}; diff --git a/jupiter/callisto/src/bot_installations.rs b/jupiter/callisto/src/bot_installations.rs index 71b328c3a..cc41dbe28 100644 --- a/jupiter/callisto/src/bot_installations.rs +++ b/jupiter/callisto/src/bot_installations.rs @@ -1,4 +1,4 @@ -//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.19 +//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.20 use sea_orm::entity::prelude::*; use serde::{Deserialize, Serialize}; diff --git a/jupiter/callisto/src/bot_keys.rs b/jupiter/callisto/src/bot_keys.rs index 6e545d770..98cdc4f9b 100644 --- a/jupiter/callisto/src/bot_keys.rs +++ b/jupiter/callisto/src/bot_keys.rs @@ -1,4 +1,4 @@ -//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.19 +//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.20 use sea_orm::entity::prelude::*; use serde::{Deserialize, Serialize}; diff --git a/jupiter/callisto/src/bot_tokens.rs b/jupiter/callisto/src/bot_tokens.rs index 632edd891..b2a9768d2 100644 --- a/jupiter/callisto/src/bot_tokens.rs +++ b/jupiter/callisto/src/bot_tokens.rs @@ -1,4 +1,4 @@ -//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.19 +//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.20 use sea_orm::entity::prelude::*; use serde::{Deserialize, Serialize}; diff --git a/jupiter/callisto/src/bots.rs b/jupiter/callisto/src/bots.rs index ccc5b4390..418f46f8f 100644 --- a/jupiter/callisto/src/bots.rs +++ b/jupiter/callisto/src/bots.rs @@ -1,4 +1,4 @@ -//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.19 +//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.20 use sea_orm::entity::prelude::*; use serde::{Deserialize, Serialize}; diff --git a/jupiter/callisto/src/buck_session.rs b/jupiter/callisto/src/buck_session.rs index 36249f8e3..bec64facf 100644 --- a/jupiter/callisto/src/buck_session.rs +++ b/jupiter/callisto/src/buck_session.rs @@ -1,4 +1,4 @@ -//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.19 +//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.20 use sea_orm::entity::prelude::*; use serde::{Deserialize, Serialize}; diff --git a/jupiter/callisto/src/buck_session_file.rs b/jupiter/callisto/src/buck_session_file.rs index daf759020..158d68f56 100644 --- a/jupiter/callisto/src/buck_session_file.rs +++ b/jupiter/callisto/src/buck_session_file.rs @@ -1,4 +1,4 @@ -//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.19 +//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.20 use sea_orm::entity::prelude::*; use serde::{Deserialize, Serialize}; diff --git a/jupiter/callisto/src/build_events.rs b/jupiter/callisto/src/build_events.rs index d3dab61cb..642b81f91 100644 --- a/jupiter/callisto/src/build_events.rs +++ b/jupiter/callisto/src/build_events.rs @@ -1,4 +1,4 @@ -//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.19 +//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.20 use sea_orm::entity::prelude::*; use serde::{Deserialize, Serialize}; diff --git a/jupiter/callisto/src/build_targets.rs b/jupiter/callisto/src/build_targets.rs index ca41aa2ff..834046d07 100644 --- a/jupiter/callisto/src/build_targets.rs +++ b/jupiter/callisto/src/build_targets.rs @@ -1,4 +1,4 @@ -//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.19 +//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.20 use sea_orm::entity::prelude::*; use serde::{Deserialize, Serialize}; diff --git a/jupiter/callisto/src/build_triggers.rs b/jupiter/callisto/src/build_triggers.rs index 95b09c7fb..bfb1c26e3 100644 --- a/jupiter/callisto/src/build_triggers.rs +++ b/jupiter/callisto/src/build_triggers.rs @@ -1,4 +1,4 @@ -//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.19 +//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.20 use sea_orm::entity::prelude::*; use serde::{Deserialize, Serialize}; diff --git a/jupiter/callisto/src/check_result.rs b/jupiter/callisto/src/check_result.rs index 3151d2f09..ec6181679 100644 --- a/jupiter/callisto/src/check_result.rs +++ b/jupiter/callisto/src/check_result.rs @@ -1,4 +1,4 @@ -//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.19 +//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.20 use sea_orm::entity::prelude::*; use serde::{Deserialize, Serialize}; diff --git a/jupiter/callisto/src/cla_sign_status.rs b/jupiter/callisto/src/cla_sign_status.rs index f256728c6..20a27fc33 100644 --- a/jupiter/callisto/src/cla_sign_status.rs +++ b/jupiter/callisto/src/cla_sign_status.rs @@ -1,4 +1,4 @@ -//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.19 +//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.20 use sea_orm::entity::prelude::*; use serde::{Deserialize, Serialize}; diff --git a/jupiter/callisto/src/commit_auths.rs b/jupiter/callisto/src/commit_auths.rs index 7be2597b0..be9bc7241 100644 --- a/jupiter/callisto/src/commit_auths.rs +++ b/jupiter/callisto/src/commit_auths.rs @@ -1,4 +1,4 @@ -//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.19 +//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.20 use sea_orm::entity::prelude::*; use serde::{Deserialize, Serialize}; diff --git a/jupiter/callisto/src/dynamic_sidebar.rs b/jupiter/callisto/src/dynamic_sidebar.rs index e1d27b8f1..f2dd1d81b 100644 --- a/jupiter/callisto/src/dynamic_sidebar.rs +++ b/jupiter/callisto/src/dynamic_sidebar.rs @@ -1,4 +1,4 @@ -//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.19 +//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.20 use sea_orm::entity::prelude::*; use serde::{Deserialize, Serialize}; diff --git a/jupiter/callisto/src/email_jobs.rs b/jupiter/callisto/src/email_jobs.rs index 195437314..fdeec0fa5 100644 --- a/jupiter/callisto/src/email_jobs.rs +++ b/jupiter/callisto/src/email_jobs.rs @@ -1,4 +1,4 @@ -//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.19 +//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.20 use sea_orm::entity::prelude::*; use serde::{Deserialize, Serialize}; diff --git a/jupiter/callisto/src/git_blob.rs b/jupiter/callisto/src/git_blob.rs index 2a47d886b..652db9277 100644 --- a/jupiter/callisto/src/git_blob.rs +++ b/jupiter/callisto/src/git_blob.rs @@ -1,4 +1,4 @@ -//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.19 +//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.20 use sea_orm::entity::prelude::*; use serde::{Deserialize, Serialize}; diff --git a/jupiter/callisto/src/git_commit.rs b/jupiter/callisto/src/git_commit.rs index c09ff948a..94ae1ffd5 100644 --- a/jupiter/callisto/src/git_commit.rs +++ b/jupiter/callisto/src/git_commit.rs @@ -1,4 +1,4 @@ -//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.19 +//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.20 use sea_orm::entity::prelude::*; use serde::{Deserialize, Serialize}; diff --git a/jupiter/callisto/src/git_issue.rs b/jupiter/callisto/src/git_issue.rs index 88af1613d..ce7f4203d 100644 --- a/jupiter/callisto/src/git_issue.rs +++ b/jupiter/callisto/src/git_issue.rs @@ -1,4 +1,4 @@ -//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.19 +//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.20 use sea_orm::entity::prelude::*; use serde::{Deserialize, Serialize}; diff --git a/jupiter/callisto/src/git_pr.rs b/jupiter/callisto/src/git_pr.rs index abf3af08f..3c8c1e6a0 100644 --- a/jupiter/callisto/src/git_pr.rs +++ b/jupiter/callisto/src/git_pr.rs @@ -1,4 +1,4 @@ -//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.19 +//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.20 use sea_orm::entity::prelude::*; use serde::{Deserialize, Serialize}; diff --git a/jupiter/callisto/src/git_repo.rs b/jupiter/callisto/src/git_repo.rs index b81eba479..9301b2c1d 100644 --- a/jupiter/callisto/src/git_repo.rs +++ b/jupiter/callisto/src/git_repo.rs @@ -1,4 +1,4 @@ -//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.19 +//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.20 use sea_orm::entity::prelude::*; use serde::{Deserialize, Serialize}; diff --git a/jupiter/callisto/src/git_tag.rs b/jupiter/callisto/src/git_tag.rs index f39838b9d..9237f3099 100644 --- a/jupiter/callisto/src/git_tag.rs +++ b/jupiter/callisto/src/git_tag.rs @@ -1,4 +1,4 @@ -//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.19 +//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.20 use sea_orm::entity::prelude::*; use serde::{Deserialize, Serialize}; diff --git a/jupiter/callisto/src/git_tree.rs b/jupiter/callisto/src/git_tree.rs index aa7f40448..df10ab480 100644 --- a/jupiter/callisto/src/git_tree.rs +++ b/jupiter/callisto/src/git_tree.rs @@ -1,4 +1,4 @@ -//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.19 +//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.20 use sea_orm::entity::prelude::*; use serde::{Deserialize, Serialize}; diff --git a/jupiter/callisto/src/gpg_key.rs b/jupiter/callisto/src/gpg_key.rs index b43624151..0d5a151c7 100644 --- a/jupiter/callisto/src/gpg_key.rs +++ b/jupiter/callisto/src/gpg_key.rs @@ -1,4 +1,4 @@ -//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.19 +//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.20 use sea_orm::entity::prelude::*; use serde::{Deserialize, Serialize}; diff --git a/jupiter/callisto/src/import_refs.rs b/jupiter/callisto/src/import_refs.rs index 584239d01..d61877117 100644 --- a/jupiter/callisto/src/import_refs.rs +++ b/jupiter/callisto/src/import_refs.rs @@ -1,4 +1,4 @@ -//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.19 +//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.20 use sea_orm::entity::prelude::*; use serde::{Deserialize, Serialize}; diff --git a/jupiter/callisto/src/issue_cl_references.rs b/jupiter/callisto/src/issue_cl_references.rs index 84b4caf7a..3d4871fb9 100644 --- a/jupiter/callisto/src/issue_cl_references.rs +++ b/jupiter/callisto/src/issue_cl_references.rs @@ -1,4 +1,4 @@ -//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.19 +//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.20 use sea_orm::entity::prelude::*; use serde::{Deserialize, Serialize}; diff --git a/jupiter/callisto/src/item_assignees.rs b/jupiter/callisto/src/item_assignees.rs index fdb2726b9..3bd6abe6d 100644 --- a/jupiter/callisto/src/item_assignees.rs +++ b/jupiter/callisto/src/item_assignees.rs @@ -1,4 +1,4 @@ -//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.19 +//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.20 use sea_orm::entity::prelude::*; use serde::{Deserialize, Serialize}; diff --git a/jupiter/callisto/src/item_labels.rs b/jupiter/callisto/src/item_labels.rs index 0d4f29ce6..ac6a41469 100644 --- a/jupiter/callisto/src/item_labels.rs +++ b/jupiter/callisto/src/item_labels.rs @@ -1,4 +1,4 @@ -//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.19 +//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.20 use sea_orm::entity::prelude::*; use serde::{Deserialize, Serialize}; diff --git a/jupiter/callisto/src/label.rs b/jupiter/callisto/src/label.rs index 8ee06ab30..07facf4bc 100644 --- a/jupiter/callisto/src/label.rs +++ b/jupiter/callisto/src/label.rs @@ -1,4 +1,4 @@ -//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.19 +//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.20 use sea_orm::entity::prelude::*; use serde::{Deserialize, Serialize}; diff --git a/jupiter/callisto/src/lfs_locks.rs b/jupiter/callisto/src/lfs_locks.rs index 69718ae13..524de63b9 100644 --- a/jupiter/callisto/src/lfs_locks.rs +++ b/jupiter/callisto/src/lfs_locks.rs @@ -1,4 +1,4 @@ -//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.19 +//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.20 use sea_orm::entity::prelude::*; use serde::{Deserialize, Serialize}; diff --git a/jupiter/callisto/src/lfs_objects.rs b/jupiter/callisto/src/lfs_objects.rs index de175217c..c87b2bf69 100644 --- a/jupiter/callisto/src/lfs_objects.rs +++ b/jupiter/callisto/src/lfs_objects.rs @@ -1,4 +1,4 @@ -//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.19 +//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.20 use sea_orm::entity::prelude::*; use serde::{Deserialize, Serialize}; diff --git a/jupiter/callisto/src/mega_blob.rs b/jupiter/callisto/src/mega_blob.rs index 31a9465fd..da31b0bc2 100644 --- a/jupiter/callisto/src/mega_blob.rs +++ b/jupiter/callisto/src/mega_blob.rs @@ -1,4 +1,4 @@ -//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.19 +//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.20 use sea_orm::entity::prelude::*; use serde::{Deserialize, Serialize}; diff --git a/jupiter/callisto/src/mega_cl.rs b/jupiter/callisto/src/mega_cl.rs index ca06bcd21..fba576bbb 100644 --- a/jupiter/callisto/src/mega_cl.rs +++ b/jupiter/callisto/src/mega_cl.rs @@ -1,4 +1,4 @@ -//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.19 +//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.20 use sea_orm::entity::prelude::*; use serde::{Deserialize, Serialize}; diff --git a/jupiter/callisto/src/mega_cl_commits.rs b/jupiter/callisto/src/mega_cl_commits.rs index 734348aed..0aa818a9b 100644 --- a/jupiter/callisto/src/mega_cl_commits.rs +++ b/jupiter/callisto/src/mega_cl_commits.rs @@ -1,4 +1,4 @@ -//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.19 +//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.20 use sea_orm::entity::prelude::*; use serde::{Deserialize, Serialize}; diff --git a/jupiter/callisto/src/mega_cl_reviewer.rs b/jupiter/callisto/src/mega_cl_reviewer.rs index ea97489b8..12fb7ec7a 100644 --- a/jupiter/callisto/src/mega_cl_reviewer.rs +++ b/jupiter/callisto/src/mega_cl_reviewer.rs @@ -1,4 +1,4 @@ -//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.19 +//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.20 use sea_orm::entity::prelude::*; use serde::{Deserialize, Serialize}; diff --git a/jupiter/callisto/src/mega_code_review_anchor.rs b/jupiter/callisto/src/mega_code_review_anchor.rs index e70f17fb4..9ac4c730b 100644 --- a/jupiter/callisto/src/mega_code_review_anchor.rs +++ b/jupiter/callisto/src/mega_code_review_anchor.rs @@ -1,4 +1,4 @@ -//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.19 +//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.20 use sea_orm::entity::prelude::*; use serde::{Deserialize, Serialize}; diff --git a/jupiter/callisto/src/mega_code_review_comment.rs b/jupiter/callisto/src/mega_code_review_comment.rs index 7fd6e045b..90ae056dc 100644 --- a/jupiter/callisto/src/mega_code_review_comment.rs +++ b/jupiter/callisto/src/mega_code_review_comment.rs @@ -1,4 +1,4 @@ -//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.19 +//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.20 use sea_orm::entity::prelude::*; use serde::{Deserialize, Serialize}; diff --git a/jupiter/callisto/src/mega_code_review_position.rs b/jupiter/callisto/src/mega_code_review_position.rs index 0215894b7..44abe8b1e 100644 --- a/jupiter/callisto/src/mega_code_review_position.rs +++ b/jupiter/callisto/src/mega_code_review_position.rs @@ -1,4 +1,4 @@ -//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.19 +//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.20 use sea_orm::entity::prelude::*; use serde::{Deserialize, Serialize}; diff --git a/jupiter/callisto/src/mega_code_review_thread.rs b/jupiter/callisto/src/mega_code_review_thread.rs index 200ebe842..ef93c0137 100644 --- a/jupiter/callisto/src/mega_code_review_thread.rs +++ b/jupiter/callisto/src/mega_code_review_thread.rs @@ -1,4 +1,4 @@ -//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.19 +//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.20 use sea_orm::entity::prelude::*; use serde::{Deserialize, Serialize}; diff --git a/jupiter/callisto/src/mega_commit.rs b/jupiter/callisto/src/mega_commit.rs index 7977b55b6..8351bf3d8 100644 --- a/jupiter/callisto/src/mega_commit.rs +++ b/jupiter/callisto/src/mega_commit.rs @@ -1,4 +1,4 @@ -//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.19 +//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.20 use sea_orm::entity::prelude::*; use serde::{Deserialize, Serialize}; diff --git a/jupiter/callisto/src/mega_conversation.rs b/jupiter/callisto/src/mega_conversation.rs index 25b35a536..e201d9611 100644 --- a/jupiter/callisto/src/mega_conversation.rs +++ b/jupiter/callisto/src/mega_conversation.rs @@ -1,4 +1,4 @@ -//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.19 +//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.20 use sea_orm::entity::prelude::*; use serde::{Deserialize, Serialize}; diff --git a/jupiter/callisto/src/mega_group.rs b/jupiter/callisto/src/mega_group.rs index a7cb714ce..f5ab6e2ee 100644 --- a/jupiter/callisto/src/mega_group.rs +++ b/jupiter/callisto/src/mega_group.rs @@ -1,4 +1,4 @@ -//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.19 +//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.20 use sea_orm::entity::prelude::*; use serde::{Deserialize, Serialize}; diff --git a/jupiter/callisto/src/mega_group_member.rs b/jupiter/callisto/src/mega_group_member.rs index bad7e8277..60f08e592 100644 --- a/jupiter/callisto/src/mega_group_member.rs +++ b/jupiter/callisto/src/mega_group_member.rs @@ -1,4 +1,4 @@ -//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.19 +//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.20 use sea_orm::entity::prelude::*; use serde::{Deserialize, Serialize}; diff --git a/jupiter/callisto/src/mega_issue.rs b/jupiter/callisto/src/mega_issue.rs index 613af9c8a..70457ed04 100644 --- a/jupiter/callisto/src/mega_issue.rs +++ b/jupiter/callisto/src/mega_issue.rs @@ -1,4 +1,4 @@ -//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.19 +//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.20 use sea_orm::entity::prelude::*; use serde::{Deserialize, Serialize}; diff --git a/jupiter/callisto/src/mega_refs.rs b/jupiter/callisto/src/mega_refs.rs index f17724899..f16411d3c 100644 --- a/jupiter/callisto/src/mega_refs.rs +++ b/jupiter/callisto/src/mega_refs.rs @@ -1,4 +1,4 @@ -//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.19 +//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.20 use sea_orm::entity::prelude::*; use serde::{Deserialize, Serialize}; diff --git a/jupiter/callisto/src/mega_resource_permission.rs b/jupiter/callisto/src/mega_resource_permission.rs index 785f88139..c1149c903 100644 --- a/jupiter/callisto/src/mega_resource_permission.rs +++ b/jupiter/callisto/src/mega_resource_permission.rs @@ -1,4 +1,4 @@ -//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.19 +//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.20 use sea_orm::entity::prelude::*; use serde::{Deserialize, Serialize}; diff --git a/jupiter/callisto/src/mega_tag.rs b/jupiter/callisto/src/mega_tag.rs index 1c883f67e..a02920907 100644 --- a/jupiter/callisto/src/mega_tag.rs +++ b/jupiter/callisto/src/mega_tag.rs @@ -1,4 +1,4 @@ -//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.19 +//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.20 use sea_orm::entity::prelude::*; use serde::{Deserialize, Serialize}; diff --git a/jupiter/callisto/src/mega_tree.rs b/jupiter/callisto/src/mega_tree.rs index de06c6c68..e48c86e4e 100644 --- a/jupiter/callisto/src/mega_tree.rs +++ b/jupiter/callisto/src/mega_tree.rs @@ -1,4 +1,4 @@ -//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.19 +//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.20 use sea_orm::entity::prelude::*; use serde::{Deserialize, Serialize}; diff --git a/jupiter/callisto/src/mega_webhook.rs b/jupiter/callisto/src/mega_webhook.rs index 1556a9d48..ecc711370 100644 --- a/jupiter/callisto/src/mega_webhook.rs +++ b/jupiter/callisto/src/mega_webhook.rs @@ -1,4 +1,4 @@ -//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.19 +//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.20 use sea_orm::entity::prelude::*; use serde::{Deserialize, Serialize}; diff --git a/jupiter/callisto/src/mega_webhook_delivery.rs b/jupiter/callisto/src/mega_webhook_delivery.rs index fe1f1f78c..64ddae16a 100644 --- a/jupiter/callisto/src/mega_webhook_delivery.rs +++ b/jupiter/callisto/src/mega_webhook_delivery.rs @@ -1,4 +1,4 @@ -//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.19 +//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.20 use sea_orm::entity::prelude::*; use serde::{Deserialize, Serialize}; diff --git a/jupiter/callisto/src/mega_webhook_event_type.rs b/jupiter/callisto/src/mega_webhook_event_type.rs index 2d630a821..1dc2090d3 100644 --- a/jupiter/callisto/src/mega_webhook_event_type.rs +++ b/jupiter/callisto/src/mega_webhook_event_type.rs @@ -1,4 +1,4 @@ -//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.19 +//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.20 use sea_orm::entity::prelude::*; use serde::{Deserialize, Serialize}; diff --git a/jupiter/callisto/src/merge_queue.rs b/jupiter/callisto/src/merge_queue.rs index ee78cccd6..031f6c0b3 100644 --- a/jupiter/callisto/src/merge_queue.rs +++ b/jupiter/callisto/src/merge_queue.rs @@ -1,4 +1,4 @@ -//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.19 +//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.20 use sea_orm::entity::prelude::*; use serde::{Deserialize, Serialize}; diff --git a/jupiter/callisto/src/mod.rs b/jupiter/callisto/src/mod.rs index 05bd3755e..a919f2a46 100644 --- a/jupiter/callisto/src/mod.rs +++ b/jupiter/callisto/src/mod.rs @@ -1,4 +1,4 @@ -//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.19 +//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.20 pub mod prelude; @@ -69,6 +69,7 @@ pub mod sea_orm_active_enums; pub mod ssh_keys; pub mod target_build_status; pub mod target_state_histories; +pub mod user_approval_status; pub mod user_notification_preferences; pub mod user_notification_settings; pub mod vault; diff --git a/jupiter/callisto/src/non_member_note_views.rs b/jupiter/callisto/src/non_member_note_views.rs index ed4639076..68436f525 100644 --- a/jupiter/callisto/src/non_member_note_views.rs +++ b/jupiter/callisto/src/non_member_note_views.rs @@ -1,4 +1,4 @@ -//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.19 +//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.20 use sea_orm::entity::prelude::*; use serde::{Deserialize, Serialize}; diff --git a/jupiter/callisto/src/note_views.rs b/jupiter/callisto/src/note_views.rs index dbc20275f..00cf9465d 100644 --- a/jupiter/callisto/src/note_views.rs +++ b/jupiter/callisto/src/note_views.rs @@ -1,4 +1,4 @@ -//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.19 +//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.20 use sea_orm::entity::prelude::*; use serde::{Deserialize, Serialize}; diff --git a/jupiter/callisto/src/notes.rs b/jupiter/callisto/src/notes.rs index b52090993..a5d88769a 100644 --- a/jupiter/callisto/src/notes.rs +++ b/jupiter/callisto/src/notes.rs @@ -1,4 +1,4 @@ -//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.19 +//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.20 use sea_orm::entity::prelude::*; use serde::{Deserialize, Serialize}; diff --git a/jupiter/callisto/src/notification_event_types.rs b/jupiter/callisto/src/notification_event_types.rs index 81b0c75e0..a50531f56 100644 --- a/jupiter/callisto/src/notification_event_types.rs +++ b/jupiter/callisto/src/notification_event_types.rs @@ -1,4 +1,4 @@ -//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.19 +//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.20 use sea_orm::entity::prelude::*; use serde::{Deserialize, Serialize}; diff --git a/jupiter/callisto/src/orion_tasks.rs b/jupiter/callisto/src/orion_tasks.rs index d9190f277..5b882a3c9 100644 --- a/jupiter/callisto/src/orion_tasks.rs +++ b/jupiter/callisto/src/orion_tasks.rs @@ -1,4 +1,4 @@ -//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.19 +//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.20 use sea_orm::entity::prelude::*; use serde::{Deserialize, Serialize}; diff --git a/jupiter/callisto/src/path_check_configs.rs b/jupiter/callisto/src/path_check_configs.rs index e39b70037..45eef0a28 100644 --- a/jupiter/callisto/src/path_check_configs.rs +++ b/jupiter/callisto/src/path_check_configs.rs @@ -1,4 +1,4 @@ -//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.19 +//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.20 use sea_orm::entity::prelude::*; use serde::{Deserialize, Serialize}; diff --git a/jupiter/callisto/src/prelude.rs b/jupiter/callisto/src/prelude.rs index 601bc21c7..c12af51f3 100644 --- a/jupiter/callisto/src/prelude.rs +++ b/jupiter/callisto/src/prelude.rs @@ -1,4 +1,4 @@ -//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.19 +//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.20 pub use super::{ access_token::Entity as AccessToken, artifact_objects::Entity as ArtifactObjects, @@ -33,6 +33,7 @@ pub use super::{ reactions::Entity as Reactions, ssh_keys::Entity as SshKeys, target_build_status::Entity as TargetBuildStatus, target_state_histories::Entity as TargetStateHistories, + user_approval_status::Entity as UserApprovalStatus, user_notification_preferences::Entity as UserNotificationPreferences, user_notification_settings::Entity as UserNotificationSettings, vault::Entity as Vault, }; diff --git a/jupiter/callisto/src/reactions.rs b/jupiter/callisto/src/reactions.rs index 4e52b1cc3..6a9a78802 100644 --- a/jupiter/callisto/src/reactions.rs +++ b/jupiter/callisto/src/reactions.rs @@ -1,4 +1,4 @@ -//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.19 +//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.20 use sea_orm::entity::prelude::*; use serde::{Deserialize, Serialize}; diff --git a/jupiter/callisto/src/sea_orm_active_enums.rs b/jupiter/callisto/src/sea_orm_active_enums.rs index 1bc722d83..74b45dce5 100644 --- a/jupiter/callisto/src/sea_orm_active_enums.rs +++ b/jupiter/callisto/src/sea_orm_active_enums.rs @@ -1,4 +1,4 @@ -//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.19 +//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.20 use sea_orm::entity::prelude::*; use serde::{Deserialize, Serialize}; diff --git a/jupiter/callisto/src/ssh_keys.rs b/jupiter/callisto/src/ssh_keys.rs index dae98351f..6756d3ee2 100644 --- a/jupiter/callisto/src/ssh_keys.rs +++ b/jupiter/callisto/src/ssh_keys.rs @@ -1,4 +1,4 @@ -//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.19 +//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.20 use sea_orm::entity::prelude::*; use serde::{Deserialize, Serialize}; diff --git a/jupiter/callisto/src/target_build_status.rs b/jupiter/callisto/src/target_build_status.rs index d9735e5ea..8afffb692 100644 --- a/jupiter/callisto/src/target_build_status.rs +++ b/jupiter/callisto/src/target_build_status.rs @@ -1,4 +1,4 @@ -//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.19 +//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.20 use sea_orm::entity::prelude::*; use serde::{Deserialize, Serialize}; diff --git a/jupiter/callisto/src/target_state_histories.rs b/jupiter/callisto/src/target_state_histories.rs index 9a020e897..ee0fd4f32 100644 --- a/jupiter/callisto/src/target_state_histories.rs +++ b/jupiter/callisto/src/target_state_histories.rs @@ -1,4 +1,4 @@ -//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.19 +//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.20 use sea_orm::entity::prelude::*; use serde::{Deserialize, Serialize}; diff --git a/jupiter/callisto/src/user_approval_status.rs b/jupiter/callisto/src/user_approval_status.rs new file mode 100644 index 000000000..f72169c86 --- /dev/null +++ b/jupiter/callisto/src/user_approval_status.rs @@ -0,0 +1,24 @@ +//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.20 + +use sea_orm::entity::prelude::*; +use serde::{Deserialize, Serialize}; + +#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)] +#[sea_orm(table_name = "user_approval_status")] +pub struct Model { + #[sea_orm(primary_key, auto_increment = false)] + pub username: String, + pub campsite_user_id: String, + pub display_name: String, + pub email: String, + pub status: String, + pub reviewed_by: Option, + pub reviewed_at: Option, + pub created_at: DateTime, + pub updated_at: DateTime, +} + +#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] +pub enum Relation {} + +impl ActiveModelBehavior for ActiveModel {} diff --git a/jupiter/callisto/src/user_notification_preferences.rs b/jupiter/callisto/src/user_notification_preferences.rs index 17639d3cf..46235d295 100644 --- a/jupiter/callisto/src/user_notification_preferences.rs +++ b/jupiter/callisto/src/user_notification_preferences.rs @@ -1,4 +1,4 @@ -//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.19 +//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.20 use sea_orm::entity::prelude::*; use serde::{Deserialize, Serialize}; diff --git a/jupiter/callisto/src/user_notification_settings.rs b/jupiter/callisto/src/user_notification_settings.rs index f37f41210..efbb0a379 100644 --- a/jupiter/callisto/src/user_notification_settings.rs +++ b/jupiter/callisto/src/user_notification_settings.rs @@ -1,4 +1,4 @@ -//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.19 +//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.20 use sea_orm::entity::prelude::*; use serde::{Deserialize, Serialize}; diff --git a/jupiter/callisto/src/vault.rs b/jupiter/callisto/src/vault.rs index ea8612c2b..4d6b1655a 100644 --- a/jupiter/callisto/src/vault.rs +++ b/jupiter/callisto/src/vault.rs @@ -1,4 +1,4 @@ -//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.19 +//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.20 use sea_orm::entity::prelude::*; use serde::{Deserialize, Serialize}; diff --git a/jupiter/src/storage/init.rs b/jupiter/src/storage/init.rs index ad1942732..6998de3c3 100644 --- a/jupiter/src/storage/init.rs +++ b/jupiter/src/storage/init.rs @@ -41,7 +41,7 @@ pub async fn database_connection(db_config: &DbConfig) -> DatabaseConnection { Err(reason) => { log::warn!( "Falling back to SQLite for {}. Reason: {}", - &db_config.db_url, + db_config.db_url, reason ); sqlite_connection(db_config) diff --git a/jupiter/src/storage/mod.rs b/jupiter/src/storage/mod.rs index 1a8d11739..975fe5870 100644 --- a/jupiter/src/storage/mod.rs +++ b/jupiter/src/storage/mod.rs @@ -21,6 +21,7 @@ pub mod lfs_db_storage; pub mod merge_queue_storage; pub mod mono_storage; pub mod notification_storage; +pub mod user_approval_storage; pub use notification_storage::NotificationStorage; pub mod note_storage; pub mod stg_common; @@ -65,6 +66,7 @@ use crate::{ merge_queue_storage::MergeQueueStorage, mono_storage::MonoStorage, note_storage::NoteStorage, + user_approval_storage::UserApprovalStorage, user_storage::UserStorage, vault_storage::VaultStorage, webhook_storage::WebhookStorage, @@ -78,6 +80,7 @@ pub struct AppService { pub gpg_storage: GpgStorage, pub lfs_db_storage: LfsDbStorage, pub cla_storage: ClaStorage, + pub user_approval_storage: UserApprovalStorage, pub user_storage: UserStorage, pub group_storage: GroupStorage, pub vault_storage: VaultStorage, @@ -112,6 +115,7 @@ impl AppService { gpg_storage: GpgStorage { base: mock.clone() }, lfs_db_storage: LfsDbStorage { base: mock.clone() }, cla_storage: ClaStorage { base: mock.clone() }, + user_approval_storage: UserApprovalStorage { base: mock.clone() }, user_storage: UserStorage { base: mock.clone() }, group_storage: GroupStorage { base: mock.clone() }, vault_storage: VaultStorage { base: mock.clone() }, @@ -164,6 +168,7 @@ impl Storage { let gpg_storage = GpgStorage { base: base.clone() }; let lfs_db_storage = LfsDbStorage { base: base.clone() }; let cla_storage = ClaStorage { base: base.clone() }; + let user_approval_storage = UserApprovalStorage { base: base.clone() }; let user_storage = UserStorage { base: base.clone() }; let group_storage = GroupStorage { base: base.clone() }; let cl_storage = ClStorage { base: base.clone() }; @@ -232,6 +237,7 @@ impl Storage { gpg_storage, lfs_db_storage, cla_storage, + user_approval_storage, user_storage, group_storage, vault_storage, @@ -391,6 +397,10 @@ impl Storage { self.app_service.cla_storage.clone() } + pub fn user_approval_storage(&self) -> UserApprovalStorage { + self.app_service.user_approval_storage.clone() + } + pub fn group_storage(&self) -> GroupStorage { self.app_service.group_storage.clone() } diff --git a/jupiter/src/storage/user_approval_storage.rs b/jupiter/src/storage/user_approval_storage.rs new file mode 100644 index 000000000..30ba1032f --- /dev/null +++ b/jupiter/src/storage/user_approval_storage.rs @@ -0,0 +1,167 @@ +use std::ops::Deref; + +use callisto::user_approval_status; +use common::errors::MegaError; +use sea_orm::{ + ColumnTrait, DbErr, EntityTrait, QueryFilter, QueryOrder, QuerySelect, Set, prelude::Expr, + sea_query::OnConflict, +}; + +use crate::storage::base_storage::{BaseStorage, StorageConnector}; + +pub const APPROVAL_STATUS_PENDING: &str = "pending"; +pub const APPROVAL_STATUS_APPROVED: &str = "approved"; +pub const APPROVAL_STATUS_REJECTED: &str = "rejected"; + +#[derive(Clone, Debug)] +pub struct UserApprovalProfile { + pub campsite_user_id: String, + pub display_name: String, + pub email: String, +} + +#[derive(Clone, Debug)] +pub struct UserApprovalStorage { + pub base: BaseStorage, +} + +impl Deref for UserApprovalStorage { + type Target = BaseStorage; + + fn deref(&self) -> &Self::Target { + &self.base + } +} + +impl UserApprovalStorage { + fn handle_record_not_inserted(result: Result) -> Result<(), MegaError> { + match result { + Ok(_) | Err(DbErr::RecordNotInserted) => Ok(()), + Err(e) => Err(e.into()), + } + } + + pub async fn get( + &self, + username: &str, + ) -> Result, MegaError> { + Ok( + user_approval_status::Entity::find_by_id(username.to_string()) + .one(self.get_connection()) + .await?, + ) + } + + /// Create a pending row if missing; refresh profile fields if already present. + pub async fn get_or_create( + &self, + username: &str, + profile: UserApprovalProfile, + ) -> Result { + let now = chrono::Utc::now().naive_utc(); + let model = user_approval_status::ActiveModel { + username: Set(username.to_string()), + campsite_user_id: Set(profile.campsite_user_id.clone()), + display_name: Set(profile.display_name.clone()), + email: Set(profile.email.clone()), + status: Set(APPROVAL_STATUS_PENDING.to_string()), + reviewed_by: Set(None), + reviewed_at: Set(None), + created_at: Set(now), + updated_at: Set(now), + }; + + Self::handle_record_not_inserted( + user_approval_status::Entity::insert(model) + .on_conflict( + OnConflict::column(user_approval_status::Column::Username) + .do_nothing() + .to_owned(), + ) + .exec(self.get_connection()) + .await, + )?; + + // Keep profile fields fresh for list display + user_approval_status::Entity::update_many() + .col_expr( + user_approval_status::Column::CampsiteUserId, + Expr::value(profile.campsite_user_id), + ) + .col_expr( + user_approval_status::Column::DisplayName, + Expr::value(profile.display_name), + ) + .col_expr( + user_approval_status::Column::Email, + Expr::value(profile.email), + ) + .col_expr(user_approval_status::Column::UpdatedAt, Expr::value(now)) + .filter(user_approval_status::Column::Username.eq(username)) + .exec(self.get_connection()) + .await?; + + self.get(username) + .await? + .ok_or_else(|| MegaError::Other("Failed to get or create user approval status".into())) + } + + pub async fn list_by_status( + &self, + status: Option<&str>, + limit: u64, + ) -> Result, MegaError> { + let mut query = user_approval_status::Entity::find() + .order_by_desc(user_approval_status::Column::CreatedAt); + + if let Some(status) = status + && status != "all" + { + query = query.filter(user_approval_status::Column::Status.eq(status)); + } + + Ok(query.limit(limit).all(self.get_connection()).await?) + } + + pub async fn set_status( + &self, + username: &str, + status: &str, + reviewed_by: &str, + ) -> Result { + if !matches!( + status, + APPROVAL_STATUS_PENDING | APPROVAL_STATUS_APPROVED | APPROVAL_STATUS_REJECTED + ) { + return Err(MegaError::Other(format!( + "Invalid approval status: {status}" + ))); + } + + let now = chrono::Utc::now().naive_utc(); + + // Ensure row exists so approve/reject of unknown usernames still works for listed users + let existing = self.get(username).await?; + if existing.is_none() { + return Err(MegaError::Other(format!( + "User approval record not found for `{username}`" + ))); + } + + user_approval_status::Entity::update_many() + .col_expr(user_approval_status::Column::Status, Expr::value(status)) + .col_expr( + user_approval_status::Column::ReviewedBy, + Expr::value(reviewed_by.to_string()), + ) + .col_expr(user_approval_status::Column::ReviewedAt, Expr::value(now)) + .col_expr(user_approval_status::Column::UpdatedAt, Expr::value(now)) + .filter(user_approval_status::Column::Username.eq(username)) + .exec(self.get_connection()) + .await?; + + self.get(username) + .await? + .ok_or_else(|| MegaError::Other("Failed to update user approval status".into())) + } +} diff --git a/jupiter/src/tests.rs b/jupiter/src/tests.rs index 648fdbd01..6bb74037b 100644 --- a/jupiter/src/tests.rs +++ b/jupiter/src/tests.rs @@ -42,6 +42,7 @@ use crate::{ mono_storage::MonoStorage, note_storage::NoteStorage, notification_storage::NotificationStorage, + user_approval_storage::UserApprovalStorage, user_storage::UserStorage, vault_storage::VaultStorage, webhook_storage::WebhookStorage, @@ -76,6 +77,7 @@ pub async fn test_storage(temp_dir: impl AsRef) -> Storage { gpg_storage: GpgStorage { base: base.clone() }, lfs_db_storage: LfsDbStorage { base: base.clone() }, cla_storage: ClaStorage { base: base.clone() }, + user_approval_storage: UserApprovalStorage { base: base.clone() }, user_storage: UserStorage { base: base.clone() }, group_storage: GroupStorage { base: base.clone() }, cl_storage: ClStorage { base: base.clone() }, diff --git a/mono/src/api/router/admin_router.rs b/mono/src/api/router/admin_router.rs index 6cb6ddbb2..bc6d3319c 100644 --- a/mono/src/api/router/admin_router.rs +++ b/mono/src/api/router/admin_router.rs @@ -1,16 +1,25 @@ //! Admin-related API endpoints. //! -//! Provides endpoints for admin permission checks: +//! Provides endpoints for admin permission checks and user account approval: //! - `GET /api/v1/admin/me` - Check if current user is admin //! - `GET /api/v1/admin/list` - List all admins (admin-only) +//! - `GET /api/v1/admin/user-approvals` - List user approval records (admin-only) +//! - `POST /api/v1/admin/user-approvals/{username}/approve` - Approve a user (admin-only) +//! - `POST /api/v1/admin/user-approvals/{username}/reject` - Reject a user (admin-only) //! //! # Auth Behavior //! - 401 Unauthorized: No valid session (handled by `LoginUser` extractor) -//! - 403 Forbidden: Logged in but not admin (for `/list` endpoint) +//! - 403 Forbidden: Logged in but not admin (for admin-only endpoints) use api_model::common::CommonResult; -use axum::{Json, extract::State}; -use ceres::model::admin::{AdminListResponse, IsAdminResponse}; +use axum::{ + Json, + extract::{Path, Query, State}, +}; +use ceres::model::{ + admin::{AdminListResponse, IsAdminResponse}, + user::{ListUserApprovalsQuery, UserApprovalListRes, UserApprovalStatusRes}, +}; use utoipa_axum::{router::OpenApiRouter, routes}; use crate::api::{ @@ -24,7 +33,10 @@ pub fn routers() -> OpenApiRouter { "/admin", OpenApiRouter::new() .routes(routes!(is_admin_me)) - .routes(routes!(admin_list)), + .routes(routes!(admin_list)) + .routes(routes!(list_user_approvals)) + .routes(routes!(approve_user)) + .routes(routes!(reject_user)), ) } @@ -81,3 +93,105 @@ async fn admin_list( admins, })))) } + +/// GET /api/v1/admin/user-approvals +/// +/// List user account approval records. Defaults to pending. +#[utoipa::path( + get, + path = "/user-approvals", + params(ListUserApprovalsQuery), + responses( + (status = 200, body = CommonResult), + (status = 401, description = "Unauthorized"), + (status = 403, description = "Forbidden - not admin"), + ), + tag = USER_TAG +)] +async fn list_user_approvals( + user: LoginUser, + State(state): State, + Query(query): Query, +) -> Result>, ApiError> { + ensure_admin(&state, &user).await?; + + let status = query.status.as_deref().unwrap_or("pending"); + let limit = query.limit.unwrap_or(100).min(500); + + let items = state + .services() + .user() + .list_user_approvals(Some(status), limit) + .await? + .into_iter() + .map(UserApprovalStatusRes::from) + .collect(); + + Ok(Json(CommonResult::success(Some(UserApprovalListRes { + items, + })))) +} + +/// POST /api/v1/admin/user-approvals/{username}/approve +#[utoipa::path( + post, + path = "/user-approvals/{username}/approve", + params( + ("username" = String, Path, description = "Username to approve") + ), + responses( + (status = 200, body = CommonResult), + (status = 401, description = "Unauthorized"), + (status = 403, description = "Forbidden - not admin"), + ), + tag = USER_TAG +)] +async fn approve_user( + user: LoginUser, + State(state): State, + Path(username): Path, +) -> Result>, ApiError> { + ensure_admin(&state, &user).await?; + + let model = state + .services() + .user() + .approve_user(&username, &user.username) + .await?; + + Ok(Json(CommonResult::success(Some( + UserApprovalStatusRes::from(model), + )))) +} + +/// POST /api/v1/admin/user-approvals/{username}/reject +#[utoipa::path( + post, + path = "/user-approvals/{username}/reject", + params( + ("username" = String, Path, description = "Username to reject") + ), + responses( + (status = 200, body = CommonResult), + (status = 401, description = "Unauthorized"), + (status = 403, description = "Forbidden - not admin"), + ), + tag = USER_TAG +)] +async fn reject_user( + user: LoginUser, + State(state): State, + Path(username): Path, +) -> Result>, ApiError> { + ensure_admin(&state, &user).await?; + + let model = state + .services() + .user() + .reject_user(&username, &user.username) + .await?; + + Ok(Json(CommonResult::success(Some( + UserApprovalStatusRes::from(model), + )))) +} diff --git a/mono/src/api/router/user_router.rs b/mono/src/api/router/user_router.rs index 8a1ad7f11..3e18294ea 100644 --- a/mono/src/api/router/user_router.rs +++ b/mono/src/api/router/user_router.rs @@ -10,6 +10,7 @@ use ceres::model::{ }, user::{ AddSSHKey, ClaContentRes, ClaSignStatusRes, ListSSHKey, ListToken, UpdateClaContentPayload, + UserApprovalStatusRes, }, }; use common::errors::MegaError; @@ -37,7 +38,8 @@ pub fn routers() -> OpenApiRouter { .routes(routes!(get_cla_sign_status)) .routes(routes!(change_sign_status)) .routes(routes!(get_cla_content)) - .routes(routes!(update_cla_content)), + .routes(routes!(update_cla_content)) + .routes(routes!(get_user_approval_status)), ) } @@ -369,3 +371,33 @@ async fn update_cla_content( content: payload.content, })))) } + +/// Get or initialize current user's account approval status +#[utoipa::path( + get, + path = "/approval-status", + responses( + (status = 200, body = CommonResult, content_type = "application/json"), + (status = 401, description = "Unauthorized"), + ), + tag = USER_TAG +)] +async fn get_user_approval_status( + user: LoginUser, + state: State, +) -> Result>, ApiError> { + let model = state + .services() + .user() + .get_or_init_user_approval_status( + &user.username, + &user.campsite_user_id, + &user.username, + &user.email, + ) + .await?; + + Ok(Json(CommonResult::success(Some( + UserApprovalStatusRes::from(model), + )))) +} diff --git a/moon/apps/web/components/AccountApprovalGuard.tsx b/moon/apps/web/components/AccountApprovalGuard.tsx new file mode 100644 index 000000000..56b94d0b3 --- /dev/null +++ b/moon/apps/web/components/AccountApprovalGuard.tsx @@ -0,0 +1,117 @@ +import React from 'react' +import Head from 'next/head' +import { useRouter } from 'next/router' + +import { Body, Button, Logo, Title2 } from '@gitmono/ui' +import { ToasterProvider } from '@gitmono/ui/Toast' + +import { FullPageError } from '@/components/Error' +import { FullPageLoading } from '@/components/FullPageLoading' +import { useMyApprovalStatus } from '@/hooks/AccountReview/useMyApprovalStatus' +import { useAdminCheck } from '@/hooks/admin/useAdminCheck' +import { useGetCurrentUser } from '@/hooks/useGetCurrentUser' +import { useSignoutUser } from '@/hooks/useSignoutUser' + +interface Props { + children: React.ReactNode + allowLoggedOut: boolean +} + +const AccountApprovalGuard: React.FC = ({ children, allowLoggedOut }) => { + const router = useRouter() + const getCurrentUser = useGetCurrentUser() + const { data: adminCheck, isPending: adminPending, isError: adminError } = useAdminCheck() + const signout = useSignoutUser() + const currentUser = getCurrentUser.data + + const isAdmin = adminCheck?.data?.is_admin === true + const loggedIn = !!currentUser?.logged_in + // Account settings must stay reachable (admin review tab, back navigation). Only gate org/home. + const isAccountRoute = router.pathname.startsWith('/me/') + + const needsApprovalCheck = !allowLoggedOut && loggedIn && !isAccountRoute && !isAdmin && !adminPending + const { + data: approvalRes, + isPending: approvalPending, + isError: approvalError + } = useMyApprovalStatus(needsApprovalCheck) + + if (allowLoggedOut) { + return <>{children} + } + + if (getCurrentUser.error) { + return + } + + if (!getCurrentUser.data && getCurrentUser.isLoading) { + return + } + + // Not logged in — AuthProvider handles redirect; don't block here + if (!loggedIn) { + return <>{children} + } + + // Always allow account/settings routes — avoids settings ↔ home redirect thrash + if (isAccountRoute) { + return <>{children} + } + + // Wait for admin check unless it already failed (e.g. CORS) — treat failure as non-admin + if (adminPending && !adminError) { + return + } + + // Admins do not need account review + if (isAdmin) { + return <>{children} + } + + if (approvalPending) { + return + } + + if (approvalError) { + return + } + + const status = approvalRes?.data?.status ?? 'pending' + + if (status === 'approved') { + return <>{children} + } + + const isRejected = status === 'rejected' + + return ( + <> + + {isRejected ? 'Account not approved' : 'Account pending review'} + + + + +
+ +
+ {isRejected ? 'Account not approved' : 'Account pending review'} + + + {isRejected + ? 'Your account was not approved by an admin, so you cannot access the home page. Contact an admin if you have questions.' + : 'Your account is waiting for admin review. You can access the home page once it is approved.'} + + +
+ +
+
+
+ + ) +} + +export default AccountApprovalGuard diff --git a/moon/apps/web/components/AccountReview/PendingAccountReviewButton.tsx b/moon/apps/web/components/AccountReview/PendingAccountReviewButton.tsx new file mode 100644 index 000000000..903b14916 --- /dev/null +++ b/moon/apps/web/components/AccountReview/PendingAccountReviewButton.tsx @@ -0,0 +1,28 @@ +import pluralize from 'pluralize' + +import { Button, UserCirclePlusIcon } from '@gitmono/ui' + +import { usePendingAccountReviewCount } from '@/hooks/usePendingAccountReviewCount' + +/** Top-right indicator for admins when users are waiting for account review. */ +export function PendingAccountReviewButton() { + const { hasPending, pendingCount, isAdmin } = usePendingAccountReviewCount() + + if (!isAdmin || !hasPending) return null + + return ( +
+
+ ) +} diff --git a/moon/apps/web/components/NavigationBar/index.tsx b/moon/apps/web/components/NavigationBar/index.tsx index 209abef7f..af02bb703 100644 --- a/moon/apps/web/components/NavigationBar/index.tsx +++ b/moon/apps/web/components/NavigationBar/index.tsx @@ -27,6 +27,7 @@ import { import { useHasMounted, useIsDesktopApp } from '@gitmono/ui/src/hooks' import { cn } from '@gitmono/ui/src/utils' +import { PendingAccountReviewButton } from '@/components/AccountReview/PendingAccountReviewButton' import { BackButton } from '@/components/BackButton' import { CallOverflowMenu } from '@/components/Calls/CallOverflowMenu' import { CallSharePopover } from '@/components/CallSharePopover' @@ -238,6 +239,7 @@ function TrailingAccessory() { return (
+ , + external: false, + url: '/me/settings/account-review', + onSelect: () => setOpen(false), + rightSlot: hasPending ? ( + + {pendingCount} + + ) : undefined + }, pauseNotificationsMenuItem, { type: 'separator' }, // { diff --git a/moon/apps/web/components/Providers/AuthAppProviders.tsx b/moon/apps/web/components/Providers/AuthAppProviders.tsx index 28f1ccb18..2052023d1 100644 --- a/moon/apps/web/components/Providers/AuthAppProviders.tsx +++ b/moon/apps/web/components/Providers/AuthAppProviders.tsx @@ -2,6 +2,7 @@ import { useState } from 'react' import { HMSRoomProvider } from '@100mslive/react-sdk' import { ShortcutProvider } from '@shopify/react-shortcuts' import { QueryClientProvider } from '@tanstack/react-query' +import AccountApprovalGuard from 'components/AccountApprovalGuard' import ConfirmEmailGuard from 'components/ConfirmEmailGuard' import { domMax, LazyMotion } from 'framer-motion' import { HotkeysProvider } from 'react-hotkeys-hook' @@ -75,9 +76,11 @@ export const AuthAppProviders: PageWithProviders = ({ children, allowLogged - {children} + + {children} - + + diff --git a/moon/apps/web/components/Sidebar/index.tsx b/moon/apps/web/components/Sidebar/index.tsx index 89f9e9b6f..597272a0f 100644 --- a/moon/apps/web/components/Sidebar/index.tsx +++ b/moon/apps/web/components/Sidebar/index.tsx @@ -8,6 +8,7 @@ import { LayeredHotkeys } from '@gitmono/ui' import { useIsDesktopApp } from '@gitmono/ui/src/hooks' import { cn } from '@gitmono/ui/src/utils' +import { PendingAccountReviewButton } from '@/components/AccountReview/PendingAccountReviewButton' import { DesktopAppNavigationButtons } from '@/components/DesktopAppNavigationButtons' import { sidebarCollapsedAtom } from '@/components/Layout/AppLayout' import { OrganizationSwitcher } from '@/components/NavigationSidebar/OrganizationSwitcher' @@ -133,6 +134,7 @@ function SidebarContent() {
+
@@ -140,7 +142,12 @@ function SidebarContent() {
- {!isDesktopApp && } + {!isDesktopApp && ( +
+ + +
+ )}
diff --git a/moon/apps/web/components/UserSettings/AccountReview/AccountReviewSection.tsx b/moon/apps/web/components/UserSettings/AccountReview/AccountReviewSection.tsx new file mode 100644 index 000000000..5a5700578 --- /dev/null +++ b/moon/apps/web/components/UserSettings/AccountReview/AccountReviewSection.tsx @@ -0,0 +1,189 @@ +import { useMemo, useState } from 'react' + +import type { UserApprovalStatusRes } from '@gitmono/types' +import { Avatar, Button, Table, TableRow, UIText } from '@gitmono/ui' + +import * as SettingsSection from '@/components/SettingsSection' +import { useAdminUserApprovals } from '@/hooks/AccountReview/useAdminUserApprovals' +import { useApproveUser } from '@/hooks/AccountReview/useApproveUser' +import { useRejectUser } from '@/hooks/AccountReview/useRejectUser' + +type FilterTab = 'all' | 'pending' | 'approved' | 'rejected' + +const FILTER_TABS: { id: FilterTab; label: string }[] = [ + { id: 'pending', label: 'Pending' }, + { id: 'approved', label: 'Approved' }, + { id: 'rejected', label: 'Rejected' }, + { id: 'all', label: 'All' } +] + +function statusLabel(status: string) { + switch (status) { + case 'pending': + return 'Pending' + case 'approved': + return 'Approved' + case 'rejected': + return 'Rejected' + default: + return status + } +} + +function statusClassName(status: string) { + switch (status) { + case 'pending': + return 'bg-amber-100 text-amber-800 dark:bg-amber-900/30 dark:text-amber-400' + case 'approved': + return 'bg-green-100 text-green-800 dark:bg-green-900/30 dark:text-green-400' + case 'rejected': + return 'bg-red-100 text-red-800 dark:bg-red-900/30 dark:text-red-400' + default: + return 'bg-neutral-100 text-neutral-800 dark:bg-neutral-900/30 dark:text-neutral-400' + } +} + +function formatDate(unixSeconds: number) { + return new Date(unixSeconds * 1000).toLocaleString(undefined, { + year: 'numeric', + month: 'short', + day: 'numeric', + hour: '2-digit', + minute: '2-digit' + }) +} + +export function AccountReviewSection() { + const [filter, setFilter] = useState('pending') + const { data, isPending, isError } = useAdminUserApprovals(filter) + const approve = useApproveUser() + const reject = useRejectUser() + + const users = data?.data?.items ?? [] + const pendingCount = useMemo(() => { + if (filter === 'pending') return users.length + return users.filter((user) => user.status === 'pending').length + }, [filter, users]) + + return ( + + + Account review + + + + Review newly registered users. Only approved users can access the home page. + {filter === 'pending' && pendingCount > 0 && ( + <> +
+ {pendingCount} user{pendingCount === 1 ? '' : 's'} pending review. + + )} +
+ + + +
+ +
+ + {isPending ? ( + + Loading… + + ) : isError ? ( + + Unable to load user approvals. Check Mono API connectivity. + + ) : users.length === 0 ? ( + + No users match this filter. + + ) : ( +
+ + {users.map((user) => ( + approve.mutate(user.username)} + onReject={() => reject.mutate(user.username)} + /> + ))} +
+
+ )} +
+ ) +} + +interface ReviewRowProps { + user: UserApprovalStatusRes + approving: boolean + rejecting: boolean + onApprove: () => void + onReject: () => void +} + +function ReviewRow({ user, approving, rejecting, onApprove, onReject }: ReviewRowProps) { + const displayName = user.display_name || user.username + + return ( + +
+
+
+ +
+
+
+ + {displayName} + + + {statusLabel(user.status)} + +
+ + @{user.username} · {user.email} + + + Registered {formatDate(user.registered_at)} + {user.reviewed_at ? ` · Reviewed ${formatDate(user.reviewed_at)}` : ''} + {user.reviewed_by ? ` by @${user.reviewed_by}` : ''} + +
+
+
+
+ {user.status !== 'rejected' && ( + + )} + {user.status !== 'approved' && ( + + )} +
+
+ ) +} diff --git a/moon/apps/web/components/UserSettings/PageWrapper.tsx b/moon/apps/web/components/UserSettings/PageWrapper.tsx index 05565471c..4d6ad095c 100644 --- a/moon/apps/web/components/UserSettings/PageWrapper.tsx +++ b/moon/apps/web/components/UserSettings/PageWrapper.tsx @@ -2,9 +2,15 @@ import { useRouter } from 'next/router' import { Avatar, UIText } from '@gitmono/ui' +import { PendingAccountReviewButton } from '@/components/AccountReview/PendingAccountReviewButton' +import { BackButton } from '@/components/BackButton' +import { ProfileDropdown } from '@/components/NavigationSidebar/ProfileDropdown' import { BasicTitlebar } from '@/components/Titlebar' import { SubnavigationTab } from '@/components/Titlebar/Subnavigation' +import { useScope } from '@/contexts/scope' +import { useAdminCheck } from '@/hooks/admin/useAdminCheck' import { useGetCurrentUser } from '@/hooks/useGetCurrentUser' +import { usePendingAccountReviewCount } from '@/hooks/usePendingAccountReviewCount' interface Props { children: React.ReactNode @@ -13,19 +19,32 @@ interface Props { export function UserSettingsPageWrapper(props: Props) { const { children } = props const router = useRouter() + const { scope } = useScope() const { data: currentUser } = useGetCurrentUser() + const { data: adminCheck } = useAdminCheck() + const isAdmin = adminCheck?.data?.is_admin || false + const { pendingCount } = usePendingAccountReviewCount() + // Prefer org home over `/` — bare `/` SSR-redirects and feels like a redirect loop from settings + const backFallback = typeof scope === 'string' && scope.length > 0 ? `/${scope}` : undefined return ( <>
} centerSlot={
Account settings
} + trailingSlot={ +
+ + +
+ } />
@@ -40,6 +59,20 @@ export function UserSettingsPageWrapper(props: Props) { > Organizations + {isAdmin && ( + + Account review + {pendingCount > 0 && ( + + {pendingCount} + + )} + + )}
diff --git a/moon/apps/web/hooks/AccountReview/useAdminUserApprovals.ts b/moon/apps/web/hooks/AccountReview/useAdminUserApprovals.ts new file mode 100644 index 000000000..b4c18e891 --- /dev/null +++ b/moon/apps/web/hooks/AccountReview/useAdminUserApprovals.ts @@ -0,0 +1,18 @@ +import { useQuery } from '@tanstack/react-query' + +import type { GetApiAdminUserApprovalsData, GetApiAdminUserApprovalsParams } from '@gitmono/types' + +import { legacyApiClient } from '@/utils/queryClient' + +export function useAdminUserApprovals(status: string, enabled = true) { + const api = legacyApiClient.v1.getApiAdminUserApprovals() + const query: GetApiAdminUserApprovalsParams = { status } + + return useQuery({ + queryKey: api.requestKey(query), + queryFn: () => api.request(query), + enabled, + staleTime: 0, + retry: false + }) +} diff --git a/moon/apps/web/hooks/AccountReview/useApproveUser.ts b/moon/apps/web/hooks/AccountReview/useApproveUser.ts new file mode 100644 index 000000000..3dbf6af5b --- /dev/null +++ b/moon/apps/web/hooks/AccountReview/useApproveUser.ts @@ -0,0 +1,20 @@ +import { useMutation, useQueryClient } from '@tanstack/react-query' + +import { apiErrorToast } from '@/utils/apiErrorToast' +import { legacyApiClient } from '@/utils/queryClient' + +export function useApproveUser() { + const queryClient = useQueryClient() + const api = legacyApiClient.v1.postApiAdminUserApprovalsApprove() + + return useMutation({ + mutationFn: (username: string) => api.request(username), + onSuccess: async () => { + await Promise.all([ + queryClient.invalidateQueries({ queryKey: legacyApiClient.v1.getApiAdminUserApprovals().baseKey }), + queryClient.invalidateQueries({ queryKey: legacyApiClient.v1.getApiUserApprovalStatus().baseKey }) + ]) + }, + onError: apiErrorToast + }) +} diff --git a/moon/apps/web/hooks/AccountReview/useMyApprovalStatus.ts b/moon/apps/web/hooks/AccountReview/useMyApprovalStatus.ts new file mode 100644 index 000000000..85fd9a82e --- /dev/null +++ b/moon/apps/web/hooks/AccountReview/useMyApprovalStatus.ts @@ -0,0 +1,17 @@ +import { useQuery } from '@tanstack/react-query' + +import type { GetApiUserApprovalStatusData } from '@gitmono/types' + +import { legacyApiClient } from '@/utils/queryClient' + +export function useMyApprovalStatus(enabled = true) { + const api = legacyApiClient.v1.getApiUserApprovalStatus() + + return useQuery({ + queryKey: api.requestKey(), + queryFn: () => api.request(), + enabled, + staleTime: 0, + retry: false + }) +} diff --git a/moon/apps/web/hooks/AccountReview/useRejectUser.ts b/moon/apps/web/hooks/AccountReview/useRejectUser.ts new file mode 100644 index 000000000..389707819 --- /dev/null +++ b/moon/apps/web/hooks/AccountReview/useRejectUser.ts @@ -0,0 +1,20 @@ +import { useMutation, useQueryClient } from '@tanstack/react-query' + +import { apiErrorToast } from '@/utils/apiErrorToast' +import { legacyApiClient } from '@/utils/queryClient' + +export function useRejectUser() { + const queryClient = useQueryClient() + const api = legacyApiClient.v1.postApiAdminUserApprovalsReject() + + return useMutation({ + mutationFn: (username: string) => api.request(username), + onSuccess: async () => { + await Promise.all([ + queryClient.invalidateQueries({ queryKey: legacyApiClient.v1.getApiAdminUserApprovals().baseKey }), + queryClient.invalidateQueries({ queryKey: legacyApiClient.v1.getApiUserApprovalStatus().baseKey }) + ]) + }, + onError: apiErrorToast + }) +} diff --git a/moon/apps/web/hooks/usePendingAccountReviewCount.ts b/moon/apps/web/hooks/usePendingAccountReviewCount.ts new file mode 100644 index 000000000..c4d3e416c --- /dev/null +++ b/moon/apps/web/hooks/usePendingAccountReviewCount.ts @@ -0,0 +1,18 @@ +import { useAdminUserApprovals } from '@/hooks/AccountReview/useAdminUserApprovals' +import { useAdminCheck } from '@/hooks/admin/useAdminCheck' + +/** Pending account-review count for system admins; 0 for everyone else. */ +export function usePendingAccountReviewCount() { + const { data: adminCheck, isPending: adminPending, isError: adminError } = useAdminCheck() + const isAdmin = adminCheck?.data?.is_admin === true + const { data, isPending: listPending } = useAdminUserApprovals('pending', isAdmin) + + const pendingCount = isAdmin ? (data?.data?.items?.length ?? 0) : 0 + + return { + pendingCount, + hasPending: pendingCount > 0, + isAdmin, + isLoading: (adminPending && !adminError) || (isAdmin && listPending) + } +} diff --git a/moon/apps/web/next.config.js b/moon/apps/web/next.config.js index c5f260092..aec921795 100644 --- a/moon/apps/web/next.config.js +++ b/moon/apps/web/next.config.js @@ -54,6 +54,7 @@ const cspResourcesByDirective = { 'http://*.gitmono.test:3001', 'http://*.gitmono.test:8000', 'http://*.gitmono.test:8004', + 'http://*.gitmono.local:3001', 'http://*.gitmega.nju:8080', 'http://*.gitmega.nju', process.env.NODE_ENV !== 'production' && 'localhost:8000', diff --git a/moon/apps/web/pages/me/settings/account-review.tsx b/moon/apps/web/pages/me/settings/account-review.tsx new file mode 100644 index 000000000..e121e9fcd --- /dev/null +++ b/moon/apps/web/pages/me/settings/account-review.tsx @@ -0,0 +1,57 @@ +import { useEffect } from 'react' +import Head from 'next/head' +import { useRouter } from 'next/router' + +import { CopyCurrentUrl } from '@/components/CopyCurrentUrl' +import { FullPageError } from '@/components/Error' +import { FullPageLoading } from '@/components/FullPageLoading' +import AuthAppProviders from '@/components/Providers/AuthAppProviders' +import { AccountReviewSection } from '@/components/UserSettings/AccountReview/AccountReviewSection' +import { UserSettingsPageWrapper } from '@/components/UserSettings/PageWrapper' +import { useAdminCheck } from '@/hooks/admin/useAdminCheck' +import { PageWithProviders } from '@/utils/types' + +const AccountReviewPage: PageWithProviders = () => { + const router = useRouter() + const { data: adminCheck, isPending, isError, isFetched } = useAdminCheck() + const isAdmin = adminCheck?.data?.is_admin === true + + useEffect(() => { + // Only bounce when we know the user is not an admin — not on API/CORS errors + if (isFetched && !isError && !isAdmin) { + router.replace('/me/settings') + } + }, [isAdmin, isError, isFetched, router]) + + if (isPending) { + return + } + + if (isError) { + return + } + + if (!isAdmin) { + return + } + + return ( + <> + + Account review + + + + + + + + + ) +} + +AccountReviewPage.getProviders = (page, pageProps) => { + return {page} +} + +export default AccountReviewPage diff --git a/moon/packages/types/generated.ts b/moon/packages/types/generated.ts index c538f717a..5b55e409a 100644 --- a/moon/packages/types/generated.ts +++ b/moon/packages/types/generated.ts @@ -4361,6 +4361,38 @@ export type CommonResultUpdateBranchStatusRes = { req_result: boolean } +export type CommonResultUserApprovalListRes = { + data?: { + items: UserApprovalStatusRes[] + } + err_message: string + req_result: boolean +} + +export type CommonResultUserApprovalStatusRes = { + data?: { + campsite_user_id: string + display_name: string + email: string + /** + * Unix timestamp + * @format int64 + */ + registered_at: number + /** + * Unix timestamp + * @format int64 + */ + reviewed_at?: number | null + reviewed_by?: string | null + /** One of: pending, approved, rejected */ + status: string + username: string + } + err_message: string + req_result: boolean +} + export type CommonResultUserEffectivePermissionResponse = { data?: { has_admin: boolean @@ -5849,6 +5881,30 @@ export type UpdateUserNotificationConfig = { preferences?: any[] | null } +export type UserApprovalListRes = { + items: UserApprovalStatusRes[] +} + +export type UserApprovalStatusRes = { + campsite_user_id: string + display_name: string + email: string + /** + * Unix timestamp + * @format int64 + */ + registered_at: number + /** + * Unix timestamp + * @format int64 + */ + reviewed_at?: number | null + reviewed_by?: string | null + /** One of: pending, approved, rejected */ + status: string + username: string +} + export type UserEffectivePermissionResponse = { has_admin: boolean has_read: boolean @@ -7307,6 +7363,23 @@ export type PostApiAdminResourcesPermissionsData = CommonResultVecResourcePermis export type DeleteApiAdminResourcesPermissionsData = CommonResultDeletePermissionsResponse +export type GetApiAdminUserApprovalsParams = { + /** Filter by status: pending, approved, rejected, or all (default: pending) */ + status?: string | null + /** + * Max rows to return (default 100, max 500) + * @format int64 + * @min 0 + */ + limit?: number | null +} + +export type GetApiAdminUserApprovalsData = CommonResultUserApprovalListRes + +export type PostApiAdminUserApprovalsApproveData = CommonResultUserApprovalStatusRes + +export type PostApiAdminUserApprovalsRejectData = CommonResultUserApprovalStatusRes + export type GetApiAdminUsersGroupsData = CommonResultUserGroupsResponse export type GetApiAdminUsersPermissionsByResourceIdData = CommonResultUserEffectivePermissionResponse @@ -7696,6 +7769,8 @@ export type GetApiTriggersByIdData = CommonResultTriggerResponse export type PostApiTriggersRetryData = CommonResultTriggerResponse +export type GetApiUserApprovalStatusData = CommonResultUserApprovalStatusRes + export type PostApiUserClaChangeSignStatusData = CommonResultClaSignStatusRes export type GetApiUserClaContentData = CommonResultClaContentRes @@ -16487,6 +16562,77 @@ supporting either retrieving the entire log at once or segmenting it by line cou } }, + /** + * @description List user account approval records. Defaults to pending. + * + * @tags User Management + * @name GetApiAdminUserApprovals + * @summary GET /api/v1/admin/user-approvals + * @request GET:/api/v1/admin/user-approvals + */ + getApiAdminUserApprovals: () => { + const base = 'GET:/api/v1/admin/user-approvals' as const + + return { + baseKey: dataTaggedQueryKey([base]), + requestKey: (params: GetApiAdminUserApprovalsParams) => + dataTaggedQueryKey([base, params]), + request: (query: GetApiAdminUserApprovalsParams, params: RequestParams = {}) => + this.request({ + path: `/api/v1/admin/user-approvals`, + method: 'GET', + query: query, + ...params + }) + } + }, + + /** + * No description + * + * @tags User Management + * @name PostApiAdminUserApprovalsApprove + * @summary POST /api/v1/admin/user-approvals/{username}/approve + * @request POST:/api/v1/admin/user-approvals/{username}/approve + */ + postApiAdminUserApprovalsApprove: () => { + const base = 'POST:/api/v1/admin/user-approvals/{username}/approve' as const + + return { + baseKey: dataTaggedQueryKey([base]), + requestKey: (username: string) => dataTaggedQueryKey([base, username]), + request: (username: string, params: RequestParams = {}) => + this.request({ + path: `/api/v1/admin/user-approvals/${username}/approve`, + method: 'POST', + ...params + }) + } + }, + + /** + * No description + * + * @tags User Management + * @name PostApiAdminUserApprovalsReject + * @summary POST /api/v1/admin/user-approvals/{username}/reject + * @request POST:/api/v1/admin/user-approvals/{username}/reject + */ + postApiAdminUserApprovalsReject: () => { + const base = 'POST:/api/v1/admin/user-approvals/{username}/reject' as const + + return { + baseKey: dataTaggedQueryKey([base]), + requestKey: (username: string) => dataTaggedQueryKey([base, username]), + request: (username: string, params: RequestParams = {}) => + this.request({ + path: `/api/v1/admin/user-approvals/${username}/reject`, + method: 'POST', + ...params + }) + } + }, + /** * No description * @@ -19428,6 +19574,29 @@ It's for local testing purposes. } }, + /** + * No description + * + * @tags User Management + * @name GetApiUserApprovalStatus + * @summary Get or initialize current user's account approval status + * @request GET:/api/v1/user/approval-status + */ + getApiUserApprovalStatus: () => { + const base = 'GET:/api/v1/user/approval-status' as const + + return { + baseKey: dataTaggedQueryKey([base]), + requestKey: () => dataTaggedQueryKey([base]), + request: (params: RequestParams = {}) => + this.request({ + path: `/api/v1/user/approval-status`, + method: 'GET', + ...params + }) + } + }, + /** * No description * From 8ba01724c526545ddb0c0db5fd023602df1bf989 Mon Sep 17 00:00:00 2001 From: "benjamin.747" Date: Tue, 14 Jul 2026 11:37:24 +0800 Subject: [PATCH 3/3] fix eslint --- Cargo.lock | 7 +++++++ Cargo.toml | 3 ++- .../UserSettings/AccountReview/AccountReviewSection.tsx | 7 ++----- moon/apps/web/components/UserSettings/PageWrapper.tsx | 2 +- 4 files changed, 12 insertions(+), 7 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 751735768..ce6bbec32 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -10191,9 +10191,16 @@ dependencies = [ "serde_json", "url", "utoipa", + "utoipa-swagger-ui-vendored", "zip", ] +[[package]] +name = "utoipa-swagger-ui-vendored" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2eebbbfe4093922c2b6734d7c679ebfebd704a0d7e56dfcb0d05818ce28977d" + [[package]] name = "uuid" version = "1.23.5" diff --git a/Cargo.toml b/Cargo.toml index 9bc3c120b..57833c244 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -106,7 +106,8 @@ base64 = "0.22.1" utoipa = { version = "5.5.0", features = ["chrono"] } utoipa-axum = "0.2.0" -utoipa-swagger-ui = "9.0.2" +# vendored: ship Swagger UI from crates.io so CI/airgapped builds don't curl github.com +utoipa-swagger-ui = { version = "9.0.2", features = ["vendored"] } tempfile = "3.27.0" dashmap = "6.2.1" once_cell = "1.21.4" diff --git a/moon/apps/web/components/UserSettings/AccountReview/AccountReviewSection.tsx b/moon/apps/web/components/UserSettings/AccountReview/AccountReviewSection.tsx index 5a5700578..e68f91db7 100644 --- a/moon/apps/web/components/UserSettings/AccountReview/AccountReviewSection.tsx +++ b/moon/apps/web/components/UserSettings/AccountReview/AccountReviewSection.tsx @@ -1,4 +1,4 @@ -import { useMemo, useState } from 'react' +import { useState } from 'react' import type { UserApprovalStatusRes } from '@gitmono/types' import { Avatar, Button, Table, TableRow, UIText } from '@gitmono/ui' @@ -60,10 +60,7 @@ export function AccountReviewSection() { const reject = useRejectUser() const users = data?.data?.items ?? [] - const pendingCount = useMemo(() => { - if (filter === 'pending') return users.length - return users.filter((user) => user.status === 'pending').length - }, [filter, users]) + const pendingCount = filter === 'pending' ? users.length : users.filter((user) => user.status === 'pending').length return ( diff --git a/moon/apps/web/components/UserSettings/PageWrapper.tsx b/moon/apps/web/components/UserSettings/PageWrapper.tsx index 4d6ad095c..bbaf2001a 100644 --- a/moon/apps/web/components/UserSettings/PageWrapper.tsx +++ b/moon/apps/web/components/UserSettings/PageWrapper.tsx @@ -67,7 +67,7 @@ export function UserSettingsPageWrapper(props: Props) { > Account review {pendingCount > 0 && ( - + {pendingCount} )}