diff options
Diffstat (limited to 'src/graphql')
| -rw-r--r-- | src/graphql/anime/index.ts | 4 | ||||
| -rw-r--r-- | src/graphql/anime/resolvers.ts | 45 | ||||
| -rw-r--r-- | src/graphql/anime/schema.graphql | 30 | ||||
| -rw-r--r-- | src/graphql/client.ts | 4 | ||||
| -rw-r--r-- | src/graphql/server.ts | 6 | ||||
| -rw-r--r-- | src/graphql/user/index.ts | 4 | ||||
| -rw-r--r-- | src/graphql/user/resolvers.ts | 438 | ||||
| -rw-r--r-- | src/graphql/user/schema.graphql | 98 |
8 files changed, 333 insertions, 296 deletions
diff --git a/src/graphql/anime/index.ts b/src/graphql/anime/index.ts index 925a7ece..d5eef7f2 100644 --- a/src/graphql/anime/index.ts +++ b/src/graphql/anime/index.ts @@ -1,4 +1,4 @@ -import typeDefs from './schema.graphql?raw'; -import { resolvers } from './resolvers'; +import typeDefs from "./schema.graphql?raw"; +import { resolvers } from "./resolvers"; export default { typeDefs, resolvers }; diff --git a/src/graphql/anime/resolvers.ts b/src/graphql/anime/resolvers.ts index f2fc4b61..2b897eda 100644 --- a/src/graphql/anime/resolvers.ts +++ b/src/graphql/anime/resolvers.ts @@ -1,27 +1,32 @@ -import type { WithIndex } from '../$types'; -import type { Resolvers as RootResolvers } from '../$types'; +import type { WithIndex } from "../$types"; +import type { Resolvers as RootResolvers } from "../$types"; type AnimeResolvers = Pick< - RootResolvers, - 'Query' | 'Anime' | 'Subtitles' | 'SubtitleSchedule' | 'Subtitle' + RootResolvers, + "Query" | "Anime" | "Subtitles" | "SubtitleSchedule" | "Subtitle" >; export const resolvers: WithIndex<AnimeResolvers> = { - Query: { - Anime: async (_, args) => { - const timezone = args.timezone || 'Asia/Tokyo'; + Query: { + Anime: async (_, args) => { + const timezone = args.timezone || "Asia/Tokyo"; - return { - subtitles: { - timezone, - schedule: Object.fromEntries( - Object.entries( - (await (await fetch(`https://subsplease.org/api/?f=schedule&tz=${timezone}`)).json()) - .schedule - ).map(([key, value]) => [key.toLowerCase(), value]) - ) - } - }; - } - } + return { + subtitles: { + timezone, + schedule: Object.fromEntries( + Object.entries( + ( + await ( + await fetch( + `https://subsplease.org/api/?f=schedule&tz=${timezone}`, + ) + ).json() + ).schedule, + ).map(([key, value]) => [key.toLowerCase(), value]), + ), + }, + }; + }, + }, }; diff --git a/src/graphql/anime/schema.graphql b/src/graphql/anime/schema.graphql index a0415944..d5774966 100644 --- a/src/graphql/anime/schema.graphql +++ b/src/graphql/anime/schema.graphql @@ -1,29 +1,29 @@ type Query { - Anime(timezone: String): Anime! + Anime(timezone: String): Anime! } type Anime { - subtitles: Subtitles + subtitles: Subtitles } type Subtitles { - timezone: String - schedule: SubtitleSchedule + timezone: String + schedule: SubtitleSchedule } type SubtitleSchedule { - monday: [Subtitle] - tuesday: [Subtitle] - wednesday: [Subtitle] - thursday: [Subtitle] - friday: [Subtitle] - saturday: [Subtitle] - sunday: [Subtitle] + monday: [Subtitle] + tuesday: [Subtitle] + wednesday: [Subtitle] + thursday: [Subtitle] + friday: [Subtitle] + saturday: [Subtitle] + sunday: [Subtitle] } type Subtitle { - title: String - page: String - image_url: String - time: String + title: String + page: String + image_url: String + time: String } diff --git a/src/graphql/client.ts b/src/graphql/client.ts index 367b8381..071c3097 100644 --- a/src/graphql/client.ts +++ b/src/graphql/client.ts @@ -1,3 +1,3 @@ -import { HoudiniClient } from '$houdini'; +import { HoudiniClient } from "$houdini"; -export default new HoudiniClient({ url: '/graphql' }); +export default new HoudiniClient({ url: "/graphql" }); diff --git a/src/graphql/server.ts b/src/graphql/server.ts index 2dc183dc..f5731080 100644 --- a/src/graphql/server.ts +++ b/src/graphql/server.ts @@ -1,6 +1,6 @@ -import { createSchema, createServer } from 'sveltekit-graphql'; -import userModule from './user'; -import animeModule from './anime'; +import { createSchema, createServer } from "sveltekit-graphql"; +import userModule from "./user"; +import animeModule from "./anime"; const schema = createSchema([userModule, animeModule]); const server = createServer(schema); diff --git a/src/graphql/user/index.ts b/src/graphql/user/index.ts index 925a7ece..d5eef7f2 100644 --- a/src/graphql/user/index.ts +++ b/src/graphql/user/index.ts @@ -1,4 +1,4 @@ -import typeDefs from './schema.graphql?raw'; -import { resolvers } from './resolvers'; +import typeDefs from "./schema.graphql?raw"; +import { resolvers } from "./resolvers"; export default { typeDefs, resolvers }; diff --git a/src/graphql/user/resolvers.ts b/src/graphql/user/resolvers.ts index 879d066d..986b9684 100644 --- a/src/graphql/user/resolvers.ts +++ b/src/graphql/user/resolvers.ts @@ -1,244 +1,276 @@ -import { userIdentity, type UserIdentity } from '$lib/Data/AniList/identity'; +import { userIdentity, type UserIdentity } from "$lib/Data/AniList/identity"; import { - addUserBadge, - getUserBadges, - removeAllUserBadges, - removeUserBadge, - setShadowHidden, - setShadowHiddenBadge, - updateUserBadge, - type Badge as DatabaseBadge -} from '$lib/Database/SB/User/badges'; -import type { WithIndex } from '../$types'; -import type { Resolvers as RootResolvers, Badge } from '../$types'; -import type { RequestEvent } from '@sveltejs/kit'; + addUserBadge, + getUserBadges, + removeAllUserBadges, + removeUserBadge, + setShadowHidden, + setShadowHiddenBadge, + updateUserBadge, + type Badge as DatabaseBadge, +} from "$lib/Database/SB/User/badges"; +import type { WithIndex } from "../$types"; +import type { Resolvers as RootResolvers, Badge } from "../$types"; +import type { RequestEvent } from "@sveltejs/kit"; import { - getUserPreferences, - setBiography, - setCSS, - setPinnedBadgeWallCategories, - toggleHideAWCBadges, - toggleHideMissingBadges, - toggleHololiveStreamPinning, - togglePinnedBadgeWallCategory, - type UserPreferences -} from '$lib/Database/SB/User/preferences'; -import privilegedUser from '$lib/Utility/privilegedUser'; + getUserPreferences, + setBiography, + setCSS, + setPinnedBadgeWallCategories, + toggleHideAWCBadges, + toggleHideMissingBadges, + toggleHololiveStreamPinning, + togglePinnedBadgeWallCategory, + type UserPreferences, +} from "$lib/Database/SB/User/preferences"; +import privilegedUser from "$lib/Utility/privilegedUser"; type Context = RequestEvent<Partial<Record<string, string>>, string | null>; -type UserResolvers = Pick<RootResolvers, 'Query' | 'Mutation' | 'User' | 'Badge' | 'Preferences'>; +type UserResolvers = Pick< + RootResolvers, + "Query" | "Mutation" | "User" | "Badge" | "Preferences" +>; const toGraphQLBadges = (databaseBadges: DatabaseBadge[]): Badge[] => - databaseBadges.map((databaseBadge) => ({ - id: databaseBadge.id ?? 0, - post: databaseBadge.post ?? '', - image: databaseBadge.image ?? '', - time: databaseBadge.time ?? new Date().toISOString(), - hidden: databaseBadge.hidden ?? false, - shadow_hidden: databaseBadge.shadow_hidden ?? false, - click_count: databaseBadge.click_count ?? 0, - category: databaseBadge.category ?? null, - description: databaseBadge.description ?? null, - source: databaseBadge.source ?? null, - designer: databaseBadge.designer ?? null - })); + databaseBadges.map((databaseBadge) => ({ + id: databaseBadge.id ?? 0, + post: databaseBadge.post ?? "", + image: databaseBadge.image ?? "", + time: databaseBadge.time ?? new Date().toISOString(), + hidden: databaseBadge.hidden ?? false, + shadow_hidden: databaseBadge.shadow_hidden ?? false, + click_count: databaseBadge.click_count ?? 0, + category: databaseBadge.category ?? null, + description: databaseBadge.description ?? null, + source: databaseBadge.source ?? null, + designer: databaseBadge.designer ?? null, + })); const auth = async (context: Context) => { - const userCookie = context.cookies.get('user'); + const userCookie = context.cookies.get("user"); - if (!userCookie) return Error('Unauthorised'); + if (!userCookie) return Error("Unauthorised"); - const user = JSON.parse(userCookie); + const user = JSON.parse(userCookie); - return await userIdentity({ - tokenType: user['token_type'], - expiresIn: user['expires_in'], - accessToken: user['access_token'], - refreshToken: user['refresh_token'] - }); + return await userIdentity({ + tokenType: user["token_type"], + expiresIn: user["expires_in"], + accessToken: user["access_token"], + refreshToken: user["refresh_token"], + }); }; const authenticatedBadgesOperation = async ( - context: Context, - operation: (identity: UserIdentity, authorised: boolean) => Promise<unknown> + context: Context, + operation: (identity: UserIdentity, authorised: boolean) => Promise<unknown>, ) => { - const identity = await auth(context); + const identity = await auth(context); - if (identity instanceof Error) throw new Error('Unauthorized'); + if (identity instanceof Error) throw new Error("Unauthorized"); - const authorised = privilegedUser(identity.id); + const authorised = privilegedUser(identity.id); - await operation(identity, authorised); + await operation(identity, authorised); - const databaseBadges = await getUserBadges(identity.id); - const badges = toGraphQLBadges(databaseBadges); + const databaseBadges = await getUserBadges(identity.id); + const badges = toGraphQLBadges(databaseBadges); - return { - id: identity.id, - badges, - preferences: null, - badgesCount: badges.length - }; + return { + id: identity.id, + badges, + preferences: null, + badgesCount: badges.length, + }; }; const authenticatedPreferencesOperation = async ( - context: Context, - operation: (identity: UserIdentity, authorised: boolean) => Promise<UserPreferences | null> + context: Context, + operation: ( + identity: UserIdentity, + authorised: boolean, + ) => Promise<UserPreferences | null>, ) => { - const identity = await auth(context); + const identity = await auth(context); - if (identity instanceof Error) throw new Error('Unauthorized'); + if (identity instanceof Error) throw new Error("Unauthorized"); - const authorised = privilegedUser(identity.id); + const authorised = privilegedUser(identity.id); - return { - id: identity.id, - badges: [] as Badge[], - preferences: await operation(identity, authorised), - badgesCount: 0 - }; + return { + id: identity.id, + badges: [] as Badge[], + preferences: await operation(identity, authorised), + badgesCount: 0, + }; }; const ensureOwnerOrPrivileged = ( - identity: UserIdentity, - authorised: boolean, - targetUserId: number + identity: UserIdentity, + authorised: boolean, + targetUserId: number, ) => { - if (!authorised && identity.id !== targetUserId) throw new Error('Unauthorized'); + if (!authorised && identity.id !== targetUserId) + throw new Error("Unauthorized"); }; const ensureBadgeOwnerOrPrivileged = async ( - identity: UserIdentity, - authorised: boolean, - badgeId: number + identity: UserIdentity, + authorised: boolean, + badgeId: number, ) => { - if (authorised) return; + if (authorised) return; - const ownsBadge = (await getUserBadges(identity.id)).some((badge) => badge.id === badgeId); + const ownsBadge = (await getUserBadges(identity.id)).some( + (badge) => badge.id === badgeId, + ); - if (!ownsBadge) throw new Error('Unauthorized'); + if (!ownsBadge) throw new Error("Unauthorized"); }; export const resolvers: WithIndex<UserResolvers> = { - Query: { - User: async (_, args) => { - if (!args.id) return null; - - const databaseBadges = await getUserBadges(args.id); - const badges = toGraphQLBadges(databaseBadges); - - return { - id: args.id, - badges, - preferences: await getUserPreferences(args.id), - badgesCount: badges.length - }; - }, - badges: async (_, args) => { - if (!args.id) return []; - - const databaseBadges = await getUserBadges(args.id, args.page || 0, args.size || 0); - - return toGraphQLBadges(databaseBadges); - } - }, - Mutation: { - shadowHideBadges: async (_, args, context) => - await authenticatedBadgesOperation(context, async (identity, authorised) => { - ensureOwnerOrPrivileged(identity, authorised, args.userId); - - await setShadowHidden(args.userId, authorised); - }), - shadowHideBadge: async (_, args, context) => - await authenticatedBadgesOperation(context, async (identity, authorised) => { - await ensureBadgeOwnerOrPrivileged(identity, authorised, args.id); - await setShadowHiddenBadge(args.id, args.state == null ? true : args.state); - }), - hideBadge: async (_, args, context) => - await authenticatedBadgesOperation(context, async (identity) => { - const allBadges = await getUserBadges(identity.id); - const category = args.category || ''; - - await Promise.all( - allBadges - .filter((badge) => badge.category === category) - .map(async (badge) => { - await updateUserBadge(identity.id, badge.id as number, { - ...badge, - hidden: - allBadges - .filter((badge) => badge.category === category) - .filter((badge) => badge.hidden).length > - allBadges.filter((badge) => badge.category === category).length / 2 - ? false - : true - }); - }) - ); - }), - updateBadge: async (_, args, context) => - await authenticatedBadgesOperation(context, async (identity) => { - const badge = { - post: args.post || undefined, - image: args.image || undefined, - description: args.description || null, - time: args.time || undefined, - category: args.category || null, - hidden: args.hidden || false, - source: args.source || null, - designer: args.designer || null - }; - - if ((await getUserBadges(identity.id)).find((badge) => badge.id === args.id)) { - await updateUserBadge(identity.id, args.id as number, badge); - } else { - await addUserBadge(identity.id, badge); - } - }), - deleteBadge: async (_, args, context) => - await authenticatedBadgesOperation( - context, - async (identity) => await removeUserBadge(identity.id, args.id) - ), - pruneUserBadges: async (_, __, context) => - await authenticatedBadgesOperation( - context as Context, - async (identity) => await removeAllUserBadges(identity.id) - ), - toggleHideMissingBadges: async (_, _args, context) => - await authenticatedPreferencesOperation( - context as Context, - async (identity) => await toggleHideMissingBadges(identity.id) - ), - toggleHideAWCBadges: async (_, _args, context) => - await authenticatedPreferencesOperation( - context as Context, - async (identity) => await toggleHideAWCBadges(identity.id) - ), - setBadgeWallCSS: async (_, args, context) => - await authenticatedPreferencesOperation( - context as Context, - async (identity) => await setCSS(identity.id, args.css) - ), - togglePinnedBadgeWallCategory: async (_, args, context) => - await authenticatedPreferencesOperation( - context as Context, - async (identity) => await togglePinnedBadgeWallCategory(identity.id, args.category) - ), - setPinnedBadgeWallCategories: async (_, args, context) => - await authenticatedPreferencesOperation( - context as Context, - async (identity) => await setPinnedBadgeWallCategories(identity.id, args.categories) - ), - setBiography: async (_, args, context) => - await authenticatedPreferencesOperation( - context as Context, - async (identity) => await setBiography(identity.id, args.biography.slice(0, 3000)) - ), - togglePinnedHololiveStream: async (_, args, context) => - await authenticatedPreferencesOperation( - context as Context, - async (identity) => await toggleHololiveStreamPinning(identity.id, args.stream) - ) - } + Query: { + User: async (_, args) => { + if (!args.id) return null; + + const databaseBadges = await getUserBadges(args.id); + const badges = toGraphQLBadges(databaseBadges); + + return { + id: args.id, + badges, + preferences: await getUserPreferences(args.id), + badgesCount: badges.length, + }; + }, + badges: async (_, args) => { + if (!args.id) return []; + + const databaseBadges = await getUserBadges( + args.id, + args.page || 0, + args.size || 0, + ); + + return toGraphQLBadges(databaseBadges); + }, + }, + Mutation: { + shadowHideBadges: async (_, args, context) => + await authenticatedBadgesOperation( + context, + async (identity, authorised) => { + ensureOwnerOrPrivileged(identity, authorised, args.userId); + + await setShadowHidden(args.userId, authorised); + }, + ), + shadowHideBadge: async (_, args, context) => + await authenticatedBadgesOperation( + context, + async (identity, authorised) => { + await ensureBadgeOwnerOrPrivileged(identity, authorised, args.id); + await setShadowHiddenBadge( + args.id, + args.state == null ? true : args.state, + ); + }, + ), + hideBadge: async (_, args, context) => + await authenticatedBadgesOperation(context, async (identity) => { + const allBadges = await getUserBadges(identity.id); + const category = args.category || ""; + + await Promise.all( + allBadges + .filter((badge) => badge.category === category) + .map(async (badge) => { + await updateUserBadge(identity.id, badge.id as number, { + ...badge, + hidden: + allBadges + .filter((badge) => badge.category === category) + .filter((badge) => badge.hidden).length > + allBadges.filter((badge) => badge.category === category) + .length / + 2 + ? false + : true, + }); + }), + ); + }), + updateBadge: async (_, args, context) => + await authenticatedBadgesOperation(context, async (identity) => { + const badge = { + post: args.post || undefined, + image: args.image || undefined, + description: args.description || null, + time: args.time || undefined, + category: args.category || null, + hidden: args.hidden || false, + source: args.source || null, + designer: args.designer || null, + }; + + if ( + (await getUserBadges(identity.id)).find( + (badge) => badge.id === args.id, + ) + ) { + await updateUserBadge(identity.id, args.id as number, badge); + } else { + await addUserBadge(identity.id, badge); + } + }), + deleteBadge: async (_, args, context) => + await authenticatedBadgesOperation( + context, + async (identity) => await removeUserBadge(identity.id, args.id), + ), + pruneUserBadges: async (_, __, context) => + await authenticatedBadgesOperation( + context as Context, + async (identity) => await removeAllUserBadges(identity.id), + ), + toggleHideMissingBadges: async (_, _args, context) => + await authenticatedPreferencesOperation( + context as Context, + async (identity) => await toggleHideMissingBadges(identity.id), + ), + toggleHideAWCBadges: async (_, _args, context) => + await authenticatedPreferencesOperation( + context as Context, + async (identity) => await toggleHideAWCBadges(identity.id), + ), + setBadgeWallCSS: async (_, args, context) => + await authenticatedPreferencesOperation( + context as Context, + async (identity) => await setCSS(identity.id, args.css), + ), + togglePinnedBadgeWallCategory: async (_, args, context) => + await authenticatedPreferencesOperation( + context as Context, + async (identity) => + await togglePinnedBadgeWallCategory(identity.id, args.category), + ), + setPinnedBadgeWallCategories: async (_, args, context) => + await authenticatedPreferencesOperation( + context as Context, + async (identity) => + await setPinnedBadgeWallCategories(identity.id, args.categories), + ), + setBiography: async (_, args, context) => + await authenticatedPreferencesOperation( + context as Context, + async (identity) => + await setBiography(identity.id, args.biography.slice(0, 3000)), + ), + togglePinnedHololiveStream: async (_, args, context) => + await authenticatedPreferencesOperation( + context as Context, + async (identity) => + await toggleHololiveStreamPinning(identity.id, args.stream), + ), + }, }; diff --git a/src/graphql/user/schema.graphql b/src/graphql/user/schema.graphql index 70ea066a..f21edd9b 100644 --- a/src/graphql/user/schema.graphql +++ b/src/graphql/user/schema.graphql @@ -1,63 +1,63 @@ type Query { - User(id: Int): User - badges(id: Int!, page: Int, size: Int): [Badge!]! + User(id: Int): User + badges(id: Int!, page: Int, size: Int): [Badge!]! } type Mutation { - shadowHideBadges(userId: Int!): User! - shadowHideBadge(id: Int!, state: Boolean): User! - hideBadge(category: String): User! - updateBadge( - id: Int - post: String - image: String - description: String - time: String - category: String - hidden: Boolean - source: String - designer: String - ): User! - deleteBadge(id: Int!): User! - pruneUserBadges: User! - toggleHideMissingBadges: User! - toggleHideAWCBadges: User! - setBadgeWallCSS(css: String!): User! - togglePinnedBadgeWallCategory(category: String!): User! - setPinnedBadgeWallCategories(categories: [String!]!): User! - setBiography(biography: String!): User! - togglePinnedHololiveStream(stream: String!): User! + shadowHideBadges(userId: Int!): User! + shadowHideBadge(id: Int!, state: Boolean): User! + hideBadge(category: String): User! + updateBadge( + id: Int + post: String + image: String + description: String + time: String + category: String + hidden: Boolean + source: String + designer: String + ): User! + deleteBadge(id: Int!): User! + pruneUserBadges: User! + toggleHideMissingBadges: User! + toggleHideAWCBadges: User! + setBadgeWallCSS(css: String!): User! + togglePinnedBadgeWallCategory(category: String!): User! + setPinnedBadgeWallCategories(categories: [String!]!): User! + setBiography(biography: String!): User! + togglePinnedHololiveStream(stream: String!): User! } type User { - id: Int! - badges: [Badge!]! - preferences: Preferences - badgesCount: Int! + id: Int! + badges: [Badge!]! + preferences: Preferences + badgesCount: Int! } type Badge { - post: String! - image: String! - description: String - id: Int! - time: String! - category: String - hidden: Boolean! - source: String - designer: String - shadow_hidden: Boolean! - click_count: Int! + post: String! + image: String! + description: String + id: Int! + time: String! + category: String + hidden: Boolean! + source: String + designer: String + shadow_hidden: Boolean! + click_count: Int! } type Preferences { - created_at: String! - updated_at: String! - user_id: Int! - pinned_hololive_streams: [String!]! - hide_missing_badges: Boolean! - biography: String - badge_wall_css: String! - hide_awc_badges: Boolean! - pinned_badge_wall_categories: [String!]! + created_at: String! + updated_at: String! + user_id: Int! + pinned_hololive_streams: [String!]! + hide_missing_badges: Boolean! + biography: String + badge_wall_css: String! + hide_awc_badges: Boolean! + pinned_badge_wall_categories: [String!]! } |