-
Notifications
You must be signed in to change notification settings - Fork 3
Feat: 워크스페이스 설정페이지 api연동 #53
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
11 commits
Select commit
Hold shift + click to select a range
730ccf9
feat: 워크스페이스 정보,멤버,현재 사용자 조회 실 API 연동
Kwon812 210db0f
feat:워크스페이스,프로필 수정 api연결, 사용자 초대링크/라우팅/RPC 생성
Kwon812 d13a5af
chore: 이메일 발송용 resend 패키지 설치
Kwon812 930a15c
chore: 이메일 초대(RESEND_API_KEY, INVITE_EMAIL_FROM) env 예시 추가
Kwon812 d57ddf8
feat: 이메일 초대 링크 발송 서버액션 추가
Kwon812 4e07945
feat: 이메일 초대를 실제 링크 발송으로 연결
Kwon812 9da2861
Merge branch 'develop' into feat/#47/workspace-setting-page
Kwon812 746062d
fix: 초대 참여 RPC를 auth.uid() 기반으로 강제하고 실행 권한 정리
Kwon812 faf5691
fix: 이메일 빈문자열 처리 추가
Kwon812 1244d9c
fix: 초대 동시참여 멱등성 보장
Kwon812 f3f4e99
fix: 마이그레이션 파일 내 명시적 begin/commit 제거
Kwon812 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,45 @@ | ||
| // 초대 참여 라우트 — 초대 코드로 워크스페이스 요약을 조회해 참여 화면을 보여준다. | ||
| // 비멤버는 RLS로 워크스페이스를 직접 조회할 수 없어 security definer RPC(get_invite_preview)를 사용한다. | ||
| import Link from 'next/link'; | ||
| import { getInvitePreview } from '@/entities/workspace/api/get-invite-preview'; | ||
| import { plusJakartaSans } from '@/shared/lib/fonts'; | ||
| import { InviteAcceptView } from '@/views/invite'; | ||
|
|
||
| interface InvitePageProps { | ||
| params: Promise<{ code: string }>; | ||
| } | ||
|
|
||
| export default async function InvitePage({ params }: InvitePageProps) { | ||
| const { code } = await params; | ||
| const preview = await getInvitePreview(code); | ||
|
|
||
| if (!preview) { | ||
| return ( | ||
| <div | ||
| className={`${plusJakartaSans.className} flex min-h-dvh items-center justify-center px-4`} | ||
| > | ||
| <section className="w-full max-w-md rounded-2xl border border-slate-200 bg-white p-8 text-center shadow-sm"> | ||
| <h1 className="text-xl font-bold text-slate-950">유효하지 않은 초대예요</h1> | ||
| <p className="mt-2 text-sm text-slate-500"> | ||
| 링크가 만료되었거나 초대가 비활성화되었을 수 있어요. 초대한 사람에게 새 링크를 | ||
| 요청해 주세요. | ||
| </p> | ||
| <Link | ||
| href="/workspaces" | ||
| className="mt-8 inline-flex h-11 items-center justify-center rounded-2xl bg-[var(--color-brand)] px-5 text-sm font-bold text-white hover:bg-indigo-500" | ||
| > | ||
| 내 워크스페이스로 | ||
| </Link> | ||
| </section> | ||
| </div> | ||
| ); | ||
| } | ||
|
|
||
| return ( | ||
| <InviteAcceptView | ||
| code={code} | ||
| workspaceName={preview.name} | ||
| memberCount={preview.memberCount} | ||
| /> | ||
| ); | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,45 @@ | ||
| 'use server'; | ||
|
|
||
| // 현재 사용자의 워크스페이스 닉네임 수정 서버액션 — workspace_members update | ||
| // RLS(members_update_self)에 의해 본인 멤버십만 수정할 수 있다. | ||
| import { revalidatePath } from 'next/cache'; | ||
| import { z } from 'zod'; | ||
| import { getCurrentUserId } from '@/shared/api/supabase/current-user'; | ||
| import { createSupabaseServerClient } from '@/shared/api/supabase/server'; | ||
|
|
||
| const updateMyNicknameInputSchema = z.object({ | ||
| workspaceId: z.guid(), | ||
| nickname: z | ||
| .string() | ||
| .trim() | ||
| .min(1, '닉네임을 입력해주세요') | ||
| .max(20, '닉네임은 20자 이내로 입력해주세요'), | ||
| }); | ||
|
|
||
| export type UpdateMyNicknameInput = z.infer<typeof updateMyNicknameInputSchema>; | ||
|
|
||
| export async function updateMyNickname(input: UpdateMyNicknameInput): Promise<void> { | ||
| const parsed = updateMyNicknameInputSchema.safeParse(input); | ||
| if (!parsed.success) { | ||
| throw new Error(parsed.error.issues[0]?.message ?? '입력값이 올바르지 않습니다'); | ||
| } | ||
|
|
||
| const userId = await getCurrentUserId(); | ||
| const supabase = await createSupabaseServerClient(); | ||
| const { error } = await supabase | ||
| .from('workspace_members') | ||
| .update({ workspace_nickname: parsed.data.nickname }) | ||
| .eq('workspace_id', parsed.data.workspaceId) | ||
| .eq('user_id', userId); | ||
|
|
||
| if (error) { | ||
| // (workspace_id, workspace_nickname) 유니크 제약 위반은 사용자 친화적 메시지로 안내한다. | ||
| if (error.code === '23505') { | ||
| throw new Error('이미 사용 중인 닉네임이에요. 다른 닉네임을 입력해주세요.'); | ||
| } | ||
| console.error('[updateMyNickname] update 실패:', error); | ||
| throw new Error('닉네임 저장에 실패했습니다. 잠시 후 다시 시도해주세요.'); | ||
| } | ||
|
|
||
| revalidatePath(`/workspaces/${parsed.data.workspaceId}/settings`); | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,30 @@ | ||
| // 초대 미리보기 조회 — get_invite_preview RPC (security definer로 비멤버도 활성 코드면 요약 조회) | ||
| // 서버 전용(next/headers 의존)이므로 barrel에 넣지 않고 RSC에서 직접 경로로 import한다. | ||
| import { cache } from 'react'; | ||
| import { createSupabaseServerClient } from '@/shared/api/supabase/server'; | ||
|
|
||
| export interface InvitePreview { | ||
| workspaceId: string; | ||
| name: string; | ||
| memberCount: number; | ||
| } | ||
|
|
||
| export const getInvitePreview = cache(async (code: string): Promise<InvitePreview | null> => { | ||
| const supabase = await createSupabaseServerClient(); | ||
| const { data, error } = await supabase.rpc('get_invite_preview', { p_code: code }); | ||
|
|
||
| if (error) { | ||
| throw new Error(`초대 정보 조회에 실패했습니다: ${error.message}`); | ||
| } | ||
|
|
||
| const preview = data?.[0]; | ||
| if (!preview) { | ||
| return null; | ||
| } | ||
|
|
||
| return { | ||
| workspaceId: preview.workspace_id, | ||
| name: preview.name, | ||
| memberCount: preview.member_count, | ||
| }; | ||
| }); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
26 changes: 26 additions & 0 deletions
26
src/entities/workspace/api/join-workspace-by-invite-code.ts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,26 @@ | ||
| 'use server'; | ||
|
|
||
| // 초대 코드로 워크스페이스 참여 서버액션 — join_workspace_by_invite_code RPC | ||
| // (참여자는 RPC 내부에서 auth.uid()로 강제하고, 활성/유효 검증 + 멤버십 insert를 | ||
| // security definer 함수가 트랜잭션으로 처리) | ||
| import { createSupabaseServerClient } from '@/shared/api/supabase/server'; | ||
|
|
||
| export async function joinWorkspaceByInviteCode(code: string): Promise<{ workspaceId: string }> { | ||
| const trimmed = code.trim(); | ||
| if (!trimmed) { | ||
| throw new Error('유효하지 않은 초대 링크입니다.'); | ||
| } | ||
|
|
||
| const supabase = await createSupabaseServerClient(); | ||
| const { data, error } = await supabase.rpc('join_workspace_by_invite_code', { | ||
| p_code: trimmed, | ||
| }); | ||
|
|
||
| if (error) { | ||
| // 유효하지 않거나 비활성화된 초대는 RPC가 던진 메시지를 그대로 전달한다. | ||
| console.error('[joinWorkspaceByInviteCode] RPC 실패:', error); | ||
| throw new Error(error.message || '워크스페이스 참여에 실패했습니다. 잠시 후 다시 시도해주세요.'); | ||
| } | ||
|
|
||
| return { workspaceId: data }; | ||
| } |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.