feat: schema updates
- added additional items to users and sandbox tables - added a random username generator
This commit is contained in:
@ -1,6 +1,7 @@
|
||||
import { User } from "@/lib/types"
|
||||
import { currentUser } from "@clerk/nextjs"
|
||||
import { redirect } from "next/navigation"
|
||||
import { generateUniqueUsername } from "@/lib/username-generator";
|
||||
|
||||
export default async function AppAuthLayout({
|
||||
children,
|
||||
@ -24,6 +25,25 @@ export default async function AppAuthLayout({
|
||||
const dbUserJSON = (await dbUser.json()) as User
|
||||
|
||||
if (!dbUserJSON.id) {
|
||||
// Try to get GitHub username if available
|
||||
const githubUsername = user.externalAccounts.find(
|
||||
account => account.provider === "github"
|
||||
)?.username;
|
||||
|
||||
const username = githubUsername || await generateUniqueUsername(async (username) => {
|
||||
// Check if username exists in database
|
||||
const userCheck = await fetch(
|
||||
`${process.env.NEXT_PUBLIC_DATABASE_WORKER_URL}/api/user/check-username?username=${username}`,
|
||||
{
|
||||
headers: {
|
||||
Authorization: `${process.env.NEXT_PUBLIC_WORKERS_KEY}`,
|
||||
},
|
||||
}
|
||||
)
|
||||
const exists = await userCheck.json()
|
||||
return exists.exists
|
||||
});
|
||||
|
||||
const res = await fetch(
|
||||
`${process.env.NEXT_PUBLIC_DATABASE_WORKER_URL}/api/user`,
|
||||
{
|
||||
@ -36,9 +56,20 @@ export default async function AppAuthLayout({
|
||||
id: user.id,
|
||||
name: user.firstName + " " + user.lastName,
|
||||
email: user.emailAddresses[0].emailAddress,
|
||||
username: username,
|
||||
avatarUrl: user.imageUrl || null,
|
||||
createdAt: new Date().toISOString(),
|
||||
}),
|
||||
}
|
||||
)
|
||||
|
||||
if (!res.ok) {
|
||||
const error = await res.text();
|
||||
console.error("Failed to create user:", error);
|
||||
} else {
|
||||
const data = await res.json();
|
||||
console.log("User created successfully:", data);
|
||||
}
|
||||
}
|
||||
|
||||
return <>{children}</>
|
||||
|
59
frontend/components/ui/alert.tsx
Normal file
59
frontend/components/ui/alert.tsx
Normal file
@ -0,0 +1,59 @@
|
||||
import * as React from "react"
|
||||
import { cva, type VariantProps } from "class-variance-authority"
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
|
||||
const alertVariants = cva(
|
||||
"relative w-full rounded-lg border px-4 py-3 text-sm [&>svg+div]:translate-y-[-3px] [&>svg]:absolute [&>svg]:left-4 [&>svg]:top-4 [&>svg]:text-foreground [&>svg~*]:pl-7",
|
||||
{
|
||||
variants: {
|
||||
variant: {
|
||||
default: "bg-background text-foreground",
|
||||
destructive:
|
||||
"border-destructive/50 text-destructive dark:border-destructive [&>svg]:text-destructive",
|
||||
},
|
||||
},
|
||||
defaultVariants: {
|
||||
variant: "default",
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
const Alert = React.forwardRef<
|
||||
HTMLDivElement,
|
||||
React.HTMLAttributes<HTMLDivElement> & VariantProps<typeof alertVariants>
|
||||
>(({ className, variant, ...props }, ref) => (
|
||||
<div
|
||||
ref={ref}
|
||||
role="alert"
|
||||
className={cn(alertVariants({ variant }), className)}
|
||||
{...props}
|
||||
/>
|
||||
))
|
||||
Alert.displayName = "Alert"
|
||||
|
||||
const AlertTitle = React.forwardRef<
|
||||
HTMLParagraphElement,
|
||||
React.HTMLAttributes<HTMLHeadingElement>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<h5
|
||||
ref={ref}
|
||||
className={cn("mb-1 font-medium leading-none tracking-tight", className)}
|
||||
{...props}
|
||||
/>
|
||||
))
|
||||
AlertTitle.displayName = "AlertTitle"
|
||||
|
||||
const AlertDescription = React.forwardRef<
|
||||
HTMLParagraphElement,
|
||||
React.HTMLAttributes<HTMLParagraphElement>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<div
|
||||
ref={ref}
|
||||
className={cn("text-sm [&_p]:leading-relaxed", className)}
|
||||
{...props}
|
||||
/>
|
||||
))
|
||||
AlertDescription.displayName = "AlertDescription"
|
||||
|
||||
export { Alert, AlertTitle, AlertDescription }
|
28
frontend/components/ui/progress.tsx
Normal file
28
frontend/components/ui/progress.tsx
Normal file
@ -0,0 +1,28 @@
|
||||
"use client"
|
||||
|
||||
import * as React from "react"
|
||||
import * as ProgressPrimitive from "@radix-ui/react-progress"
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
|
||||
const Progress = React.forwardRef<
|
||||
React.ElementRef<typeof ProgressPrimitive.Root>,
|
||||
React.ComponentPropsWithoutRef<typeof ProgressPrimitive.Root>
|
||||
>(({ className, value, ...props }, ref) => (
|
||||
<ProgressPrimitive.Root
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"relative h-2 w-full overflow-hidden rounded-full bg-primary/20",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
<ProgressPrimitive.Indicator
|
||||
className="h-full w-full flex-1 bg-primary transition-all"
|
||||
style={{ transform: `translateX(-${100 - (value || 0)}%)` }}
|
||||
/>
|
||||
</ProgressPrimitive.Root>
|
||||
))
|
||||
Progress.displayName = ProgressPrimitive.Root.displayName
|
||||
|
||||
export { Progress }
|
32
frontend/components/ui/tooltip.tsx
Normal file
32
frontend/components/ui/tooltip.tsx
Normal file
@ -0,0 +1,32 @@
|
||||
"use client"
|
||||
|
||||
import * as React from "react"
|
||||
import * as TooltipPrimitive from "@radix-ui/react-tooltip"
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
|
||||
const TooltipProvider = TooltipPrimitive.Provider
|
||||
|
||||
const Tooltip = TooltipPrimitive.Root
|
||||
|
||||
const TooltipTrigger = TooltipPrimitive.Trigger
|
||||
|
||||
const TooltipContent = React.forwardRef<
|
||||
React.ElementRef<typeof TooltipPrimitive.Content>,
|
||||
React.ComponentPropsWithoutRef<typeof TooltipPrimitive.Content>
|
||||
>(({ className, sideOffset = 4, ...props }, ref) => (
|
||||
<TooltipPrimitive.Portal>
|
||||
<TooltipPrimitive.Content
|
||||
ref={ref}
|
||||
sideOffset={sideOffset}
|
||||
className={cn(
|
||||
"z-50 overflow-hidden rounded-md bg-primary px-3 py-1.5 text-xs text-primary-foreground animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
</TooltipPrimitive.Portal>
|
||||
))
|
||||
TooltipContent.displayName = TooltipPrimitive.Content.displayName
|
||||
|
||||
export { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider }
|
@ -4,6 +4,9 @@ export type User = {
|
||||
id: string
|
||||
name: string
|
||||
email: string
|
||||
username: string
|
||||
avatarUrl: string | null
|
||||
createdAt: Date
|
||||
generations: number
|
||||
sandbox: Sandbox[]
|
||||
usersToSandboxes: UsersToSandboxes[]
|
||||
|
82
frontend/lib/username-generator.ts
Normal file
82
frontend/lib/username-generator.ts
Normal file
@ -0,0 +1,82 @@
|
||||
// Constants for username generation
|
||||
const WORDS = {
|
||||
adjectives: [
|
||||
"azure", "crimson", "golden", "silver", "violet", "emerald", "cobalt", "amber", "coral", "jade",
|
||||
"cyber", "digital", "quantum", "neural", "binary", "cosmic", "stellar", "atomic", "crypto", "nano",
|
||||
"swift", "brave", "clever", "wise", "noble", "rapid", "bright", "sharp", "keen", "bold",
|
||||
"dynamic", "epic", "mega", "ultra", "hyper", "super", "prime", "elite", "alpha", "omega",
|
||||
"pixel", "vector", "sonic", "laser", "matrix", "nexus", "proxy", "cloud", "data", "tech",
|
||||
],
|
||||
nouns: [
|
||||
"coder", "hacker", "dev", "ninja", "guru", "wizard", "admin", "mod", "chief", "boss",
|
||||
"wolf", "eagle", "phoenix", "dragon", "tiger", "falcon", "shark", "lion", "hawk", "bear",
|
||||
"byte", "bit", "node", "stack", "cache", "chip", "core", "net", "web", "app",
|
||||
"star", "nova", "pulsar", "comet", "nebula", "quasar", "cosmos", "orbit", "astro", "solar",
|
||||
"mind", "soul", "spark", "pulse", "force", "power", "wave", "storm", "flash", "surge",
|
||||
],
|
||||
prefixes: [
|
||||
"the", "mr", "ms", "dr", "pro", "master", "lord", "captain", "chief", "agent",
|
||||
],
|
||||
} as const;
|
||||
|
||||
// Helper function to get random element from array
|
||||
const getRandomElement = <T>(array: readonly T[]): T => {
|
||||
return array[Math.floor(Math.random() * array.length)];
|
||||
};
|
||||
|
||||
// Username pattern generators
|
||||
const usernamePatterns = {
|
||||
basic: (): string => {
|
||||
const adjective = getRandomElement(WORDS.adjectives);
|
||||
const noun = getRandomElement(WORDS.nouns);
|
||||
const number = Math.floor(Math.random() * 10000);
|
||||
return `${adjective}${noun}${number}`;
|
||||
},
|
||||
|
||||
prefixed: (): string => {
|
||||
const prefix = getRandomElement(WORDS.prefixes);
|
||||
const noun = getRandomElement(WORDS.nouns);
|
||||
const number = Math.floor(Math.random() * 100);
|
||||
return `${prefix}${noun}${number}`;
|
||||
},
|
||||
|
||||
doubleAdjective: (): string => {
|
||||
const adj1 = getRandomElement(WORDS.adjectives);
|
||||
const adj2 = getRandomElement(WORDS.adjectives);
|
||||
const noun = getRandomElement(WORDS.nouns);
|
||||
return `${adj1}${adj2}${noun}`;
|
||||
},
|
||||
|
||||
doubleNoun: (): string => {
|
||||
const noun1 = getRandomElement(WORDS.nouns);
|
||||
const noun2 = getRandomElement(WORDS.nouns);
|
||||
const number = Math.floor(Math.random() * 100);
|
||||
return `${noun1}${number}${noun2}`;
|
||||
},
|
||||
};
|
||||
|
||||
export function generateUsername(): string {
|
||||
const patterns = Object.values(usernamePatterns);
|
||||
const selectedPattern = getRandomElement(patterns);
|
||||
return selectedPattern();
|
||||
}
|
||||
|
||||
export async function generateUniqueUsername(
|
||||
checkExists: (username: string) => Promise<boolean>
|
||||
): Promise<string> {
|
||||
const MAX_ATTEMPTS = 10;
|
||||
let attempts = 0;
|
||||
let username = generateUsername();
|
||||
|
||||
while (await checkExists(username) && attempts < MAX_ATTEMPTS) {
|
||||
username = generateUsername();
|
||||
attempts++;
|
||||
}
|
||||
|
||||
if (attempts >= MAX_ATTEMPTS) {
|
||||
// Add a large random number to ensure uniqueness
|
||||
username = generateUsername() + Math.floor(Math.random() * 1000000);
|
||||
}
|
||||
|
||||
return username;
|
||||
}
|
1987
frontend/package-lock.json
generated
1987
frontend/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -27,9 +27,11 @@
|
||||
"@radix-ui/react-icons": "^1.3.0",
|
||||
"@radix-ui/react-label": "^2.0.2",
|
||||
"@radix-ui/react-popover": "^1.1.1",
|
||||
"@radix-ui/react-progress": "^1.1.0",
|
||||
"@radix-ui/react-select": "^2.0.0",
|
||||
"@radix-ui/react-slot": "^1.0.2",
|
||||
"@radix-ui/react-switch": "^1.0.3",
|
||||
"@radix-ui/react-tooltip": "^1.1.3",
|
||||
"@react-three/fiber": "^8.16.6",
|
||||
"@uiw/codemirror-theme-vscode": "^4.23.5",
|
||||
"@uiw/react-codemirror": "^4.23.5",
|
||||
@ -57,6 +59,7 @@
|
||||
"react-resizable-panels": "^2.0.16",
|
||||
"react-syntax-highlighter": "^15.5.0",
|
||||
"remark-gfm": "^4.0.0",
|
||||
"shadcn": "^2.1.6",
|
||||
"socket.io-client": "^4.7.5",
|
||||
"sonner": "^1.4.41",
|
||||
"tailwind-merge": "^2.3.0",
|
||||
|
Reference in New Issue
Block a user