diff --git a/package.json b/package.json index b297096..3423a6f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "pgt-web-app", - "version": "0.1.9", + "version": "0.1.10", "private": true, "type": "module", "scripts": { diff --git a/src/app/api/me/info/route.ts b/src/app/api/me/info/route.ts index 52b2542..d993e0c 100644 --- a/src/app/api/me/info/route.ts +++ b/src/app/api/me/info/route.ts @@ -29,25 +29,24 @@ export async function GET() { // Get user ID const userId = deriveUserId(payload.authSource); + // Create user if not exists + await userService.findOrCreateUser(payload.authSource); + // Get complete user info const userInfo = await userService.getUserInfo(userId); if (!userInfo) { - return NextResponse.json({ error: "User not found" }, { status: 404 }); + return ApiResponse.notFound("User not found"); } return ApiResponse.success(userInfo); - } catch (error) { logger.error("User info error:", error); if (error instanceof Error && error.message === "Invalid token") { - return NextResponse.json({ error: "Unauthorized" }, { status: 401 }); + return ApiResponse.unauthorized("Unauthorized"); } - return NextResponse.json( - { error: "Internal server error" }, - { status: 500 } - ); + return ApiResponse.error("Internal server error"); } } \ No newline at end of file diff --git a/src/components/auth/UserStatus.tsx b/src/components/auth/UserStatus.tsx index 6bba0f8..ce2d444 100644 --- a/src/components/auth/UserStatus.tsx +++ b/src/components/auth/UserStatus.tsx @@ -85,7 +85,12 @@ export function UserStatus() { // Get auth info from user metadata const authSource = user.metadata.authSource - const username = user.metadata.username + let username = user.metadata.username + + if (authSource.type === 'wallet') { + username = username.slice(0, 6) + '...' + username.slice(-4) + } + const ProviderIcon = AUTH_PROVIDER_ICONS[authSource.type] const providerName = AUTH_PROVIDER_NAMES[authSource.type] diff --git a/src/components/web3/WalletAuthDialog.tsx b/src/components/web3/WalletAuthDialog.tsx index a3d9284..7e6c950 100644 --- a/src/components/web3/WalletAuthDialog.tsx +++ b/src/components/web3/WalletAuthDialog.tsx @@ -7,10 +7,10 @@ import { useWallet } from "@/contexts/WalletContext"; import { useToast } from "@/hooks/use-toast"; import { WALLET_MESSAGE_VERSIONS, LATEST_WALLET_MESSAGE_VERSION } from "@/constants/wallet-messages"; import { Icons } from "@/components/icons"; -import { TransactionPayload } from "@/types/wallet"; import { ApiResponse } from "@/lib/api-response"; import { AppError } from "@/lib/errors"; import { HTTPStatus } from "@/constants/errors"; +import { useAuth } from "@/contexts/AuthContext"; interface WalletAuthDialogProps { open: boolean; @@ -29,6 +29,7 @@ interface LinkingDialogProps { function LinkingDialog({ open, onOpenChange, walletToken, existingToken, onConfirm, onCancel }: LinkingDialogProps) { const [isLinking, setIsLinking] = useState(false); const { toast } = useToast(); + const { refresh } = useAuth(); const handleConfirm = async () => { setIsLinking(true); @@ -58,6 +59,7 @@ function LinkingDialog({ open, onOpenChange, walletToken, existingToken, onConfi } finally { setIsLinking(false); onOpenChange(false); + await refresh(); } }; @@ -94,6 +96,7 @@ function LinkingDialog({ open, onOpenChange, walletToken, existingToken, onConfi export function WalletAuthDialog({ open, onOpenChange }: WalletAuthDialogProps) { const { state } = useWallet(); const { toast } = useToast(); + const { refresh } = useAuth(); const [isAuthenticating, setIsAuthenticating] = useState(false); const [showLinkingDialog, setShowLinkingDialog] = useState(false); const [authTokens, setAuthTokens] = useState<{ @@ -157,7 +160,6 @@ export function WalletAuthDialog({ open, onOpenChange }: WalletAuthDialogProps) }); onOpenChange(false); } - } catch (error) { toast({ title: "Authentication failed", @@ -166,6 +168,7 @@ export function WalletAuthDialog({ open, onOpenChange }: WalletAuthDialogProps) }); } finally { setIsAuthenticating(false); + await refresh(); } }; @@ -211,7 +214,7 @@ export function WalletAuthDialog({ open, onOpenChange }: WalletAuthDialogProps) onOpenChange={setShowLinkingDialog} walletToken={authTokens.walletToken} existingToken={authTokens.existingToken} - onConfirm={() => { + onConfirm={async () => { toast({ title: "Accounts linked", description: "Your accounts have been successfully linked", diff --git a/src/components/web3/dialogs/OCVTransactionDialog.tsx b/src/components/web3/dialogs/OCVTransactionDialog.tsx index 11b8a1b..a3d0f98 100644 --- a/src/components/web3/dialogs/OCVTransactionDialog.tsx +++ b/src/components/web3/dialogs/OCVTransactionDialog.tsx @@ -116,15 +116,13 @@ export function OCVTransactionDialog({ console.log("Connected to network:", network) } - // Create transaction with memo - const tx = { + // Send payment to self with memo + const response = await mina.sendPayment({ to: account, - amount: "0", - memo, - } - - // Send the transaction - const response = await mina.sendTransaction(tx) + amount: 0, + memo: memo, + }) + hash = response.hash break diff --git a/src/contexts/AuthContext.tsx b/src/contexts/AuthContext.tsx index b516b8a..950d18c 100644 --- a/src/contexts/AuthContext.tsx +++ b/src/contexts/AuthContext.tsx @@ -29,14 +29,13 @@ interface AuthContextType { const AuthContext = createContext(undefined) /** - * Checks if authentication cookies exist + * Checks if user is authenticated. + * + * This will be implemented later, it's not necessry to implement it now. + * The goal of this is to minimize unecessary api calls. */ -function hasAuthCookies(): boolean { - // Check for either access_token or refresh_token - return document.cookie.split(';').some(cookie => { - const trimmed = cookie.trim() - return trimmed.startsWith('access_token=') || trimmed.startsWith('refresh_token=') - }) +function isAuthenticated(): boolean { + return true; } export function AuthProvider({ children }: { children: React.ReactNode }) { @@ -47,15 +46,14 @@ export function AuthProvider({ children }: { children: React.ReactNode }) { const refresh = useCallback(async () => { try { - // Only attempt to fetch user info if we have auth cookies - if (!hasAuthCookies()) { + if (!isAuthenticated()) { setUser(null) return } const res = await fetch('/api/me/info') - if (res.status === 401) { + if (res.status === 401 || res.status === 403) { setUser(null) return } @@ -79,7 +77,7 @@ export function AuthProvider({ children }: { children: React.ReactNode }) { useEffect(() => { // Only run initial auth check if we have cookies - if (hasAuthCookies()) { + if (isAuthenticated()) { refresh().finally(() => setIsLoading(false)) } else { setIsLoading(false) @@ -93,7 +91,7 @@ export function AuthProvider({ children }: { children: React.ReactNode }) { const logout = useCallback(async () => { try { // Only attempt logout if we have auth cookies - if (!hasAuthCookies()) { + if (!isAuthenticated()) { setUser(null) router.push('/') return diff --git a/src/lib/api-response.ts b/src/lib/api-response.ts index 1db9605..a423948 100644 --- a/src/lib/api-response.ts +++ b/src/lib/api-response.ts @@ -4,6 +4,20 @@ import { HTTPStatus } from '@/constants/errors'; import logger from '@/logging'; export class ApiResponse { + static notFound(data: T | { error: string } | string) { + if (typeof data === 'string') { + data = { error: data }; + } + return NextResponse.json(data, { status: HTTPStatus.NOT_FOUND }); + } + + static unauthorized(data: T | { error: string } | string) { + if (typeof data === 'string') { + data = { error: data }; + } + return NextResponse.json(data, { status: HTTPStatus.UNAUTHORIZED }); + } + static success(data: T) { return NextResponse.json(data); } diff --git a/src/services/UserService.ts b/src/services/UserService.ts index 04ea069..30827c9 100644 --- a/src/services/UserService.ts +++ b/src/services/UserService.ts @@ -1,6 +1,7 @@ import { PrismaClient, User } from "@prisma/client"; import { deriveUserId, generateLinkId } from "@/lib/user/derive"; import type { AuthSource } from "@/lib/user/types"; +import logger from "@/logging"; type AuthSourceType = AuthSource['type']; diff --git a/src/types/wallet.ts b/src/types/wallet.ts index a4d9b06..d50363d 100644 --- a/src/types/wallet.ts +++ b/src/types/wallet.ts @@ -73,6 +73,17 @@ interface SignedData { }; } +interface SendPaymentArgs { + readonly to: string + readonly amount: number + readonly fee?: number + readonly memo?: string +} + +type SendTransactionResult = { + hash: string +} + export interface AuroWallet { requestAccounts(): Promise; getAccounts?(): Promise; @@ -88,6 +99,7 @@ export interface AuroWallet { handler: (payload: WalletEventPayload[T]) => void ): void; signMessage: (args: { message: string }) => Promise; + sendPayment: (args: SendPaymentArgs) => Promise; } declare global {