diff --git a/.env.example b/.env.example index 25e5e7a..2b4799a 100644 --- a/.env.example +++ b/.env.example @@ -1,11 +1,4 @@ -# Environment variables declared in this file are automatically made available to Prisma. -# See the documentation for more detail: https://pris.ly/d/prisma-schema#accessing-environment-variables-from-the-schema - -# Prisma supports the native connection string format for PostgreSQL, MySQL, SQLite, SQL Server, MongoDB and CockroachDB. -# See the documentation for all the connection string options: https://pris.ly/d/connection-strings - -DATABASE_URL="postgresql://..." NEXT_PUBLIC_SUPABASE_URL=YOUR_SUPABASE_URL NEXT_PUBLIC_SUPABASE_ANON_KEY=YOUR_SUPABASE_ANON_KEY -PROJECT_ID=SUPABASE_REFERENCE_ID \ No newline at end of file +PROJECT_ID=SUPABASE_REFERENCE_ID diff --git a/components/Auth.tsx b/components/Auth.tsx new file mode 100644 index 0000000..c4b9f21 --- /dev/null +++ b/components/Auth.tsx @@ -0,0 +1,74 @@ +import { Dispatch, SetStateAction } from 'react'; +import Login from './forms/Login'; +import Register from './forms/Register'; +import PasswordReset from './forms/PasswordReset'; +import { useState } from 'react'; + +export default function Auth({ + type, + setAuthType, +}: { + type: 'login' | 'register' | 'resetPassword'; + setAuthType: Dispatch>; +}) { + const [serverError, setServerError] = useState(null); + + return ( +
+

+ {serverError ?? serverError} +

+
+ {renderAuth(type, setServerError, setAuthType)} +
+ {type == 'login' ? ( +
+
+ Not registered?{' '} + setAuthType('register')} + > + Sign up now + +
+
+ ) : ( +
+
+ Have an account?{' '} + setAuthType('login')} + > + Login here + +
+
+ )} +
+ ); +} + +function renderAuth( + type: 'login' | 'register' | 'resetPassword', + setServerError: Dispatch>, + setAuthType: Dispatch> +) { + switch (type) { + case 'login': + return ( + + ); + case 'register': + return ( + + ); + case 'resetPassword': + return ; + default: + return ( + + ); + } +} diff --git a/components/deletemewhen.txt b/components/deletemewhen.txt deleted file mode 100644 index d8f0cc0..0000000 --- a/components/deletemewhen.txt +++ /dev/null @@ -1 +0,0 @@ -delete me when you add and commit another file to this directory. This file should only exist if the directory is empty in order to maintain directory/project structure \ No newline at end of file diff --git a/components/forms/Login.tsx b/components/forms/Login.tsx new file mode 100644 index 0000000..c8f6676 --- /dev/null +++ b/components/forms/Login.tsx @@ -0,0 +1,117 @@ +import { Dispatch, SetStateAction } from 'react'; +import styles from '@/styles/Auth.module.css'; +import { + createSessionSchema, + CreateSessionInput, +} from '@/types/client/session'; +import { useForm } from 'react-hook-form'; +import { zodResolver } from '@hookform/resolvers/zod'; +import { useRouter } from 'next/router'; +import { useSupabaseClient } from '@supabase/auth-helpers-react'; +import { Database } from '@/types/database.supabase'; +import { Input } from './Styles'; +import EmailIcon from '../icons/EmailIcon'; +import PasswordIcon from '../icons/PasswordIcon'; + +export default function Login({ + setServerError, + setAuthType, +}: { + setServerError: Dispatch>; + setAuthType: Dispatch>; +}) { + const router = useRouter(); + const supabase = useSupabaseClient(); + const { + register, + handleSubmit, + formState: { errors, isSubmitting }, + } = useForm({ + resolver: zodResolver(createSessionSchema), + mode: 'onChange' + }); + + const onSubmit = async (formData: CreateSessionInput) => { + const { data, error } = await supabase.auth.signInWithPassword({ + email: formData.email, + password: formData.password, + }); + + if (error) { + setServerError(error.message); + setTimeout(() => { + setServerError(null); + }, 7000); + } + if (data && !error) { + router.push('/'); + } + }; + return ( +
+
+
+ +
+ + {errors.email && ( +

+ {errors.email.message} +

+ )} +
+ +
+
+ +
+ + {errors.password && ( +

+ {errors.password.message} +

+ )} +
+ setAuthType('resetPassword')} + className="text-sm mt-3 text-frost-800 font-bold hover:text-frost-600 hover:cursor-pointer" + > + Forgot password? + + +
+ +
+
+ ); +} diff --git a/components/forms/PasswordReset.tsx b/components/forms/PasswordReset.tsx new file mode 100644 index 0000000..369523a --- /dev/null +++ b/components/forms/PasswordReset.tsx @@ -0,0 +1,101 @@ +import { Dispatch, SetStateAction } from 'react'; +import { Input } from './Styles'; +import styles from '@/styles/Auth.module.css'; +import { CreatePasswordInputRecovery, createPasswordRecoverySchema } from '@/types/client/passwordRecovery'; +import { useForm } from 'react-hook-form'; +import { zodResolver } from '@hookform/resolvers/zod'; +import { useSupabaseClient } from '@supabase/auth-helpers-react'; +import { Database } from '@/types/database.supabase'; +import { toast } from 'react-toastify'; +import EmailIcon from '../icons/EmailIcon'; + +export default function PasswordReset({ + setServerError, + setAuthType, +}: { + setServerError: Dispatch>; + setAuthType: Dispatch>; +}) { + + const supabase = useSupabaseClient(); + + const { + register, + handleSubmit, + formState: { errors, isSubmitting }, + } = useForm({ + resolver: zodResolver(createPasswordRecoverySchema), + mode: 'onChange' + }); + + + const onSubmit = async (formData: CreatePasswordInputRecovery) => { + const { data, error } = await supabase.auth.resetPasswordForEmail(formData.email, { + redirectTo: 'http://localhost:3000/passwordreset', + }); + + if (error) { + setServerError(error.message); + setTimeout(() => { + setServerError(null); + }, 7000); + } + if (data && !error) { + toast.success(`An email has been sent to ${formData.email}`, { + position: 'top-center', + autoClose: 3000 + }); + setAuthType('login'); + } + }; + + return ( +
+
Password Recovery
+ +
+ +
+ +
+ + {errors.email && ( +

+ {errors.email.message} +

+ )} +
+ +
+ +
+
+ ); +} diff --git a/components/forms/Register.tsx b/components/forms/Register.tsx new file mode 100644 index 0000000..757968a --- /dev/null +++ b/components/forms/Register.tsx @@ -0,0 +1,152 @@ +import { Dispatch, SetStateAction } from 'react'; +import styles from '@/styles/Auth.module.css'; +import { createUserSchema, CreateUserInput } from '@/types/client/user'; +import { zodResolver } from '@hookform/resolvers/zod'; +import { useForm } from 'react-hook-form'; +import { useSupabaseClient } from '@supabase/auth-helpers-react'; +import { Database } from '@/types/database.supabase'; +import { Input } from './Styles'; +import EmailIcon from '../icons/EmailIcon'; +import PasswordIcon from '../icons/PasswordIcon'; +import UsernameIcon from '../icons/UsernameIcon'; + +export default function Register({ + setServerError, + setAuthType, +}: { + setServerError: Dispatch>; + setAuthType: Dispatch>; +}) { + const supabase = useSupabaseClient(); + const { + register, + handleSubmit, + formState: { errors, isSubmitting }, + } = useForm({ + resolver: zodResolver(createUserSchema), + mode: 'onChange' + }); + + const onSubmit = async (formData: CreateUserInput) => { + const { data, error } = await supabase.auth.signUp({ + email: formData.email, + password: formData.password, + options: { + data: { + username: formData.username, + }, + }, + }); + if (error) { + setServerError(error.message); + setTimeout(() => { + setServerError(null); + }, 7000); + } + if (data && !error) { + setAuthType('login'); + } + }; + + return ( +
+
+
+ +
+ + {errors.username && ( + + {errors.username.message} + + )} +
+
+
+ +
+ + {errors.email && ( +

+ {errors.email.message} +

+ )} +
+ +
+
+ +
+ + {errors.password && ( +

+ {errors.password.message} +

+ )} +
+ +
+
+ +
+ + {errors.passwordConfirmation && ( +

+ {errors.passwordConfirmation.message} +

+ )} +
+
+ +
+
+ ); +} diff --git a/components/forms/Styles.ts b/components/forms/Styles.ts new file mode 100644 index 0000000..a358299 --- /dev/null +++ b/components/forms/Styles.ts @@ -0,0 +1,16 @@ +export const Input = `w-full +py-2 +pl-6 +self-start +text-base +font-normal +placeholder:text-white +placeholder:opacity-80 +rounded-2xl +transition +ease-in-out +focus:outline-frost-50 +m-0 +focus:outline-none +bg-inherit +flex-1`; diff --git a/components/forms/UpdatePassword.tsx b/components/forms/UpdatePassword.tsx new file mode 100644 index 0000000..666ecfb --- /dev/null +++ b/components/forms/UpdatePassword.tsx @@ -0,0 +1,116 @@ +import { Input } from './Styles'; +import styles from '@/styles/Auth.module.css'; +import { createUpdatePasswordSchema, CreateUpdatePasswordInput } from '@/types/client/updatePassword'; +import { useForm } from 'react-hook-form'; +import { zodResolver } from '@hookform/resolvers/zod'; +import { Database } from '@/types/database.supabase'; +import { useSupabaseClient } from '@supabase/auth-helpers-react'; +import { Dispatch, SetStateAction } from 'react'; +import { toast } from 'react-toastify'; +import { useRouter } from 'next/router'; +import PasswordIcon from '../icons/PasswordIcon'; + +export default function UpdatePassword({setServerError} : {setServerError: Dispatch>;}) { + const router = useRouter(); + const supabase = useSupabaseClient(); + + const { + register, + handleSubmit, + formState: { errors, isSubmitting }, + } = useForm({ + resolver: zodResolver(createUpdatePasswordSchema), + mode: 'onChange' + }); + + const onSubmit = async (formData: CreateUpdatePasswordInput) => { + const { data, error } = await supabase.auth.updateUser({ + password: formData.password + }); + + if (error) { + setServerError(error.message); + setTimeout(() => { + setServerError(null); + }, 7000); + } + if (data && !error) { + toast.success('Password successfully updated', { + position: 'top-center', + autoClose: 3000 + }); + router.push('/'); + } + }; + + + return ( +
+ +
+
+ +
+ + {errors.password && ( +

+ {errors.password.message} +

+ )} +
+ +
+
+ +
+ + {errors.passwordConfirmation && ( +

+ {errors.passwordConfirmation.message} +

+ )} +
+ +
+ +
+
+ ); +} diff --git a/components/icons/EmailIcon.tsx b/components/icons/EmailIcon.tsx new file mode 100644 index 0000000..ba1f99d --- /dev/null +++ b/components/icons/EmailIcon.tsx @@ -0,0 +1,14 @@ +export default function EmailIcon() { + return ( + + ); +} diff --git a/components/icons/PasswordIcon.tsx b/components/icons/PasswordIcon.tsx new file mode 100644 index 0000000..6d02a6f --- /dev/null +++ b/components/icons/PasswordIcon.tsx @@ -0,0 +1,14 @@ +export default function PasswordIcon() { + return ( + + ); +} diff --git a/components/icons/UsernameIcon.tsx b/components/icons/UsernameIcon.tsx new file mode 100644 index 0000000..7f32a8c --- /dev/null +++ b/components/icons/UsernameIcon.tsx @@ -0,0 +1,14 @@ +export default function UsernameIcon() { + return ( + + ); +} diff --git a/middleware.ts b/middleware.ts index 6409118..075e114 100644 --- a/middleware.ts +++ b/middleware.ts @@ -5,14 +5,18 @@ import type { NextRequest } from 'next/server'; export async function middleware(req: NextRequest) { // We need to create a response and hand it to the supabase client to be able to modify the response headers. const res = NextResponse.next(); + // Forward req if User tries to reset password, authorization will happen on the client + if (req.nextUrl.pathname == '/passwordreset') return res; // Create authenticated Supabase Client. const supabase = createMiddlewareSupabaseClient({ req, res }); // Check if we have a session + const { data: { session }, } = await supabase.auth.getSession(); const redirectUrl = req.nextUrl.clone(); + // Check auth condition if (session?.user) { // Authentication successful, forward request to protected route. @@ -22,7 +26,6 @@ export async function middleware(req: NextRequest) { redirectUrl.pathname = '/'; return NextResponse.redirect(redirectUrl); } - return res; } diff --git a/package.json b/package.json index b332716..2ca86f4 100644 --- a/package.json +++ b/package.json @@ -26,6 +26,7 @@ "react": "18.2.0", "react-dom": "18.2.0", "react-hook-form": "^7.42.1", + "react-toastify": "^9.1.1", "swr": "^2.0.1", "typescript": "4.9.4", "zod": "^3.20.2" @@ -36,7 +37,6 @@ "autoprefixer": "^10.4.13", "eslint": "^8.32.0", "postcss": "^8.4.21", - "prisma": "^4.8.1", "supabase": "^1.33.0", "tailwindcss": "^3.2.4", "typescript": "^4.9.4" diff --git a/pages/_404/[...path].tsx b/pages/_404/[...path].tsx index 53ce545..4a3c42e 100644 --- a/pages/_404/[...path].tsx +++ b/pages/_404/[...path].tsx @@ -1,7 +1,7 @@ export const Custom404 = () =>
; export const getServerSideProps = () => { - return { redirect: { destination: '/home', permanent: false } }; + return { redirect: { destination: '/', permanent: false } }; }; export default Custom404; diff --git a/pages/_app.tsx b/pages/_app.tsx index 2f8ef03..64e095d 100644 --- a/pages/_app.tsx +++ b/pages/_app.tsx @@ -2,8 +2,10 @@ import '@/styles/globals.css'; import { createBrowserSupabaseClient } from '@supabase/auth-helpers-nextjs'; import { SessionContextProvider, Session } from '@supabase/auth-helpers-react'; import { AppProps } from 'next/app'; -import { useState } from 'react'; import { Source_Sans_3 } from '@next/font/google'; +import { ToastContainer } from 'react-toastify'; +import 'react-toastify/dist/ReactToastify.css'; +import { Database } from '@/types/database.supabase'; const sourceSans3 = Source_Sans_3({ subsets: ['latin'] }); @@ -13,13 +15,15 @@ function App({ }: AppProps<{ initialSession: Session; }>) { - const [supabase] = useState(() => createBrowserSupabaseClient()); + + const supabase = createBrowserSupabaseClient(); return ( +
diff --git a/pages/index.tsx b/pages/index.tsx index 4762a80..58cd82a 100644 --- a/pages/index.tsx +++ b/pages/index.tsx @@ -4,12 +4,21 @@ import { Inter } from '@next/font/google'; import styles from '@/styles/Home.module.css'; import { Auth, ThemeSupa } from '@supabase/auth-ui-react'; import { useUser, useSupabaseClient } from '@supabase/auth-helpers-react'; +import { useRouter } from 'next/router'; const inter = Inter({ subsets: ['latin'] }); export default function Home() { const user = useUser(); const supabase = useSupabaseClient(); + const router = useRouter(); + + const handleLogout = async () => { + const { error } = await supabase.auth.signOut(); + if (error) console.log(error); + router.push('/login'); + }; + return ( <> @@ -72,7 +81,12 @@ export default function Home() { theme="dark" /> ) : ( -

Account page will go here.

+ )} diff --git a/pages/login.tsx b/pages/login.tsx index 4d8814d..34e8d30 100644 --- a/pages/login.tsx +++ b/pages/login.tsx @@ -1,10 +1,15 @@ -import { useUser, useSupabaseClient } from '@supabase/auth-helpers-react'; import Head from 'next/head'; +import styles from '@/styles/Auth.module.css'; +import Auth from '@/components/Auth'; +import { useState } from 'react'; +import Image from 'next/image'; +import Yeti from '../public/Yeti.png'; export default function Login() { - const user = useUser(); - const supabase = useSupabaseClient(); - // + const [authType, setAuthType] = useState< + 'login' | 'register' | 'resetPassword' + >('login'); + return ( <> @@ -13,7 +18,40 @@ export default function Login() { - login page + +
+
+
+
+
+
FrostCord
+
+
+ Yeti +
+
+
+
+ FrostCord Logo +
+ +
+
+
+
); } diff --git a/pages/passwordreset.tsx b/pages/passwordreset.tsx new file mode 100644 index 0000000..d583a1c --- /dev/null +++ b/pages/passwordreset.tsx @@ -0,0 +1,84 @@ +import { useState } from 'react'; +import { useUser } from '@supabase/auth-helpers-react'; +import styles from '@/styles/Auth.module.css'; +import UpdatePassword from '@/components/forms/UpdatePassword'; +import { useRouter } from 'next/router'; + +export default function Passwordreset() { + + const user = useUser(); + const router = useRouter(); + const [serverError, setServerError] = useState(null); + + if (!user) { + return ( +
+
+
+
+
+
Unauthorized
+
return to router.push('/login')}>login
+
+
+
+
+
+ ); + } + + return ( +
+
+
+
+
+
Update Password
+

+ {serverError ?? serverError} +

+ +
+
+
+
+
+ ); +} diff --git a/prisma/schema.prisma b/prisma/schema.prisma deleted file mode 100644 index d205f42..0000000 --- a/prisma/schema.prisma +++ /dev/null @@ -1,11 +0,0 @@ -// This is your Prisma schema file, -// learn more about it in the docs: https://pris.ly/d/prisma-schema - -generator client { - provider = "prisma-client-js" -} - -datasource db { - provider = "postgresql" - url = env("DATABASE_URL") -} diff --git a/public/Yeti.png b/public/Yeti.png new file mode 100644 index 0000000..62f6353 Binary files /dev/null and b/public/Yeti.png differ diff --git a/public/bubbles.svg b/public/bubbles.svg new file mode 100644 index 0000000..2e79530 --- /dev/null +++ b/public/bubbles.svg @@ -0,0 +1,12 @@ + + + + bubbles + Created with Sketch. + + + + + + + diff --git a/styles/Auth.module.css b/styles/Auth.module.css new file mode 100644 index 0000000..b147c80 --- /dev/null +++ b/styles/Auth.module.css @@ -0,0 +1,122 @@ +@media only screen and (min-width: 1280px) { + .authMD { + display: grid; + grid-template-columns: + repeat(3, minmax(16px, 1fr)) minmax(0, 192px) minmax(0, 192px) minmax( + 0, + 192px + ) + minmax(0, 128px) minmax(0, 192px) minmax(0, 128px) + repeat(3, minmax(16px, 1fr)); + + grid-template-rows: minmax(8px, 1fr) minmax(640px, 768px) minmax(32px, 1fr); + } + + .rightSide { + background-image: linear-gradient( + to top left, + hsla(197, 79%, 74%, 10%), + hsla(197, 81%, 39%, 10%) + ), + linear-gradient( + to top, + hsla(197, 79%, 74%, 100%), + hsla(197, 81%, 39%, 70%) + ), + url('../public/bubbles.svg'), + linear-gradient( + to top, + hsla(197, 79%, 74%, 90%), + hsla(197, 81%, 39%, 100%) + ); + box-shadow: inset 0 10px 4px hsla(197, 81%, 44%, 30%), + 17px 17px 4px hsla(0, 0%, 18%, 0.749), + 18px 25px 20px hsla(0, 0%, 21%, 0.749), + -12px 1px 4px hsla(0, 0%, 21%, 0.749); + } +} + +@media only screen and (min-width: 1280px) { + .resetPWMD { + display: grid; + grid-template-columns: + repeat(3, minmax(16px, 1fr)) minmax(0, 192px) minmax(0, 192px) minmax( + 0, + 128px + ) + minmax(0, 128px) minmax(0, 192px) minmax(0, 128px) + repeat(3, minmax(16px, 1fr)); + + grid-template-rows: minmax(8px, 1fr) minmax(640px, 1fr) minmax(32px, 1fr); + } + + .mainContainer { + filter: drop-shadow(0 2px 10px rgba(51, 50, 50, 0.904)) + drop-shadow(0 10px 8px rgba(75, 73, 73, 0.08)); + background-color: #354045; + } +} + +@media only screen and (max-width: 1280px) { + .mainBackground { + background-image: linear-gradient( + to top left, + hsla(197, 79%, 74%, 10%), + hsla(197, 81%, 39%, 10%) + ), + linear-gradient( + to top, + hsla(197, 79%, 74%, 100%), + hsla(197, 81%, 39%, 70%) + ), + url('../public/bubbles.svg'), + linear-gradient( + to top, + hsla(197, 79%, 74%, 90%), + hsla(197, 81%, 39%, 100%) + ); + box-shadow: inset 0 10px 4px hsla(197, 81%, 44%, 30%), + 17px 17px 4px hsla(0, 0%, 18%, 0.749), + 18px 25px 20px hsla(0, 0%, 21%, 0.749); + } +} + +.icon { + padding: 8px; + margin-left: -1px; + position: absolute; + box-sizing: border-box; + top: 50%; + left: 4px; + transform: translateY(-50%); +} + +.iconError { + padding: 8px; + position: absolute; + box-sizing: border-box; + top: 32%; + left: 4px; + transform: translateY(-50%); +} + +.button { + box-shadow: inset 0 4px 3px hsl(194, 71%, 48%), + 2px 4px 2px hsla(0, 0%, 0%, 0.2), 3px 10px 7px hsla(0, 1%, 28%, 0.2); +} + +.input { + box-shadow: inset 0 5px 2px hsla(0, 0%, 0%, 0.1), + 1px 5px 4px hsla(0, 1%, 90%, 0.18), inset 0 4px 1px hsla(0, 0%, 0%, 0.1), + inset -4px 0 4px hsla(0, 0%, 0%, 0.1); +} + +.frostCord { + filter: drop-shadow(0 2px 3px rgba(237, 232, 232, 0.904)) + drop-shadow(0 10px 8px rgba(227, 214, 214, 0.08)); +} + +.logo { + box-shadow: inset 0 2px 1px hsl(194, 71%, 50%), + 2px 10px 8px hsla(0, 0%, 16%, 0.649), 5px 20px 20px hsla(0, 0%, 16%, 0.649); +} diff --git a/types/client/passwordRecovery.ts b/types/client/passwordRecovery.ts new file mode 100644 index 0000000..b639014 --- /dev/null +++ b/types/client/passwordRecovery.ts @@ -0,0 +1,7 @@ +import { object, TypeOf, z } from 'zod'; + +export const createPasswordRecoverySchema = object({ + email: z.string().min(1, { message: 'Email is required' }), +}); + +export type CreatePasswordInputRecovery = TypeOf; diff --git a/types/client/session.ts b/types/client/session.ts new file mode 100644 index 0000000..21b06b6 --- /dev/null +++ b/types/client/session.ts @@ -0,0 +1,8 @@ +import { object, TypeOf, z } from 'zod'; + +export const createSessionSchema = object({ + email: z.string().min(1, { message: 'Email is required' }), + password: z.string().min(1, { message: 'Password is required' }), +}); + +export type CreateSessionInput = TypeOf; diff --git a/types/client/updatePassword.ts b/types/client/updatePassword.ts new file mode 100644 index 0000000..6985ec7 --- /dev/null +++ b/types/client/updatePassword.ts @@ -0,0 +1,15 @@ +import { object, TypeOf, z, string } from 'zod'; + +export const createUpdatePasswordSchema = object({ + password: string({ + required_error: 'Password is required', + }).min(6, 'Password must be at least 6 characters'), + passwordConfirmation: z + .string() + .min(1, { message: 'Password confirmation is required' }), +}).refine((data) => data.password === data.passwordConfirmation, { + message: 'Passwords do not match', + path: ['passwordConfirmation'], +}); + +export type CreateUpdatePasswordInput = TypeOf; diff --git a/types/client/user.ts b/types/client/user.ts new file mode 100644 index 0000000..6f6db67 --- /dev/null +++ b/types/client/user.ts @@ -0,0 +1,19 @@ +import { object, TypeOf, z, string } from 'zod'; + +export const createUserSchema = object({ + username: string({ + required_error: 'Username is required', + }).min(3, 'Username must be at least 3 characters'), + email: z.string().min(1, { message: 'Email is required' }), + password: string({ + required_error: 'Password is required', + }).min(6, 'Password must be at least 6 characters'), + passwordConfirmation: z + .string() + .min(1, { message: 'Password confirmation is required' }), +}).refine((data) => data.password === data.passwordConfirmation, { + message: 'Passwords do not match', + path: ['passwordConfirmation'], +}); + +export type CreateUserInput = TypeOf; diff --git a/types/database.supabase.ts b/types/database.supabase.ts index 3a6f956..0598f2b 100644 --- a/types/database.supabase.ts +++ b/types/database.supabase.ts @@ -9,9 +9,117 @@ export type Json = export interface Database { public: { Tables: { + channels: { + Row: { + channel_id: number + created_at: string | null + description: string | null + name: string | null + server_id: number + } + Insert: { + channel_id?: number + created_at?: string | null + description?: string | null + name?: string | null + server_id: number + } + Update: { + channel_id?: number + created_at?: string | null + description?: string | null + name?: string | null + server_id?: number + } + } + direct_message_channels: { + Row: { + created_at: string + id: number + owner_id: string + recepient_id: string + } + Insert: { + created_at?: string + id?: number + owner_id: string + recepient_id: string + } + Update: { + created_at?: string + id?: number + owner_id?: string + recepient_id?: string + } + } + direct_messages: { + Row: { + author_id: string + content: string + direct_message_id: number + edited_time: string | null + id: number + is_edited: boolean + is_pinned: boolean + sent_time: string + } + Insert: { + author_id: string + content: string + direct_message_id: number + edited_time?: string | null + id?: number + is_edited?: boolean + is_pinned?: boolean + sent_time?: string + } + Update: { + author_id?: string + content?: string + direct_message_id?: number + edited_time?: string | null + id?: number + is_edited?: boolean + is_pinned?: boolean + sent_time?: string + } + } + messages: { + Row: { + author_id: number + channel_id: number + content: string + edited_time: string | null + id: number + is_edited: boolean + is_pinned: boolean + sent_time: string + } + Insert: { + author_id: number + channel_id: number + content: string + edited_time?: string | null + id?: number + is_edited?: boolean + is_pinned?: boolean + sent_time?: string + } + Update: { + author_id?: number + channel_id?: number + content?: string + edited_time?: string | null + id?: number + is_edited?: boolean + is_pinned?: boolean + sent_time?: string + } + } profiles: { Row: { avatar_url: string | null + email: string | null full_name: string | null id: string updated_at: string | null @@ -20,6 +128,7 @@ export interface Database { } Insert: { avatar_url?: string | null + email?: string | null full_name?: string | null id: string updated_at?: string | null @@ -28,6 +137,7 @@ export interface Database { } Update: { avatar_url?: string | null + email?: string | null full_name?: string | null id?: string updated_at?: string | null @@ -35,6 +145,112 @@ export interface Database { website?: string | null } } + server_invites: { + Row: { + created_at: string | null + expires_at: string | null + id: number + server_id: number + url_id: string + uses_remaining: number | null + } + Insert: { + created_at?: string | null + expires_at?: string | null + id?: number + server_id: number + url_id: string + uses_remaining?: number | null + } + Update: { + created_at?: string | null + expires_at?: string | null + id?: number + server_id?: number + url_id?: string + uses_remaining?: number | null + } + } + server_roles: { + Row: { + created_at: string | null + id: number + name: string + permissions: number + server_id: number + } + Insert: { + created_at?: string | null + id: number + name?: string + permissions: number + server_id: number + } + Update: { + created_at?: string | null + id?: number + name?: string + permissions?: number + server_id?: number + } + } + server_user_roles: { + Row: { + id: number + role_id: number + server_user_id: number + } + Insert: { + id?: number + role_id: number + server_user_id: number + } + Update: { + id?: number + role_id?: number + server_user_id?: number + } + } + server_users: { + Row: { + id: number + joined_at: string | null + nickname: string | null + profile_id: string + server_id: number + } + Insert: { + id?: number + joined_at?: string | null + nickname?: string | null + profile_id: string + server_id: number + } + Update: { + id?: number + joined_at?: string | null + nickname?: string | null + profile_id?: string + server_id?: number + } + } + servers: { + Row: { + created_at: string | null + id: number + name: string + } + Insert: { + created_at?: string | null + id: number + name?: string + } + Update: { + created_at?: string | null + id?: number + name?: string + } + } } Views: { [_ in never]: never