diff --git a/src/app/(editor)/page.tsx b/src/app/(editor)/page.tsx index e455b64..0159d1e 100644 --- a/src/app/(editor)/page.tsx +++ b/src/app/(editor)/page.tsx @@ -1,5 +1,5 @@ import Link from "next/link"; -import Editor from "@/components/Editor"; +import Editor from "@/components/editor/Editor"; export default function Home() { return ( diff --git a/src/app/(noneditor)/about/page.tsx b/src/app/(noneditor)/about/page.tsx index 5da116f..526e2fa 100644 --- a/src/app/(noneditor)/about/page.tsx +++ b/src/app/(noneditor)/about/page.tsx @@ -1,7 +1,7 @@ import { Metadata } from "next"; export const metadata: Metadata = { - title: "Rhyme – About", + title: "About rhyme", description: "Information about the Rhyme and its creators", }; diff --git a/src/app/(noneditor)/auth/page.tsx b/src/app/(noneditor)/auth/page.tsx new file mode 100644 index 0000000..6a43a88 --- /dev/null +++ b/src/app/(noneditor)/auth/page.tsx @@ -0,0 +1,24 @@ +import { Metadata } from "next"; +import AuthForm from "@/components/forms/AuthForm"; + +export const metadata: Metadata = { + title: "Authenticate - rhyme", + description: "Register or log into Rhyme account to save, show and load notes", +}; + +export default function About() { + return ( + <> +
+

Authenticate

+

Please, use this form to log in/register:

+ +
+
+

Welcome to rhyme!

+

Free service for writing and saving notes, lyrics, poetry, etc...

+

Made with ❤️ by Kirill Siukhin

+
+ + ); +} diff --git a/src/app/(noneditor)/login/page.tsx b/src/app/(noneditor)/login/page.tsx deleted file mode 100644 index b8fec5e..0000000 --- a/src/app/(noneditor)/login/page.tsx +++ /dev/null @@ -1,16 +0,0 @@ -import { Metadata } from "next"; -import LoginForm from "@/components/forms/LoginForm"; - -export const metadata: Metadata = { - title: "Rhyme – Log In", - description: "Log into Rhyme account to save, show and load notes", -}; - -export default function About() { - return ( - <> -

Login Page

- - - ); -} diff --git a/src/app/actions.ts b/src/app/actions.ts index 4541230..58e9e2a 100644 --- a/src/app/actions.ts +++ b/src/app/actions.ts @@ -10,7 +10,7 @@ import bcrypt from "bcrypt"; const JWT_SECRET = process.env.JWT_SECRET!; -export async function login(_prevState: unknown, formData: FormData) { +export async function authenticate(_prevState: unknown, formData: FormData) { const username = formData.get("username") as string; const password = formData.get("password") as string; @@ -19,43 +19,13 @@ export async function login(_prevState: unknown, formData: FormData) { .where(eq(usersTable.username, username)); if (users.length === 0) { - return { error: "Invalid username or password" }; + await db.insert(usersTable).values({ + username, + password: bcrypt.hashSync(password, bcrypt.genSaltSync()), + }); + } else if (!bcrypt.compareSync(password, users[0].password)) { + return { error: "Invalid password or username is already taken" }; } - - const user = users[0]; - - if (!bcrypt.compareSync(password, user.password)) { - return { error: "Invalid username or password" }; - } - - const token = jwt.sign({ sub: user.username }, JWT_SECRET, { expiresIn: "7d" }); - - const cookieStore = await cookies(); - cookieStore.set("session", token, { - httpOnly: true, - path: "/", - maxAge: 60 * 60 * 24 * 7, - }); - - redirect("/"); -} - -export async function register(_prevState: unknown, formData: FormData) { - const username = formData.get("username") as string; - const password = formData.get("password") as string; - - const users = await db.select() - .from(usersTable) - .where(eq(usersTable.username, username)); - - if (users.length !== 0) { - return { error: "Username already taken!" }; - } - - await db.insert(usersTable).values({ - username, - password: bcrypt.hashSync(password, bcrypt.genSaltSync()), - }); const token = jwt.sign({ sub: username }, JWT_SECRET, { expiresIn: "7d" }); diff --git a/src/app/favicon.ico b/src/app/favicon.ico index 718d6fe..3a09c97 100644 Binary files a/src/app/favicon.ico and b/src/app/favicon.ico differ diff --git a/src/components/Header.tsx b/src/components/Header.tsx index 561c308..e01553b 100644 --- a/src/components/Header.tsx +++ b/src/components/Header.tsx @@ -21,7 +21,7 @@ export default function Header({ showToolbar = true }: { showToolbar?: boolean } } /> - + } /> diff --git a/src/components/Block.tsx b/src/components/editor/Block.tsx similarity index 97% rename from src/components/Block.tsx rename to src/components/editor/Block.tsx index 51aea34..317d24f 100644 --- a/src/components/Block.tsx +++ b/src/components/editor/Block.tsx @@ -3,7 +3,7 @@ import { ChangeEvent } from "react"; import { Lock, LockOpen, Menu, Minus, Plus, X } from "lucide-react"; import { Action, IBlock, ILine } from "@/lib/editorReducer"; -import IconOnlyButton from "./IconOnlyButton"; +import IconOnlyButton from "../ui/IconOnlyButton"; import LineInput from "./LineInput"; export default function Block({ diff --git a/src/components/Editor.tsx b/src/components/editor/Editor.tsx similarity index 94% rename from src/components/Editor.tsx rename to src/components/editor/Editor.tsx index 4aa567e..dce59c7 100644 --- a/src/components/Editor.tsx +++ b/src/components/editor/Editor.tsx @@ -3,7 +3,7 @@ import { useReducer } from "react"; import { Plus } from "lucide-react"; import { editorReducer } from "@/lib/editorReducer"; -import IconOnlyButton from "./IconOnlyButton"; +import IconOnlyButton from "../ui/IconOnlyButton"; import Block from "./Block"; export default function Editor() { diff --git a/src/components/LineInput.tsx b/src/components/editor/LineInput.tsx similarity index 100% rename from src/components/LineInput.tsx rename to src/components/editor/LineInput.tsx diff --git a/src/components/forms/AuthForm.tsx b/src/components/forms/AuthForm.tsx new file mode 100644 index 0000000..8d26e68 --- /dev/null +++ b/src/components/forms/AuthForm.tsx @@ -0,0 +1,17 @@ +"use client"; + +import { useActionState } from "react"; +import { authenticate } from "@/app/actions"; + +export default function AuthForm() { + const [state, formAction] = useActionState(authenticate, null); + + return ( +
+ + + + {state?.error &&

{state.error}

} +
+ ); +} diff --git a/src/components/forms/LoginForm.tsx b/src/components/forms/LoginForm.tsx deleted file mode 100644 index 1e5db96..0000000 --- a/src/components/forms/LoginForm.tsx +++ /dev/null @@ -1,17 +0,0 @@ -"use client"; - -import { useActionState } from "react"; -import { login } from "@/app/actions"; - -export default function LoginForm() { - const [state, formAction] = useActionState(login, null); - - return ( -
- - - - {state?.error &&

{state.error}

} -
- ); -} diff --git a/src/components/IconOnlyButton.tsx b/src/components/ui/IconOnlyButton.tsx similarity index 100% rename from src/components/IconOnlyButton.tsx rename to src/components/ui/IconOnlyButton.tsx diff --git a/src/lib/auth.ts b/src/lib/auth.ts new file mode 100644 index 0000000..b4eeecb --- /dev/null +++ b/src/lib/auth.ts @@ -0,0 +1,11 @@ +import { cookies } from "next/headers"; + +export async function getAuth() { + const cookieStore = await cookies(); + const token = cookieStore.get("session")?.value; + if (!token) { + return null; + } + + // TODO +}