diff --git a/api/src/api/scope.rs b/api/src/api/scope.rs index 3826b825..7386f49a 100644 --- a/api/src/api/scope.rs +++ b/api/src/api/scope.rs @@ -219,7 +219,7 @@ async fn invite_member_handler( let scope = req.param_scope()?; Span::current().record("scope", &field::display(&scope)); - let ApiAddScopeMemberRequest { github_login } = decode_json(&mut req).await?; + let invite = decode_json::(&mut req).await?; let iam = req.iam(); let current_user = iam.check_current_user_access()?.to_owned(); @@ -232,14 +232,21 @@ async fn invite_member_handler( let iam = req.iam(); iam.check_scope_admin_access(&scope).await?; - let new_user = lookup_user_by_github_login( - db, - github_oauth2_client, - ¤t_user, - &github_login, - ) - .await? - .ok_or(ApiError::UserNotFound)?; + let new_user = match invite { + ApiAddScopeMemberRequest::GithubLogin(github_login) => { + lookup_user_by_github_login( + db, + github_oauth2_client, + ¤t_user, + &github_login, + ) + .await? + .ok_or(ApiError::UserNotFound)? + } + ApiAddScopeMemberRequest::Id(id) => { + db.get_user(id).await?.ok_or(ApiError::UserNotFound)? + } + }; if db.get_scope_member(&scope, new_user.id).await?.is_some() { return Err(ApiError::AlreadyScopeMember); diff --git a/api/src/api/types.rs b/api/src/api/types.rs index 93289625..4572d2c8 100644 --- a/api/src/api/types.rs +++ b/api/src/api/types.rs @@ -283,8 +283,9 @@ impl From<(ScopeMember, UserPublic)> for ApiScopeMember { #[derive(Debug, Deserialize)] #[serde(rename_all = "camelCase")] -pub struct ApiAddScopeMemberRequest { - pub github_login: String, +pub enum ApiAddScopeMemberRequest { + GithubLogin(String), + Id(Uuid), } #[derive(Debug, Deserialize)] diff --git a/frontend/routes/@[scope]/(_islands)/ScopeInviteForm.tsx b/frontend/routes/@[scope]/(_islands)/ScopeInviteForm.tsx index 110b60b3..cb53425c 100644 --- a/frontend/routes/@[scope]/(_islands)/ScopeInviteForm.tsx +++ b/frontend/routes/@[scope]/(_islands)/ScopeInviteForm.tsx @@ -1,6 +1,6 @@ // Copyright 2024 the JSR authors. All rights reserved. MIT license. import { useSignal } from "@preact/signals"; -import { useCallback } from "preact/hooks"; +import { useCallback, useRef } from "preact/hooks"; import { JSX } from "preact/jsx-runtime"; import { ScopeInvite } from "../../../utils/api_types.ts"; import { api, path } from "../../../utils/api.ts"; @@ -12,18 +12,25 @@ interface ScopeInviteFormProps { export function ScopeInviteForm(props: ScopeInviteFormProps) { const submitting = useSignal(false); const error = useSignal(""); + const kind = useSignal<"github" | "id">("github"); + const inputRef = useRef(null); const onSubmit = useCallback( (e: JSX.TargetedEvent) => { e.preventDefault(); const formData = new FormData(e.currentTarget); - const githubLogin = String(formData.get("githubLogin")); + + const kind = String(formData.get("kind")); + const inviteValue = String(formData.get("inviteValue")); submitting.value = true; api.post( path`/scopes/${props.scope}/members`, - { githubLogin }, + { + githubLogin: kind === "github" ? inviteValue : undefined, + id: kind === "id" ? inviteValue : undefined, + }, ).then((res) => { submitting.value = false; if (!res.ok) { @@ -47,14 +54,36 @@ export function ScopeInviteForm(props: ScopeInviteFormProps) { onSubmit={onSubmit} >
- +
+ + +