diff options
| author | 3raphat <96657413+3raphat@users.noreply.github.com> | 2025-06-23 15:53:45 +0700 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2025-06-23 15:53:45 +0700 |
| commit | c94700b06926505a469ae8f1a2360673a6f3b58d (patch) | |
| tree | e777d5598cd508c8287fc0169fa30aeb86583af0 | |
| parent | befec3131bb79e8884c6e2bfe6188036d30c41f0 (diff) | |
| parent | d92aed2dac5ea3ff015ce974b9519e03e2e9823c (diff) | |
| download | opencut-c94700b06926505a469ae8f1a2360673a6f3b58d.tar.gz | |
Merge branch 'main' into update-env
| -rw-r--r-- | README.md | 36 | ||||
| -rw-r--r-- | apps/web/src/app/auth/login/page.tsx | 98 | ||||
| -rw-r--r-- | apps/web/src/app/auth/signup/page.tsx | 108 | ||||
| -rw-r--r-- | apps/web/src/lib/auth.ts | 2 |
4 files changed, 237 insertions, 7 deletions
@@ -15,13 +15,37 @@ A free, open-source video editor for web, desktop, and mobile. - Real-time preview - No watermarks or subscriptions -## Development +## Project Structure -```bash -cd apps/web -npm install -npm run dev -``` +- `apps/web/` – Main Next.js web application +- `src/components/` – UI and editor components +- `src/hooks/` – Custom React hooks +- `src/lib/` – Utility and API logic +- `src/stores/` – State management (Zustand, etc.) +- `src/types/` – TypeScript types + +## Getting Started + +1. **Clone the repository:** + ```bash + git clone <repo-url> + cd OpenCut + ``` +2. **Install dependencies:** + ```bash + cd apps/web + npm install + # or, with Bun + bun install + ``` +3. **Run the development server:** + ```bash + npm run dev + # or, with Bun + bun run dev + ``` +4. **Open in browser:** + Visit [http://localhost:3000](http://localhost:3000) ## Contributing diff --git a/apps/web/src/app/auth/login/page.tsx b/apps/web/src/app/auth/login/page.tsx new file mode 100644 index 00000000..5eadb85c --- /dev/null +++ b/apps/web/src/app/auth/login/page.tsx @@ -0,0 +1,98 @@ +"use client"; + +import { useSearchParams, useRouter } from "next/navigation"; +import { authClient } from "@/lib/auth-client"; +import { Button } from "@/components/ui/button"; +import { + Card, + CardContent, + CardDescription, + CardHeader, + CardTitle, +} from "@/components/ui/card"; +import { Suspense, useState } from "react"; +import { Input } from "@/components/ui/input"; +import { Label } from "@/components/ui/label"; +import Link from "next/link"; +import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert"; + +function LoginForm() { + const searchParams = useSearchParams(); + const router = useRouter(); + const redirectUrl = searchParams.get("redirect"); + const [email, setEmail] = useState(""); + const [password, setPassword] = useState(""); + const [error, setError] = useState<string | null>(null); + + const handleLogin = async () => { + setError(null); + const { error } = await authClient.signIn.email({ + email, + password, + }); + + if (error) { + setError(error.message || "An unexpected error occurred."); + return; + } + + router.push(redirectUrl || "/"); + }; + + return ( + <div className="flex flex-col space-y-4"> + {error && ( + <Alert variant="destructive"> + <AlertTitle>Error</AlertTitle> + <AlertDescription>{error}</AlertDescription> + </Alert> + )} + <div className="space-y-2"> + <Label htmlFor="email">Email</Label> + <Input + id="email" + type="email" + placeholder="m@example.com" + value={email} + onChange={(e) => setEmail(e.target.value)} + /> + </div> + <div className="space-y-2"> + <Label htmlFor="password">Password</Label> + <Input + id="password" + type="password" + value={password} + onChange={(e) => setPassword(e.target.value)} + /> + </div> + <Button onClick={handleLogin}>Login</Button> + </div> + ); +} + +export default function LoginPage() { + return ( + <div className="flex h-screen items-center justify-center"> + <Card className="w-[350px]"> + <CardHeader> + <CardTitle>Login</CardTitle> + <CardDescription> + Enter your email and password to login. + </CardDescription> + </CardHeader> + <CardContent> + <Suspense fallback={<div>Loading...</div>}> + <LoginForm /> + </Suspense> + <div className="mt-4 text-center text-sm"> + Don't have an account?{" "} + <Link href="/auth/signup" className="underline"> + Sign up + </Link> + </div> + </CardContent> + </Card> + </div> + ); +} diff --git a/apps/web/src/app/auth/signup/page.tsx b/apps/web/src/app/auth/signup/page.tsx new file mode 100644 index 00000000..f328851c --- /dev/null +++ b/apps/web/src/app/auth/signup/page.tsx @@ -0,0 +1,108 @@ +"use client"; + +import { useSearchParams, useRouter } from "next/navigation"; +import { authClient } from "@/lib/auth-client"; +import { Button } from "@/components/ui/button"; +import { + Card, + CardContent, + CardDescription, + CardHeader, + CardTitle, +} from "@/components/ui/card"; +import { Suspense, useState } from "react"; +import { Input } from "@/components/ui/input"; +import { Label } from "@/components/ui/label"; +import Link from "next/link"; +import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert"; + +function SignUpForm() { + const searchParams = useSearchParams(); + const router = useRouter(); + const redirectUrl = searchParams.get("redirect"); + const [name, setName] = useState(""); + const [email, setEmail] = useState(""); + const [password, setPassword] = useState(""); + const [error, setError] = useState<string | null>(null); + + const handleSignUp = async () => { + setError(null); + const { error } = await authClient.signUp.email({ + name, + email, + password, + }); + + if (error) { + setError(error.message || "An unexpected error occurred."); + return; + } + + router.push(redirectUrl || "/"); + }; + + return ( + <div className="flex flex-col space-y-4"> + {error && ( + <Alert variant="destructive"> + <AlertTitle>Error</AlertTitle> + <AlertDescription>{error}</AlertDescription> + </Alert> + )} + <div className="space-y-2"> + <Label htmlFor="name">Name</Label> + <Input + id="name" + type="text" + placeholder="John Doe" + value={name} + onChange={(e) => setName(e.target.value)} + /> + </div> + <div className="space-y-2"> + <Label htmlFor="email">Email</Label> + <Input + id="email" + type="email" + placeholder="m@example.com" + value={email} + onChange={(e) => setEmail(e.target.value)} + /> + </div> + <div className="space-y-2"> + <Label htmlFor="password">Password</Label> + <Input + id="password" + type="password" + value={password} + onChange={(e) => setPassword(e.target.value)} + /> + </div> + <Button onClick={handleSignUp}>Sign Up</Button> + </div> + ); +} + +export default function SignUpPage() { + return ( + <div className="flex h-screen items-center justify-center"> + <Card className="w-[350px]"> + <CardHeader> + <CardTitle>Sign Up</CardTitle> + <CardDescription>Create an account to get started.</CardDescription> + </CardHeader> + <CardContent> + <Suspense fallback={<div>Loading...</div>}> + <SignUpForm /> + </Suspense> + <div className="mt-4 text-center text-sm"> + Already have an account?{" "} + <Link href="/auth/login" className="underline"> + Login + </Link> + </div> + </CardContent> + </Card> + </div> + ); +} diff --git a/apps/web/src/lib/auth.ts b/apps/web/src/lib/auth.ts index d381e6a1..4cfaa6f2 100644 --- a/apps/web/src/lib/auth.ts +++ b/apps/web/src/lib/auth.ts @@ -14,7 +14,7 @@ export const auth = betterAuth({ },
},
emailAndPassword: {
- enabled: false,
+ enabled: true,
},
appName: "OpenCut",
trustedOrigins: ["http://localhost:3000"],
|
