mirror of
https://github.com/jorgev259/soc_site-astro.git
synced 2025-06-29 07:57:41 +00:00
Replace GQL with Prisma
This commit is contained in:
parent
1c142f651f
commit
8a7f1e8c8a
36 changed files with 1156 additions and 6302 deletions
26
package.json
26
package.json
|
|
@ -4,41 +4,28 @@
|
||||||
"version": "0.0.1",
|
"version": "0.0.1",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "npm run dev",
|
"start": "npm run dev",
|
||||||
"dev": "concurrently \"prisma generate --watch\" \"graphql-codegen -w\" \"astro dev\"",
|
"dev": "astro dev",
|
||||||
"build": "npm run paraglide:compile && prisma generate && graphql-codegen && astro build",
|
"build": "npm run paraglide:compile && prisma generate && astro build",
|
||||||
"preview": "astro preview",
|
"preview": "astro preview",
|
||||||
"paraglide:compile": "paraglide-js compile --project ./project.inlang --outdir ./src\\paraglide"
|
"paraglide:compile": "paraglide-js compile --project ./project.inlang --outdir ./src\\paraglide"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@apollo/client": "^3.11.4",
|
|
||||||
"@apollo/server": "^4.11.2",
|
|
||||||
"@astrojs/node": "9.0.0",
|
"@astrojs/node": "9.0.0",
|
||||||
"@astrojs/react": "4.1.3",
|
"@astrojs/react": "4.1.3",
|
||||||
"@astrojs/rss": "4.0.11",
|
"@astrojs/rss": "4.0.11",
|
||||||
"@astrojs/tailwind": "5.1.4",
|
"@astrojs/tailwind": "5.1.4",
|
||||||
"@eddeee888/gcg-typescript-resolver-files": "^0.10.4",
|
|
||||||
"@graphql-codegen/cli": "^5.0.2",
|
|
||||||
"@graphql-tools/merge": "^9.0.6",
|
|
||||||
"@graphql-tools/resolvers-composition": "^7.0.1",
|
|
||||||
"@graphql-tools/schema": "^10.0.4",
|
|
||||||
"@inlang/paraglide-astro": "^0.2.2",
|
"@inlang/paraglide-astro": "^0.2.2",
|
||||||
"@oslojs/crypto": "^1.0.1",
|
"@prisma/client": "^6.2.1",
|
||||||
"@oslojs/encoding": "^1.1.0",
|
|
||||||
"@prisma/client": "^5.22.0",
|
|
||||||
"@types/react": "^18.3.12",
|
"@types/react": "^18.3.12",
|
||||||
"@types/react-dom": "^18.3.1",
|
"@types/react-dom": "^18.3.1",
|
||||||
"apollo-upload-client": "^18.0.1",
|
|
||||||
"astro": "5.1.5",
|
"astro": "5.1.5",
|
||||||
"astro-icon": "^1.1.1",
|
"astro-icon": "^1.1.1",
|
||||||
|
"better-auth": "^1.1.11",
|
||||||
"clsx": "^2.1.1",
|
"clsx": "^2.1.1",
|
||||||
"fast-glob": "^3.3.2",
|
"fast-glob": "^3.3.2",
|
||||||
"fs-extra": "^11.2.0",
|
"fs-extra": "^11.2.0",
|
||||||
"generate-password-ts": "^1.6.5",
|
"generate-password-ts": "^1.6.5",
|
||||||
"graphql": "^16.9.0",
|
"nodemailer": "^6.10.0",
|
||||||
"graphql-scalars": "^1.23.0",
|
|
||||||
"graphql-yoga": "^5.10.2",
|
|
||||||
"nodemailer": "^6.9.16",
|
|
||||||
"oslo": "^1.2.1",
|
|
||||||
"react": "^18.3.1",
|
"react": "^18.3.1",
|
||||||
"react-dom": "^18.3.1",
|
"react-dom": "^18.3.1",
|
||||||
"react-hot-toast": "^2.4.1",
|
"react-hot-toast": "^2.4.1",
|
||||||
|
|
@ -50,6 +37,7 @@
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@inlang/paraglide-js": "1.11.2",
|
"@inlang/paraglide-js": "1.11.2",
|
||||||
"@parcel/watcher": "^2.4.1",
|
"@parcel/watcher": "^2.4.1",
|
||||||
|
"@types/nodemailer": "^6.4.17",
|
||||||
"concurrently": "^8.2.2",
|
"concurrently": "^8.2.2",
|
||||||
"eslint": "^9.11.1",
|
"eslint": "^9.11.1",
|
||||||
"eslint-config-prettier": "^9.1.0",
|
"eslint-config-prettier": "^9.1.0",
|
||||||
|
|
@ -58,6 +46,6 @@
|
||||||
"neostandard": "^0.11.6",
|
"neostandard": "^0.11.6",
|
||||||
"prettier": "^3.3.3",
|
"prettier": "^3.3.3",
|
||||||
"prettier-config-standard": "^7.0.0",
|
"prettier-config-standard": "^7.0.0",
|
||||||
"prisma": "^5.22.0"
|
"prisma": "^6.2.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,4 @@
|
||||||
---
|
---
|
||||||
import { gql } from '@/graphql/__generated__/client/index.js'
|
|
||||||
import { getApolloClient } from '@/graphql/apolloClientSSR.js'
|
|
||||||
import { Image, Picture } from 'astro:assets'
|
import { Image, Picture } from 'astro:assets'
|
||||||
import * as m from '../paraglide/messages.js'
|
import * as m from '../paraglide/messages.js'
|
||||||
|
|
||||||
|
|
@ -12,29 +10,18 @@ import DropdownItem from './header/DropdownItem.astro'
|
||||||
import Toggler from './header/Toggler.astro'
|
import Toggler from './header/Toggler.astro'
|
||||||
import NavButton from './header/NavButton.astro'
|
import NavButton from './header/NavButton.astro'
|
||||||
import LoginNav from './header/LoginNav.astro'
|
import LoginNav from './header/LoginNav.astro'
|
||||||
|
import prismaClient from 'utils/prisma-client.js'
|
||||||
|
|
||||||
const headerQuery = gql(`
|
const { value: bannerId } = (await prismaClient.config.findUnique({ where: { name: 'banner' } })) ?? {}
|
||||||
query HeaderInfo {
|
const { value: bannerPosition } = (await prismaClient.config.findUnique({ where: { name: 'banner-position' } })) ?? {}
|
||||||
banner: config(name: "banner") {
|
|
||||||
value
|
|
||||||
}
|
|
||||||
bannerPosition: config(name: "banner-position") {
|
|
||||||
value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`)
|
|
||||||
|
|
||||||
const client = await getApolloClient()
|
|
||||||
const { data: headerInfo } = await client.query({ query: headerQuery })
|
|
||||||
const { banner, bannerPosition } = headerInfo
|
|
||||||
---
|
---
|
||||||
|
|
||||||
<header class='relative'>
|
<header class='relative'>
|
||||||
<div class='relative h-[150px] bg-top bg-no-repeat bg-cover'>
|
<div class='relative h-[150px] bg-top bg-no-repeat bg-cover'>
|
||||||
<div class='absolute size-full'>
|
<div class='absolute size-full'>
|
||||||
<Picture
|
<Picture
|
||||||
class={`size-full object-cover object-${bannerPosition?.value || 'top'}`}
|
class={`size-full object-cover object-${bannerPosition || 'top'}`}
|
||||||
src={`https://cdn.sittingonclouds.net/live/${banner?.value}.png`}
|
src={`https://cdn.sittingonclouds.net/live/${bannerId}.png`}
|
||||||
alt=''
|
alt=''
|
||||||
width={2560}
|
width={2560}
|
||||||
height={150}
|
height={150}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,4 @@
|
||||||
import { useState } from 'react'
|
import { useState, type FormEvent, type SyntheticEvent } from 'react'
|
||||||
import { gql } from '@/graphql/__generated__/client'
|
|
||||||
import { useMutation } from '@apollo/client/react/hooks'
|
|
||||||
|
|
||||||
import Button from 'components/Button'
|
import Button from 'components/Button'
|
||||||
import * as m from 'paraglide/messages.js'
|
import * as m from 'paraglide/messages.js'
|
||||||
|
|
@ -16,9 +14,9 @@ const loginMutation = gql(`
|
||||||
|
|
||||||
export default function LoginBtn() {
|
export default function LoginBtn() {
|
||||||
const [modalOpen, setModalOpen] = useState(false)
|
const [modalOpen, setModalOpen] = useState(false)
|
||||||
const [mutate, { loading }] = useMutation(loginMutation, { client: apolloClient })
|
const [loading, setLoading] = useState(false)
|
||||||
|
|
||||||
const handleSubmit = (ev) => {
|
const handleSubmit = (ev: FormEvent) => {
|
||||||
ev.preventDefault()
|
ev.preventDefault()
|
||||||
const formData = new FormData(ev.target)
|
const formData = new FormData(ev.target)
|
||||||
const variables = Object.fromEntries(formData)
|
const variables = Object.fromEntries(formData)
|
||||||
|
|
|
||||||
|
|
@ -3,10 +3,18 @@ import RegisterBtn from './RegisterButton'
|
||||||
import LoginBtn from './LoginButton'
|
import LoginBtn from './LoginButton'
|
||||||
import LogoutBtn from './LogoutButton'
|
import LogoutBtn from './LogoutButton'
|
||||||
|
|
||||||
const { user } = Astro.locals
|
const session = Astro.locals.session
|
||||||
---
|
---
|
||||||
|
|
||||||
<div class='absolute top-0 right-0 space-x-2 mr-10'>
|
<div class='absolute top-0 right-0 space-x-2 mr-10'>
|
||||||
{!user ? <LoginBtn client:only='react' /> : <LogoutBtn client:only='react' />}
|
{
|
||||||
{!user ? <RegisterBtn client:only='react' /> : null}
|
session ? (
|
||||||
|
<LogoutBtn client:only='react' />
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<LoginBtn client:only='react' />
|
||||||
|
<RegisterBtn client:only='react' />
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -1,34 +1,23 @@
|
||||||
import { gql } from '@/graphql/__generated__/client'
|
// import toast from 'react-hot-toast'
|
||||||
import { useMutation } from '@apollo/client/react/hooks'
|
|
||||||
|
|
||||||
import Button from 'components/Button'
|
import Button from 'components/Button'
|
||||||
import * as m from 'paraglide/messages.js'
|
import * as m from 'paraglide/messages.js'
|
||||||
import apolloClient from '@/graphql/apolloClient'
|
|
||||||
import toast from 'react-hot-toast'
|
|
||||||
|
|
||||||
const loginMutation = gql(`
|
|
||||||
mutation Logout {
|
|
||||||
logout
|
|
||||||
}
|
|
||||||
`)
|
|
||||||
|
|
||||||
export default function LogoutBtn() {
|
export default function LogoutBtn() {
|
||||||
const [mutate, { loading }] = useMutation(loginMutation, { client: apolloClient })
|
const handleClick = (ev) => {
|
||||||
|
|
||||||
const handleSubmit = (ev) => {
|
|
||||||
ev.preventDefault()
|
ev.preventDefault()
|
||||||
|
|
||||||
mutate()
|
/* mutate()
|
||||||
.then(() => {
|
.then(() => {
|
||||||
window.refresh()
|
window.refresh()
|
||||||
})
|
})
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
toast.error(err.message)
|
toast.error(err.message)
|
||||||
})
|
}) */
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Button className='rounded-t-none' loading={loading} onClick={handleSubmit}>
|
<Button className='rounded-t-none' onClick={handleClick}>
|
||||||
{m.logout()}
|
{m.logout()}
|
||||||
</Button>
|
</Button>
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -1,36 +1,26 @@
|
||||||
import { useState } from 'react'
|
import { useState } from 'react'
|
||||||
import { gql } from '@/graphql/__generated__/client'
|
// import toast from 'react-hot-toast'
|
||||||
import { useMutation } from '@apollo/client/react/hooks'
|
|
||||||
|
|
||||||
import Button from 'components/Button'
|
|
||||||
import * as m from 'paraglide/messages.js'
|
import * as m from 'paraglide/messages.js'
|
||||||
|
import Button from 'components/Button'
|
||||||
import Modal from 'components/Modal'
|
import Modal from 'components/Modal'
|
||||||
import apolloClient from '@/graphql/apolloClient'
|
|
||||||
import toast from 'react-hot-toast'
|
|
||||||
|
|
||||||
const registerMutation = gql(`
|
|
||||||
mutation Register($username: String!, $email: String!, $pfp: File) {
|
|
||||||
registerUser(username: $username, email: $email, pfp: $pfp)
|
|
||||||
}
|
|
||||||
`)
|
|
||||||
|
|
||||||
export default function RegisterBtn() {
|
export default function RegisterBtn() {
|
||||||
const [modalOpen, setModalOpen] = useState(false)
|
const [modalOpen, setModalOpen] = useState(false)
|
||||||
const [mutate, { loading }] = useMutation(registerMutation, { client: apolloClient, ignoreResults: true })
|
|
||||||
|
|
||||||
const handleSubmit = (ev) => {
|
const handleSubmit = (ev) => {
|
||||||
ev.preventDefault()
|
ev.preventDefault()
|
||||||
const formData = new FormData(ev.target)
|
const formData = new FormData(ev.target)
|
||||||
const variables = Object.fromEntries(formData)
|
const variables = Object.fromEntries(formData)
|
||||||
|
|
||||||
mutate({ variables })
|
/* mutate({ variables })
|
||||||
.then(() => {
|
.then(() => {
|
||||||
toast.success(m.emailSuccess())
|
toast.success(m.emailSuccess())
|
||||||
setModalOpen(false)
|
setModalOpen(false)
|
||||||
})
|
})
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
toast.error(err.message)
|
toast.error(err.message)
|
||||||
})
|
}) */
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
@ -70,9 +60,9 @@ export default function RegisterBtn() {
|
||||||
</div>
|
</div>
|
||||||
<div className='bg-zinc-200 px-4 py-3 text-right gap-x-2 flex justify-end'>
|
<div className='bg-zinc-200 px-4 py-3 text-right gap-x-2 flex justify-end'>
|
||||||
<Button className='bg-zinc-500 hover:bg-zinc-600'>{m.close()}</Button>
|
<Button className='bg-zinc-500 hover:bg-zinc-600'>{m.close()}</Button>
|
||||||
<Button loading={loading} disabled={loading}>
|
{/* <Button loading={loading} disabled={loading}>
|
||||||
{m.register()}
|
{m.register()}
|
||||||
</Button>
|
</Button> */}
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</Modal>
|
</Modal>
|
||||||
|
|
|
||||||
|
|
@ -1,16 +0,0 @@
|
||||||
import { ApolloClient, InMemoryCache } from '@apollo/client/core'
|
|
||||||
import createUploadLink from 'apollo-upload-client/createUploadLink.mjs'
|
|
||||||
|
|
||||||
const httpLink = createUploadLink({
|
|
||||||
uri: `${window.origin}/api/graphql`,
|
|
||||||
headers: { 'Apollo-Require-Preflight': true },
|
|
||||||
credentials: 'include'
|
|
||||||
})
|
|
||||||
|
|
||||||
const apolloClient = new ApolloClient({
|
|
||||||
link: httpLink,
|
|
||||||
cache: new InMemoryCache(),
|
|
||||||
credentials: 'same-origin'
|
|
||||||
})
|
|
||||||
|
|
||||||
export default apolloClient
|
|
||||||
|
|
@ -1,18 +0,0 @@
|
||||||
import { ApolloClient, InMemoryCache } from '@apollo/client/core'
|
|
||||||
import { SchemaLink } from '@apollo/client/link/schema'
|
|
||||||
import type { AstroCookies, AstroGlobal } from 'astro'
|
|
||||||
|
|
||||||
import schema from './schema'
|
|
||||||
|
|
||||||
export interface ResolverContext {
|
|
||||||
cookies?: AstroCookies
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function getApolloClient(cookies?: AstroCookies) {
|
|
||||||
return new ApolloClient({
|
|
||||||
ssrMode: true,
|
|
||||||
link: new SchemaLink({ schema, context: { cookies } }),
|
|
||||||
cache: new InMemoryCache(),
|
|
||||||
credentials: 'same-origin'
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
@ -1,7 +0,0 @@
|
||||||
import { mergeResolvers } from '@graphql-tools/merge'
|
|
||||||
import type { IResolvers } from '@graphql-tools/utils'
|
|
||||||
|
|
||||||
const imports: IResolvers[] = Object.values(import.meta.glob('./**/*.ts', { eager: true, import: 'default' }))
|
|
||||||
const resolvers = mergeResolvers(imports)
|
|
||||||
|
|
||||||
export default resolvers
|
|
||||||
|
|
@ -1,54 +0,0 @@
|
||||||
import { composeResolvers } from '@graphql-tools/resolvers-composition'
|
|
||||||
// import axios from 'axios'
|
|
||||||
|
|
||||||
import { isAuthedApp } from 'server/utils/resolvers'
|
|
||||||
import { getSession, getUser } from 'next/utils/getSession'
|
|
||||||
|
|
||||||
// const token = process.env.IRONCLAD
|
|
||||||
|
|
||||||
const resolversComposition = {
|
|
||||||
'Mutation.*': [isAuthedApp]
|
|
||||||
}
|
|
||||||
|
|
||||||
const resolvers = {
|
|
||||||
Mutation: {
|
|
||||||
updateComment: async (_, { text, anon, albumId }, { db }) => {
|
|
||||||
const { username } = await getSession()
|
|
||||||
const row = await db.models.comment.findOne({
|
|
||||||
where: { albumId, username }
|
|
||||||
})
|
|
||||||
|
|
||||||
if (row) {
|
|
||||||
await row.update({ text, anon })
|
|
||||||
await row.save()
|
|
||||||
} else await db.models.comment.create({ albumId, username, text, anon })
|
|
||||||
|
|
||||||
return true
|
|
||||||
},
|
|
||||||
addFavorite: async (_, { albumId }, { db }) => {
|
|
||||||
const user = await getUser(db)
|
|
||||||
await user.addAlbum(albumId)
|
|
||||||
return true
|
|
||||||
},
|
|
||||||
removeFavorite: async (_, { albumId }, { db }) => {
|
|
||||||
const user = await getUser(db)
|
|
||||||
await user.removeAlbum(albumId)
|
|
||||||
return true
|
|
||||||
},
|
|
||||||
rateAlbum: async (_, { albumId, score }, { db }) => {
|
|
||||||
const { username } = await getSession()
|
|
||||||
const row = await db.models.rating.findOne({
|
|
||||||
where: { albumId, username }
|
|
||||||
})
|
|
||||||
|
|
||||||
if (row) {
|
|
||||||
await row.update({ score })
|
|
||||||
await row.save()
|
|
||||||
} else await db.models.rating.create({ albumId, username, score })
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default composeResolvers(resolvers, resolversComposition)
|
|
||||||
|
|
@ -1,81 +0,0 @@
|
||||||
import { composeResolvers } from '@graphql-tools/resolvers-composition'
|
|
||||||
|
|
||||||
import { createLog, createUpdateLog } from 'server/utils/log'
|
|
||||||
import { getImgColor, img } from 'server/utils/image'
|
|
||||||
import { hasRole } from 'server/utils/resolvers'
|
|
||||||
import { handleComplete } from 'server/utils/requests'
|
|
||||||
import { slugify } from 'server/utils/slugify'
|
|
||||||
import { UserInputError } from 'next/server/utils/graphQLErrors'
|
|
||||||
|
|
||||||
const resolversComposition = { 'Mutation.*': hasRole('CREATE') }
|
|
||||||
const resolvers = {
|
|
||||||
Mutation: {
|
|
||||||
createAlbum: async (parent, data, { db }, info) =>
|
|
||||||
db.transaction(async (transaction) => {
|
|
||||||
data.artists = data.artists
|
|
||||||
? data.artists.map((artist) => {
|
|
||||||
return { name: artist, slug: slugify(artist) }
|
|
||||||
})
|
|
||||||
: []
|
|
||||||
await db.models.artist.bulkCreate(data.artists, {
|
|
||||||
ignoreDuplicates: true,
|
|
||||||
transaction
|
|
||||||
})
|
|
||||||
|
|
||||||
const album = await db.models.album.create(data, {
|
|
||||||
include: [
|
|
||||||
db.models.disc,
|
|
||||||
db.models.store,
|
|
||||||
{
|
|
||||||
model: db.models.download,
|
|
||||||
include: [db.models.link]
|
|
||||||
}
|
|
||||||
],
|
|
||||||
transaction
|
|
||||||
})
|
|
||||||
|
|
||||||
await Promise.all([
|
|
||||||
album.setArtists(
|
|
||||||
data.artists
|
|
||||||
.filter(({ slug }) => slug.length > 0)
|
|
||||||
.map(({ slug }) => slug),
|
|
||||||
{ transaction }
|
|
||||||
),
|
|
||||||
album.setCategories(data.categories || [], { transaction }),
|
|
||||||
album.setClassifications(data.classifications || [], { transaction }),
|
|
||||||
album.setPlatforms(data.platforms || [], { transaction }),
|
|
||||||
album.setGames(data.games || [], { transaction }),
|
|
||||||
album.setAnimations(data.animations || [], { transaction }),
|
|
||||||
album.setRelated(data.related || [], { transaction }),
|
|
||||||
createLog(db, 'createAlbum', data, transaction)
|
|
||||||
])
|
|
||||||
|
|
||||||
const { id } = album.dataValues
|
|
||||||
album.placeholder = data.cover
|
|
||||||
? await img(data.cover, 'album', id)
|
|
||||||
: undefined
|
|
||||||
album.headerColor = data.cover
|
|
||||||
? await getImgColor(`album/${id}`)
|
|
||||||
: undefined
|
|
||||||
|
|
||||||
await album.save({ transaction })
|
|
||||||
|
|
||||||
if (album.status === 'show') handleComplete(db, data, album)
|
|
||||||
|
|
||||||
return album
|
|
||||||
}),
|
|
||||||
|
|
||||||
deleteAlbum: async (parent, { id }, { db }, info) => {
|
|
||||||
const album = await db.models.album.findByPk(id)
|
|
||||||
if (!album) throw UserInputError('Not Found')
|
|
||||||
|
|
||||||
return db.transaction(async (transaction) => {
|
|
||||||
await createUpdateLog(db, 'deleteAlbum', album, transaction)
|
|
||||||
await album.destroy({ transaction })
|
|
||||||
return 1
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default composeResolvers(resolvers, resolversComposition)
|
|
||||||
|
|
@ -1,86 +0,0 @@
|
||||||
import { composeResolvers } from '@graphql-tools/resolvers-composition'
|
|
||||||
import { mergeResolvers } from '@graphql-tools/merge'
|
|
||||||
|
|
||||||
import { hasRole, isAuthedApp } from 'server/utils/resolvers'
|
|
||||||
import { getUser } from 'next/utils/getSession'
|
|
||||||
import { requestPOST } from 'server/utils/requests'
|
|
||||||
import { UserInputError } from 'next/server/utils/graphQLErrors'
|
|
||||||
|
|
||||||
const resolvers = {
|
|
||||||
Mutation: {
|
|
||||||
editRequest: async (parent, data, { db }, info) => {
|
|
||||||
const request = await db.models.request.findByPk(data.id)
|
|
||||||
if (!request) throw UserInputError('Request not found')
|
|
||||||
|
|
||||||
await db.transaction(async (transaction) => {
|
|
||||||
await request.set(data, { transaction })
|
|
||||||
|
|
||||||
if (request.changed('state')) {
|
|
||||||
switch (request.state) {
|
|
||||||
case 'complete':
|
|
||||||
await requestPOST('complete', { requestId: request.id })
|
|
||||||
break
|
|
||||||
|
|
||||||
case 'hold':
|
|
||||||
await requestPOST('hold', {
|
|
||||||
requestId: request.id,
|
|
||||||
reason: data.reason
|
|
||||||
})
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
await request.save({ transaction })
|
|
||||||
})
|
|
||||||
|
|
||||||
return request
|
|
||||||
},
|
|
||||||
|
|
||||||
rejectRequest: async (parent, data, { db }, info) => {
|
|
||||||
const request = await db.models.request.findByPk(data.id)
|
|
||||||
if (!request) throw UserInputError('Request not found')
|
|
||||||
|
|
||||||
await requestPOST('reject', {
|
|
||||||
requestId: request.id,
|
|
||||||
reason: data.reason
|
|
||||||
})
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const submitActions = {
|
|
||||||
Mutation: {
|
|
||||||
submitAlbum: async (parent, data, { db }, info) => {
|
|
||||||
const { request: requestId, title, vgmdb, links } = data
|
|
||||||
let request
|
|
||||||
|
|
||||||
if (requestId) {
|
|
||||||
request = await db.models.request.findByPk(requestId)
|
|
||||||
|
|
||||||
if (!request) throw UserInputError('Request not found')
|
|
||||||
if (request.state === 'complete')
|
|
||||||
throw UserInputError('Request already complete')
|
|
||||||
}
|
|
||||||
|
|
||||||
const user = await getUser(db)
|
|
||||||
|
|
||||||
return db.models.submission.create({
|
|
||||||
title,
|
|
||||||
vgmdb,
|
|
||||||
links,
|
|
||||||
requestId,
|
|
||||||
userUsername: user.username
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const requestResolvers = composeResolvers(resolvers, {
|
|
||||||
'Mutation.*': hasRole('REQUESTS')
|
|
||||||
})
|
|
||||||
const submitResolvers = composeResolvers(submitActions, {
|
|
||||||
'Mutation.*': [isAuthedApp]
|
|
||||||
})
|
|
||||||
|
|
||||||
export default mergeResolvers([requestResolvers, submitResolvers])
|
|
||||||
|
|
@ -1,37 +0,0 @@
|
||||||
import { composeResolvers } from '@graphql-tools/resolvers-composition'
|
|
||||||
import fs from 'fs-extra'
|
|
||||||
import path from 'path'
|
|
||||||
|
|
||||||
import { img } from 'server/utils/image'
|
|
||||||
import { hasRole } from 'server/utils/resolvers'
|
|
||||||
import { UserInputError } from 'next/server/utils/graphQLErrors'
|
|
||||||
|
|
||||||
const resolversComposition = { 'Mutation.*': hasRole('UPDATE') }
|
|
||||||
const resolvers = {
|
|
||||||
Mutation: {
|
|
||||||
config: async (parent, data, { db, payload }, info) =>
|
|
||||||
db.models.config
|
|
||||||
.upsert(data)
|
|
||||||
.then(() => db.models.config.findByPk(data.name)),
|
|
||||||
|
|
||||||
uploadBanner: async (parent, { banner }, { db, payload }) => {
|
|
||||||
const timestamp = Date.now()
|
|
||||||
await img(banner, 'live', timestamp)
|
|
||||||
await db.models.config.upsert({ name: 'banner', value: timestamp })
|
|
||||||
|
|
||||||
return 1
|
|
||||||
},
|
|
||||||
|
|
||||||
selectBanner: async (parent, { name }, { db }) => {
|
|
||||||
const filePath = path.join('/var/www/soc_img/img/live', `${name}.png`)
|
|
||||||
if (!(await fs.pathExists(filePath)))
|
|
||||||
throw UserInputError(`Banner '${name}' doesnt exist`)
|
|
||||||
|
|
||||||
await db.models.config.upsert({ name: 'banner', value: name })
|
|
||||||
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default composeResolvers(resolvers, resolversComposition)
|
|
||||||
|
|
@ -1,335 +0,0 @@
|
||||||
import { composeResolvers } from '@graphql-tools/resolvers-composition'
|
|
||||||
|
|
||||||
import { createLog, createUpdateLog } from 'server/utils/log'
|
|
||||||
import { img, getImgColor } from 'server/utils/image'
|
|
||||||
import { hasRole } from 'server/utils/resolvers'
|
|
||||||
import { handleComplete } from 'server/utils/requests'
|
|
||||||
import { slugify } from 'server/utils/slugify'
|
|
||||||
|
|
||||||
const resolversComposition = { 'Mutation.*': hasRole('UPDATE') }
|
|
||||||
const resolvers = {
|
|
||||||
Mutation: {
|
|
||||||
createPublisher: async (parent, data, { db }, info) =>
|
|
||||||
db.transaction(async (transaction) => {
|
|
||||||
const pub = await db.models.publisher.create(data, { transaction })
|
|
||||||
data.id = pub.id
|
|
||||||
|
|
||||||
await createLog(db, 'createPublisher', data, transaction)
|
|
||||||
|
|
||||||
return pub
|
|
||||||
}),
|
|
||||||
updatePublisher: async (parent, { id, name }, { db }, info) => {
|
|
||||||
const pub = await db.models.publisher.findByPk(id)
|
|
||||||
pub.name = name
|
|
||||||
|
|
||||||
return db.transaction(async (transaction) => {
|
|
||||||
await pub.save({ transaction })
|
|
||||||
|
|
||||||
await createUpdateLog(db, 'updatePublisher', pub, transaction)
|
|
||||||
return pub
|
|
||||||
})
|
|
||||||
},
|
|
||||||
deletePublisher: async (parent, { id }, { db }) => {
|
|
||||||
const pub = await db.models.publisher.findByPk(id)
|
|
||||||
|
|
||||||
return db.transaction(async (transaction) => {
|
|
||||||
await pub.destroy({ transaction })
|
|
||||||
|
|
||||||
await createLog(db, 'deletePublisher', pub.dataValues, transaction)
|
|
||||||
})
|
|
||||||
},
|
|
||||||
|
|
||||||
createPlatform: async (parent, data, { db }, info) =>
|
|
||||||
db.transaction(async (transaction) => {
|
|
||||||
const plat = db.models.platform.create(data, { transaction })
|
|
||||||
data.id = plat.id
|
|
||||||
|
|
||||||
await createLog(db, 'createPlatform', data, transaction)
|
|
||||||
return plat
|
|
||||||
}),
|
|
||||||
updatePlatform: async (parent, { key, name, type }, { db }, info) => {
|
|
||||||
const plat = await db.models.platform.findByPk(key)
|
|
||||||
if (name) plat.name = name
|
|
||||||
if (type !== plat.type) plat.type = type
|
|
||||||
|
|
||||||
return db.transaction(async (transaction) => {
|
|
||||||
await plat.save({ transaction })
|
|
||||||
|
|
||||||
await createUpdateLog(db, 'updatePlatform', plat, transaction)
|
|
||||||
return plat
|
|
||||||
})
|
|
||||||
},
|
|
||||||
deletePlatform: async (parent, { key }, { db }) => {
|
|
||||||
const plat = await db.models.platform.findByPk(key)
|
|
||||||
|
|
||||||
return db.transaction(async (transaction) => {
|
|
||||||
await plat.destroy({ transaction })
|
|
||||||
await createLog(db, 'deletePlatform', plat.dataValues, transaction)
|
|
||||||
})
|
|
||||||
},
|
|
||||||
|
|
||||||
createStudio: async (parent, data, { db }, info) =>
|
|
||||||
db.transaction(async (transaction) => {
|
|
||||||
const studio = db.models.studio.create(data, { transaction })
|
|
||||||
data.slug = studio.slug
|
|
||||||
await createLog(db, 'createStudio', data, transaction)
|
|
||||||
return studio
|
|
||||||
}),
|
|
||||||
updateStudio: async (parent, { slug, name }, { db }, info) => {
|
|
||||||
const studio = await db.models.studio.findByPk(slug)
|
|
||||||
studio.name = name
|
|
||||||
|
|
||||||
return db.transaction(async (transaction) => {
|
|
||||||
await studio.save({ transaction })
|
|
||||||
await createUpdateLog(db, 'updateStudio', studio, transaction)
|
|
||||||
return studio
|
|
||||||
})
|
|
||||||
},
|
|
||||||
deleteStudio: async (parent, { slug, name }, { db }, info) => {
|
|
||||||
const studio = await db.models.studio.findByPk(slug)
|
|
||||||
|
|
||||||
return db.transaction(async (transaction) => {
|
|
||||||
studio.destroy({ transaction })
|
|
||||||
await createLog(db, 'deleteStudio', studio.dataValues, transaction)
|
|
||||||
})
|
|
||||||
},
|
|
||||||
|
|
||||||
createSeries: async (parent, data, { db }, info) =>
|
|
||||||
db.transaction(async (transaction) => {
|
|
||||||
const series = await db.models.series.create(data, { transaction })
|
|
||||||
const { slug } = series.dataValues
|
|
||||||
|
|
||||||
series.placeholder = data.cover
|
|
||||||
? await img(data.cover, 'series', slug)
|
|
||||||
: undefined
|
|
||||||
series.headerColor = data.cover
|
|
||||||
? await getImgColor(`series/${slug}`)
|
|
||||||
: undefined
|
|
||||||
await series.save({ transaction })
|
|
||||||
|
|
||||||
await createLog(db, 'createSeries', data, transaction)
|
|
||||||
return series
|
|
||||||
}),
|
|
||||||
updateSeries: async (parent, { slug, name, cover }, { db }, info) => {
|
|
||||||
const series = await db.models.series.findByPk(slug)
|
|
||||||
if (name) series.name = name
|
|
||||||
if (cover) {
|
|
||||||
series.placeholder = await img(cover, 'series', slug)
|
|
||||||
series.headerColor = await getImgColor(`series/${slug}`)
|
|
||||||
}
|
|
||||||
|
|
||||||
return db.transaction(async (transaction) => {
|
|
||||||
await series.save({ transaction })
|
|
||||||
await createUpdateLog(db, 'updateSeries', series, transaction)
|
|
||||||
return series
|
|
||||||
})
|
|
||||||
},
|
|
||||||
deleteSeries: async (parent, { slug }, { db }) => {
|
|
||||||
const series = await db.models.series.findByPk(slug)
|
|
||||||
|
|
||||||
return db.transaction(async (transaction) => {
|
|
||||||
await series.destroy({ transaction })
|
|
||||||
await createLog(db, 'deleteSeries', series.dataValues, transaction)
|
|
||||||
})
|
|
||||||
},
|
|
||||||
|
|
||||||
createGame: async (parent, data, { db }, info) => {
|
|
||||||
const game = await db.models.game.create(data)
|
|
||||||
|
|
||||||
return db.transaction(async (transaction) => {
|
|
||||||
await Promise.all([
|
|
||||||
game.setSeries(data.series, { transaction }),
|
|
||||||
game.setPublishers(data.publishers, { transaction }),
|
|
||||||
game.setPlatforms(data.platforms, { transaction })
|
|
||||||
])
|
|
||||||
|
|
||||||
game.placeholder = data.cover
|
|
||||||
? await img(data.cover, 'game', data.slug)
|
|
||||||
: ''
|
|
||||||
game.headerColor = data.cover
|
|
||||||
? await getImgColor(`game/${data.slug}`)
|
|
||||||
: undefined
|
|
||||||
|
|
||||||
await game.save({ transaction })
|
|
||||||
await createLog(db, 'createGame', data, transaction)
|
|
||||||
|
|
||||||
return game
|
|
||||||
})
|
|
||||||
},
|
|
||||||
updateGame: async (parent, args, { db }, info) => {
|
|
||||||
const {
|
|
||||||
slug,
|
|
||||||
name,
|
|
||||||
cover,
|
|
||||||
releaseDate,
|
|
||||||
series = [],
|
|
||||||
publishers,
|
|
||||||
platforms
|
|
||||||
} = args
|
|
||||||
const game = await db.models.game.findByPk(slug)
|
|
||||||
|
|
||||||
game.name = name
|
|
||||||
game.releaseDate = releaseDate
|
|
||||||
|
|
||||||
if (cover) {
|
|
||||||
game.placeholder = await img(cover, 'game', slug)
|
|
||||||
series.headerColor = await getImgColor(`game/${slug}`)
|
|
||||||
}
|
|
||||||
|
|
||||||
// make more comprehensible log
|
|
||||||
return db.transaction(async (transaction) => {
|
|
||||||
game.setSeries(series, { transaction })
|
|
||||||
game.setPublishers(publishers, { transaction })
|
|
||||||
game.setPlatforms(platforms, { transaction })
|
|
||||||
await game.save({ transaction })
|
|
||||||
await createUpdateLog(db, 'updateGame', game, transaction)
|
|
||||||
|
|
||||||
return game
|
|
||||||
})
|
|
||||||
},
|
|
||||||
deleteGame: async (parent, { slug }, { db }) => {
|
|
||||||
const game = await db.models.game.findByPk(slug)
|
|
||||||
const log = {
|
|
||||||
...game.dataValues,
|
|
||||||
series: await game.getSeries(),
|
|
||||||
publishers: await game.getPublishers(),
|
|
||||||
platforms: await game.getPlatforms()
|
|
||||||
}
|
|
||||||
|
|
||||||
return db.transaction(async (transaction) => {
|
|
||||||
await game.destroy({ transaction })
|
|
||||||
await createLog(db, 'deleteSeries', log, transaction)
|
|
||||||
})
|
|
||||||
},
|
|
||||||
|
|
||||||
createAnimation: async (parent, data, { db }, info) => {
|
|
||||||
return db.transaction(async (transaction) => {
|
|
||||||
const anim = await db.models.animation.create(data, { transaction })
|
|
||||||
await anim.setStudios(data.studios, { transaction })
|
|
||||||
|
|
||||||
anim.placeholder = data.cover
|
|
||||||
? await img(data.cover, 'anim', anim.id)
|
|
||||||
: ''
|
|
||||||
anim.headerColor = data.cover
|
|
||||||
? await getImgColor(`anim/${anim.id}`)
|
|
||||||
: undefined
|
|
||||||
await anim.save({ transaction })
|
|
||||||
|
|
||||||
await createLog(db, 'createAnimation', data, transaction)
|
|
||||||
|
|
||||||
return anim
|
|
||||||
})
|
|
||||||
},
|
|
||||||
updateAnimation: async (parent, data, { db }, info) => {
|
|
||||||
const anim = await db.models.animation.findByPk(data.id)
|
|
||||||
Object.entries(data).forEach(([key, value]) => {
|
|
||||||
anim[key] = value
|
|
||||||
})
|
|
||||||
|
|
||||||
if (data.cover) {
|
|
||||||
anim.placeholder = await img(data.cover, 'anim', anim.id)
|
|
||||||
anim.headerColor = await getImgColor(`anim/${anim.id}`)
|
|
||||||
}
|
|
||||||
|
|
||||||
return db.transaction(async (transaction) => {
|
|
||||||
anim.setStudios(data.studios, { transaction })
|
|
||||||
|
|
||||||
await anim.save({ transaction })
|
|
||||||
await createUpdateLog(db, 'updateAnimation', anim, transaction)
|
|
||||||
return anim
|
|
||||||
})
|
|
||||||
},
|
|
||||||
deleteAnimation: async (parent, { id }, { db }) => {
|
|
||||||
const anim = await db.models.animation.findByPk(id)
|
|
||||||
|
|
||||||
const log = {
|
|
||||||
...anim.dataValues,
|
|
||||||
studios: await anim.getStudios()
|
|
||||||
}
|
|
||||||
|
|
||||||
return db.transaction(async (transaction) => {
|
|
||||||
await anim.destroy({ transaction })
|
|
||||||
await createLog(db, 'deleteAnim', log, transaction)
|
|
||||||
})
|
|
||||||
},
|
|
||||||
|
|
||||||
updateAlbum: async (parent, data, { db }, info) => {
|
|
||||||
try {
|
|
||||||
const album = await db.models.album.findByPk(data.id)
|
|
||||||
const triggerPost =
|
|
||||||
data.status !== album.status.repeat(1) && data.status === 'show'
|
|
||||||
data.artists = data.artists
|
|
||||||
? data.artists.map((artist) => {
|
|
||||||
return { name: artist, slug: slugify(artist) }
|
|
||||||
})
|
|
||||||
: []
|
|
||||||
|
|
||||||
await db.transaction(async (transaction) => {
|
|
||||||
await db.models.artist.bulkCreate(data.artists, {
|
|
||||||
ignoreDuplicates: true,
|
|
||||||
transaction
|
|
||||||
})
|
|
||||||
|
|
||||||
// implement better log lol lmao
|
|
||||||
|
|
||||||
await Promise.all([
|
|
||||||
album.update(data, { transaction }),
|
|
||||||
album.setArtists(
|
|
||||||
data.artists.map(({ slug }) => slug),
|
|
||||||
{ transaction }
|
|
||||||
),
|
|
||||||
album.setCategories(data.categories || [], { transaction }),
|
|
||||||
album.setClassifications(data.classifications || [], {
|
|
||||||
transaction
|
|
||||||
}),
|
|
||||||
album.setPlatforms(data.platforms || [], { transaction }),
|
|
||||||
album.setGames(data.games || []),
|
|
||||||
{ transaction },
|
|
||||||
album.setRelated(data.related || [], { transaction }),
|
|
||||||
album.setAnimations(data.animations || [], { transaction }),
|
|
||||||
db.models.disc
|
|
||||||
.destroy({ where: { albumId: album.dataValues.id }, transaction })
|
|
||||||
.then(() =>
|
|
||||||
(data.discs || []).map((disc) =>
|
|
||||||
album.createDisc(disc, { transaction })
|
|
||||||
)
|
|
||||||
),
|
|
||||||
db.models.store
|
|
||||||
.destroy({ where: { albumId: album.dataValues.id }, transaction })
|
|
||||||
.then(() =>
|
|
||||||
(data.stores || []).map((store) =>
|
|
||||||
album.createStore(store, { transaction })
|
|
||||||
)
|
|
||||||
),
|
|
||||||
db.models.download
|
|
||||||
.destroy({ where: { albumId: album.dataValues.id }, transaction })
|
|
||||||
.then(() =>
|
|
||||||
(data.downloads || []).map((download) =>
|
|
||||||
album.createDownload(download, {
|
|
||||||
include: [db.models.link],
|
|
||||||
transaction
|
|
||||||
})
|
|
||||||
)
|
|
||||||
),
|
|
||||||
createUpdateLog(db, 'updateAlbum', album, transaction)
|
|
||||||
])
|
|
||||||
|
|
||||||
if (data.cover) {
|
|
||||||
album.placeholder = await img(data.cover, 'album', album.id)
|
|
||||||
album.headerColor = await getImgColor(`album/${album.id}`)
|
|
||||||
await album.save({ transaction })
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
if (triggerPost) handleComplete(db, data, album)
|
|
||||||
|
|
||||||
return album
|
|
||||||
} catch (err) {
|
|
||||||
console.log(err)
|
|
||||||
throw new Error(err.message)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default composeResolvers(resolvers, resolversComposition)
|
|
||||||
|
|
@ -1,245 +0,0 @@
|
||||||
import { composeResolvers } from '@graphql-tools/resolvers-composition'
|
|
||||||
import type { Resolvers } from '@/graphql/__generated__/types.generated'
|
|
||||||
import generator from 'generate-password-ts'
|
|
||||||
import sharp from 'sharp'
|
|
||||||
import fs from 'fs-extra'
|
|
||||||
import type { User } from '@auth/core/types'
|
|
||||||
import prismaClient from 'prisma/client'
|
|
||||||
import nodemailer from 'nodemailer'
|
|
||||||
|
|
||||||
import { AuthenticationError, UserInputError } from 'utils/graphQLErrors'
|
|
||||||
import forgorTemplate from 'utils/forgorTemplate'
|
|
||||||
import {
|
|
||||||
argon2id,
|
|
||||||
COOKIE_NAME,
|
|
||||||
createSession,
|
|
||||||
generateSessionToken,
|
|
||||||
invalidateSession,
|
|
||||||
setSessionTokenCookie,
|
|
||||||
validateSessionToken
|
|
||||||
} from 'utils/session'
|
|
||||||
|
|
||||||
async function processImage(imagePath) {
|
|
||||||
const sharpImg = sharp(imagePath)
|
|
||||||
const meta = await sharpImg.metadata()
|
|
||||||
const placeholderImgWidth = 20
|
|
||||||
const imgAspectRatio = meta.width / meta.height
|
|
||||||
const placeholderImgHeight = Math.round(placeholderImgWidth / imgAspectRatio)
|
|
||||||
|
|
||||||
const buffer = await sharpImg.resize(placeholderImgWidth, placeholderImgHeight).toBuffer()
|
|
||||||
|
|
||||||
return `data:image/${meta.format};base64,${buffer.toString('base64')}`
|
|
||||||
}
|
|
||||||
|
|
||||||
async function cropPFP(pfpFile: File, username: string, imgId: string) {
|
|
||||||
if (!imgId)
|
|
||||||
return 'data:image/webp;base64,UklGRlQAAABXRUJQVlA4IEgAAACwAQCdASoEAAQAAUAmJZgCdAEO9p5AAPa//NFYLcn+a7b+3z7ynq/qXv+iG0yH/y1D9eBf9pqWugq9G0RnxmxwsjaA2bW8AAA='
|
|
||||||
|
|
||||||
const pathString = '/var/www/soc_img/img/user'
|
|
||||||
const fullPath = `${pathString}/${username}_${imgId}.png`
|
|
||||||
|
|
||||||
await fs.ensureDir(pathString)
|
|
||||||
|
|
||||||
let sharpImage = sharp(await pfpFile.arrayBuffer())
|
|
||||||
const metadata = await sharpImage.metadata()
|
|
||||||
const { width, height } = metadata
|
|
||||||
|
|
||||||
if (width !== height) {
|
|
||||||
sharpImage = sharpImage.extract(
|
|
||||||
width > height
|
|
||||||
? {
|
|
||||||
left: Math.floor((width - height) / 2),
|
|
||||||
top: 0,
|
|
||||||
width: height,
|
|
||||||
height
|
|
||||||
}
|
|
||||||
: {
|
|
||||||
left: 0,
|
|
||||||
top: Math.floor((height - width) / 2),
|
|
||||||
width,
|
|
||||||
height: width
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
await sharpImage.resize({ width: 200, height: 200 }).png().toFile(fullPath)
|
|
||||||
|
|
||||||
return await processImage(fullPath)
|
|
||||||
}
|
|
||||||
|
|
||||||
export const mailConfig = JSON.parse(process.env.MAILSERVER || '{}')
|
|
||||||
export const transporter = nodemailer.createTransport(mailConfig)
|
|
||||||
|
|
||||||
function addHours(date: Date, hours: number) {
|
|
||||||
const hoursToAdd = hours * 60 * 60 * 1000
|
|
||||||
date.setTime(date.getTime() + hoursToAdd)
|
|
||||||
return date
|
|
||||||
}
|
|
||||||
|
|
||||||
async function createForgor(user: User) {
|
|
||||||
await prismaClient.forgors.deleteMany({ where: { username: user.username } })
|
|
||||||
|
|
||||||
const key = generator.generate({
|
|
||||||
length: 15,
|
|
||||||
numbers: true,
|
|
||||||
uppercase: false,
|
|
||||||
strict: true
|
|
||||||
})
|
|
||||||
|
|
||||||
const row = await prismaClient.forgors.create({
|
|
||||||
data: { key, username: user.username, expires: addHours(new Date(), 24) }
|
|
||||||
})
|
|
||||||
const html = forgorTemplate.replaceAll('{{forgor_link}}', `https://sittingonclouds.net/forgor?key=${key}`)
|
|
||||||
const message = {
|
|
||||||
from: mailConfig.auth.user,
|
|
||||||
to: user.email,
|
|
||||||
subject: 'Password Reset',
|
|
||||||
html
|
|
||||||
}
|
|
||||||
await transporter.sendMail(message)
|
|
||||||
|
|
||||||
return row
|
|
||||||
}
|
|
||||||
|
|
||||||
const resolversComposition = {
|
|
||||||
//'Mutation.updateUser': [isAuthedApp]
|
|
||||||
}
|
|
||||||
|
|
||||||
const resolvers: Resolvers = {
|
|
||||||
Mutation: {
|
|
||||||
registerUser: async (_, args) => {
|
|
||||||
const { username, email } = args
|
|
||||||
const pfp: File = args.pfp
|
|
||||||
|
|
||||||
const checkUser = await prismaClient.users.findFirst({ where: { OR: [{ username }, { email }] } })
|
|
||||||
if (checkUser) throw UserInputError('Username/email already in use')
|
|
||||||
|
|
||||||
const password = generator.generate({
|
|
||||||
length: 30,
|
|
||||||
numbers: true,
|
|
||||||
upercase: true,
|
|
||||||
strict: true
|
|
||||||
})
|
|
||||||
const hashPassword = await argon2id.hash(password)
|
|
||||||
|
|
||||||
const imgId = pfp.size > 0 ? Date.now().toString() : null
|
|
||||||
const [hash, placeholder] = await Promise.all([hashPassword, cropPFP(pfp, username, imgId)])
|
|
||||||
const user = await prismaClient.users.create({ data: { username, email, password: hash, placeholder, imgId } })
|
|
||||||
await createForgor(user)
|
|
||||||
|
|
||||||
return true
|
|
||||||
},
|
|
||||||
login: async (_, args, context) => {
|
|
||||||
const { username, password } = args
|
|
||||||
let { user } = await validateSessionToken(context.cookies?.get(COOKIE_NAME)?.value)
|
|
||||||
if (user) throw AuthenticationError('Already logged in')
|
|
||||||
|
|
||||||
user = await prismaClient.users.findUnique({ where: { username } })
|
|
||||||
if (!user) throw UserInputError('Invalid Username/email')
|
|
||||||
|
|
||||||
const checkPassword = await argon2id.verify(user.password, password)
|
|
||||||
if (!checkPassword) throw UserInputError('Invalid Username/email')
|
|
||||||
|
|
||||||
const token = generateSessionToken()
|
|
||||||
const session = await createSession(user.username, token)
|
|
||||||
setSessionTokenCookie(context.cookies, token, session.expiresAt)
|
|
||||||
|
|
||||||
return token
|
|
||||||
},
|
|
||||||
logout: async (_, args, context) => {
|
|
||||||
await invalidateSession(context.locals.session.id)
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
/*
|
|
||||||
updateUserRoles: async (
|
|
||||||
parent,
|
|
||||||
{ username, roles },
|
|
||||||
{ db, payload },
|
|
||||||
info
|
|
||||||
) => {
|
|
||||||
const user = await db.models.user.findByPk(username)
|
|
||||||
user.setRoles(roles)
|
|
||||||
await user.save()
|
|
||||||
return true
|
|
||||||
},
|
|
||||||
deleteUser: async (parent, { username }, { db, payload }, info) => {
|
|
||||||
const user = await db.models.user.findByPk(username)
|
|
||||||
if (!user) throw UserInputError('Not Found')
|
|
||||||
user.destroy()
|
|
||||||
return 1
|
|
||||||
},
|
|
||||||
|
|
||||||
createForgorLink: async (_, { key }, { db }) => {
|
|
||||||
const user = await db.models.user.findOne({
|
|
||||||
where: { [Op.or]: [{ username: key }, { email: key }] }
|
|
||||||
})
|
|
||||||
if (!user) throw UserInputError('Not Found')
|
|
||||||
|
|
||||||
await createForgor(user, db)
|
|
||||||
return true
|
|
||||||
},
|
|
||||||
updatePass: async (_, { key, pass }, { db }) => {
|
|
||||||
const row = await db.models.forgor.findByPk(key)
|
|
||||||
if (!row) throw ForbiddenError()
|
|
||||||
|
|
||||||
const now = DateTime.now()
|
|
||||||
const expires = DateTime.fromJSDate(row.expires)
|
|
||||||
|
|
||||||
if (now > expires) throw ForbiddenError()
|
|
||||||
|
|
||||||
const user = await db.models.user.findByPk(row.username)
|
|
||||||
user.password = await bcrypt.hash(pass, 10)
|
|
||||||
|
|
||||||
return db.transaction(async (transaction) => {
|
|
||||||
await user.save({ transaction })
|
|
||||||
await row.destroy({ transaction })
|
|
||||||
return true
|
|
||||||
})
|
|
||||||
},
|
|
||||||
updateUser: async (_, { username, email, password, pfp }, { db }) => {
|
|
||||||
const user = await getUser(db)
|
|
||||||
if (username) user.username = username
|
|
||||||
if (email) user.email = email
|
|
||||||
if (password) user.password = await bcrypt.hash(password, 10)
|
|
||||||
if (pfp) {
|
|
||||||
const pathString = '/var/www/soc_img/img/user'
|
|
||||||
await fs.remove(
|
|
||||||
path.join(pathString, `${user.username}_${user.imgId}.png`)
|
|
||||||
)
|
|
||||||
|
|
||||||
const imgId = Date.now()
|
|
||||||
user.placeholder = await cropPFP(pfp, username, imgId)
|
|
||||||
user.imgId = imgId
|
|
||||||
}
|
|
||||||
|
|
||||||
await user.save()
|
|
||||||
return true
|
|
||||||
},
|
|
||||||
|
|
||||||
createRole: async (parent, args, { db, payload }) =>
|
|
||||||
db.models.role.create(args),
|
|
||||||
updateRole: async (parent, { key, name, permissions }, { db, payload }) => {
|
|
||||||
const role = await db.models.role.findByPk(key)
|
|
||||||
if (!role) throw UserInputError('Not Found')
|
|
||||||
|
|
||||||
if (role.name !== name) {
|
|
||||||
await db.query(
|
|
||||||
`UPDATE roles SET name = "${name}" WHERE name = "${key}"`
|
|
||||||
)
|
|
||||||
}
|
|
||||||
role.permissions = permissions
|
|
||||||
|
|
||||||
await role.save()
|
|
||||||
return role
|
|
||||||
},
|
|
||||||
deleteRole: async (parent, { name }, { db, payload }) => {
|
|
||||||
const role = await db.models.role.findByPk(name)
|
|
||||||
if (!role) throw UserInputError('Not Found')
|
|
||||||
await role.destroy()
|
|
||||||
|
|
||||||
return name
|
|
||||||
}*/
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default composeResolvers(resolvers, resolversComposition)
|
|
||||||
|
|
@ -1,58 +0,0 @@
|
||||||
import Sequelize from 'sequelize'
|
|
||||||
|
|
||||||
const { Op } = Sequelize
|
|
||||||
|
|
||||||
const resolvers = {
|
|
||||||
Query: {
|
|
||||||
artists: (parent, args, { db }, info) => db.models.artist.findAll(),
|
|
||||||
platforms: (parent, args, { db }, info) => db.models.platform.findAll(),
|
|
||||||
publishers: (parent, args, { db }, info) => db.models.publisher.findAll(),
|
|
||||||
publisher: (parent, { id }, { db }, info) =>
|
|
||||||
db.models.publisher.findByPk(id),
|
|
||||||
categories: (parent, args, { db }, info) => db.models.category.findAll(),
|
|
||||||
classifications: (parent, args, { db }, info) =>
|
|
||||||
db.models.classification.findAll(),
|
|
||||||
series: (parent, args, { db }, info) => db.models.series.findAll(),
|
|
||||||
games: (parent, args, { db }, info) => db.models.game.findAll(),
|
|
||||||
game: (parent, { slug }, { db }, info) => db.models.game.findByPk(slug),
|
|
||||||
album: (_, { id }, { db }) => db.models.album.findByPk(id),
|
|
||||||
downloads: (parent, { id }, { db }) =>
|
|
||||||
db.models.download.findAll({ where: { albumId: id } }),
|
|
||||||
albums: (_, __, { db }, info) => db.models.album.findAll({}),
|
|
||||||
|
|
||||||
platform: async (parent, { id }, { db }) => db.models.platform.findByPk(id),
|
|
||||||
animation: (parent, { id }, { db }) => db.models.animation.findByPk(id),
|
|
||||||
animations: (parent, args, { db }) => db.models.animation.findAll(),
|
|
||||||
studio: (parent, { slug }, { db }) => db.models.studio.findByPk(slug),
|
|
||||||
studios: (parent, _, { db }) => db.models.studio.findAll(),
|
|
||||||
seriesOne: (parent, { slug }, { db }, info) =>
|
|
||||||
db.models.series.findByPk(slug),
|
|
||||||
albumCount: async (parent, params, { db }) => db.models.album.count(),
|
|
||||||
recentSeries: (parent, { limit }, { db }) =>
|
|
||||||
db.models.series.findAll({
|
|
||||||
limit,
|
|
||||||
order: [['createdAt', 'DESC']]
|
|
||||||
}),
|
|
||||||
recentPublishers: (parent, { limit }, { db }) =>
|
|
||||||
db.models.publisher.findAll({
|
|
||||||
limit,
|
|
||||||
order: [['createdAt', 'DESC']]
|
|
||||||
}),
|
|
||||||
recentPlatforms: (parent, { limit, type }, { db }) =>
|
|
||||||
db.models.platform.findAll({
|
|
||||||
limit,
|
|
||||||
order: [['createdAt', 'DESC']],
|
|
||||||
where: { type: { [Op.like]: `%${type}%` } }
|
|
||||||
}),
|
|
||||||
|
|
||||||
getRandomAlbum: async (parent, { limit = 1 }, { db }) => {
|
|
||||||
const result = await db.models.album.findAll({
|
|
||||||
order: db.random(),
|
|
||||||
limit
|
|
||||||
})
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default resolvers
|
|
||||||
|
|
@ -1,84 +0,0 @@
|
||||||
import { Op, fn, col, where } from 'sequelize'
|
|
||||||
|
|
||||||
const resolvers = {
|
|
||||||
Query: {
|
|
||||||
requests: (
|
|
||||||
_,
|
|
||||||
{ state = ['complete', 'hold', 'pending'], donator = [true, false] },
|
|
||||||
{ db }
|
|
||||||
) => db.models.request.findAll({ where: { state, donator } }),
|
|
||||||
request: (_, { link }, { db }) =>
|
|
||||||
db.models.request.findOne({ where: { link } }),
|
|
||||||
|
|
||||||
searchRequests: async (
|
|
||||||
_,
|
|
||||||
{
|
|
||||||
state = ['complete', 'hold', 'pending'],
|
|
||||||
donator = [true, false],
|
|
||||||
limit = 10,
|
|
||||||
page = 0,
|
|
||||||
filter
|
|
||||||
},
|
|
||||||
{ db }
|
|
||||||
) => {
|
|
||||||
const options = { limit, offset: limit * page }
|
|
||||||
const optionsWhere = { state, donator }
|
|
||||||
|
|
||||||
async function exactSearch() {
|
|
||||||
if (!filter) return
|
|
||||||
|
|
||||||
const results = await db.models.request.findAndCountAll({
|
|
||||||
where: {
|
|
||||||
...optionsWhere,
|
|
||||||
[Op.or]: [
|
|
||||||
{ id: filter },
|
|
||||||
{ link: filter },
|
|
||||||
{ user: filter },
|
|
||||||
{ userID: filter }
|
|
||||||
]
|
|
||||||
},
|
|
||||||
...options
|
|
||||||
})
|
|
||||||
|
|
||||||
if (results.rows.length > 0) return results
|
|
||||||
}
|
|
||||||
|
|
||||||
function looseSearch() {
|
|
||||||
return db.models.request.findAndCountAll({
|
|
||||||
where: [
|
|
||||||
optionsWhere,
|
|
||||||
where(fn('LOWER', col('title')), { [Op.like]: `%${filter || ''}%` })
|
|
||||||
],
|
|
||||||
...options
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
return (await exactSearch()) || looseSearch()
|
|
||||||
},
|
|
||||||
|
|
||||||
submissions: (_, args, context) => {
|
|
||||||
const { filter = '', state = ['pending'] } = args
|
|
||||||
const { db } = context
|
|
||||||
|
|
||||||
return db.models.submission.findAll({
|
|
||||||
where: {
|
|
||||||
[Op.and]: [
|
|
||||||
{ state: { [Op.in]: state } },
|
|
||||||
{
|
|
||||||
[Op.or]: [
|
|
||||||
{ id: filter },
|
|
||||||
{ vgmdb: filter },
|
|
||||||
{ userUsername: filter },
|
|
||||||
where(fn('LOWER', col('title')), {
|
|
||||||
[Op.like]: `%${filter.toLowerCase()}%`
|
|
||||||
})
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default resolvers
|
|
||||||
|
|
@ -1,98 +0,0 @@
|
||||||
import prismaClient from 'prisma/client'
|
|
||||||
|
|
||||||
import type { Resolvers } from '@/graphql/__generated__/types.generated'
|
|
||||||
|
|
||||||
const resolvers: Resolvers = {
|
|
||||||
Query: {
|
|
||||||
searchAlbum: async (_, args, context, info) => {
|
|
||||||
const {
|
|
||||||
title,
|
|
||||||
categories,
|
|
||||||
limit = 10,
|
|
||||||
offset = 0,
|
|
||||||
order = ['createdAt'],
|
|
||||||
mode = 'DESC',
|
|
||||||
status = ['show']
|
|
||||||
} = args
|
|
||||||
const fuzzyCondition = title
|
|
||||||
? {
|
|
||||||
OR: [
|
|
||||||
{ title: { search: title.toLowerCase().split(' ').join(' & ') } },
|
|
||||||
{ subTitle: { search: title.toLowerCase().split(' ').join(' & ') } }
|
|
||||||
]
|
|
||||||
}
|
|
||||||
: {}
|
|
||||||
|
|
||||||
const result = prismaClient.albums.findMany({
|
|
||||||
take: limit,
|
|
||||||
skip: offset,
|
|
||||||
where: {
|
|
||||||
AND: {
|
|
||||||
...fuzzyCondition,
|
|
||||||
// category condition
|
|
||||||
status: { in: status }
|
|
||||||
}
|
|
||||||
},
|
|
||||||
include: { Album_Category: !!categories },
|
|
||||||
orderBy: [...order.map((o) => ({ [o]: mode.toLowerCase() }))]
|
|
||||||
// order: [literal("`album`.`status` = 'coming' DESC")]
|
|
||||||
})
|
|
||||||
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
/* searchAlbumByArtist: async (parent, { name, categories, limit, page = 0, order = ['createdAt'], mode = 'DESC', status = ['show'] }, { db }) => {
|
|
||||||
const include = [{ model: db.models.artist, where: { name: { [Op.like]: `%${name}%` } } }]
|
|
||||||
|
|
||||||
if (categories) include.push({ model: db.models.class, where: { name: { [Op.in]: categories } } })
|
|
||||||
|
|
||||||
return searchPage({ limit, page, model: 'album' }, {
|
|
||||||
where: { status: { [Op.in]: status } },
|
|
||||||
include,
|
|
||||||
order: order.map(o => [o, mode])
|
|
||||||
}, db)
|
|
||||||
},
|
|
||||||
searchAnimation: (parent, { title = '', limit, page = 0, order = 'createdAt', mode = 'DESC' }, { db }) => searchPage({ title, limit, page, model: 'animation' }, {
|
|
||||||
where: { title: { [Op.like]: `%${title}%` } },
|
|
||||||
order: [[order, mode]]
|
|
||||||
}, db),
|
|
||||||
searchStudio: (parent, { name = '', limit, page = 0, order = 'createdAt', mode = 'DESC' }, { db }) => searchPage({ name, limit, page, model: 'studio' }, {
|
|
||||||
where: { name: { [Op.like]: `%${name}%` } },
|
|
||||||
order: [[order, mode]]
|
|
||||||
}, db),
|
|
||||||
searchGame: (parent, { name = '', limit, page = 0, order = 'createdAt', mode = 'DESC' }, { db }) => searchPage({ name, limit, page, model: 'game' }, {
|
|
||||||
where: { name: { [Op.like]: `%${name}%` } },
|
|
||||||
order: [[order, mode]]
|
|
||||||
}, db),
|
|
||||||
searchSeries: (parent, { name = '', limit, page = 0, order = 'createdAt', mode = 'DESC' }, { db }) => searchPage({ name, limit, page, model: 'series' }, {
|
|
||||||
where: { name: { [Op.like]: `%${name}%` } },
|
|
||||||
order: [[order, mode]]
|
|
||||||
}, db),
|
|
||||||
searchSeriesByName: (parent, { name }, { db }) => db.models.series.findAll({
|
|
||||||
where: {
|
|
||||||
name: {
|
|
||||||
[Op.like]: `%${name}%`
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
searchPublishersByName: (parent, { name }, { db }) => db.models.publisher.findAll({
|
|
||||||
where: {
|
|
||||||
name: {
|
|
||||||
[Op.like]: `%${name}%`
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
searchPlatformsByName: (parent, { name, categories }, { db }) => db.models.platform.findAll({
|
|
||||||
where: {
|
|
||||||
name: {
|
|
||||||
[Op.like]: `%${name}%`
|
|
||||||
},
|
|
||||||
type: { [Op.or]: categories }
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
searchPlatformsByCategories: (parent, { categories }, { db }) => categories.length === 0
|
|
||||||
? []
|
|
||||||
: db.models.platform.findAll({ where: { type: { [Op.or]: categories } } }) */
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default resolvers
|
|
||||||
|
|
@ -1,32 +0,0 @@
|
||||||
import fg from 'fast-glob'
|
|
||||||
|
|
||||||
import { composeResolvers } from '@graphql-tools/resolvers-composition'
|
|
||||||
import type { Resolvers } from '@/graphql/__generated__/types.generated'
|
|
||||||
import prismaClient from 'prisma/client'
|
|
||||||
import { checkPerm } from 'utils/resolvers'
|
|
||||||
|
|
||||||
const resolversComposition = {
|
|
||||||
'Query.banners': checkPerm('UPDATE')
|
|
||||||
}
|
|
||||||
|
|
||||||
const resolvers: Resolvers = {
|
|
||||||
Query: {
|
|
||||||
config: (_, { name }, __, ___) =>
|
|
||||||
prismaClient.config.upsert({
|
|
||||||
where: { name },
|
|
||||||
create: { name },
|
|
||||||
update: {}
|
|
||||||
}),
|
|
||||||
banners: async (parent, args) => {
|
|
||||||
const filePaths = await fg(['/var/www/soc_img/img/live/**/ /**.png'])
|
|
||||||
const images = filePaths.map((f) => f.split('/').pop())
|
|
||||||
|
|
||||||
return images
|
|
||||||
},
|
|
||||||
highlight: () => prismaClient.config.findUnique({ where: { name: 'highlight' } }),
|
|
||||||
recentComments: async (parent, { limit: take = 5 }) =>
|
|
||||||
prismaClient.comments.findMany({ take, orderBy: [{ updatedAt: 'desc' }] })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default composeResolvers(resolvers, resolversComposition)
|
|
||||||
|
|
@ -1,33 +0,0 @@
|
||||||
import { Op } from 'sequelize'
|
|
||||||
import { composeResolvers } from '@graphql-tools/resolvers-composition'
|
|
||||||
|
|
||||||
import info from 'next/constants/info.json'
|
|
||||||
import { hasRole } from 'server/utils/resolvers'
|
|
||||||
import { getUser } from 'next/utils/getSession'
|
|
||||||
|
|
||||||
const { permissions } = info
|
|
||||||
|
|
||||||
const resolversComposition = { 'Query.users': hasRole('MANAGE_USER') }
|
|
||||||
const resolvers = {
|
|
||||||
Query: {
|
|
||||||
me: (parent, args, { db }) => getUser(db),
|
|
||||||
permissions: () => permissions,
|
|
||||||
roles: (parent, args, { db }) => db.models.role.findAll(),
|
|
||||||
users: (parent, args, { db }) => {
|
|
||||||
const search = args.search.trim()
|
|
||||||
if (search.length < 3) return []
|
|
||||||
|
|
||||||
return db.models.user.findAll({
|
|
||||||
where: {
|
|
||||||
[Op.or]: [
|
|
||||||
{ username: { [Op.like]: `%${search}%` } },
|
|
||||||
{ email: search }
|
|
||||||
]
|
|
||||||
}
|
|
||||||
})
|
|
||||||
},
|
|
||||||
user: async (_, { user }, { db }) => user
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default composeResolvers(resolvers, resolversComposition)
|
|
||||||
|
|
@ -1,9 +0,0 @@
|
||||||
// import getPuppeteer from 'vgmdb-parser/lib/puppeteer'
|
|
||||||
|
|
||||||
const resolvers = {
|
|
||||||
Query: {
|
|
||||||
// vgmdb: (_, { url }) => getPuppeteer(url)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default resolvers
|
|
||||||
|
|
@ -1,149 +0,0 @@
|
||||||
// import { GraphQLUpload } from 'graphql-upload-minimal'
|
|
||||||
import prismaClient from 'prisma/client'
|
|
||||||
|
|
||||||
import type { Resolvers } from '@/graphql/__generated__/types.generated'
|
|
||||||
|
|
||||||
const resolvers: Resolvers = {
|
|
||||||
// Upload: GraphQLUpload,
|
|
||||||
Album: {
|
|
||||||
artists: (album) => prismaClient.artist.findMany({ where: { Album_Artist: { some: { albumId: album.id } } } })
|
|
||||||
/* categories: (parent, args, context, info) => parent.getCategories(),
|
|
||||||
classifications: (parent, args, context, info) =>
|
|
||||||
parent.getClassifications(),
|
|
||||||
platforms: (parent, args, context, info) =>
|
|
||||||
parent.getPlatforms({ order: ['name'] }),
|
|
||||||
games: (parent, args, context, info) => parent.getGames(),
|
|
||||||
discs: (parent, args, context, info) =>
|
|
||||||
parent.getDiscs({ order: [['number', 'ASC']] }),
|
|
||||||
related: (parent, args, context, info) => parent.getRelated(),
|
|
||||||
stores: (parent) => parent.getStores(),
|
|
||||||
animations: (parent) => parent.getAnimations(),
|
|
||||||
downloads: (parent) => parent.getDownloads(),
|
|
||||||
comments: (parent) => parent.getComments(),
|
|
||||||
isFavorite: async (album, _, { db }) => {
|
|
||||||
const user = await getUser(db)
|
|
||||||
return user ? album.hasUser(user.username) : null
|
|
||||||
},
|
|
||||||
selfComment: async (album, _, { db }) => {
|
|
||||||
const user = await getUser(db)
|
|
||||||
return user
|
|
||||||
? db.models.comment.findOne({
|
|
||||||
where: { albumId: album.id, username: user.username }
|
|
||||||
})
|
|
||||||
: null
|
|
||||||
},
|
|
||||||
selfScore: async (album, _, { db }) => {
|
|
||||||
const user = await getUser(db)
|
|
||||||
return user
|
|
||||||
? (
|
|
||||||
await db.models.rating.findOne({
|
|
||||||
where: { albumId: album.id, username: user.username }
|
|
||||||
})
|
|
||||||
)?.score
|
|
||||||
: null
|
|
||||||
},
|
|
||||||
favorites: (album, _, { db }) => album.countUsers(),
|
|
||||||
placeholder: (album, _, { db }) => checkPlaceholder(album, 'album'),
|
|
||||||
headerColor: (album, _, { db }) => checkHeaderColor(album, 'album'),
|
|
||||||
avgRating: async (album, _, { db }) => solveRating(album) */
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Comment: {
|
|
||||||
username: (parent) => (parent.anon ? null : parent.username),
|
|
||||||
album: (comment, _, { db }) => comment.getAlbum()
|
|
||||||
},
|
|
||||||
|
|
||||||
Category: {
|
|
||||||
albums: (parent) => parent.getAlbums(),
|
|
||||||
count: (parent, args, { db }) =>
|
|
||||||
db.models.album.count({
|
|
||||||
include: [{ model: db.models.category, where: { name: parent.name } }]
|
|
||||||
})
|
|
||||||
},
|
|
||||||
|
|
||||||
Download: {
|
|
||||||
links: async (download) => {
|
|
||||||
const links = await download.getLinks()
|
|
||||||
const filterLinks = links.filter(
|
|
||||||
(link) => !link.url.includes('adshrink.it')
|
|
||||||
)
|
|
||||||
const outLinks = filterLinks.length === 0 ? links : filterLinks
|
|
||||||
|
|
||||||
// return outLinks.filter((link) => link.provider !== 'TERABOX')
|
|
||||||
return outLinks
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
Link: {
|
|
||||||
url: async (link) => {
|
|
||||||
const download = await link.getDownload()
|
|
||||||
const links = await download.getLinks()
|
|
||||||
|
|
||||||
return links.every((link) => link.url.includes('adshrink.it'))
|
|
||||||
? link.directUrl
|
|
||||||
: link.url
|
|
||||||
},
|
|
||||||
|
|
||||||
directUrl: async (link, args, { db }) => {
|
|
||||||
const download = await link.getDownload()
|
|
||||||
const links = await download.getLinks()
|
|
||||||
|
|
||||||
const fallback = links.every((link) => link.url.includes('adshrink.it'))
|
|
||||||
if (fallback) return
|
|
||||||
|
|
||||||
const user = await getUser(db)
|
|
||||||
if (!user) return null
|
|
||||||
|
|
||||||
const roles = await user.getRoles()
|
|
||||||
const perms = roles.map((r) => r.permissions).flat()
|
|
||||||
|
|
||||||
const donator = perms.includes('DIRECT')
|
|
||||||
if (!donator) return null
|
|
||||||
|
|
||||||
return link.directUrl
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
Game: {
|
|
||||||
albums: async (game, { order = [] }) => game.getAlbums({ order }),
|
|
||||||
series: (parent, args, context, info) => parent.getSeries(),
|
|
||||||
publishers: (parent, args, context, info) => parent.getPublishers(),
|
|
||||||
platforms: (parent, args, context, info) =>
|
|
||||||
parent.getPlatforms({ order: ['name'] }),
|
|
||||||
placeholder: (game, _, { db }) => checkPlaceholder(game, 'game'),
|
|
||||||
headerColor: (game, _, { db }) => checkHeaderColor(game, 'game')
|
|
||||||
},
|
|
||||||
|
|
||||||
Platform: {
|
|
||||||
albums: (parent) => parent.getAlbums(),
|
|
||||||
games: (platform, args, { db }) => platform.getGames()
|
|
||||||
},
|
|
||||||
|
|
||||||
Animation: {
|
|
||||||
studios: (parent) => parent.getStudios(),
|
|
||||||
albums: (anim, { order = [] }) => anim.getAlbums({ order }),
|
|
||||||
placeholder: (anim, _, { db }) => checkPlaceholder(anim, 'anim'),
|
|
||||||
headerColor: (anim, _, { db }) => checkPlaceholder(anim, 'anim')
|
|
||||||
},
|
|
||||||
|
|
||||||
Studio: {
|
|
||||||
animations: (studio) => studio.getAnimations()
|
|
||||||
},
|
|
||||||
|
|
||||||
Series: {
|
|
||||||
games: (parent, args, context, info) => parent.getGames(),
|
|
||||||
placeholder: (series, _, { db }) => checkPlaceholder(series, 'series'),
|
|
||||||
headerColor: (series, _, { db }) => checkPlaceholder(series, 'series')
|
|
||||||
},
|
|
||||||
|
|
||||||
Publisher: {
|
|
||||||
games: (parent, args, context, info) => parent.getGames()
|
|
||||||
},
|
|
||||||
|
|
||||||
Disc: {
|
|
||||||
album: (parent) => parent.getAlbum(),
|
|
||||||
tracks: (parent) => parent.body.split(',')
|
|
||||||
} */
|
|
||||||
}
|
|
||||||
|
|
||||||
export default resolvers
|
|
||||||
|
|
@ -1,55 +0,0 @@
|
||||||
// import type { Resolvers } from '@/graphql/__generated__/types.generated'
|
|
||||||
|
|
||||||
const userResolvable = {
|
|
||||||
/* roles: parent => parent.getRoles(),
|
|
||||||
permissions: async parent => {
|
|
||||||
const roles = await parent.getRoles()
|
|
||||||
return roles.map(r => r.permissions).flat()
|
|
||||||
}, */
|
|
||||||
pages: async (_, __, { session }) => {
|
|
||||||
const roles = await parent.getRoles()
|
|
||||||
const permissions = roles.map((r) => r.permissions).flat()
|
|
||||||
|
|
||||||
return pages.filter(
|
|
||||||
({ perms }) =>
|
|
||||||
perms.length === 0 || perms.some((r) => permissions.includes(r))
|
|
||||||
)
|
|
||||||
}
|
|
||||||
/* comments: (user, _, { db }) => user.getComments({ where: { albumId: { [Op.not]: null } } }),
|
|
||||||
favorites: user => user.getAlbums(),
|
|
||||||
imgUrl: async user => `https://cdn.sittingonclouds.net/user/${user.imgId ? `${user.username}_${user.imgId}` : 'default'
|
|
||||||
}.png` */
|
|
||||||
}
|
|
||||||
|
|
||||||
const resolvers = {
|
|
||||||
// User: userResolvable,
|
|
||||||
UserMe: {
|
|
||||||
pages: async (_, __, { session }) => {
|
|
||||||
const roles = await parent.getRoles()
|
|
||||||
const permissions = roles.map((r) => r.permissions).flat()
|
|
||||||
|
|
||||||
return pages.filter(
|
|
||||||
({ perms }) =>
|
|
||||||
perms.length === 0 || perms.some((r) => permissions.includes(r))
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/*Role: { permissions: parent => typeof parent.permissions === 'string' || parent.permissions instanceof String ? JSON.parse(parent.permissions) : parent.permissions },
|
|
||||||
Submission: {
|
|
||||||
submitter: submission => submission.getUser(),
|
|
||||||
links: async (submission, _, { db }) => {
|
|
||||||
const user = await getUser(db)
|
|
||||||
if (!user) return null
|
|
||||||
|
|
||||||
const roles = await user.getRoles()
|
|
||||||
const perms = roles.map(r => r.permissions).flat()
|
|
||||||
|
|
||||||
if (!perms.includes('REQUESTS')) return null
|
|
||||||
|
|
||||||
return submission.links
|
|
||||||
},
|
|
||||||
request: submission => submission.getRequest()
|
|
||||||
}*/
|
|
||||||
}
|
|
||||||
|
|
||||||
export default resolvers
|
|
||||||
|
|
@ -1,6 +0,0 @@
|
||||||
import { makeExecutableSchema } from '@graphql-tools/schema'
|
|
||||||
import { typeDefs } from '@/graphql/__generated__/typeDefs.generated'
|
|
||||||
import resolvers from '@/graphql/resolvers'
|
|
||||||
|
|
||||||
const schema = makeExecutableSchema({ typeDefs, resolvers })
|
|
||||||
export default schema
|
|
||||||
|
|
@ -1,271 +0,0 @@
|
||||||
type Album {
|
|
||||||
id: ID!
|
|
||||||
title: String!
|
|
||||||
subTitle: String
|
|
||||||
releaseDate: String!
|
|
||||||
label: String
|
|
||||||
vgmdb: String
|
|
||||||
description: String
|
|
||||||
stores: [Store]!
|
|
||||||
discs: [Disc]!
|
|
||||||
artists: [Artist]!
|
|
||||||
categories: [Category]!
|
|
||||||
classifications: [Classification]!
|
|
||||||
platforms: [Platform]!
|
|
||||||
games: [Game]!
|
|
||||||
animations: [Animation]!
|
|
||||||
downloads: [Download]!
|
|
||||||
related: [Album]!
|
|
||||||
updatedAt: Float!
|
|
||||||
createdAt: Float!
|
|
||||||
status: String!
|
|
||||||
placeholder: String
|
|
||||||
headerColor: String!
|
|
||||||
}
|
|
||||||
|
|
||||||
type Disc {
|
|
||||||
id: ID!
|
|
||||||
number: Int
|
|
||||||
body: String
|
|
||||||
tracks: [String]
|
|
||||||
album: Album
|
|
||||||
}
|
|
||||||
|
|
||||||
type Download {
|
|
||||||
id: ID!
|
|
||||||
title: String
|
|
||||||
small: Boolean
|
|
||||||
links: [Link]
|
|
||||||
}
|
|
||||||
|
|
||||||
type Store {
|
|
||||||
id: ID!
|
|
||||||
provider: String
|
|
||||||
url: String
|
|
||||||
}
|
|
||||||
|
|
||||||
type Link {
|
|
||||||
id: ID!
|
|
||||||
provider: String
|
|
||||||
custom: String
|
|
||||||
url: String
|
|
||||||
directUrl: String
|
|
||||||
}
|
|
||||||
|
|
||||||
type Artist {
|
|
||||||
name: String!
|
|
||||||
slug: String!
|
|
||||||
albums: [Album]
|
|
||||||
}
|
|
||||||
|
|
||||||
type Category {
|
|
||||||
name: String!
|
|
||||||
albums: [Album]!
|
|
||||||
count: Int!
|
|
||||||
}
|
|
||||||
|
|
||||||
type Classification {
|
|
||||||
name: String!
|
|
||||||
}
|
|
||||||
|
|
||||||
type Game {
|
|
||||||
slug: String!
|
|
||||||
name: String
|
|
||||||
releaseDate: String
|
|
||||||
placeholder: String
|
|
||||||
publishers: [Publisher]
|
|
||||||
platforms: [Platform]
|
|
||||||
albums(order: [String]): [Album]
|
|
||||||
series: [Series]
|
|
||||||
headerColor: String!
|
|
||||||
}
|
|
||||||
|
|
||||||
type Animation {
|
|
||||||
id: ID!
|
|
||||||
title: String
|
|
||||||
subTitle: String
|
|
||||||
releaseDate: String
|
|
||||||
placeholder: String
|
|
||||||
studios: [Studio]!
|
|
||||||
albums(order: [String]): [Album]!
|
|
||||||
headerColor: String!
|
|
||||||
}
|
|
||||||
|
|
||||||
type Studio {
|
|
||||||
slug: String!
|
|
||||||
name: String
|
|
||||||
animations: [Animation]!
|
|
||||||
}
|
|
||||||
|
|
||||||
type Platform {
|
|
||||||
id: ID!
|
|
||||||
name: String
|
|
||||||
type: String!
|
|
||||||
albums: [Album]
|
|
||||||
games: [Game]!
|
|
||||||
}
|
|
||||||
|
|
||||||
type Publisher {
|
|
||||||
id: ID!
|
|
||||||
name: String
|
|
||||||
games: [Game]
|
|
||||||
}
|
|
||||||
|
|
||||||
type Series {
|
|
||||||
slug: String!
|
|
||||||
name: String
|
|
||||||
placeholder: String
|
|
||||||
games: [Game]
|
|
||||||
headerColor: String!
|
|
||||||
}
|
|
||||||
|
|
||||||
type Query {
|
|
||||||
albums: [Album!]!
|
|
||||||
downloads(id: ID!): [Download]!
|
|
||||||
albumCount: Float!
|
|
||||||
categories: [Category]!
|
|
||||||
classifications: [Classification]!
|
|
||||||
album(id: ID): Album
|
|
||||||
artists: [Artist!]!
|
|
||||||
platforms: [Platform!]!
|
|
||||||
platform(id: ID): Platform!
|
|
||||||
publishers: [Publisher]!
|
|
||||||
publisher(id: ID!): Publisher!
|
|
||||||
series: [Series]!
|
|
||||||
seriesOne(slug: String): Series
|
|
||||||
games: [Game!]!
|
|
||||||
game(slug: String): Game!
|
|
||||||
animations: [Animation]!
|
|
||||||
animation(id: ID): Animation!
|
|
||||||
studios: [Studio]!
|
|
||||||
studio(slug: String!): Studio!
|
|
||||||
|
|
||||||
highlight: Album!
|
|
||||||
|
|
||||||
searchAlbum(
|
|
||||||
title: String
|
|
||||||
categories: [String]
|
|
||||||
offset: Int
|
|
||||||
limit: Int
|
|
||||||
order: [String]
|
|
||||||
mode: String
|
|
||||||
status: [String!]
|
|
||||||
): [Album]!
|
|
||||||
searchAlbumByArtist(
|
|
||||||
name: String!
|
|
||||||
categories: [String]
|
|
||||||
limit: Int
|
|
||||||
page: Int
|
|
||||||
order: [String]
|
|
||||||
mode: String
|
|
||||||
status: [String!]
|
|
||||||
): SearchAlbumResult
|
|
||||||
searchAnimation(title: String, limit: Int, page: Int, order: String, mode: String): SearchAnimResult
|
|
||||||
searchStudio(name: String, limit: Int, page: Int, order: String, mode: String): SearchStudioResult
|
|
||||||
searchGame(name: String, limit: Int, page: Int, order: String, mode: String): SearchGameResult
|
|
||||||
searchSeries(name: String, limit: Int, page: Int, order: String, mode: String): SearchSeriesResult
|
|
||||||
|
|
||||||
searchSeriesByName(name: String): [Series]
|
|
||||||
recentSeries(limit: Int!): [Series]
|
|
||||||
searchPublishersByName(name: String): [Publisher]
|
|
||||||
recentPublishers(limit: Int!): [Publisher]
|
|
||||||
searchPlatformsByName(name: String, categories: [String]!): [Platform]
|
|
||||||
searchPlatformsByCategories(categories: [String]!): [Platform]!
|
|
||||||
recentPlatforms(limit: Int!, type: [String]!): [Platform]
|
|
||||||
|
|
||||||
getRandomAlbum(limit: Int): [Album!]!
|
|
||||||
}
|
|
||||||
|
|
||||||
type Mutation {
|
|
||||||
createPlatform(name: String, type: String!): Platform!
|
|
||||||
updatePlatform(key: ID!, name: String, type: String!): Platform!
|
|
||||||
deletePlatform(key: ID!): Int
|
|
||||||
|
|
||||||
createPublisher(name: String): Publisher!
|
|
||||||
updatePublisher(id: ID!, name: String): Publisher!
|
|
||||||
deletePublisher(id: ID!): Int
|
|
||||||
|
|
||||||
createSeries(slug: String, name: String, cover: File!): Series!
|
|
||||||
updateSeries(slug: String, name: String, cover: File): Series!
|
|
||||||
deleteSeries(slug: String!): Int
|
|
||||||
|
|
||||||
createGame(
|
|
||||||
releaseDate: String
|
|
||||||
slug: String
|
|
||||||
name: String
|
|
||||||
publishers: [ID]
|
|
||||||
series: [String]
|
|
||||||
platforms: [ID]
|
|
||||||
cover: File!
|
|
||||||
): Game!
|
|
||||||
updateGame(
|
|
||||||
releaseDate: String
|
|
||||||
slug: String
|
|
||||||
name: String
|
|
||||||
publishers: [ID]
|
|
||||||
series: [String]
|
|
||||||
platforms: [ID]
|
|
||||||
cover: File
|
|
||||||
): Game!
|
|
||||||
deleteGame(slug: String!): Int
|
|
||||||
|
|
||||||
createStudio(slug: String, name: String): Studio!
|
|
||||||
updateStudio(slug: String, name: String): Studio!
|
|
||||||
deleteStudio(slug: String!): Int
|
|
||||||
|
|
||||||
createAnimation(title: String, subTitle: String, releaseDate: String, studios: [String], cover: File): Animation
|
|
||||||
updateAnimation(
|
|
||||||
id: ID!
|
|
||||||
title: String
|
|
||||||
subTitle: String
|
|
||||||
releaseDate: String
|
|
||||||
studios: [String]
|
|
||||||
cover: File
|
|
||||||
): Animation
|
|
||||||
deleteAnimation(id: ID!): Int
|
|
||||||
|
|
||||||
createAlbum(
|
|
||||||
title: String
|
|
||||||
subTitle: String
|
|
||||||
cover: File
|
|
||||||
releaseDate: String
|
|
||||||
label: String
|
|
||||||
vgmdb: String
|
|
||||||
description: String
|
|
||||||
stores: [StoreInput]
|
|
||||||
downloads: [DownloadInput]
|
|
||||||
artists: [String]
|
|
||||||
categories: [String]
|
|
||||||
classifications: [String]
|
|
||||||
platforms: [ID]
|
|
||||||
games: [String]
|
|
||||||
animations: [ID]
|
|
||||||
discs: [DiscInput]
|
|
||||||
related: [ID]
|
|
||||||
status: String!
|
|
||||||
request: ID
|
|
||||||
): Album!
|
|
||||||
updateAlbum(
|
|
||||||
id: ID!
|
|
||||||
title: String
|
|
||||||
subTitle: String
|
|
||||||
cover: File
|
|
||||||
releaseDate: String
|
|
||||||
label: String
|
|
||||||
vgmdb: String
|
|
||||||
description: String
|
|
||||||
stores: [StoreInput]
|
|
||||||
downloads: [DownloadInput]
|
|
||||||
artists: [String]
|
|
||||||
categories: [String]
|
|
||||||
classifications: [String]
|
|
||||||
platforms: [ID]
|
|
||||||
games: [String]
|
|
||||||
animations: [ID]
|
|
||||||
discs: [DiscInput]
|
|
||||||
related: [ID]
|
|
||||||
status: String!
|
|
||||||
request: ID
|
|
||||||
): Album!
|
|
||||||
deleteAlbum(id: ID!): Int
|
|
||||||
}
|
|
||||||
|
|
@ -1,42 +0,0 @@
|
||||||
type Comment {
|
|
||||||
id: ID!
|
|
||||||
text: String!
|
|
||||||
anon: Boolean!
|
|
||||||
album: Album!
|
|
||||||
username: String
|
|
||||||
}
|
|
||||||
|
|
||||||
type Album {
|
|
||||||
comments: [Comment]!
|
|
||||||
selfComment: Comment
|
|
||||||
isFavorite: Boolean
|
|
||||||
favorites: Int!
|
|
||||||
avgRating: AvgRating!
|
|
||||||
selfScore: Int
|
|
||||||
}
|
|
||||||
|
|
||||||
type User {
|
|
||||||
comments: [Comment]!
|
|
||||||
favorites: [Album]!
|
|
||||||
}
|
|
||||||
|
|
||||||
type UserMe {
|
|
||||||
comments: [Comment]!
|
|
||||||
favorites: [Album]!
|
|
||||||
}
|
|
||||||
|
|
||||||
type AvgRating {
|
|
||||||
score: Float!
|
|
||||||
users: Int!
|
|
||||||
}
|
|
||||||
|
|
||||||
type Query {
|
|
||||||
recentComments(limit: Int): [Comment]!
|
|
||||||
}
|
|
||||||
|
|
||||||
type Mutation {
|
|
||||||
updateComment(text: String!, anon: Boolean!, albumId: ID!): Boolean
|
|
||||||
addFavorite(albumId: String!): Boolean
|
|
||||||
removeFavorite(albumId: String!): Boolean
|
|
||||||
rateAlbum(albumId: ID!, score: Int!): Boolean
|
|
||||||
}
|
|
||||||
|
|
@ -1,27 +0,0 @@
|
||||||
input ArtistInput {
|
|
||||||
name: String!
|
|
||||||
slug: String!
|
|
||||||
}
|
|
||||||
|
|
||||||
input StoreInput {
|
|
||||||
provider: String!
|
|
||||||
url: String!
|
|
||||||
}
|
|
||||||
|
|
||||||
input DownloadInput {
|
|
||||||
title: String
|
|
||||||
small: Boolean
|
|
||||||
links: [LinkInput]
|
|
||||||
}
|
|
||||||
|
|
||||||
input LinkInput {
|
|
||||||
provider: String
|
|
||||||
custom: String
|
|
||||||
url: String
|
|
||||||
directUrl: String
|
|
||||||
}
|
|
||||||
|
|
||||||
input DiscInput {
|
|
||||||
number: Int
|
|
||||||
body: String
|
|
||||||
}
|
|
||||||
|
|
@ -1,64 +0,0 @@
|
||||||
type Request {
|
|
||||||
id: ID!
|
|
||||||
title: String
|
|
||||||
link: String
|
|
||||||
user: String
|
|
||||||
userID: String
|
|
||||||
state: String!
|
|
||||||
donator: Boolean!
|
|
||||||
reason: String
|
|
||||||
comments: String
|
|
||||||
message: String
|
|
||||||
}
|
|
||||||
|
|
||||||
type Submission {
|
|
||||||
id: ID!
|
|
||||||
title: String!
|
|
||||||
vgmdb: String
|
|
||||||
request: Request
|
|
||||||
links: String
|
|
||||||
score: Int!
|
|
||||||
state: String!
|
|
||||||
submitter: User!
|
|
||||||
}
|
|
||||||
|
|
||||||
type RequestResult {
|
|
||||||
count: Int!
|
|
||||||
rows: [Request]!
|
|
||||||
}
|
|
||||||
|
|
||||||
type Query {
|
|
||||||
requests(
|
|
||||||
state: [String!],
|
|
||||||
donator: [Boolean]!
|
|
||||||
): [Request]!
|
|
||||||
request(link: String!): Request
|
|
||||||
searchRequests(
|
|
||||||
state: [String!]
|
|
||||||
donator: [Boolean!]
|
|
||||||
limit: Int
|
|
||||||
page: Int
|
|
||||||
filter: String
|
|
||||||
): RequestResult!
|
|
||||||
submissions(filter: String, state: [String]): [Submission]!
|
|
||||||
}
|
|
||||||
|
|
||||||
type Mutation {
|
|
||||||
editRequest(
|
|
||||||
id: ID!
|
|
||||||
title: String
|
|
||||||
link: String
|
|
||||||
state: String
|
|
||||||
comments: String
|
|
||||||
reason: String
|
|
||||||
): Request!
|
|
||||||
|
|
||||||
submitAlbum(
|
|
||||||
title: String!
|
|
||||||
vgmdb: String
|
|
||||||
request: ID
|
|
||||||
links: String!
|
|
||||||
): Submission!
|
|
||||||
|
|
||||||
rejectRequest(id: ID!, reason: String): Boolean!
|
|
||||||
}
|
|
||||||
|
|
@ -1,24 +0,0 @@
|
||||||
type SearchAlbumResult {
|
|
||||||
rows: [Album]
|
|
||||||
count: Int
|
|
||||||
}
|
|
||||||
|
|
||||||
type SearchAnimResult {
|
|
||||||
rows: [Animation]
|
|
||||||
count: Int
|
|
||||||
}
|
|
||||||
|
|
||||||
type SearchStudioResult {
|
|
||||||
rows: [Studio]
|
|
||||||
count: Int
|
|
||||||
}
|
|
||||||
|
|
||||||
type SearchGameResult {
|
|
||||||
rows: [Game]
|
|
||||||
count: Int
|
|
||||||
}
|
|
||||||
|
|
||||||
type SearchSeriesResult {
|
|
||||||
rows: [Series]
|
|
||||||
count: Int
|
|
||||||
}
|
|
||||||
|
|
@ -1,19 +0,0 @@
|
||||||
scalar File
|
|
||||||
scalar JSON
|
|
||||||
scalar JSONObject
|
|
||||||
|
|
||||||
type Config {
|
|
||||||
name: String!
|
|
||||||
value: String
|
|
||||||
}
|
|
||||||
|
|
||||||
type Query {
|
|
||||||
config(name: String): Config
|
|
||||||
banners: [String]!
|
|
||||||
}
|
|
||||||
|
|
||||||
type Mutation {
|
|
||||||
uploadBanner(banner: File!): Int
|
|
||||||
selectBanner(name: String!): Int
|
|
||||||
config(name: String!, value: String!): Config!
|
|
||||||
}
|
|
||||||
|
|
@ -1,55 +0,0 @@
|
||||||
type User {
|
|
||||||
username: String!
|
|
||||||
roles: [Role]!
|
|
||||||
permissions: [String]!
|
|
||||||
pages: [Page]!
|
|
||||||
createdAt: Float!
|
|
||||||
imgUrl: String!
|
|
||||||
}
|
|
||||||
|
|
||||||
type UserMe {
|
|
||||||
email: String!
|
|
||||||
username: String!
|
|
||||||
roles: [Role]!
|
|
||||||
permissions: [String]!
|
|
||||||
pages: [Page]!
|
|
||||||
createdAt: Float!
|
|
||||||
imgUrl: String!
|
|
||||||
}
|
|
||||||
|
|
||||||
type Page {
|
|
||||||
url: String!
|
|
||||||
perms: [String!]!
|
|
||||||
}
|
|
||||||
|
|
||||||
type Role {
|
|
||||||
name: String!
|
|
||||||
permissions: [String]!
|
|
||||||
}
|
|
||||||
|
|
||||||
type Query {
|
|
||||||
me: UserMe
|
|
||||||
roles: [Role]!
|
|
||||||
permissions: [String]!
|
|
||||||
users(search: String): [User]!
|
|
||||||
user(username: String!): User
|
|
||||||
|
|
||||||
login(username: String!, password: String!): Int!
|
|
||||||
}
|
|
||||||
|
|
||||||
type Mutation {
|
|
||||||
login(username: String!, password: String!): String!
|
|
||||||
logout: Boolean!
|
|
||||||
|
|
||||||
registerUser(email: String!, username: String!, pfp: File): Boolean!
|
|
||||||
updateUserRoles(username: String!, roles: [String]!): Boolean!
|
|
||||||
deleteUser(username: String!): Int
|
|
||||||
|
|
||||||
createForgorLink(key: String!): Boolean!
|
|
||||||
updatePass(key: String!, pass: String!): Boolean!
|
|
||||||
updateUser(username: String, password: String, email: String, pfp: File): Boolean!
|
|
||||||
|
|
||||||
createRole(name: String!, permissions: [String]!): Role
|
|
||||||
updateRole(key: String!, name: String!, permissions: [String]!): Role
|
|
||||||
deleteRole(name: String!): String
|
|
||||||
}
|
|
||||||
|
|
@ -1,19 +0,0 @@
|
||||||
type VgmResult {
|
|
||||||
title: String
|
|
||||||
subTitle: String
|
|
||||||
releaseDate: String
|
|
||||||
coverUrl: String
|
|
||||||
artists: [String]!
|
|
||||||
categories: [String]!
|
|
||||||
classifications: [String]!
|
|
||||||
trackList: [VGMDBDisc]!
|
|
||||||
}
|
|
||||||
|
|
||||||
type VGMDBDisc {
|
|
||||||
number: Int
|
|
||||||
tracks: [String!]
|
|
||||||
}
|
|
||||||
|
|
||||||
type Query {
|
|
||||||
vgmdb(url: String!): VgmResult
|
|
||||||
}
|
|
||||||
|
|
@ -1,15 +0,0 @@
|
||||||
import type { APIRoute } from 'astro'
|
|
||||||
import { createYoga } from 'graphql-yoga'
|
|
||||||
|
|
||||||
import schema from '@/graphql/schema'
|
|
||||||
|
|
||||||
const { handleRequest } = createYoga({
|
|
||||||
schema,
|
|
||||||
graphqlEndpoint: '/api/graphql',
|
|
||||||
fetchAPI: { Request, Response }
|
|
||||||
})
|
|
||||||
|
|
||||||
export const POST: APIRoute = async (context) => {
|
|
||||||
const { request } = context
|
|
||||||
return handleRequest(request, context)
|
|
||||||
}
|
|
||||||
|
|
@ -1,40 +1,27 @@
|
||||||
import rss, { type RSSFeedItem } from '@astrojs/rss'
|
import rss, { type RSSFeedItem } from '@astrojs/rss'
|
||||||
import type { APIContext } from 'astro'
|
import type { APIContext } from 'astro'
|
||||||
|
import prismaClient from 'utils/prisma-client'
|
||||||
import { getApolloClient } from '@/graphql/apolloClientSSR'
|
|
||||||
import { gql } from '@/graphql/__generated__/client'
|
|
||||||
|
|
||||||
const addedQuery = gql(`
|
|
||||||
query LastAdded ($limit: Int) {
|
|
||||||
added: searchAlbum(limit: $limit, status: ["show"]) {
|
|
||||||
id
|
|
||||||
createdAt
|
|
||||||
title
|
|
||||||
subTitle
|
|
||||||
artists {
|
|
||||||
name
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`)
|
|
||||||
|
|
||||||
export async function GET(context: APIContext) {
|
export async function GET(context: APIContext) {
|
||||||
const client = await getApolloClient()
|
const albums = await prismaClient.albums.findMany({
|
||||||
const { data } = await client.query({ query: addedQuery, variables: { limit: 15 } })
|
where: { status: 'show' },
|
||||||
const { added } = data
|
include: { artistList: { include: { artist: { select: { name: true } } } } },
|
||||||
|
take: 15,
|
||||||
|
orderBy: { createdAt: 'desc' }
|
||||||
|
})
|
||||||
|
|
||||||
const items: RSSFeedItem[] = added.map((album) => ({
|
const items: RSSFeedItem[] = albums.map((album) => ({
|
||||||
guid: `album/${album?.id}`,
|
guid: `album/${album.id}`,
|
||||||
title: album?.title,
|
title: album.title || 'Error: Missing title',
|
||||||
pubDate: new Date(album?.createdAt || ''),
|
pubDate: new Date(album.createdAt || ''),
|
||||||
description: album?.subTitle || album?.artists.map((a) => a?.name).join(' - '),
|
description: album.subTitle || album.artistList.map((a) => a.artist.name).join(' - '),
|
||||||
link: `https://www.sittingonclouds.net/album/${album?.id}`,
|
link: `https://www.sittingonclouds.net/album/${album.id}`,
|
||||||
customData: `<media:content
|
customData: `<media:content
|
||||||
type="image/png"
|
type="image/png"
|
||||||
width="100"
|
width="100"
|
||||||
height="100"
|
height="100"
|
||||||
medium="image"
|
medium="image"
|
||||||
url="https://cdn.sittingonclouds.net/album/${album?.id}.png" />
|
url="https://cdn.sittingonclouds.net/album/${album.id}.png" />
|
||||||
`
|
`
|
||||||
}))
|
}))
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue