mirror of
https://github.com/jorgev259/soc_site-astro.git
synced 2025-06-29 07:57:41 +00:00
Implement SignIn/SignOut
This commit is contained in:
parent
bce35d73ca
commit
adeb3fd3bf
16 changed files with 987 additions and 77 deletions
|
|
@ -2,6 +2,7 @@
|
|||
import { defineConfig } from 'astro/config'
|
||||
import tailwind from '@astrojs/tailwind'
|
||||
import node from '@astrojs/node'
|
||||
import react from '@astrojs/react';
|
||||
import paraglide from '@inlang/paraglide-astro'
|
||||
import auth from 'auth-astro'
|
||||
import icon from 'astro-icon'
|
||||
|
|
@ -23,8 +24,9 @@ export default defineConfig({
|
|||
tailwind(),
|
||||
auth(),
|
||||
paraglide({ project: './project.inlang', outdir: './src/paraglide' }),
|
||||
icon({ iconDir: 'src/img/icons' })
|
||||
icon({ iconDir: 'src/img/icons' }),
|
||||
react()
|
||||
],
|
||||
output: 'server',
|
||||
adapter: node({ mode: 'standalone' })
|
||||
})
|
||||
})
|
||||
|
|
@ -18,7 +18,10 @@ export default defineConfig({
|
|||
async authorize(credentials) {
|
||||
if (!credentials?.username || !credentials.password) throw new InvalidLoginError()
|
||||
|
||||
const user = await prismaClient.users.findUnique({ where: { username: credentials.username } })
|
||||
const user = await prismaClient.users.findUnique({
|
||||
select: { username: true, password: true },
|
||||
where: { username: credentials.username }
|
||||
})
|
||||
if (!user) throw new InvalidLoginError()
|
||||
|
||||
const valid = await bcrypt.compare(credentials.password, user.password)
|
||||
|
|
|
|||
|
|
@ -3,6 +3,10 @@
|
|||
"register": "Register",
|
||||
"login": "Login",
|
||||
"logout": "Logout",
|
||||
"username": "Username",
|
||||
"password": "Password",
|
||||
"email": "Email",
|
||||
"recoverPassword": "Recover Password",
|
||||
"home": "Home",
|
||||
"lastaddednav": "Last Added",
|
||||
"albumlist": "Album List",
|
||||
|
|
@ -21,5 +25,6 @@
|
|||
"managealbums": "Manage Albums",
|
||||
"manageusers": "Manage Users",
|
||||
"managerequests": "Manage Requests",
|
||||
"managesubmissions": "Manage Submissions"
|
||||
"managesubmissions": "Manage Submissions",
|
||||
"profilePic": "Profile picture"
|
||||
}
|
||||
13
package.json
13
package.json
|
|
@ -11,7 +11,9 @@
|
|||
},
|
||||
"dependencies": {
|
||||
"@apollo/client": "^3.11.4",
|
||||
"@apollo/server": "^4.11.2",
|
||||
"@astrojs/node": "^8.3.3",
|
||||
"@astrojs/react": "^3.6.2",
|
||||
"@astrojs/rss": "^4.0.7",
|
||||
"@astrojs/tailwind": "^5.1.1",
|
||||
"@auth/core": "^0.35.0",
|
||||
|
|
@ -22,13 +24,24 @@
|
|||
"@graphql-tools/schema": "^10.0.4",
|
||||
"@inlang/paraglide-astro": "^0.2.2",
|
||||
"@prisma/client": "^5.22.0",
|
||||
"@types/react": "^18.3.12",
|
||||
"@types/react-dom": "^18.3.1",
|
||||
"apollo-upload-client": "^18.0.1",
|
||||
"astro": "^4.15.7",
|
||||
"astro-icon": "^1.1.1",
|
||||
"auth-astro": "^4.1.2",
|
||||
"bcrypt": "^5.1.1",
|
||||
"clsx": "^2.1.1",
|
||||
"fs-extra": "^11.2.0",
|
||||
"generate-password-ts": "^1.6.5",
|
||||
"graphql": "^16.9.0",
|
||||
"graphql-scalars": "^1.23.0",
|
||||
"graphql-yoga": "^5.10.2",
|
||||
"nodemailer": "^6.9.16",
|
||||
"react": "^18.3.1",
|
||||
"react-dom": "^18.3.1",
|
||||
"react-svg-spinners": "^0.3.1",
|
||||
"sharp": "^0.33.5",
|
||||
"tailwindcss": "^3.4.12",
|
||||
"typescript": "^5.6.2"
|
||||
},
|
||||
|
|
|
|||
|
|
@ -317,10 +317,10 @@ model favorites {
|
|||
|
||||
model forgors {
|
||||
id Int @id @default(autoincrement())
|
||||
expires DateTime? @db.DateTime(0)
|
||||
expires DateTime? @default(dbgenerated("DATE_ADD(NOW(), INTERVAL 24 HOUR)")) @db.DateTime(0)
|
||||
key String? @db.VarChar(255)
|
||||
createdAt DateTime @db.DateTime(0)
|
||||
updatedAt DateTime @db.DateTime(0)
|
||||
createdAt DateTime @default(now()) @db.DateTime(0)
|
||||
updatedAt DateTime @default(now()) @db.DateTime(0)
|
||||
username String? @db.VarChar(255)
|
||||
users users? @relation(fields: [username], references: [username], map: "forgors_ibfk_1")
|
||||
|
||||
|
|
@ -515,8 +515,8 @@ model users {
|
|||
username String @id @db.VarChar(255)
|
||||
email String? @db.VarChar(255)
|
||||
password String? @db.VarChar(255)
|
||||
createdAt DateTime @db.DateTime(0)
|
||||
updatedAt DateTime @db.DateTime(0)
|
||||
createdAt DateTime @default(now()) @db.DateTime(0)
|
||||
updatedAt DateTime @default(now()) @db.DateTime(0)
|
||||
placeholder String? @db.Text
|
||||
imgId String? @db.VarChar(255)
|
||||
User_Role User_Role[]
|
||||
|
|
|
|||
|
|
@ -1,12 +0,0 @@
|
|||
---
|
||||
const { class: className } = Astro.props
|
||||
---
|
||||
|
||||
<button
|
||||
class:list={[
|
||||
'bg-blue-600 hover:bg-blue-700 py-2 px-3.5 rounded-lg',
|
||||
className
|
||||
]}
|
||||
>
|
||||
<slot />
|
||||
</button>
|
||||
26
src/components/Button.tsx
Normal file
26
src/components/Button.tsx
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
import type { PropsWithChildren } from 'react'
|
||||
import clsx from 'clsx'
|
||||
import { BarsRotateFade } from 'react-svg-spinners'
|
||||
|
||||
export default function Button(props: PropsWithChildren<{ className?: string; loading?: boolean }>) {
|
||||
const { children, className, loading = false, ...restProps } = props
|
||||
return (
|
||||
<button
|
||||
className={clsx(
|
||||
loading ? 'bg-blue-400 cursor-progress' : 'bg-blue-600 hover:bg-blue-700',
|
||||
'py-2 px-3.5 rounded-lg',
|
||||
className
|
||||
)}
|
||||
{...restProps}
|
||||
>
|
||||
<div className='relative flex'>
|
||||
<span className={clsx({ invisible: loading })}>{children}</span>
|
||||
{loading ? (
|
||||
<div className='absolute top-0 left-0 w-full flex justify-center'>
|
||||
<BarsRotateFade color='white' />
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
</button>
|
||||
)
|
||||
}
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
---
|
||||
import { gql } from '@/graphql/__generated__/client/index.js'
|
||||
import { getApolloClient } from '@/graphql/apolloClient.js'
|
||||
import { getApolloClient } from '@/graphql/apolloClientSSR.js'
|
||||
import { Image, Picture } from 'astro:assets'
|
||||
import { getSession } from 'auth-astro/server'
|
||||
import * as m from '../paraglide/messages.js'
|
||||
|
|
@ -8,11 +8,11 @@ import * as m from '../paraglide/messages.js'
|
|||
import logo from 'img/logos/winter.png'
|
||||
// import logoEs from 'img/logos/default_es.png'
|
||||
|
||||
import Button from './Button.astro'
|
||||
import Dropdown from './header/Dropdown.astro'
|
||||
import DropdownItem from './header/DropdownItem.astro'
|
||||
import Toggler from './header/Toggler.astro'
|
||||
import NavButton from './header/NavButton.astro'
|
||||
import LoginNav from './header/LoginNav.astro'
|
||||
|
||||
const headerQuery = gql(`
|
||||
query HeaderInfo {
|
||||
|
|
@ -48,16 +48,7 @@ const session = await getSession(Astro.request)
|
|||
<Image src={logo} class='h-full py-0.5 w-auto' alt='logo' height={150} width={265} />
|
||||
</a>
|
||||
|
||||
<div class='absolute top-0 right-0 space-x-2 mr-10'>
|
||||
{
|
||||
session === null ? (
|
||||
<Button class='rounded-t-none'>{m.login()}</Button>
|
||||
) : (
|
||||
<Button class='rounded-t-none'>{m.logout()}</Button>
|
||||
)
|
||||
}
|
||||
<Button class='rounded-t-none'>{m.register()}</Button>
|
||||
</div>
|
||||
<LoginNav />
|
||||
</div>
|
||||
</div>
|
||||
<nav class='w-full md:h-[55px] bg-dark'>
|
||||
|
|
|
|||
15
src/components/header/LoginNav.astro
Normal file
15
src/components/header/LoginNav.astro
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
---
|
||||
import { getSession } from 'auth-astro/server'
|
||||
import { SignIn, SignOut } from 'auth-astro/components'
|
||||
|
||||
import RegisterBtn from './RegisterButton'
|
||||
import * as m from 'paraglide/messages.js'
|
||||
|
||||
const session = await getSession(Astro.request)
|
||||
const btnClass = 'bg-blue-600 hover:bg-blue-700 py-2 px-3.5 rounded-lg rounded-t-none'
|
||||
---
|
||||
|
||||
<div class='absolute top-0 right-0 space-x-2 mr-10'>
|
||||
{session === null ? <SignIn class={btnClass}>{m.login()}</SignIn> : <SignOut class={btnClass}>{m.logout()}</SignOut>}
|
||||
{!session ? <RegisterBtn client:only='react' /> : null}
|
||||
</div>
|
||||
|
|
@ -1,14 +1,15 @@
|
|||
---
|
||||
import Button from '../Button.astro'
|
||||
import Button from '../Button'
|
||||
import clsx from 'clsx'
|
||||
|
||||
const { class: className } = Astro.props
|
||||
---
|
||||
|
||||
<Button
|
||||
class:list={[
|
||||
className={clsx(
|
||||
'w-full md:w-fit md:h-full bg-dark hover:bg-dark-hover py-3.5 md:py-1 px-2 rounded-none text-left md:text-center',
|
||||
className
|
||||
]}
|
||||
)}
|
||||
>
|
||||
<slot />
|
||||
</Button>
|
||||
|
|
|
|||
|
|
@ -1,23 +1,15 @@
|
|||
import { ApolloClient, InMemoryCache } from '@apollo/client/core'
|
||||
import { SchemaLink } from '@apollo/client/link/schema'
|
||||
import { makeExecutableSchema } from '@graphql-tools/schema'
|
||||
import { getSession } from 'auth-astro/server'
|
||||
import type { Session } from '@auth/core/types'
|
||||
import prismaClient, { type users } from 'prisma/client'
|
||||
import createUploadLink from 'apollo-upload-client/createUploadLink.mjs'
|
||||
|
||||
import { typeDefs } from './__generated__/typeDefs.generated'
|
||||
import resolvers from '@/graphql/resolvers'
|
||||
const httpLink = createUploadLink({
|
||||
uri: `${window.origin}/api/graphql`,
|
||||
headers: { 'Apollo-Require-Preflight': true },
|
||||
credentials: 'include'
|
||||
})
|
||||
|
||||
const schema = makeExecutableSchema({ typeDefs, resolvers })
|
||||
export type ResolverContext = { request?: Request; session?: Session; user?: users }
|
||||
const apolloClient = new ApolloClient({
|
||||
link: httpLink,
|
||||
cache: new InMemoryCache()
|
||||
})
|
||||
|
||||
export async function getApolloClient(request?: Request) {
|
||||
const session = async () => (request ? getSession(request) : null)
|
||||
const user = async () => (session ? prismaClient.users.findUnique({ where: { username: session.id } }) : null)
|
||||
|
||||
return new ApolloClient({
|
||||
ssrMode: true,
|
||||
link: new SchemaLink({ schema, context: { request, session, user } }),
|
||||
cache: new InMemoryCache()
|
||||
})
|
||||
}
|
||||
export default apolloClient
|
||||
|
|
|
|||
24
src/graphql/apolloClientSSR.ts
Normal file
24
src/graphql/apolloClientSSR.ts
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
import { ApolloClient, InMemoryCache } from '@apollo/client/core'
|
||||
import { SchemaLink } from '@apollo/client/link/schema'
|
||||
import { getSession } from 'auth-astro/server'
|
||||
import type { Session } from '@auth/core/types'
|
||||
import prismaClient, { type users } from 'prisma/client'
|
||||
import { makeExecutableSchema } from '@graphql-tools/schema'
|
||||
|
||||
import { typeDefs } from '@/graphql/__generated__/typeDefs.generated'
|
||||
import resolvers from '@/graphql/resolvers'
|
||||
|
||||
export type ResolverContext = { request?: Request; session?: Session; user?: users }
|
||||
|
||||
const schema = makeExecutableSchema({ typeDefs, resolvers })
|
||||
|
||||
export async function getApolloClient(request?: Request) {
|
||||
const session = async () => (request ? getSession(request) : null)
|
||||
const user = async () => (session ? prismaClient.users.findUnique({ where: { username: session.id } }) : null)
|
||||
|
||||
return new ApolloClient({
|
||||
ssrMode: true,
|
||||
link: new SchemaLink({ schema, context: { request, session, user } }),
|
||||
cache: new InMemoryCache()
|
||||
})
|
||||
}
|
||||
|
|
@ -38,10 +38,7 @@ type Query {
|
|||
}
|
||||
|
||||
type Mutation {
|
||||
login(username: String!, password: String!): Int!
|
||||
logout: Int!
|
||||
|
||||
registerUser(email: String!, username: String!, pfp: Upload): Boolean!
|
||||
registerUser(email: String!, username: String!, pfp: File): Boolean!
|
||||
updateUserRoles(username: String!, roles: [String]!): Boolean!
|
||||
deleteUser(username: String!): Int
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import rss, { type RSSFeedItem } from '@astrojs/rss'
|
||||
import type { APIContext } from 'astro'
|
||||
|
||||
import { getApolloClient } from '@/graphql/apolloClient.js'
|
||||
import { getApolloClient } from '@/graphql/apolloClientSSR'
|
||||
import { gql } from '@/graphql/__generated__/client'
|
||||
|
||||
const addedQuery = gql(`
|
||||
|
|
|
|||
13
src/utils/graphQLErrors.ts
Normal file
13
src/utils/graphQLErrors.ts
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
import { GraphQLError } from 'graphql'
|
||||
import { ApolloServerErrorCode } from '@apollo/server/errors'
|
||||
|
||||
export const AuthenticationError = (message: string = '') =>
|
||||
new GraphQLError(message, { extensions: { code: 'UNAUTHENTICATED' } })
|
||||
|
||||
export const ForbiddenError = (message: string = '') =>
|
||||
new GraphQLError(message, { extensions: { code: 'FORBIDDEN' } })
|
||||
|
||||
export const UserInputError = (message: string = '') =>
|
||||
new GraphQLError(message, {
|
||||
extensions: { code: ApolloServerErrorCode.BAD_USER_INPUT }
|
||||
})
|
||||
Loading…
Add table
Add a link
Reference in a new issue