Skip to content
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

chore: sync main branch with live #724

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 3 additions & 5 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,10 @@ OPENAI_API_KEY=****
# Generate a random secret: https://generate-secret.vercel.app/32 or `openssl rand -base64 32`
AUTH_SECRET=****

/*
* The following keys below are automatically created and
* added to your environment when you deploy on vercel
*/
# The following keys below are automatically created and
# added to your environment when you deploy on vercel

# Instructions to create kv database here: https://vercel.com/docs/storage/vercel-blob
# Instructions to create a Vercel Blob Store here: https://vercel.com/docs/storage/vercel-blob
BLOB_READ_WRITE_TOKEN=****

# Instructions to create a database here: https://vercel.com/docs/storage/vercel-postgres/quickstart
Expand Down
23 changes: 2 additions & 21 deletions .eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,29 +6,10 @@
"prettier",
"plugin:tailwindcss/recommended"
],
"plugins": ["import", "tailwindcss"],
"plugins": ["tailwindcss"],
"rules": {
"tailwindcss/no-custom-classname": "off",
"tailwindcss/classnames-order": "off",
"import/order": [
"error",
{
"groups": [
"builtin",
"external",
"internal",
["parent", "sibling"],
"index",
"object",
"type"
],
"newlines-between": "always",
"alphabetize": {
"order": "asc",
"caseInsensitive": true
}
}
]
"tailwindcss/classnames-order": "off"
},
"settings": {
"import/resolver": {
Expand Down
25 changes: 25 additions & 0 deletions .github/workflows/lint.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
name: Lint
on:
push:

jobs:
build:
runs-on: ubuntu-22.04
strategy:
matrix:
node-version: [20]
steps:
- uses: actions/checkout@v4
- name: Install pnpm
uses: pnpm/action-setup@v4
with:
version: 9
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
cache: 'pnpm'
- name: Install dependencies
run: pnpm install
- name: Run lint
run: pnpm lint
8 changes: 0 additions & 8 deletions ai/index.ts

This file was deleted.

63 changes: 31 additions & 32 deletions app/(auth)/actions.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
"use server";
'use server';

import { z } from "zod";
import { z } from 'zod';

import { createUser, getUser } from "@/db/queries";
import { createUser, getUser } from '@/lib/db/queries';

import { signIn } from "./auth";
import { signIn } from './auth';

const authFormSchema = z.object({
email: z.string().email(),
password: z.string().min(6),
});

export interface LoginActionState {
status: "idle" | "in_progress" | "success" | "failed" | "invalid_data";
status: 'idle' | 'in_progress' | 'success' | 'failed' | 'invalid_data';
}

export const login = async (
Expand All @@ -21,34 +21,34 @@ export const login = async (
): Promise<LoginActionState> => {
try {
const validatedData = authFormSchema.parse({
email: formData.get("email"),
password: formData.get("password"),
email: formData.get('email'),
password: formData.get('password'),
});

await signIn("credentials", {
await signIn('credentials', {
email: validatedData.email,
password: validatedData.password,
redirect: false,
});

return { status: "success" };
return { status: 'success' };
} catch (error) {
if (error instanceof z.ZodError) {
return { status: "invalid_data" };
return { status: 'invalid_data' };
}

return { status: "failed" };
return { status: 'failed' };
}
};

export interface RegisterActionState {
status:
| "idle"
| "in_progress"
| "success"
| "failed"
| "user_exists"
| "invalid_data";
| 'idle'
| 'in_progress'
| 'success'
| 'failed'
| 'user_exists'
| 'invalid_data';
}

export const register = async (
Expand All @@ -57,29 +57,28 @@ export const register = async (
): Promise<RegisterActionState> => {
try {
const validatedData = authFormSchema.parse({
email: formData.get("email"),
password: formData.get("password"),
email: formData.get('email'),
password: formData.get('password'),
});

let [user] = await getUser(validatedData.email);
const [user] = await getUser(validatedData.email);

if (user) {
return { status: "user_exists" } as RegisterActionState;
} else {
await createUser(validatedData.email, validatedData.password);
await signIn("credentials", {
email: validatedData.email,
password: validatedData.password,
redirect: false,
});

return { status: "success" };
return { status: 'user_exists' } as RegisterActionState;
}
await createUser(validatedData.email, validatedData.password);
await signIn('credentials', {
email: validatedData.email,
password: validatedData.password,
redirect: false,
});

return { status: 'success' };
} catch (error) {
if (error instanceof z.ZodError) {
return { status: "invalid_data" };
return { status: 'invalid_data' };
}

return { status: "failed" };
return { status: 'failed' };
}
};
2 changes: 1 addition & 1 deletion app/(auth)/api/auth/[...nextauth]/route.ts
Original file line number Diff line number Diff line change
@@ -1 +1 @@
export { GET, POST } from "@/app/(auth)/auth";
export { GET, POST } from '@/app/(auth)/auth';
14 changes: 7 additions & 7 deletions app/(auth)/auth.config.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { NextAuthConfig } from "next-auth";
import type { NextAuthConfig } from 'next-auth';

export const authConfig = {
pages: {
signIn: "/login",
newUser: "/",
signIn: '/login',
newUser: '/',
},
providers: [
// added later in auth.ts since it requires bcrypt which is only compatible with Node.js
Expand All @@ -12,12 +12,12 @@ export const authConfig = {
callbacks: {
authorized({ auth, request: { nextUrl } }) {
let isLoggedIn = !!auth?.user;
let isOnChat = nextUrl.pathname.startsWith("/");
let isOnRegister = nextUrl.pathname.startsWith("/register");
let isOnLogin = nextUrl.pathname.startsWith("/login");
// let isOnChat = nextUrl.pathname.startsWith("/");
let isOnRegister = nextUrl.pathname.startsWith('/register');
let isOnLogin = nextUrl.pathname.startsWith('/login');

if (isLoggedIn && (isOnLogin || isOnRegister)) {
return Response.redirect(new URL("/", nextUrl));
return Response.redirect(new URL('/', nextUrl as unknown as URL));
}

if (isOnRegister || isOnLogin) {
Expand Down
18 changes: 10 additions & 8 deletions app/(auth)/auth.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { compare } from "bcrypt-ts";
import NextAuth, { User, Session } from "next-auth";
import Credentials from "next-auth/providers/credentials";
import { compare } from 'bcrypt-ts';
import NextAuth, { type User, type Session } from 'next-auth';
import Credentials from 'next-auth/providers/credentials';

import { getUser } from "@/db/queries";
import { getUser } from '@/lib/db/queries';

import { authConfig } from "./auth.config";
import { authConfig } from './auth.config';

interface ExtendedSession extends Session {
user: User;
Expand All @@ -21,10 +21,12 @@ export const {
Credentials({
credentials: {},
async authorize({ email, password }: any) {
let users = await getUser(email);
const users = await getUser(email);
if (users.length === 0) return null;
let passwordsMatch = await compare(password, users[0].password!);
if (passwordsMatch) return users[0] as any;
// biome-ignore lint: Forbidden non-null assertion.
const passwordsMatch = await compare(password, users[0].password!);
if (!passwordsMatch) return null;
return users[0] as any;
},
}),
],
Expand Down
40 changes: 21 additions & 19 deletions app/(auth)/login/page.tsx
Original file line number Diff line number Diff line change
@@ -1,44 +1,46 @@
"use client";
'use client';

import Link from "next/link";
import { useRouter } from "next/navigation";
import { useActionState, useEffect, useState } from "react";
import { toast } from "sonner";
import Link from 'next/link';
import { useRouter } from 'next/navigation';
import { useActionState, useEffect, useState } from 'react';
import { toast } from 'sonner';

import { AuthForm } from "@/components/custom/auth-form";
import { SubmitButton } from "@/components/custom/submit-button";
import { AuthForm } from '@/components/auth-form';
import { SubmitButton } from '@/components/submit-button';

import { login, LoginActionState } from "../actions";
import { login, type LoginActionState } from '../actions';

export default function Page() {
const router = useRouter();

const [email, setEmail] = useState("");
const [email, setEmail] = useState('');
const [isSuccessful, setIsSuccessful] = useState(false);

const [state, formAction] = useActionState<LoginActionState, FormData>(
login,
{
status: "idle",
status: 'idle',
},
);

useEffect(() => {
if (state.status === "failed") {
toast.error("Invalid credentials!");
} else if (state.status === "invalid_data") {
toast.error("Failed validating your submission!");
} else if (state.status === "success") {
if (state.status === 'failed') {
toast.error('Invalid credentials!');
} else if (state.status === 'invalid_data') {
toast.error('Failed validating your submission!');
} else if (state.status === 'success') {
setIsSuccessful(true);
router.refresh();
}
}, [state.status, router]);

const handleSubmit = (formData: FormData) => {
setEmail(formData.get("email") as string);
setEmail(formData.get('email') as string);
formAction(formData);
};

return (
<div className="flex h-screen w-screen items-center justify-center bg-background">
<div className="flex h-dvh w-screen items-start pt-12 md:pt-0 md:items-center justify-center bg-background">
<div className="w-full max-w-md overflow-hidden rounded-2xl flex flex-col gap-12">
<div className="flex flex-col items-center justify-center gap-2 px-4 text-center sm:px-16">
<h3 className="text-xl font-semibold dark:text-zinc-50">Sign In</h3>
Expand All @@ -47,7 +49,7 @@ export default function Page() {
</p>
</div>
<AuthForm action={handleSubmit} defaultEmail={email}>
<SubmitButton>Sign in</SubmitButton>
<SubmitButton isSuccessful={isSuccessful}>Sign in</SubmitButton>
<p className="text-center text-sm text-gray-600 mt-4 dark:text-zinc-400">
{"Don't have an account? "}
<Link
Expand All @@ -56,7 +58,7 @@ export default function Page() {
>
Sign up
</Link>
{" for free."}
{' for free.'}
</p>
</AuthForm>
</div>
Expand Down
Loading