-
Notifications
You must be signed in to change notification settings - Fork 0
Auth: User invitation flow #111
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
Open
Ayush8923
wants to merge
46
commits into
main
Choose a base branch
from
feat/invitation-flow
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
46 commits
Select commit
Hold shift + click to select a range
a0267da
feat(*): Admin flow initiate
Ayush8923 feea8c5
fix(*): added the note of after creating the key
Ayush8923 39604a2
fix(*): added the index.ts file for easy to import from the index
Ayush8923 fc7837f
fix(*): off the exhaustive deps
Ayush8923 e355d4a
fix(*): added the skeleton loader
Ayush8923 d4d4f1c
fix(*): some cleanups
Ayush8923 a4da445
fix(*): few clenaups
Ayush8923 713e9ad
feat(*): google integration flow
Ayush8923 06acfdc
feat(*): added the logout api endpoint
Ayush8923 b2ef783
fix(*): align the bottom action items tab
Ayush8923 3fbc5d3
fix(*): UI updates and users flow
Ayush8923 a236f2d
fix(*): small updates
Ayush8923 7e45d41
fix(*): some cases handling & UI updates
Ayush8923 e57c2e4
fix(*): some edge cases handled
Ayush8923 0e4d1dc
fix(*): authentication handle in the knowledge base
Ayush8923 8c4e3cc
fix(*): implement the edit projects
Ayush8923 bd1e94f
Merge branch 'feat/admin-flow' of https://github.com/ProjectTech4DevA…
Ayush8923 1b6598b
Merge branch 'feat/admin-flow' of https://github.com/ProjectTech4DevA…
Ayush8923 525f850
fix(*): remove the js comment
Ayush8923 2e3c91b
Merge branch 'main' of https://github.com/ProjectTech4DevAI/kaapi-fro…
Ayush8923 8a98eeb
Merge branch 'feat/admin-flow' of https://github.com/ProjectTech4DevA…
Ayush8923 32826e2
fix(*): some apikey intilization
Ayush8923 b6d59f8
fix(*): some of the UI updates
Ayush8923 3301f9d
fix(*): update the api response data
Ayush8923 9e14e58
fix(*): cleanups & handling refresh token
Ayush8923 be27625
fix(*): update the default setting route
Ayush8923 28513c8
Merge branch 'main' into feat/admin-flow
Ayush8923 9959073
Merge branch 'feat/admin-flow' into feat/google-integration
Ayush8923 1726212
Merge branch 'main' into feat/admin-flow
Ayush8923 4a0e744
Merge branch 'feat/admin-flow' into feat/google-integration
Ayush8923 275fcfc
fix(*): centralize the local storage key and clear the local storage …
Ayush8923 376e838
Merge branch 'main' of https://github.com/ProjectTech4DevAI/kaapi-fro…
Ayush8923 e223533
Merge branch 'feat/admin-flow' of https://github.com/ProjectTech4DevA…
Ayush8923 adb54ec
Merge branch 'feat/admin-flow' of https://github.com/ProjectTech4DevA…
Ayush8923 05bd0d3
fix(*): UI updates
Ayush8923 4c32ea0
Merge branch 'main' into feat/admin-flow
Ayush8923 e0c4783
Merge branch 'feat/admin-flow' into feat/google-integration
Ayush8923 06b672b
cleanups(*): stt and tts flow
Ayush8923 f985a1e
fix(*): few edge cases handles
Ayush8923 c206f3a
feat(*): User invitation flow
Ayush8923 fa0490f
fix(*): move invite page inside the auth group
Ayush8923 a20bb4d
fix(*): few updates on invite page
Ayush8923 16f139e
Feat: Google Integration (#102)
Ayush8923 44cd484
Merge branch 'feat/admin-flow' of https://github.com/ProjectTech4DevA…
Ayush8923 bed2d71
fix(*): update the invite page
Ayush8923 250769d
Merge branch 'main' of https://github.com/ProjectTech4DevAI/kaapi-fro…
Ayush8923 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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,224 @@ | ||
| "use client"; | ||
|
|
||
| import { useEffect, useState, Suspense } from "react"; | ||
| import { useSearchParams, useRouter } from "next/navigation"; | ||
| import { useAuth } from "@/app/lib/context/AuthContext"; | ||
| import { InviteVerifyResponse } from "@/app/lib/types/auth"; | ||
| import { | ||
| CheckCircleIcon, | ||
| WarningIcon, | ||
| SpinnerIcon, | ||
| } from "@/app/components/icons"; | ||
| import { Button } from "@/app/components"; | ||
|
|
||
| type Status = "verifying" | "success" | "error"; | ||
|
|
||
| function InviteContent() { | ||
| const searchParams = useSearchParams(); | ||
| const router = useRouter(); | ||
| const { loginWithToken } = useAuth(); | ||
| const [status, setStatus] = useState<Status>("verifying"); | ||
| const [error, setError] = useState(""); | ||
| const [progress, setProgress] = useState(0); | ||
|
|
||
| useEffect(() => { | ||
| const token = searchParams.get("token"); | ||
|
|
||
| if (!token) { | ||
| setStatus("error"); | ||
| setError("Invalid invitation link. No token found."); | ||
| return; | ||
| } | ||
|
|
||
| let cancelled = false; | ||
|
|
||
| (async () => { | ||
| try { | ||
| const res = await fetch( | ||
| `/api/auth/invite?token=${encodeURIComponent(token)}`, | ||
| { credentials: "include" }, | ||
| ); | ||
|
|
||
| const data: InviteVerifyResponse = await res.json(); | ||
|
|
||
| if (cancelled) return; | ||
|
|
||
| if (!res.ok || !data.success || !data.data) { | ||
| setStatus("error"); | ||
| setError(data.error || "Invitation link is invalid or has expired."); | ||
| return; | ||
| } | ||
|
|
||
| loginWithToken(data.data.access_token, data.data.user); | ||
| setStatus("success"); | ||
| } catch { | ||
| if (!cancelled) { | ||
| setStatus("error"); | ||
| setError("Failed to verify invitation. Please try again."); | ||
| } | ||
| } | ||
| })(); | ||
|
|
||
| return () => { | ||
| cancelled = true; | ||
| }; | ||
| }, [searchParams, loginWithToken]); | ||
|
|
||
| useEffect(() => { | ||
| if (status !== "success") return; | ||
|
|
||
| const duration = 2000; | ||
| const interval = 30; | ||
| let elapsed = 0; | ||
|
|
||
| const timer = setInterval(() => { | ||
| elapsed += interval; | ||
| const pct = Math.min((elapsed / duration) * 100, 100); | ||
| setProgress(pct); | ||
|
|
||
| if (elapsed >= duration) { | ||
| clearInterval(timer); | ||
| router.push("/evaluations"); | ||
| } | ||
| }, interval); | ||
|
|
||
| return () => clearInterval(timer); | ||
| }, [status, router]); | ||
|
|
||
| return ( | ||
| <div className="min-h-screen bg-bg-secondary flex flex-col items-center justify-center p-4 relative overflow-hidden"> | ||
| <div className="absolute inset-0 overflow-hidden pointer-events-none"> | ||
| <div className="absolute -top-1/2 -right-1/4 w-[600px] h-[600px] rounded-full bg-linear-to-br from-blue-50 to-purple-50 opacity-60 blur-3xl" /> | ||
| <div className="absolute -bottom-1/2 -left-1/4 w-[500px] h-[500px] rounded-full bg-linear-to-tr from-green-50 to-blue-50 opacity-40 blur-3xl" /> | ||
| </div> | ||
|
|
||
| <div className="w-full max-w-sm relative z-10"> | ||
| <div className="text-center mb-8"> | ||
| <h2 className="text-lg font-semibold text-text-primary tracking-tight"> | ||
| Kaapi Konsole | ||
| </h2> | ||
| <p className="text-xs text-text-secondary mt-0.5">by Tech4Dev</p> | ||
| </div> | ||
|
|
||
| <div | ||
| className={`bg-white rounded-2xl border shadow-sm overflow-hidden transition-all duration-500 ${ | ||
| status === "error" | ||
| ? "border-red-200" | ||
| : status === "success" | ||
| ? "border-green-200" | ||
| : "border-border" | ||
| }`} | ||
| > | ||
| <div | ||
| className="h-1 transition-all duration-700" | ||
| style={{ | ||
| background: | ||
| status === "error" | ||
| ? "linear-gradient(90deg, #fca5a5, #ef4444)" | ||
| : status === "success" | ||
| ? "linear-gradient(90deg, #86efac, #22c55e)" | ||
| : "linear-gradient(90deg, #dbeafe, #c7d2fe, #ddd6fe)", | ||
| }} | ||
| /> | ||
|
|
||
| <div className="px-8 py-10"> | ||
| <div className="flex justify-center mb-5"> | ||
| <div | ||
| className={`w-16 h-16 rounded-full flex items-center justify-center transition-all duration-500 ${ | ||
| status === "verifying" | ||
| ? "bg-neutral-50 border border-border" | ||
| : status === "success" | ||
| ? "bg-green-50 border border-green-200" | ||
| : "bg-red-50 border border-red-200" | ||
| }`} | ||
| > | ||
| {status === "verifying" && ( | ||
| <SpinnerIcon className="w-7 h-7 text-text-secondary animate-spin" /> | ||
| )} | ||
| {status === "success" && ( | ||
| <CheckCircleIcon className="w-8 h-8 text-green-600" /> | ||
| )} | ||
| {status === "error" && ( | ||
| <WarningIcon className="w-8 h-8 text-red-500" /> | ||
| )} | ||
| </div> | ||
| </div> | ||
|
|
||
| {/* Title */} | ||
| <h1 className="text-center text-xl font-semibold text-text-primary mb-2"> | ||
| {status === "verifying" && "Verifying invitation"} | ||
| {status === "success" && "Welcome aboard!"} | ||
| {status === "error" && "Something went wrong"} | ||
| </h1> | ||
|
|
||
| <p className="text-sm text-text-secondary leading-relaxed"> | ||
| {status === "verifying" && | ||
| "Please wait while we verify your invitation and set up your account."} | ||
| {status === "success" && | ||
| "Your account has been activated. Redirecting you to the dashboard..."} | ||
| {status === "error" && error} | ||
| </p> | ||
|
|
||
| {status === "success" && ( | ||
| <div className="mt-6 flex justify-center"> | ||
| <div className="h-1 w-32 rounded-full bg-neutral-100 overflow-hidden"> | ||
| <div | ||
| className="h-full bg-green-500 rounded-full transition-[width] duration-75 ease-linear" | ||
| style={{ width: `${progress}%` }} | ||
| /> | ||
| </div> | ||
| </div> | ||
| )} | ||
|
|
||
| {status === "verifying" && ( | ||
| <div className="mt-6 flex justify-center gap-1.5"> | ||
| {[0, 1, 2].map((i) => ( | ||
| <div | ||
| key={i} | ||
| className="w-1.5 h-1.5 rounded-full bg-text-secondary animate-pulse" | ||
| style={{ animationDelay: `${i * 200}ms` }} | ||
| /> | ||
| ))} | ||
| </div> | ||
| )} | ||
|
|
||
| {status === "error" && ( | ||
| <div className="mt-8 space-y-3"> | ||
| <Button fullWidth onClick={() => router.push("/evaluations")}> | ||
| Go to Dashboard | ||
| </Button> | ||
| <button | ||
| onClick={() => window.location.reload()} | ||
| className="w-full text-center text-xs text-text-secondary hover:text-text-primary transition-colors cursor-pointer" | ||
| > | ||
| Try again | ||
| </button> | ||
| </div> | ||
| )} | ||
| </div> | ||
| </div> | ||
|
|
||
| {status === "error" && ( | ||
| <p className="text-center text-xs text-text-secondary mt-5 leading-relaxed"> | ||
| If this keeps happening, please contact your organization | ||
| administrator for a new invitation link. | ||
| </p> | ||
| )} | ||
| </div> | ||
| </div> | ||
| ); | ||
| } | ||
|
|
||
| export default function InvitePage() { | ||
| return ( | ||
| <Suspense | ||
| fallback={ | ||
| <div className="min-h-screen bg-bg-secondary flex items-center justify-center"> | ||
| <SpinnerIcon className="w-8 h-8 text-text-secondary animate-spin" /> | ||
| </div> | ||
| } | ||
| > | ||
| <InviteContent /> | ||
| </Suspense> | ||
| ); | ||
| } |
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,35 @@ | ||
| import { NextRequest, NextResponse } from "next/server"; | ||
| import { apiClient } from "@/app/lib/apiClient"; | ||
|
|
||
| export async function GET(request: NextRequest) { | ||
| try { | ||
| const { searchParams } = new URL(request.url); | ||
| const token = searchParams.get("token"); | ||
|
|
||
| if (!token) { | ||
| return NextResponse.json( | ||
| { success: false, error: "Missing invitation token" }, | ||
| { status: 400 }, | ||
| ); | ||
| } | ||
|
|
||
| const { status, data, headers } = await apiClient( | ||
| request, | ||
| `/api/v1/auth/invite/verify?token=${encodeURIComponent(token)}`, | ||
| ); | ||
|
|
||
| const res = NextResponse.json(data, { status }); | ||
|
|
||
| const setCookies = headers.getSetCookie?.() ?? []; | ||
| for (const cookie of setCookies) { | ||
| res.headers.append("Set-Cookie", cookie); | ||
| } | ||
|
|
||
| return res; | ||
| } catch { | ||
| return NextResponse.json( | ||
| { success: false, error: "Failed to connect to backend" }, | ||
| { status: 500 }, | ||
| ); | ||
| } | ||
| } | ||
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
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
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
Repository: ProjectTech4DevAI/kaapi-frontend
Length of output: 740
🏁 Script executed:
Repository: ProjectTech4DevAI/kaapi-frontend
Length of output: 1247
🏁 Script executed:
Repository: ProjectTech4DevAI/kaapi-frontend
Length of output: 58
🏁 Script executed:
Repository: ProjectTech4DevAI/kaapi-frontend
Length of output: 103
🏁 Script executed:
Repository: ProjectTech4DevAI/kaapi-frontend
Length of output: 58
🏁 Script executed:
Repository: ProjectTech4DevAI/kaapi-frontend
Length of output: 2853
🏁 Script executed:
Repository: ProjectTech4DevAI/kaapi-frontend
Length of output: 317
🏁 Script executed:
# Check the google and logout routes to see their full pattern cat -n app/api/auth/google/route.tsRepository: ProjectTech4DevAI/kaapi-frontend
Length of output: 1419
🏁 Script executed:
Repository: ProjectTech4DevAI/kaapi-frontend
Length of output: 1253
🏁 Script executed:
Repository: ProjectTech4DevAI/kaapi-frontend
Length of output: 3002
🏁 Script executed:
Repository: ProjectTech4DevAI/kaapi-frontend
Length of output: 58
🏁 Script executed:
Repository: ProjectTech4DevAI/kaapi-frontend
Length of output: 690
🏁 Script executed:
Repository: ProjectTech4DevAI/kaapi-frontend
Length of output: 58
🏁 Script executed:
Repository: ProjectTech4DevAI/kaapi-frontend
Length of output: 972
🏁 Script executed:
Repository: ProjectTech4DevAI/kaapi-frontend
Length of output: 58
🏁 Script executed:
Repository: ProjectTech4DevAI/kaapi-frontend
Length of output: 58
🏁 Script executed:
Repository: ProjectTech4DevAI/kaapi-frontend
Length of output: 412
Add explicit no-cache headers to invite verification response.
This GET endpoint returns sensitive auth data and forwards session cookies. Responses should be marked non-cacheable to prevent browsers or proxies from caching the auth token/cookies.
🔧 Proposed patch
📝 Committable suggestion
🤖 Prompt for AI Agents