Migrate to Sequelize V7

This commit is contained in:
Jorge Vargas 2024-08-29 23:55:25 -06:00
parent a5a5f4ee2a
commit a79cc8e172
45 changed files with 329 additions and 422 deletions

View file

@ -1,21 +1,24 @@
/** @type {import("eslint").Linter.Config} */ /** @type {import("eslint").Linter.Config} */
module.exports = { module.exports = {
extends: ["plugin:astro/recommended"], extends: ['plugin:astro/recommended'],
parser: "@typescript-eslint/parser", parser: '@typescript-eslint/parser',
parserOptions: { parserOptions: {
tsconfigRootDir: __dirname, tsconfigRootDir: __dirname,
sourceType: "module", sourceType: 'module',
ecmaVersion: "latest", ecmaVersion: 'latest'
}, },
overrides: [ overrides: [
{ {
files: ["*.astro"], files: ['*.astro'],
parser: "astro-eslint-parser", parser: 'astro-eslint-parser',
parserOptions: { parserOptions: {
parser: "@typescript-eslint/parser", parser: '@typescript-eslint/parser',
extraFileExtensions: [".astro"], extraFileExtensions: ['.astro']
}, },
rules: {}, rules: {}
}, }
], ],
}; rules: {
'no-multiple-empty-lines': [2, { max: 1 }]
}
}

View file

@ -27,14 +27,13 @@
"@graphql-tools/resolvers-composition": "^7.0.1", "@graphql-tools/resolvers-composition": "^7.0.1",
"@graphql-tools/schema": "^10.0.4", "@graphql-tools/schema": "^10.0.4",
"@inlang/paraglide-astro": "^0.2.2", "@inlang/paraglide-astro": "^0.2.2",
"@sequelize/core": "^7.0.0-alpha.37",
"@sequelize/mysql": "^7.0.0-alpha.41",
"astro": "^4.13.1", "astro": "^4.13.1",
"astro-icon": "^1.1.1", "astro-icon": "^1.1.1",
"auth-astro": "^4.1.2", "auth-astro": "^4.1.2",
"bcrypt": "^5.1.1", "bcrypt": "^5.1.1",
"graphql-scalars": "^1.23.0", "graphql-scalars": "^1.23.0",
"lodash": "^4.17.21",
"mysql2": "^3.11.0",
"sequelize": "^6.37.3",
"sharp": "^0.33.5", "sharp": "^0.33.5",
"tailwindcss": "^3.4.8" "tailwindcss": "^3.4.8"
}, },
@ -42,7 +41,6 @@
"@inlang/paraglide-js": "1.11.2", "@inlang/paraglide-js": "1.11.2",
"@parcel/watcher": "^2.4.1", "@parcel/watcher": "^2.4.1",
"@types/bcrypt": "^5.0.2", "@types/bcrypt": "^5.0.2",
"@types/lodash": "^4.17.7",
"@typescript-eslint/parser": "^6.21.0", "@typescript-eslint/parser": "^6.21.0",
"concurrently": "^8.2.2", "concurrently": "^8.2.2",
"eslint": "^8.57.0", "eslint": "^8.57.0",

View file

@ -1,14 +1,15 @@
/** @type {import("prettier").Config} */ /** @type {import("prettier").Config} */
module.exports = { module.exports = {
...require("prettier-config-standard"), ...require('prettier-config-standard'),
pluginSearchDirs: [__dirname], pluginSearchDirs: [__dirname],
plugins: [require.resolve("prettier-plugin-astro")], plugins: [require.resolve('prettier-plugin-astro')],
overrides: [ overrides: [
{ {
files: "*.astro", files: '*.astro',
options: { options: {
parser: "astro", parser: 'astro'
}, }
}, }
], ]
}; }

View file

@ -2,26 +2,26 @@ import ApolloPackage from '@apollo/client'
const { ApolloClient, InMemoryCache } = ApolloPackage; const { ApolloClient, InMemoryCache } = ApolloPackage;
import { SchemaLink } from "@apollo/client/link/schema" import { SchemaLink } from "@apollo/client/link/schema"
import { makeExecutableSchema } from "@graphql-tools/schema"; import { makeExecutableSchema } from "@graphql-tools/schema";
import { loadFilesSync } from '@graphql-tools/load-files' import { getSession } from 'auth-astro/server';
import { mergeResolvers } from '@graphql-tools/merge' import type { Session } from '@auth/core/types';
import path from "node:path"
import { typeDefs } from "./__generated__/typeDefs.generated"; import { typeDefs } from "./__generated__/typeDefs.generated";
// import { resolvers } from "./__generated__/resolvers.generated"; import resolvers from '@/graphql/resolvers'
import db from "@/sequelize"; import type { MySqlDialect } from '@sequelize/mysql';
import resolverArray from '@/graphql/resolvers' import { Sequelize, type ModelStatic, type Options } from '@sequelize/core'
export const resolvers = mergeResolvers(resolverArray)
const schema = makeExecutableSchema({ typeDefs, resolvers }) const schema = makeExecutableSchema({ typeDefs, resolvers })
export type ResolverContext = { request?: Request, db: any /*session?: Session */ } export type ResolverContext = { request?: Request, session?: Session }
const envOptions: Options<MySqlDialect> = JSON.parse(import.meta.env.SEQUELIZE) || {}
export async function getApolloClient(request?: Request) { export async function getApolloClient(request?: Request) {
// const session = request ? await getSession(request) : undefined const session = request ? await getSession(request) : null
const db = new Sequelize(envOptions)
envOptions.models = Object.values<ModelStatic>(await import.meta.glob('@/sequelize/models/**/*.ts', { eager: true, import: 'default' }))
return new ApolloClient({ return new ApolloClient({
ssrMode: true, ssrMode: true,
link: new SchemaLink({ schema, context: { request, db } }), link: new SchemaLink({ schema, context: { request, session } }),
cache: new InMemoryCache() cache: new InMemoryCache()
}) })
} }

View file

@ -1,7 +1,7 @@
// import mutations from './mutations' import { mergeResolvers } from '@graphql-tools/merge'
import queries from './queries' import type { IResolvers } from '@graphql-tools/utils'
import types from './types'
const resolvers = { /*...mutations,*/ ...queries, ...types } const imports: IResolvers[] = Object.values(import.meta.glob('./**/*.ts', { eager: true, import: 'default' }))
const resolvers = mergeResolvers(imports)
export default resolvers export default resolvers

View file

@ -1,12 +0,0 @@
import merge from 'lodash/merge'
import comments from './comments'
import create from './create'
import requests from './requests'
import site from './site'
import update from './update'
import user from './user'
const mutations = merge(comments, create, requests, site, update, user)
export default mutations

View file

@ -1,26 +1,11 @@
import bcrypt from 'bcrypt'
import generator from 'generate-password'
import { composeResolvers } from '@graphql-tools/resolvers-composition' import { composeResolvers } from '@graphql-tools/resolvers-composition'
import { DateTime } from 'luxon' import type { Resolvers } from '@/graphql/__generated__/types.generated'
import { Op } from 'sequelize'
import path from 'path'
import fs from 'fs-extra'
import sharp from 'sharp'
import { createForgor } from '@/server/utils/forgor'
import { isAuthedApp } from '@/server/utils/resolvers'
import { processImage } from '@/server/utils/image'
import { getSession, getUser } from '@/next/utils/getSession'
import {
ForbiddenError,
UserInputError
} from '@/next/server/utils/graphQLErrors'
const resolversComposition = { const resolversComposition = {
'Mutation.updateUser': [isAuthedApp] //'Mutation.updateUser': [isAuthedApp]
} }
const streamToString = (stream) => { /* const streamToString = (stream) => {
const chunks = [] const chunks = []
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
stream.on('data', (chunk) => chunks.push(Buffer.from(chunk))) stream.on('data', (chunk) => chunks.push(Buffer.from(chunk)))
@ -45,51 +30,28 @@ async function cropPFP(streamItem, username, imgId) {
sharpImage = sharpImage.extract( sharpImage = sharpImage.extract(
width > height width > height
? { ? {
left: Math.floor((width - height) / 2), left: Math.floor((width - height) / 2),
top: 0, top: 0,
width: height, width: height,
height height
} }
: { : {
left: 0, left: 0,
top: Math.floor((height - width) / 2), top: Math.floor((height - width) / 2),
width, width,
height: width height: width
} }
) )
} }
await sharpImage.resize({ width: 200, height: 200 }).png().toFile(fullPath) await sharpImage.resize({ width: 200, height: 200 }).png().toFile(fullPath)
return await processImage(fullPath) return await processImage(fullPath)
} }*/
const resolvers = { const resolvers: Resolvers = {
Mutation: { Mutation: {
login: async (_, { username, password }, { db }) => { /*registerUser: async (_, { username, email, pfp }, { db }) => {
const user = await db.models.user.findByPk(username)
if (!user) throw UserInputError()
const valid = await bcrypt.compare(password, user.password)
if (!valid) throw UserInputError()
const session = await getSession()
session.username = user.username
// Remove this when new site version is fully implemented
session.permissions = (await user.getRoles())
.map((r) => r.permissions)
.flat()
await session.save()
return 200
},
logout: async () => {
const session = await getSession()
await session.destroy()
return 200
},
registerUser: async (_, { username, email, pfp }, { db }) => {
await Promise.all([ await Promise.all([
db.models.user.findByPk(username).then((result) => { db.models.user.findByPk(username).then((result) => {
if (result) throw UserInputError('Username already in use') if (result) throw UserInputError('Username already in use')
@ -213,7 +175,7 @@ const resolvers = {
await role.destroy() await role.destroy()
return name return name
} }*/
} }
} }

View file

@ -1,9 +1,8 @@
import type { Resolvers } from '@/graphql/__generated__/types.generated' import Sequelize from '@/sequelize'
import Sequelize from 'sequelize'
const { Op } = Sequelize const { Op } = Sequelize
const resolvers: Resolvers = { const resolvers = {
Query: { Query: {
artists: (parent, args, { db }, info) => db.models.artist.findAll(), artists: (parent, args, { db }, info) => db.models.artist.findAll(),
platforms: (parent, args, { db }, info) => db.models.platform.findAll(), platforms: (parent, args, { db }, info) => db.models.platform.findAll(),
@ -19,9 +18,7 @@ const resolvers: Resolvers = {
album: (_, { id }, { db }) => db.models.album.findByPk(id), album: (_, { id }, { db }) => db.models.album.findByPk(id),
downloads: (parent, { id }, { db }) => downloads: (parent, { id }, { db }) =>
db.models.download.findAll({ where: { albumId: id } }), db.models.download.findAll({ where: { albumId: id } }),
albums: (_, __, { db }, info) => albums: (_, __, { db }, info) => db.models.album.findAll({}),
db.models.album.findAll({
}),
platform: async (parent, { id }, { db }) => db.models.platform.findByPk(id), platform: async (parent, { id }, { db }) => db.models.platform.findByPk(id),
animation: (parent, { id }, { db }) => db.models.animation.findByPk(id), animation: (parent, { id }, { db }) => db.models.animation.findByPk(id),

View file

@ -1,16 +0,0 @@
import merge from 'lodash/merge'
import search from './search'
import site from './site'
/*
import album from './album'
import requests from './requests'
import user from './user'
import vgmdb from './vgmdb'
*/
const queries = merge(search, site)
export default queries

View file

@ -1,24 +1,30 @@
import { Op, fn, col, where } from 'sequelize' import { Op, fn, col, where } from '@/sequelize'
const resolvers = { const resolvers = {
Query: { Query: {
requests: (_, { requests: (
state = ['complete', 'hold', 'pending'], _,
donator = [true, false] { state = ['complete', 'hold', 'pending'], donator = [true, false] },
}, { db }) => db.models.request.findAll({ where: { state, donator } }), { db }
request: (_, { link }, { db }) => db.models.request.findOne({ where: { link } }), ) => db.models.request.findAll({ where: { state, donator } }),
request: (_, { link }, { db }) =>
db.models.request.findOne({ where: { link } }),
searchRequests: async (_, { searchRequests: async (
state = ['complete', 'hold', 'pending'], _,
donator = [true, false], {
limit = 10, state = ['complete', 'hold', 'pending'],
page = 0, donator = [true, false],
filter limit = 10,
}, { db }) => { page = 0,
filter
},
{ db }
) => {
const options = { limit, offset: limit * page } const options = { limit, offset: limit * page }
const optionsWhere = { state, donator } const optionsWhere = { state, donator }
async function exactSearch () { async function exactSearch() {
if (!filter) return if (!filter) return
const results = await db.models.request.findAndCountAll({ const results = await db.models.request.findAndCountAll({
@ -37,7 +43,7 @@ const resolvers = {
if (results.rows.length > 0) return results if (results.rows.length > 0) return results
} }
function looseSearch () { function looseSearch() {
return db.models.request.findAndCountAll({ return db.models.request.findAndCountAll({
where: [ where: [
optionsWhere, optionsWhere,
@ -47,7 +53,7 @@ const resolvers = {
}) })
} }
return await exactSearch() || looseSearch() return (await exactSearch()) || looseSearch()
}, },
submissions: (_, args, context) => { submissions: (_, args, context) => {
@ -63,7 +69,9 @@ const resolvers = {
{ id: filter }, { id: filter },
{ vgmdb: filter }, { vgmdb: filter },
{ userUsername: filter }, { userUsername: filter },
where(fn('LOWER', col('title')), { [Op.like]: `%${filter.toLowerCase()}%` }) where(fn('LOWER', col('title')), {
[Op.like]: `%${filter.toLowerCase()}%`
})
] ]
} }
] ]

View file

@ -1,14 +1,14 @@
//@ts-ignore import { Op, literal } from '@sequelize/core'
import { Op, literal } from 'sequelize'
import type { Resolvers } from '@/graphql/__generated__/types.generated' import type { Resolvers } from '@/graphql/__generated__/types.generated'
import Category from '@/sequelize/models/category'
import Album from '@/sequelize/models/album'
const fuzzySearch = (words: string[]) => `^${words.map(w => `(?=.*\b${w}\b)`)}.+/i` const fuzzySearch = (words: string[]) => `^${words.map(w => `(?=.*\b${w}\b)`)}.+/i`
const resolvers: Resolvers = { const resolvers: Resolvers = {
Query: { Query: {
searchAlbum: (_, args, { db }) => { searchAlbum: async (_, args, { db }) => {
const { title, categories, limit = 10, offset = 0, order = ['createdAt'], mode = 'DESC', status = ['show'] } = args const { title, categories, limit = 10, offset = 0, order = ['createdAt'], mode = 'DESC', status = ['show'] } = args
const fuzzyCondition = title ? { const fuzzyCondition = title ? {
[Op.or]: [ [Op.or]: [
@ -18,9 +18,9 @@ const resolvers: Resolvers = {
} : {} } : {}
const include = [] const include = []
if (categories) include.push({ model: db.models.category, where: { name: { [Op.in]: categories } } }) if (categories) include.push({ model: Category, where: { name: { [Op.in]: categories } } })
return db.models.album.findAndCountAll({ const result = await Album.findAndCountAll({
limit, offset, limit, offset,
where: { where: {
...fuzzyCondition, ...fuzzyCondition,
@ -29,6 +29,8 @@ const resolvers: Resolvers = {
include, include,
order: [literal('`album`.`status` = \'coming\' DESC'), ...order.map(o => [o, mode])] order: [literal('`album`.`status` = \'coming\' DESC'), ...order.map(o => [o, mode])]
}) })
return result
} }
/* searchAlbumByArtist: async (parent, { name, categories, limit, page = 0, order = ['createdAt'], mode = 'DESC', status = ['show'] }, { db }) => { /* 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}%` } } }] const include = [{ model: db.models.artist, where: { name: { [Op.like]: `%${name}%` } } }]

View file

@ -2,6 +2,8 @@
import type { Resolvers } from '@/graphql/__generated__/types.generated' import type { Resolvers } from '@/graphql/__generated__/types.generated'
import { composeResolvers } from '@graphql-tools/resolvers-composition' import { composeResolvers } from '@graphql-tools/resolvers-composition'
import Config from '@/sequelize/models/config'
// import { hasRole } from '@/server/utils/resolvers' // import { hasRole } from '@/server/utils/resolvers'
const resolversComposition = { const resolversComposition = {
@ -9,33 +11,31 @@ const resolversComposition = {
} }
const resolvers: Resolvers = { const resolvers: Resolvers = {
Query: { Query: {
config: (_, args, context) => { config: async (_, args) => {
const { name } = args const { name } = args
const { db } = context const [result] = await Config.findOrCreate({ where: { name } })
return db.models.config return result
.findOrCreate({ where: { name } })
.then(() => db.models.config.findByPk(name))
}, },
highlight: async (parent, args, { db }) => { /* highlight: async (parent, args, { db }) => {
const { value } = await db.models.config.findByPk('highlight') const { value } = await db.models.config.findByPk('highlight')
return db.models.album.findByPk(value) return db.models.album.findByPk(value)
}, },
/* banners: async (parent, args) => { banners: async (parent, args) => {
const filePaths = await fg(['/var/www/soc_img/img/live/**/ /**.png']) const filePaths = await fg(['/var/www/soc_img/img/live/**/ /**.png'])
const images = filePaths.map((f) => f.split('/').pop()) const images = filePaths.map((f) => f.split('/').pop())
return images return images
}, */ },
recentComments: async (parent, { limit = 5 }, { db }) => { recentComments: async (parent, { limit = 5 }, { db }) => {
return db.models.comment.findAll({ return db.models.comment.findAll({
limit, limit,
order: [['updatedAt', 'DESC']] order: [['updatedAt', 'DESC']]
}) })
} } */
} }
} }

View file

@ -1,11 +1,11 @@
// import { GraphQLUpload } from 'graphql-upload-minimal' // import { GraphQLUpload } from 'graphql-upload-minimal'
import type { Resolvers } from "@/graphql/__generated__/types.generated" import type { Resolvers } from "@/graphql/__generated__/types.generated"
import type Album from "@/sequelize/models/album"
const resolvers: Resolvers = { const resolvers: Resolvers = {
// Upload: GraphQLUpload, // Upload: GraphQLUpload,
Album: { Album: {
// @ts-ignore artists: (parent: Album, args, context, info) => parent.getArtists(),
artists: (parent, args, context, info) => parent.getArtists(),
/* categories: (parent, args, context, info) => parent.getCategories(), /* categories: (parent, args, context, info) => parent.getCategories(),
classifications: (parent, args, context, info) => classifications: (parent, args, context, info) =>
parent.getClassifications(), parent.getClassifications(),

View file

@ -1,8 +0,0 @@
// import merge from 'lodash/merge'
import album from './album'
// import user from './user'
const types = /*(merge(*/album/*, user)*/
export default types

View file

@ -1,31 +1,34 @@
import { Op } from 'sequelize' import type { Resolvers } from '@/graphql/__generated__/types.generated'
import pages from '@/next/constants/pages.json' const userResolvable: Resolvers = {
import { getUser } from '@/next/utils/getSession' /* roles: parent => parent.getRoles(),
const userResolvable = {
roles: parent => parent.getRoles(),
permissions: async parent => { permissions: async parent => {
const roles = await parent.getRoles() const roles = await parent.getRoles()
return roles.map(r => r.permissions).flat() return roles.map(r => r.permissions).flat()
}, }, */
pages: async parent => { pages: async (_, __, { session }) => {
const roles = await parent.getRoles() const roles = await parent.getRoles()
const permissions = roles.map(r => r.permissions).flat() const permissions = roles.map(r => r.permissions).flat()
return pages.filter(({ perms }) => perms.length === 0 || perms.some(r => permissions.includes(r))) return pages.filter(({ perms }) => perms.length === 0 || perms.some(r => permissions.includes(r)))
}, },
comments: (user, _, { db }) => user.getComments({ where: { albumId: { [Op.not]: null } } }), /* comments: (user, _, { db }) => user.getComments({ where: { albumId: { [Op.not]: null } } }),
favorites: user => user.getAlbums(), favorites: user => user.getAlbums(),
imgUrl: async user => `https://cdn.sittingonclouds.net/user/${ imgUrl: async user => `https://cdn.sittingonclouds.net/user/${user.imgId ? `${user.username}_${user.imgId}` : 'default'
user.imgId ? `${user.username}_${user.imgId}` : 'default' }.png` */
}.png`
} }
const funcs = { const resolvers: Resolvers = {
User: userResolvable, // User: userResolvable,
UserMe: userResolvable, UserMe: {
Role: { permissions: parent => typeof parent.permissions === 'string' || parent.permissions instanceof String ? JSON.parse(parent.permissions) : parent.permissions }, 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: { Submission: {
submitter: submission => submission.getUser(), submitter: submission => submission.getUser(),
links: async (submission, _, { db }) => { links: async (submission, _, { db }) => {
@ -40,7 +43,7 @@ const funcs = {
return submission.links return submission.links
}, },
request: submission => submission.getRequest() request: submission => submission.getRequest()
} }*/
} }
export default funcs export default resolvers

View file

@ -1,57 +1,60 @@
type User { type User {
username: String! username: String!
roles: [Role]! roles: [Role]!
permissions: [String]! permissions: [String]!
pages: [Page]! pages: [Page]!
createdAt: Float! createdAt: Float!
placeholder: String! imgUrl: String!
imgUrl: String! }
}
type UserMe { type UserMe {
email: String! email: String!
username: String! username: String!
roles: [Role]! roles: [Role]!
permissions: [String]! permissions: [String]!
pages: [Page]! pages: [Page]!
createdAt: Float! createdAt: Float!
placeholder: String! imgUrl: String!
imgUrl: String! }
}
type Page { type Page {
url: String! url: String!
perms: [String!]! perms: [String!]!
} }
type Role { type Role {
name: String! name: String!
permissions: [String]! permissions: [String]!
} }
type Query { type Query {
me: UserMe me: UserMe
roles: [Role]! roles: [Role]!
permissions: [String]! permissions: [String]!
users(search: String): [User]! users(search: String): [User]!
user(username: String!): User user(username: String!): User
login(username: String!, password: String!): Int! login(username: String!, password: String!): Int!
} }
type Mutation { type Mutation {
login(username: String!, password: String!): Int! login(username: String!, password: String!): Int!
logout: Int! logout: Int!
registerUser(email: String!, username: String!, pfp: Upload): Boolean!
updateUserRoles(username: String!, roles: [String]!): Boolean!
deleteUser(username: String!): Int
createForgorLink(key: String!): Boolean! registerUser(email: String!, username: String!, pfp: Upload): Boolean!
updatePass(key: String!, pass:String!): Boolean! updateUserRoles(username: String!, roles: [String]!): Boolean!
updateUser(username: String, password: String, email: String, pfp: Upload): Boolean! deleteUser(username: String!): Int
createRole(name: String!, permissions: [String]!): Role createForgorLink(key: String!): Boolean!
updateRole(key:String!, name: String!, permissions: [String]!): Role updatePass(key: String!, pass: String!): Boolean!
deleteRole(name: String!): String updateUser(
} username: String
password: String
email: String
pfp: Upload
): Boolean!
createRole(name: String!, permissions: [String]!): Role
updateRole(key: String!, name: String!, permissions: [String]!): Role
deleteRole(name: String!): String
}

View file

@ -1,22 +0,0 @@
import mysql2 from 'mysql2'
import { Sequelize } from 'sequelize'
import relations from './relations'
import models from './models'
const options = process.env.GITHUB_ACTIONS
? 'sqlite::memory:'
: JSON.parse(import.meta.env.SEQUELIZE)
if (!process.env.GITHUB_ACTIONS && options.dialect === 'mysql')
options.dialectModule = mysql2
if (import.meta.env.DEV && options.logging === undefined)
options.logging = console.log
const db = new Sequelize(options)
Object.values(models).forEach((model) => model(db))
relations(db)
export default db

View file

@ -1,22 +0,0 @@
import { DataTypes } from 'sequelize'
import { PLACEHOLDER } from '@/constants'
const model = (sequelize) =>
sequelize.define('album', {
id: {
type: DataTypes.INTEGER,
primaryKey: true,
autoIncrement: true
},
title: DataTypes.STRING,
subTitle: DataTypes.TEXT,
releaseDate: DataTypes.DATEONLY,
label: DataTypes.STRING,
vgmdb: DataTypes.STRING,
description: DataTypes.STRING,
status: { type: DataTypes.STRING, defaultValue: 'show' },
placeholder: { type: DataTypes.TEXT, defaultValue: PLACEHOLDER },
headerColor: { type: DataTypes.STRING, defaultValue: '#ffffff' }
})
export default model

View file

@ -0,0 +1,47 @@
import { DataTypes, Model, type BelongsToManyGetAssociationsMixin, type CreationOptional, type InferAttributes, type InferCreationAttributes, type NonAttribute } from '@sequelize/core';
import { Attribute, PrimaryKey, AutoIncrement, Default, BelongsToMany } from '@sequelize/core/decorators-legacy';
import Category from './category';
import Artist from './artist';
export default class Album extends Model<InferAttributes<Album>, InferCreationAttributes<Album>> {
@Attribute(DataTypes.INTEGER)
@PrimaryKey
@AutoIncrement
declare id: CreationOptional<number>
@Attribute(DataTypes.STRING)
declare title: string
@Attribute(DataTypes.TEXT)
declare subTitle: string
@Attribute(DataTypes.DATEONLY)
declare releaseDate: string
@Attribute(DataTypes.STRING)
declare label: string
@Attribute(DataTypes.STRING)
declare vgmdb: string
@Attribute(DataTypes.STRING)
declare description: string
@Attribute(DataTypes.ENUM('show', 'hidden', 'coming'))
@Default('hidden')
declare status: CreationOptional<string>
@Attribute(DataTypes.STRING)
@Default('#ff7c12')
declare headerColor: CreationOptional<string>
@BelongsToMany(() => Category, { through: 'Album_Category', foreignKey: { onDelete: 'SET NULL' } })
declare categories?: NonAttribute<Category[]>
@BelongsToMany(() => Artist, { through: 'Album_Artist', foreignKey: { onDelete: 'SET NULL' } })
declare artists?: NonAttribute<Artist[]>
declare getArtists: BelongsToManyGetAssociationsMixin<Artist>
}

View file

@ -1,4 +1,4 @@
import { DataTypes } from 'sequelize' import { DataTypes } from '@/sequelize'
import { PLACEHOLDER } from '@/constants' import { PLACEHOLDER } from '@/constants'
const animation = (sequelize) => { const animation = (sequelize) => {

View file

@ -1,20 +0,0 @@
import { DataTypes } from 'sequelize'
const model = (sequelize) => {
const Artist = sequelize.define(
'artist',
{
slug: {
type: DataTypes.STRING,
primaryKey: true
},
name: DataTypes.STRING
},
{
freezeTableName: true
}
)
return Artist
}
export default model

View file

@ -0,0 +1,15 @@
import { DataTypes, Model, type CreationOptional, type InferAttributes, type InferCreationAttributes, type NonAttribute } from '@sequelize/core';
import { Attribute, PrimaryKey, AutoIncrement, Default, BelongsToMany, Table, NotNull, HasMany } from '@sequelize/core/decorators-legacy';
import Album from './album';
@Table({ freezeTableName: true })
export default class Artist extends Model<InferAttributes<Artist>, InferCreationAttributes<Artist>> {
@PrimaryKey
@Attribute(DataTypes.STRING)
declare slug: string
@Attribute(DataTypes.STRING)
@NotNull
declare name: string
}

View file

@ -1,19 +0,0 @@
import { DataTypes } from 'sequelize'
const model = (sequelize) => {
const Category = sequelize.define(
'category',
{
name: {
type: DataTypes.STRING,
primaryKey: true
}
},
{
freezeTableName: true
}
)
return Category
}
export default model

View file

@ -0,0 +1,10 @@
import { DataTypes, Model, type CreationOptional, type InferAttributes, type InferCreationAttributes } from '@sequelize/core';
import { Attribute, PrimaryKey, Table } from '@sequelize/core/decorators-legacy';
@Table({ freezeTableName: true })
export default class Category extends Model<InferAttributes<Category>, InferCreationAttributes<Category>> {
@PrimaryKey
@Attribute(DataTypes.STRING)
declare name: string
}

View file

@ -1,4 +1,4 @@
import { DataTypes } from 'sequelize' import { DataTypes } from '@/sequelize'
const model = (sequelize) => { const model = (sequelize) => {
const Classification = sequelize.define( const Classification = sequelize.define(
'classification', 'classification',

View file

@ -1,4 +1,4 @@
import { DataTypes } from 'sequelize' import { DataTypes } from '@/sequelize'
const model = (sequelize) => { const model = (sequelize) => {
sequelize.define('comment', { sequelize.define('comment', {

View file

@ -1,23 +0,0 @@
import { DataTypes } from 'sequelize'
const model = (sequelize) => {
const config = sequelize.define(
'config',
{
name: {
type: DataTypes.STRING,
primaryKey: true
},
value: {
type: DataTypes.STRING,
defaultValue: ''
}
},
{
freezeTableName: true
}
)
return config
}
export default model

View file

@ -0,0 +1,15 @@
import { DataTypes, Model, type CreationOptional, type InferAttributes, type InferCreationAttributes } from '@sequelize/core';
import { Attribute, PrimaryKey, AutoIncrement, Default, Table } from '@sequelize/core/decorators-legacy';
@Table({
freezeTableName: true,
})
export default class Config extends Model<InferAttributes<Config>, InferCreationAttributes<Config>> {
@Attribute(DataTypes.STRING)
@PrimaryKey
declare name: string
@Attribute(DataTypes.STRING)
declare value: string
}

View file

@ -1,4 +1,4 @@
import { DataTypes } from 'sequelize' import { DataTypes } from '@/sequelize'
const model = (sequelize) => { const model = (sequelize) => {
const Disc = sequelize.define('disc', { const Disc = sequelize.define('disc', {
number: DataTypes.INTEGER, number: DataTypes.INTEGER,

View file

@ -1,4 +1,4 @@
import { DataTypes } from 'sequelize' import { DataTypes } from '@/sequelize'
const model = (sequelize) => { const model = (sequelize) => {
const Download = sequelize.define('download', { const Download = sequelize.define('download', {
title: DataTypes.STRING, title: DataTypes.STRING,

View file

@ -1,4 +1,4 @@
import { DataTypes } from 'sequelize' import { DataTypes } from '@/sequelize'
import { PLACEHOLDER } from '@/constants' import { PLACEHOLDER } from '@/constants'
const model = (sequelize) => { const model = (sequelize) => {

View file

@ -1,45 +0,0 @@
import album from './album'
import animation from './animation'
import artist from './artist'
import category from './category'
import classification from './classification'
import comment from './comment'
import config from './config'
import disc from './disc'
import download from './download'
import game from './game'
import link from './link'
import log from './log'
import platform from './platform'
import publisher from './publisher'
import request from './request'
import role from './role'
import series from './series'
import store from './store'
import submission from './submission'
import user from './user'
const models = {
album,
animation,
artist,
category,
classification,
comment,
config,
disc,
download,
game,
link,
log,
platform,
publisher,
request,
role,
series,
store,
submission,
user
}
export default models

View file

@ -1,4 +1,4 @@
import { DataTypes } from 'sequelize' import { DataTypes } from '@/sequelize'
const model = (sequelize) => { const model = (sequelize) => {
const Link = sequelize.define('link', { const Link = sequelize.define('link', {
url: DataTypes.STRING, url: DataTypes.STRING,

View file

@ -1,4 +1,4 @@
import { DataTypes } from 'sequelize' import { DataTypes } from '@/sequelize'
const model = (sequelize) => { const model = (sequelize) => {
return sequelize.define('log', { return sequelize.define('log', {
id: { id: {

View file

@ -1,4 +1,4 @@
import { DataTypes } from 'sequelize' import { DataTypes } from '@/sequelize'
const model = (sequelize) => { const model = (sequelize) => {
const Platform = sequelize.define( const Platform = sequelize.define(
'platform', 'platform',

View file

@ -1,4 +1,4 @@
import { DataTypes } from 'sequelize' import { DataTypes } from '@/sequelize'
const model = (sequelize) => { const model = (sequelize) => {
const Publisher = sequelize.define( const Publisher = sequelize.define(
'publisher', 'publisher',

View file

@ -1,4 +1,4 @@
import { DataTypes } from 'sequelize' import { DataTypes } from '@/sequelize'
const request = (sequelize) => const request = (sequelize) =>
sequelize.define('request', { sequelize.define('request', {

View file

@ -1,4 +1,4 @@
import { DataTypes } from 'sequelize' import { DataTypes } from '@/sequelize'
const model = (sequelize) => { const model = (sequelize) => {
const Role = sequelize.define('role', { const Role = sequelize.define('role', {
name: { name: {

View file

@ -1,4 +1,4 @@
import { DataTypes } from 'sequelize' import { DataTypes } from '@/sequelize'
import { PLACEHOLDER } from '@/constants' import { PLACEHOLDER } from '@/constants'
const model = (sequelize) => { const model = (sequelize) => {

View file

@ -1,4 +1,4 @@
import { DataTypes } from 'sequelize' import { DataTypes } from '@/sequelize'
const model = (sequelize) => { const model = (sequelize) => {
const Store = sequelize.define('store', { const Store = sequelize.define('store', {
url: DataTypes.STRING, url: DataTypes.STRING,

View file

@ -1,4 +1,4 @@
import { DataTypes } from 'sequelize' import { DataTypes } from '@/sequelize'
const model = (sequelize) => const model = (sequelize) =>
sequelize.define('submission', { sequelize.define('submission', {

View file

@ -1,4 +1,4 @@
import { DataTypes } from 'sequelize' import { DataTypes } from '@/sequelize'
const model = (sequelize) => { const model = (sequelize) => {
const User = sequelize.define('user', { const User = sequelize.define('user', {

View file

@ -1,11 +1,27 @@
export default function relations (sequelize) { export default function relations(sequelize) {
const { const {
album, classification, disc, download, link, album,
publisher, game, series, classification,
platform, artist, category, store, disc,
animation, studio, download,
user, role, forgor, log, comment, rating, link,
submission, request publisher,
game,
series,
platform,
artist,
category,
store,
animation,
studio,
user,
role,
forgor,
log,
comment,
rating,
submission,
request
} = sequelize.models } = sequelize.models
user.belongsToMany(role, { through: 'User_Role' }) user.belongsToMany(role, { through: 'User_Role' })
@ -32,15 +48,25 @@ export default function relations (sequelize) {
platform.belongsToMany(game, { through: 'Game_Platform' }) platform.belongsToMany(game, { through: 'Game_Platform' })
album.belongsToMany(artist, { onDelete: 'SET NULL', through: 'Album_Artist' }) album.belongsToMany(artist, { onDelete: 'SET NULL', through: 'Album_Artist' })
album.belongsToMany(classification, { onDelete: 'SET NULL', through: 'Album_Classification' }) album.belongsToMany(classification, {
album.belongsToMany(category, { onDelete: 'SET NULL', through: 'Album_Category' }) onDelete: 'SET NULL',
album.belongsToMany(platform, { onDelete: 'SET NULL', through: 'Album_Platform' }) through: 'Album_Classification'
})
// album.belongsToMany(category, { onDelete: 'SET NULL', through: 'Album_Category' })
album.belongsToMany(platform, {
onDelete: 'SET NULL',
through: 'Album_Platform'
})
album.belongsToMany(game, { onDelete: 'SET NULL', through: 'Album_Game' }) album.belongsToMany(game, { onDelete: 'SET NULL', through: 'Album_Game' })
album.belongsToMany(animation, { through: 'Album_Animation' }) album.belongsToMany(animation, { through: 'Album_Animation' })
album.hasMany(disc, { onDelete: 'SET NULL' }) album.hasMany(disc, { onDelete: 'SET NULL' })
album.hasMany(download, { onDelete: 'SET NULL' }) album.hasMany(download, { onDelete: 'SET NULL' })
album.hasMany(store, { onDelete: 'SET NULL' }) album.hasMany(store, { onDelete: 'SET NULL' })
album.belongsToMany(album, { onDelete: 'SET NULL', through: 'related_album', as: 'related' }) album.belongsToMany(album, {
onDelete: 'SET NULL',
through: 'related_album',
as: 'related'
})
platform.belongsToMany(album, { through: 'Album_Platform' }) platform.belongsToMany(album, { through: 'Album_Platform' })

View file

@ -1,12 +1,16 @@
{ {
"extends": "astro/tsconfigs/strict", "extends": "astro/tsconfigs/strict",
"compilerOptions": { "compilerOptions": {
"experimentalDecorators": true,
"moduleResolution": "Bundler", "moduleResolution": "Bundler",
"allowJs": true, "allowJs": true,
"baseUrl": "src", "baseUrl": "src",
"paths": { "paths": {
"@/*": [ "@/*": [
"*" "*"
],
"@/root/*": [
"../*"
] ]
} }
} }