diff --git a/astro.config.mjs b/astro.config.mjs index 0745c50..cd7b0fb 100644 --- a/astro.config.mjs +++ b/astro.config.mjs @@ -4,7 +4,6 @@ import tailwind from '@astrojs/tailwind' import node from '@astrojs/node' import react from '@astrojs/react'; import paraglide from '@inlang/paraglide-astro' -import auth from 'auth-astro' import icon from 'astro-icon' import { languageTags } from './project.inlang/settings.json' @@ -22,11 +21,13 @@ export default defineConfig({ }, integrations: [ tailwind(), - auth(), paraglide({ project: './project.inlang', outdir: './src/paraglide' }), icon({ iconDir: 'src/img/icons' }), react() ], output: 'server', - adapter: node({ mode: 'standalone' }) + adapter: node({ mode: 'standalone' }), + security: { + checkOrigin: true + } }) \ No newline at end of file diff --git a/auth.config.ts b/auth.config.ts deleted file mode 100644 index d80b703..0000000 --- a/auth.config.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { CredentialsSignin } from '@auth/core/errors' -import { defineConfig } from 'auth-astro' -import Credentials from '@auth/core/providers/credentials' -import bcrypt from 'bcrypt' -import prismaClient from 'prisma/client' - -class InvalidLoginError extends CredentialsSignin { - code = 'Invalid username/email or password' -} - -export default defineConfig({ - providers: [ - Credentials({ - credentials: { - username: { label: 'Username', required: true }, - password: { label: 'Password', type: 'password', required: true } - }, - async authorize(credentials) { - if (!credentials?.username || !credentials.password) throw new InvalidLoginError() - - const user = await prismaClient.users.findUnique({ - select: { username: true, password: true }, - where: { username: credentials.username } - }) - if (!user) throw new InvalidLoginError() - - const valid = await bcrypt.compare(credentials.password, user.password) - if (!valid) throw new InvalidLoginError() - - return { id: user.username } - } - }) - ] -}) diff --git a/package.json b/package.json index b7ae5d1..f279f09 100644 --- a/package.json +++ b/package.json @@ -16,28 +16,29 @@ "@astrojs/react": "^3.6.2", "@astrojs/rss": "^4.0.7", "@astrojs/tailwind": "^5.1.1", - "@auth/core": "^0.35.0", "@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", + "@oslojs/crypto": "^1.0.1", + "@oslojs/encoding": "^1.1.0", "@prisma/client": "^5.22.0", "@types/react": "^18.3.12", "@types/react-dom": "^18.3.1", "apollo-upload-client": "^18.0.1", "astro": "^4.15.7", "astro-icon": "^1.1.1", - "auth-astro": "^4.1.2", - "bcrypt": "^5.1.1", "clsx": "^2.1.1", + "fast-glob": "^3.3.2", "fs-extra": "^11.2.0", "generate-password-ts": "^1.6.5", "graphql": "^16.9.0", "graphql-scalars": "^1.23.0", "graphql-yoga": "^5.10.2", "nodemailer": "^6.9.16", + "oslo": "^1.2.1", "react": "^18.3.1", "react-dom": "^18.3.1", "react-hot-toast": "^2.4.1", @@ -49,7 +50,6 @@ "devDependencies": { "@inlang/paraglide-js": "1.11.2", "@parcel/watcher": "^2.4.1", - "@types/bcrypt": "^5.0.2", "concurrently": "^8.2.2", "eslint": "^9.11.1", "eslint-config-prettier": "^9.1.0", diff --git a/prisma/migrations/20241120013541_fix_forgors/migration.sql b/prisma/migrations/20241120013541_fix_forgors/migration.sql new file mode 100644 index 0000000..c5cabbb --- /dev/null +++ b/prisma/migrations/20241120013541_fix_forgors/migration.sql @@ -0,0 +1,23 @@ +-- AlterTable +ALTER TABLE `config` MODIFY `createdAt` DATETIME(0) NOT NULL DEFAULT CURRENT_TIMESTAMP(0), + MODIFY `updatedAt` DATETIME(0) NOT NULL DEFAULT CURRENT_TIMESTAMP(0); + +-- AlterTable +ALTER TABLE `forgors` MODIFY `createdAt` DATETIME(0) NOT NULL DEFAULT CURRENT_TIMESTAMP(0), + MODIFY `updatedAt` DATETIME(0) NOT NULL DEFAULT CURRENT_TIMESTAMP(0); + +-- AlterTable +ALTER TABLE `users` MODIFY `createdAt` DATETIME(0) NOT NULL DEFAULT CURRENT_TIMESTAMP(0), + MODIFY `updatedAt` DATETIME(0) NOT NULL DEFAULT CURRENT_TIMESTAMP(0); + +-- CreateTable +CREATE TABLE `Session` ( + `id` VARCHAR(191) NOT NULL, + `username` VARCHAR(255) NOT NULL, + `expiresAt` DATETIME(3) NOT NULL, + + PRIMARY KEY (`id`) +) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; + +-- AddForeignKey +ALTER TABLE `Session` ADD CONSTRAINT `Session_id_fkey` FOREIGN KEY (`id`) REFERENCES `users`(`username`) ON DELETE CASCADE ON UPDATE CASCADE; diff --git a/prisma/migrations/20241120032217_sessions_2/migration.sql b/prisma/migrations/20241120032217_sessions_2/migration.sql new file mode 100644 index 0000000..de3f71d --- /dev/null +++ b/prisma/migrations/20241120032217_sessions_2/migration.sql @@ -0,0 +1,10 @@ +/* + Warnings: + + - You are about to drop the column `username` on the `Session` table. All the data in the column will be lost. + - Added the required column `userId` to the `Session` table without a default value. This is not possible if the table is not empty. + +*/ +-- AlterTable +ALTER TABLE `Session` DROP COLUMN `username`, + ADD COLUMN `userId` VARCHAR(255) NOT NULL; diff --git a/prisma/migrations/20241120032504_sessions_2/migration.sql b/prisma/migrations/20241120032504_sessions_2/migration.sql new file mode 100644 index 0000000..ae7b2d8 --- /dev/null +++ b/prisma/migrations/20241120032504_sessions_2/migration.sql @@ -0,0 +1,5 @@ +-- DropForeignKey +ALTER TABLE `Session` DROP FOREIGN KEY `Session_id_fkey`; + +-- AddForeignKey +ALTER TABLE `Session` ADD CONSTRAINT `Session_userId_fkey` FOREIGN KEY (`userId`) REFERENCES `users`(`username`) ON DELETE CASCADE ON UPDATE CASCADE; diff --git a/prisma/migrations/migration_lock.toml b/prisma/migrations/migration_lock.toml new file mode 100644 index 0000000..e5a788a --- /dev/null +++ b/prisma/migrations/migration_lock.toml @@ -0,0 +1,3 @@ +# Please do not edit this file manually +# It should be added in your version-control system (i.e. Git) +provider = "mysql" \ No newline at end of file diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 260f1b4..7c2c863 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -149,8 +149,8 @@ model User_Role { updatedAt DateTime @db.DateTime(0) userUsername String @db.VarChar(255) roleName String @db.VarChar(255) - users users @relation(fields: [userUsername], references: [username], onDelete: Cascade, map: "User_Role_ibfk_1") - roles roles @relation(fields: [roleName], references: [name], onDelete: Cascade, map: "User_Role_ibfk_2") + user users @relation(fields: [userUsername], references: [username], onDelete: Cascade, map: "User_Role_ibfk_1") + role roles @relation(fields: [roleName], references: [name], onDelete: Cascade, map: "User_Role_ibfk_2") @@id([userUsername, roleName]) @@index([roleName], map: "roleName") @@ -317,7 +317,7 @@ model favorites { model forgors { id Int @id @default(autoincrement()) - expires DateTime? @default(dbgenerated("DATE_ADD(NOW(), INTERVAL 24 HOUR)")) @db.DateTime(0) + expires DateTime? @db.DateTime(0) key String? @db.VarChar(255) createdAt DateTime @default(now()) @db.DateTime(0) updatedAt DateTime @default(now()) @db.DateTime(0) @@ -519,7 +519,7 @@ model users { updatedAt DateTime @default(now()) @db.DateTime(0) placeholder String? @db.Text imgId String? @db.VarChar(255) - User_Role User_Role[] + roles User_Role[] albumHistories albumHistories[] comments comments[] favorites favorites[] @@ -527,4 +527,13 @@ model users { logs logs[] ratings ratings[] submissions submissions[] + sessions Session[] +} + +model Session { + id String @id + userId String @db.VarChar(255) + expiresAt DateTime + + user users @relation(references: [username], fields: [userId], onDelete: Cascade) } diff --git a/src/components/header/LoginButton.tsx b/src/components/header/LoginButton.tsx new file mode 100644 index 0000000..d1935b8 --- /dev/null +++ b/src/components/header/LoginButton.tsx @@ -0,0 +1,71 @@ +import { useState } from 'react' +import { gql } from '@/graphql/__generated__/client' +import { useMutation } from '@apollo/client/react/hooks' + +import Button from 'components/Button' +import * as m from 'paraglide/messages.js' +import Modal from 'components/Modal' +import apolloClient from '@/graphql/apolloClient' +import toast from 'react-hot-toast' + +const loginMutation = gql(` + mutation Login($username: String!, $password: String!) { + login(username: $username, password: $password) + } +`) + +export default function LoginBtn() { + const [modalOpen, setModalOpen] = useState(false) + const [mutate, { loading }] = useMutation(loginMutation, { client: apolloClient }) + + const handleSubmit = (ev) => { + ev.preventDefault() + const formData = new FormData(ev.target) + const variables = Object.fromEntries(formData) + + mutate({ variables }) + .then((res) => { + // toast.success(m.emailSuccess()) + setModalOpen(false) + }) + .catch((err) => { + toast.error(err.message) + }) + } + + return ( + <> + + {modalOpen ? ( + +
+
+
+
+ + +
+
+
+ + +
+
+
+ + +
+
+
+ ) : null} + + ) +} diff --git a/src/components/header/LoginNav.astro b/src/components/header/LoginNav.astro index 340bec9..667c158 100644 --- a/src/components/header/LoginNav.astro +++ b/src/components/header/LoginNav.astro @@ -1,15 +1,12 @@ --- -import { getSession } from 'auth-astro/server' -import { SignIn, SignOut } from 'auth-astro/components' - import RegisterBtn from './RegisterButton' -import * as m from 'paraglide/messages.js' +import LoginBtn from './LoginButton' +import LogoutBtn from './LogoutButton' -const session = await getSession(Astro.request) -const btnClass = 'bg-blue-600 hover:bg-blue-700 py-2 px-3.5 rounded-lg rounded-t-none' +const { user } = Astro.locals ---
- {session === null ? {m.login()} : {m.logout()}} - {!session ? : null} + {!user ? : } + {!user ? : null}
diff --git a/src/components/header/LogoutButton.tsx b/src/components/header/LogoutButton.tsx new file mode 100644 index 0000000..6d6e3ba --- /dev/null +++ b/src/components/header/LogoutButton.tsx @@ -0,0 +1,35 @@ +import { gql } from '@/graphql/__generated__/client' +import { useMutation } from '@apollo/client/react/hooks' + +import Button from 'components/Button' +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() { + const [mutate, { loading }] = useMutation(loginMutation, { client: apolloClient }) + + const handleSubmit = (ev) => { + ev.preventDefault() + + mutate() + .then(() => { + window.refresh() + }) + .catch((err) => { + toast.error(err.message) + }) + } + + return ( + + ) +} diff --git a/src/components/header/RegisterButton.tsx b/src/components/header/RegisterButton.tsx index c04cc60..f992ef5 100644 --- a/src/components/header/RegisterButton.tsx +++ b/src/components/header/RegisterButton.tsx @@ -1,4 +1,3 @@ -import type { Session } from '@auth/core/types' import { useState } from 'react' import { gql } from '@/graphql/__generated__/client' import { useMutation } from '@apollo/client/react/hooks' @@ -15,13 +14,10 @@ const registerMutation = gql(` } `) -export default function RegisterBtn(props: { session: Session }) { - const { session } = props +export default function RegisterBtn() { const [modalOpen, setModalOpen] = useState(false) const [mutate, { loading }] = useMutation(registerMutation, { client: apolloClient, ignoreResults: true }) - if (session) return null - const handleSubmit = (ev) => { ev.preventDefault() const formData = new FormData(ev.target) diff --git a/src/env.d.ts b/src/env.d.ts index e16c13c..e46db51 100644 --- a/src/env.d.ts +++ b/src/env.d.ts @@ -1 +1,8 @@ /// +declare namespace App { + // Note: 'import {} from ""' syntax does not work in .d.ts files. + interface Locals { + session: import('./src/utils/session').Session | null + user: import('./src/utils/session').User | null + } +} diff --git a/src/graphql/apolloClient.ts b/src/graphql/apolloClient.ts index 207d747..fc5579e 100644 --- a/src/graphql/apolloClient.ts +++ b/src/graphql/apolloClient.ts @@ -9,7 +9,8 @@ const httpLink = createUploadLink({ const apolloClient = new ApolloClient({ link: httpLink, - cache: new InMemoryCache() + cache: new InMemoryCache(), + credentials: 'same-origin' }) export default apolloClient diff --git a/src/graphql/apolloClientSSR.ts b/src/graphql/apolloClientSSR.ts index 5809317..d6f2c32 100644 --- a/src/graphql/apolloClientSSR.ts +++ b/src/graphql/apolloClientSSR.ts @@ -1,20 +1,18 @@ import { ApolloClient, InMemoryCache } from '@apollo/client/core' import { SchemaLink } from '@apollo/client/link/schema' -import { getSession } from 'auth-astro/server' -import type { Session } from '@auth/core/types' -import prismaClient, { type users } from 'prisma/client' +import type { AstroCookies, AstroGlobal } from 'astro' import schema from './schema' -export type ResolverContext = { request?: Request; session?: Session; user?: users } - -export async function getApolloClient(request?: Request) { - const session = async () => (request ? getSession(request) : null) - const user = async () => (session ? prismaClient.users.findUnique({ where: { username: session.id } }) : null) +export interface ResolverContext { + cookies?: AstroCookies +} +export async function getApolloClient(cookies?: AstroCookies) { return new ApolloClient({ ssrMode: true, - link: new SchemaLink({ schema, context: { request, session, user } }), - cache: new InMemoryCache() + link: new SchemaLink({ schema, context: { cookies } }), + cache: new InMemoryCache(), + credentials: 'same-origin' }) } diff --git a/src/graphql/resolvers/mutations/user.ts b/src/graphql/resolvers/mutations/user.ts index 0559170..910c169 100644 --- a/src/graphql/resolvers/mutations/user.ts +++ b/src/graphql/resolvers/mutations/user.ts @@ -1,15 +1,23 @@ import { composeResolvers } from '@graphql-tools/resolvers-composition' import type { Resolvers } from '@/graphql/__generated__/types.generated' import generator from 'generate-password-ts' -import bcrypt from 'bcrypt' 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 { UserInputError } from 'utils/graphQLErrors' +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) @@ -62,6 +70,12 @@ async function cropPFP(pfpFile: File, username: string, imgId: string) { 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 } }) @@ -72,7 +86,9 @@ async function createForgor(user: User) { strict: true }) - const row = await prismaClient.forgors.create({ data: { key, username: user.username } }) + 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, @@ -104,12 +120,34 @@ const resolvers: Resolvers = { 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([bcrypt.hash(password, 10), cropPFP(pfp, username, imgId)]) + 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 } /* diff --git a/src/graphql/resolvers/queries/site.ts b/src/graphql/resolvers/queries/site.ts index 093f7a3..7ff045a 100644 --- a/src/graphql/resolvers/queries/site.ts +++ b/src/graphql/resolvers/queries/site.ts @@ -1,40 +1,31 @@ -// import fg from 'fast-glob' -import { composeResolvers } from '@graphql-tools/resolvers-composition' +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': hasRole('UPDATE') */ + 'Query.banners': checkPerm('UPDATE') } const resolvers: Resolvers = { Query: { - config: async (_, { name }) => + config: (_, { name }, __, ___) => prismaClient.config.upsert({ where: { name }, create: { name }, update: {} - }) - - /* highlight: async (parent, args, { db }) => { - const { value } = await db.models.config.findByPk('highlight') - 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 images = filePaths.map((f) => f.split('/').pop()) + const images = filePaths.map((f) => f.split('/').pop()) -return images -}, - -recentComments: async (parent, { limit = 5 }, { db }) => { -return db.models.comment.findAll({ -limit, -order: [['updatedAt', 'DESC']] -}) -} */ + return images + }, + highlight: () => prismaClient.config.findUnique({ where: { name: 'highlight' } }), + recentComments: async (parent, { limit: take = 5 }) => + prismaClient.comments.findMany({ take, orderBy: [{ updatedAt: 'desc' }] }) } } diff --git a/src/graphql/typeDefs/user.graphql b/src/graphql/typeDefs/user.graphql index fe3f87e..8354bba 100644 --- a/src/graphql/typeDefs/user.graphql +++ b/src/graphql/typeDefs/user.graphql @@ -38,6 +38,9 @@ type Query { } 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 diff --git a/src/middleware.ts b/src/middleware.ts new file mode 100644 index 0000000..60eabb2 --- /dev/null +++ b/src/middleware.ts @@ -0,0 +1,22 @@ +import { defineMiddleware } from 'astro:middleware' +import { setSessionTokenCookie, deleteSessionTokenCookie, validateSessionToken, COOKIE_NAME } from 'utils/session' + +export const onRequest = defineMiddleware(async (context, next) => { + const token = context.cookies.get(COOKIE_NAME)?.value + if (!token) { + context.locals.user = null + context.locals.session = null + return next() + } + + const { session, user } = await validateSessionToken(token) + if (session !== null) { + setSessionTokenCookie(context.cookies, token, session.expiresAt) + } else { + deleteSessionTokenCookie(context.cookies) + } + + context.locals.session = session + context.locals.user = user + return next() +}) diff --git a/src/utils/graphQLErrors.ts b/src/utils/graphQLErrors.ts index 9cc0f89..ca2090a 100644 --- a/src/utils/graphQLErrors.ts +++ b/src/utils/graphQLErrors.ts @@ -1,13 +1,13 @@ import { GraphQLError } from 'graphql' import { ApolloServerErrorCode } from '@apollo/server/errors' -export const AuthenticationError = (message: string = '') => +export const AuthenticationError = (message: string = 'Authentication Error') => new GraphQLError(message, { extensions: { code: 'UNAUTHENTICATED' } }) -export const ForbiddenError = (message: string = '') => +export const ForbiddenError = (message: string = 'Forbidden') => new GraphQLError(message, { extensions: { code: 'FORBIDDEN' } }) -export const UserInputError = (message: string = '') => +export const UserInputError = (message: string = 'Bad input') => new GraphQLError(message, { extensions: { code: ApolloServerErrorCode.BAD_USER_INPUT } }) diff --git a/src/utils/resolvers.ts b/src/utils/resolvers.ts index 4294839..b22189a 100644 --- a/src/utils/resolvers.ts +++ b/src/utils/resolvers.ts @@ -1,32 +1,26 @@ -// import User from "sequelize/models/user"; -import type { Session } from '@auth/core' +import type { ResolverContext } from '@/graphql/apolloClientSSR' +import { AuthenticationError, ForbiddenError } from './graphQLErrors' +import prismaClient from 'prisma/client' +import { COOKIE_NAME, validateSessionToken } from './session' -export async function getUser(session: Session | null) { - if (!session) return null - - const { id } = session - const user = await User.findByPk(id) - return user -} - -const isAuthed = (next) => async (root, args, context, info) => { - const session = await getSession() - const { username = null } = session - - if (!username) throw AuthenticationError() - return next(root, args, context, info) -} - -const hasPerm = (perm) => (next) => async (root, args, context, info) => { - const { db } = context - const user = await getUser(db) - const roles = await user.getRoles() - const permissions = roles.map((r) => r.permissions).flat() - if (!permissions.includes(perm)) throw ForbiddenError() +export const checkAuth = (next) => async (root, args, context: ResolverContext, info) => { + const { user } = await validateSessionToken(context.cookies?.get(COOKIE_NAME)?.value) + if (!user) throw AuthenticationError() return next(root, args, context, info) } -consoley.log() +export const checkPerm = (perm: string) => (next) => async (root, args, context: ResolverContext, info) => { + const { user } = await validateSessionToken(context.cookies?.get(COOKIE_NAME)?.value) + if (!user) throw AuthenticationError() -export const hasRole = (role) => [isAuthedApp, hasPermApp(role)] + const { roles } = await prismaClient.users.findUnique({ + where: { username: user?.username }, + include: { roles: { include: { role: { select: { permissions: true } } } } } + }) + const perms = roles.map((r) => r.role.permissions).flat() + + if (!perms.includes(perm)) throw ForbiddenError() + + return next(root, args, context, info) +} diff --git a/src/utils/session.ts b/src/utils/session.ts new file mode 100644 index 0000000..1115bce --- /dev/null +++ b/src/utils/session.ts @@ -0,0 +1,90 @@ +// Taken from https://lucia-auth.com +import { encodeBase32LowerCaseNoPadding, encodeHexLowerCase } from '@oslojs/encoding' +import { sha256 } from '@oslojs/crypto/sha2' +import { Argon2id } from 'oslo/password' +import type { AstroCookies } from 'astro' + +import prismaClient from 'prisma/client' +import { type users, type session } from '@prisma/client' + +export const argon2id = new Argon2id() +export const COOKIE_NAME = 'astro_soc' + +export function generateSessionToken(): string { + const bytes = new Uint8Array(20) + crypto.getRandomValues(bytes) + const token = encodeBase32LowerCaseNoPadding(bytes) + return token +} + +export async function createSession(username: string, token: string): Promise { + const sessionId = encodeHexLowerCase(sha256(new TextEncoder().encode(token))) + const session = await prismaClient.session.create({ + data: { + id: sessionId, + userId: username, + expiresAt: new Date(Date.now() + 1000 * 60 * 60 * 24 * 30) + } + }) + return session +} + +const EMPTY_SESSION = { session: null, user: null } + +export async function validateSessionToken(token?: string): Promise { + if (!token) return EMPTY_SESSION + + const sessionId = encodeHexLowerCase(sha256(new TextEncoder().encode(token))) + const result = await prismaClient.session.findUnique({ + where: { + id: sessionId + }, + include: { + user: { + select: { + username: true, + createdAt: true, + placeholder: true, + imgId: true + } + } + } + }) + + if (result === null) return EMPTY_SESSION + + const { user, ...session } = result + if (Date.now() >= session.expiresAt.getTime()) { + await prismaClient.session.delete({ where: { id: sessionId } }) + return EMPTY_SESSION + } + if (Date.now() >= session.expiresAt.getTime() - 1000 * 60 * 60 * 24 * 15) { + session.expiresAt = new Date(Date.now() + 1000 * 60 * 60 * 24 * 30) + await prismaClient.session.update({ + where: { id: session.id }, + data: { expiresAt: session.expiresAt } + }) + } + return { session, user } +} + +export async function invalidateSession(sessionId: string): void { + await prismaClient.session.delete({ where: { id: sessionId } }) +} + +export type SessionValidationResult = { session: session; user: users } | { session: null; user: null } + +const COOKIE_OPTIONS = { + httpOnly: true, + sameSite: 'lax', + secure: import.meta.env.PROD, + path: '/' +} + +export function setSessionTokenCookie(cookies: AstroCookies, token: string, expiresAt: Date) { + cookies.set(COOKIE_NAME, token, { ...COOKIE_OPTIONS, expires: expiresAt }) +} + +export function deleteSessionTokenCookie(cookies: AstroCookies) { + cookies.delete(COOKIE_NAME, { ...COOKIE_OPTIONS, maxAge: 0 }) +} diff --git a/yarn.lock b/yarn.lock index b9d2c9f..c32cc8b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -310,19 +310,6 @@ is-wsl "^3.0.0" which-pm-runs "^1.1.0" -"@auth/core@^0.35.0": - version "0.35.0" - resolved "https://registry.yarnpkg.com/@auth/core/-/core-0.35.0.tgz#2d2c20896c13ee40aa0fffe0bff613b180d1d16f" - integrity sha512-XvMALiYn5ZQd1hVeG1t+jCU89jRrc7ortl/05wkBrPHnRWZScxAK5jKuzBz+AOBQXewDjYcMpzeF5tTqg6rDhQ== - dependencies: - "@panva/hkdf" "^1.1.1" - "@types/cookie" "0.6.0" - cookie "0.6.0" - jose "^5.1.3" - oauth4webapi "^2.10.4" - preact "10.11.3" - preact-render-to-string "5.2.3" - "@babel/code-frame@^7.0.0", "@babel/code-frame@^7.24.7": version "7.24.7" resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.24.7.tgz#882fd9e09e8ee324e496bd040401c6f046ef4465" @@ -732,7 +719,7 @@ dependencies: "@babel/helper-plugin-utils" "^7.24.7" -"@babel/runtime@^7.0.0", "@babel/runtime@^7.20.13", "@babel/runtime@^7.21.0": +"@babel/runtime@^7.0.0", "@babel/runtime@^7.21.0": version "7.25.6" resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.25.6.tgz#9afc3289f7184d8d7f98b099884c26317b9264d2" integrity sha512-VBj9MYyDb9tuLq7yzqjgzt6Q+IBQLrGZfdjOekyEirZPHxXWoTSGUTMrpsfi58Up73d13NfYLv8HT9vmznjzhQ== @@ -796,6 +783,20 @@ ts-morph "^22.0.0" tslib "^2.3.0" +"@emnapi/core@^0.45.0": + version "0.45.0" + resolved "https://registry.yarnpkg.com/@emnapi/core/-/core-0.45.0.tgz#e58dc1f70ab3af3f6708991ba56d988088daf204" + integrity sha512-DPWjcUDQkCeEM4VnljEOEcXdAD7pp8zSZsgOujk/LGIwCXWbXJngin+MO4zbH429lzeC3WbYLGjE2MaUOwzpyw== + dependencies: + tslib "^2.4.0" + +"@emnapi/runtime@^0.45.0": + version "0.45.0" + resolved "https://registry.yarnpkg.com/@emnapi/runtime/-/runtime-0.45.0.tgz#e754de04c683263f34fd0c7f32adfe718bbe4ddd" + integrity sha512-Txumi3td7J4A/xTTwlssKieHKTGl3j4A1tglBx72auZ49YK7ePY6XZricgIg9mnZT4xPfA+UPCUdnhRuEFDL+w== + dependencies: + tslib "^2.4.0" + "@emnapi/runtime@^1.2.0": version "1.2.0" resolved "https://registry.yarnpkg.com/@emnapi/runtime/-/runtime-1.2.0.tgz#71d018546c3a91f3b51106530edbc056b9f2f2e3" @@ -1893,20 +1894,195 @@ dependencies: typescript "5.2.2" -"@mapbox/node-pre-gyp@^1.0.11": - version "1.0.11" - resolved "https://registry.yarnpkg.com/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.11.tgz#417db42b7f5323d79e93b34a6d7a2a12c0df43fa" - integrity sha512-Yhlar6v9WQgUp/He7BdgzOz8lqMQ8sU+jkCq7Wx8Myc5YFJLbEe7lgui/V7G1qB1DJykHSGwreceSaD60Y0PUQ== +"@node-rs/argon2-android-arm-eabi@1.7.0": + version "1.7.0" + resolved "https://registry.yarnpkg.com/@node-rs/argon2-android-arm-eabi/-/argon2-android-arm-eabi-1.7.0.tgz#9de6d4519e7b757d0e3480d4b108fd0d698be332" + integrity sha512-udDqkr5P9E+wYX1SZwAVPdyfYvaF4ry9Tm+R9LkfSHbzWH0uhU6zjIwNRp7m+n4gx691rk+lqqDAIP8RLKwbhg== + +"@node-rs/argon2-android-arm64@1.7.0": + version "1.7.0" + resolved "https://registry.yarnpkg.com/@node-rs/argon2-android-arm64/-/argon2-android-arm64-1.7.0.tgz#9e0d65e64002537f7145006d103582eceb87a8e3" + integrity sha512-s9j/G30xKUx8WU50WIhF0fIl1EdhBGq0RQ06lEhZ0Gi0ap8lhqbE2Bn5h3/G2D1k0Dx+yjeVVNmt/xOQIRG38A== + +"@node-rs/argon2-darwin-arm64@1.7.0": + version "1.7.0" + resolved "https://registry.yarnpkg.com/@node-rs/argon2-darwin-arm64/-/argon2-darwin-arm64-1.7.0.tgz#1d8f8d7a637313cf3b74ec81f8e21f6555d681b4" + integrity sha512-ZIz4L6HGOB9U1kW23g+m7anGNuTZ0RuTw0vNp3o+2DWpb8u8rODq6A8tH4JRL79S+Co/Nq608m9uackN2pe0Rw== + +"@node-rs/argon2-darwin-x64@1.7.0": + version "1.7.0" + resolved "https://registry.yarnpkg.com/@node-rs/argon2-darwin-x64/-/argon2-darwin-x64-1.7.0.tgz#2a63cffbd4212d40242f65d7c140cae16ba7ca24" + integrity sha512-5oi/pxqVhODW/pj1+3zElMTn/YukQeywPHHYDbcAW3KsojFjKySfhcJMd1DjKTc+CHQI+4lOxZzSUzK7mI14Hw== + +"@node-rs/argon2-freebsd-x64@1.7.0": + version "1.7.0" + resolved "https://registry.yarnpkg.com/@node-rs/argon2-freebsd-x64/-/argon2-freebsd-x64-1.7.0.tgz#637b15bada735c47e57ad5d6403f70083ae6c104" + integrity sha512-Ify08683hA4QVXYoIm5SUWOY5DPIT/CMB0CQT+IdxQAg/F+qp342+lUkeAtD5bvStQuCx/dFO3bnnzoe2clMhA== + +"@node-rs/argon2-linux-arm-gnueabihf@1.7.0": + version "1.7.0" + resolved "https://registry.yarnpkg.com/@node-rs/argon2-linux-arm-gnueabihf/-/argon2-linux-arm-gnueabihf-1.7.0.tgz#fe6d43cb9c9e43788dfb6bb76c78736cc5b6f717" + integrity sha512-7DjDZ1h5AUHAtRNjD19RnQatbhL+uuxBASuuXIBu4/w6Dx8n7YPxwTP4MXfsvuRgKuMWiOb/Ub/HJ3kXVCXRkg== + +"@node-rs/argon2-linux-arm64-gnu@1.7.0": + version "1.7.0" + resolved "https://registry.yarnpkg.com/@node-rs/argon2-linux-arm64-gnu/-/argon2-linux-arm64-gnu-1.7.0.tgz#f5e3f5a9dd2d55fe3dd9816e0581e99a1abe1698" + integrity sha512-nJDoMP4Y3YcqGswE4DvP080w6O24RmnFEDnL0emdI8Nou17kNYBzP2546Nasx9GCyLzRcYQwZOUjrtUuQ+od2g== + +"@node-rs/argon2-linux-arm64-musl@1.7.0": + version "1.7.0" + resolved "https://registry.yarnpkg.com/@node-rs/argon2-linux-arm64-musl/-/argon2-linux-arm64-musl-1.7.0.tgz#0e57f0d6ba78d3fd683583cffe5ec00b2e422ae5" + integrity sha512-BKWS8iVconhE3jrb9mj6t1J9vwUqQPpzCbUKxfTGJfc+kNL58F1SXHBoe2cDYGnHrFEHTY0YochzXoAfm4Dm/A== + +"@node-rs/argon2-linux-x64-gnu@1.7.0": + version "1.7.0" + resolved "https://registry.yarnpkg.com/@node-rs/argon2-linux-x64-gnu/-/argon2-linux-x64-gnu-1.7.0.tgz#d72bd52c970fe120b97ddb4a1f7719ccacf47c4a" + integrity sha512-EmgqZOlf4Jurk/szW1iTsVISx25bKksVC5uttJDUloTgsAgIGReCpUUO1R24pBhu9ESJa47iv8NSf3yAfGv6jQ== + +"@node-rs/argon2-linux-x64-musl@1.7.0": + version "1.7.0" + resolved "https://registry.yarnpkg.com/@node-rs/argon2-linux-x64-musl/-/argon2-linux-x64-musl-1.7.0.tgz#850b4fa411bd005d6815228d4495075434f46a5d" + integrity sha512-/o1efYCYIxjfuoRYyBTi2Iy+1iFfhqHCvvVsnjNSgO1xWiWrX0Rrt/xXW5Zsl7vS2Y+yu8PL8KFWRzZhaVxfKA== + +"@node-rs/argon2-wasm32-wasi@1.7.0": + version "1.7.0" + resolved "https://registry.yarnpkg.com/@node-rs/argon2-wasm32-wasi/-/argon2-wasm32-wasi-1.7.0.tgz#83b361b9e2ece101288306606fa883c2cba87e7d" + integrity sha512-Evmk9VcxqnuwQftfAfYEr6YZYSPLzmKUsbFIMep5nTt9PT4XYRFAERj7wNYp+rOcBenF3X4xoB+LhwcOMTNE5w== dependencies: - detect-libc "^2.0.0" - https-proxy-agent "^5.0.0" - make-dir "^3.1.0" - node-fetch "^2.6.7" - nopt "^5.0.0" - npmlog "^5.0.1" - rimraf "^3.0.2" - semver "^7.3.5" - tar "^6.1.11" + "@emnapi/core" "^0.45.0" + "@emnapi/runtime" "^0.45.0" + "@tybys/wasm-util" "^0.8.1" + memfs-browser "^3.4.13000" + +"@node-rs/argon2-win32-arm64-msvc@1.7.0": + version "1.7.0" + resolved "https://registry.yarnpkg.com/@node-rs/argon2-win32-arm64-msvc/-/argon2-win32-arm64-msvc-1.7.0.tgz#7a6dc4d456c94cd2bdaa720e11dd325e158fb3dd" + integrity sha512-qgsU7T004COWWpSA0tppDqDxbPLgg8FaU09krIJ7FBl71Sz8SFO40h7fDIjfbTT5w7u6mcaINMQ5bSHu75PCaA== + +"@node-rs/argon2-win32-ia32-msvc@1.7.0": + version "1.7.0" + resolved "https://registry.yarnpkg.com/@node-rs/argon2-win32-ia32-msvc/-/argon2-win32-ia32-msvc-1.7.0.tgz#8405f226b06354915ae10f919c54d0883f2fb632" + integrity sha512-JGafwWYQ/HpZ3XSwP4adQ6W41pRvhcdXvpzIWtKvX+17+xEXAe2nmGWM6s27pVkg1iV2ZtoYLRDkOUoGqZkCcg== + +"@node-rs/argon2-win32-x64-msvc@1.7.0": + version "1.7.0" + resolved "https://registry.yarnpkg.com/@node-rs/argon2-win32-x64-msvc/-/argon2-win32-x64-msvc-1.7.0.tgz#3983e08350dacfb30697923faf1ce34243e11c86" + integrity sha512-9oq4ShyFakw8AG3mRls0AoCpxBFcimYx7+jvXeAf2OqKNO+mSA6eZ9z7KQeVCi0+SOEUYxMGf5UiGiDb9R6+9Q== + +"@node-rs/argon2@1.7.0": + version "1.7.0" + resolved "https://registry.yarnpkg.com/@node-rs/argon2/-/argon2-1.7.0.tgz#80f3fdd7c5643b763a98bd0f5a2f2f975bcd95e5" + integrity sha512-zfULc+/tmcWcxn+nHkbyY8vP3+MpEqKORbszt4UkpqZgBgDAAIYvuDN/zukfTgdmo6tmJKKVfzigZOPk4LlIog== + optionalDependencies: + "@node-rs/argon2-android-arm-eabi" "1.7.0" + "@node-rs/argon2-android-arm64" "1.7.0" + "@node-rs/argon2-darwin-arm64" "1.7.0" + "@node-rs/argon2-darwin-x64" "1.7.0" + "@node-rs/argon2-freebsd-x64" "1.7.0" + "@node-rs/argon2-linux-arm-gnueabihf" "1.7.0" + "@node-rs/argon2-linux-arm64-gnu" "1.7.0" + "@node-rs/argon2-linux-arm64-musl" "1.7.0" + "@node-rs/argon2-linux-x64-gnu" "1.7.0" + "@node-rs/argon2-linux-x64-musl" "1.7.0" + "@node-rs/argon2-wasm32-wasi" "1.7.0" + "@node-rs/argon2-win32-arm64-msvc" "1.7.0" + "@node-rs/argon2-win32-ia32-msvc" "1.7.0" + "@node-rs/argon2-win32-x64-msvc" "1.7.0" + +"@node-rs/bcrypt-android-arm-eabi@1.9.0": + version "1.9.0" + resolved "https://registry.yarnpkg.com/@node-rs/bcrypt-android-arm-eabi/-/bcrypt-android-arm-eabi-1.9.0.tgz#d31dab5aa9bb70a5fa09e30f101f3a997eb84a28" + integrity sha512-nOCFISGtnodGHNiLrG0WYLWr81qQzZKYfmwHc7muUeq+KY0sQXyHOwZk9OuNQAWv/lnntmtbwkwT0QNEmOyLvA== + +"@node-rs/bcrypt-android-arm64@1.9.0": + version "1.9.0" + resolved "https://registry.yarnpkg.com/@node-rs/bcrypt-android-arm64/-/bcrypt-android-arm64-1.9.0.tgz#0e1c7345460eb413484509b58d691f3152e1505f" + integrity sha512-+ZrIAtigVmjYkqZQTThHVlz0+TG6D+GDHWhVKvR2DifjtqJ0i+mb9gjo++hN+fWEQdWNGxKCiBBjwgT4EcXd6A== + +"@node-rs/bcrypt-darwin-arm64@1.9.0": + version "1.9.0" + resolved "https://registry.yarnpkg.com/@node-rs/bcrypt-darwin-arm64/-/bcrypt-darwin-arm64-1.9.0.tgz#b11d643b0031453670918b6168f0762fd56dd244" + integrity sha512-CQiS+F9Pa0XozvkXR1g7uXE9QvBOPOplDg0iCCPRYTN9PqA5qYxhwe48G3o+v2UeQceNRrbnEtWuANm7JRqIhw== + +"@node-rs/bcrypt-darwin-x64@1.9.0": + version "1.9.0" + resolved "https://registry.yarnpkg.com/@node-rs/bcrypt-darwin-x64/-/bcrypt-darwin-x64-1.9.0.tgz#f6b175a6b12c979de490e6c106b2458ff52fc5f0" + integrity sha512-4pTKGawYd7sNEjdJ7R/R67uwQH1VvwPZ0SSUMmeNHbxD5QlwAPXdDH11q22uzVXsvNFZ6nGQBg8No5OUGpx6Ug== + +"@node-rs/bcrypt-freebsd-x64@1.9.0": + version "1.9.0" + resolved "https://registry.yarnpkg.com/@node-rs/bcrypt-freebsd-x64/-/bcrypt-freebsd-x64-1.9.0.tgz#b2045f83c1b984e9bd76d7566cfa79c3e645aeff" + integrity sha512-UmWzySX4BJhT/B8xmTru6iFif3h0Rpx3TqxRLCcbgmH43r7k5/9QuhpiyzpvKGpKHJCFNm4F3rC2wghvw5FCIg== + +"@node-rs/bcrypt-linux-arm-gnueabihf@1.9.0": + version "1.9.0" + resolved "https://registry.yarnpkg.com/@node-rs/bcrypt-linux-arm-gnueabihf/-/bcrypt-linux-arm-gnueabihf-1.9.0.tgz#946c743f417528dd1ddb0950259b52502e4ab962" + integrity sha512-8qoX4PgBND2cVwsbajoAWo3NwdfJPEXgpCsZQZURz42oMjbGyhhSYbovBCskGU3EBLoC8RA2B1jFWooeYVn5BA== + +"@node-rs/bcrypt-linux-arm64-gnu@1.9.0": + version "1.9.0" + resolved "https://registry.yarnpkg.com/@node-rs/bcrypt-linux-arm64-gnu/-/bcrypt-linux-arm64-gnu-1.9.0.tgz#702882f1ff1ac02a18f5bdb0010f6a429ba86445" + integrity sha512-TuAC6kx0SbcIA4mSEWPi+OCcDjTQUMl213v5gMNlttF+D4ieIZx6pPDGTaMO6M2PDHTeCG0CBzZl0Lu+9b0c7Q== + +"@node-rs/bcrypt-linux-arm64-musl@1.9.0": + version "1.9.0" + resolved "https://registry.yarnpkg.com/@node-rs/bcrypt-linux-arm64-musl/-/bcrypt-linux-arm64-musl-1.9.0.tgz#207f1d7147d7e31d4fe03d02797f977d5dadebe8" + integrity sha512-/sIvKDABOI8QOEnLD7hIj02BVaNOuCIWBKvxcJOt8+TuwJ6zmY1UI5kSv9d99WbiHjTp97wtAUbZQwauU4b9ew== + +"@node-rs/bcrypt-linux-x64-gnu@1.9.0": + version "1.9.0" + resolved "https://registry.yarnpkg.com/@node-rs/bcrypt-linux-x64-gnu/-/bcrypt-linux-x64-gnu-1.9.0.tgz#4557dac7a939eaf24cb0ee1b9688b6d9e105373d" + integrity sha512-DyyhDHDsLBsCKz1tZ1hLvUZSc1DK0FU0v52jK6IBQxrj24WscSU9zZe7ie/V9kdmA4Ep57BfpWX8Dsa2JxGdgQ== + +"@node-rs/bcrypt-linux-x64-musl@1.9.0": + version "1.9.0" + resolved "https://registry.yarnpkg.com/@node-rs/bcrypt-linux-x64-musl/-/bcrypt-linux-x64-musl-1.9.0.tgz#c86ad58d70b71f703bbdd90c623f63ca38e2f751" + integrity sha512-duIiuqQ+Lew8ASSAYm6ZRqcmfBGWwsi81XLUwz86a2HR7Qv6V4yc3ZAUQovAikhjCsIqe8C11JlAZSK6+PlXYg== + +"@node-rs/bcrypt-wasm32-wasi@1.9.0": + version "1.9.0" + resolved "https://registry.yarnpkg.com/@node-rs/bcrypt-wasm32-wasi/-/bcrypt-wasm32-wasi-1.9.0.tgz#42ae8e2f80e5293d36dee53a14da34a5888ff0e6" + integrity sha512-ylaGmn9Wjwv/D5lxtawttx3H6Uu2WTTR7lWlRHGT6Ga/MB1Vj4OjSGUW8G8zIVnKuXpGbZ92pgHlt4HUpSLctw== + dependencies: + "@emnapi/core" "^0.45.0" + "@emnapi/runtime" "^0.45.0" + "@tybys/wasm-util" "^0.8.1" + memfs-browser "^3.4.13000" + +"@node-rs/bcrypt-win32-arm64-msvc@1.9.0": + version "1.9.0" + resolved "https://registry.yarnpkg.com/@node-rs/bcrypt-win32-arm64-msvc/-/bcrypt-win32-arm64-msvc-1.9.0.tgz#4e31a59a76b5cb091e69b9548c798ce27908cb82" + integrity sha512-2h86gF7QFyEzODuDFml/Dp1MSJoZjxJ4yyT2Erf4NkwsiA5MqowUhUsorRwZhX6+2CtlGa7orbwi13AKMsYndw== + +"@node-rs/bcrypt-win32-ia32-msvc@1.9.0": + version "1.9.0" + resolved "https://registry.yarnpkg.com/@node-rs/bcrypt-win32-ia32-msvc/-/bcrypt-win32-ia32-msvc-1.9.0.tgz#89f23c2f66e578d13526a0b33405332ced6b8348" + integrity sha512-kqxalCvhs4FkN0+gWWfa4Bdy2NQAkfiqq/CEf6mNXC13RSV673Ev9V8sRlQyNpCHCNkeXfOT9pgoBdJmMs9muA== + +"@node-rs/bcrypt-win32-x64-msvc@1.9.0": + version "1.9.0" + resolved "https://registry.yarnpkg.com/@node-rs/bcrypt-win32-x64-msvc/-/bcrypt-win32-x64-msvc-1.9.0.tgz#9f061a05d5f2dc360a016081bfaed190b2145c84" + integrity sha512-2y0Tuo6ZAT2Cz8V7DHulSlv1Bip3zbzeXyeur+uR25IRNYXKvI/P99Zl85Fbuu/zzYAZRLLlGTRe6/9IHofe/w== + +"@node-rs/bcrypt@1.9.0": + version "1.9.0" + resolved "https://registry.yarnpkg.com/@node-rs/bcrypt/-/bcrypt-1.9.0.tgz#ad1560997939f7c7c4a7500ac272709ce5bde6f6" + integrity sha512-u2OlIxW264bFUfvbFqDz9HZKFjwe8FHFtn7T/U8mYjPZ7DWYpbUB+/dkW/QgYfMSfR0ejkyuWaBBe0coW7/7ig== + optionalDependencies: + "@node-rs/bcrypt-android-arm-eabi" "1.9.0" + "@node-rs/bcrypt-android-arm64" "1.9.0" + "@node-rs/bcrypt-darwin-arm64" "1.9.0" + "@node-rs/bcrypt-darwin-x64" "1.9.0" + "@node-rs/bcrypt-freebsd-x64" "1.9.0" + "@node-rs/bcrypt-linux-arm-gnueabihf" "1.9.0" + "@node-rs/bcrypt-linux-arm64-gnu" "1.9.0" + "@node-rs/bcrypt-linux-arm64-musl" "1.9.0" + "@node-rs/bcrypt-linux-x64-gnu" "1.9.0" + "@node-rs/bcrypt-linux-x64-musl" "1.9.0" + "@node-rs/bcrypt-wasm32-wasi" "1.9.0" + "@node-rs/bcrypt-win32-arm64-msvc" "1.9.0" + "@node-rs/bcrypt-win32-ia32-msvc" "1.9.0" + "@node-rs/bcrypt-win32-x64-msvc" "1.9.0" "@nodelib/fs.scandir@2.1.5": version "2.1.5" @@ -2164,16 +2340,31 @@ "@octokit/webhooks-types" "7.4.0" aggregate-error "^3.1.0" -"@oslojs/encoding@^1.0.0": +"@oslojs/asn1@1.0.0": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@oslojs/asn1/-/asn1-1.0.0.tgz#25edb31585b369efdc103e9a1eb822df9c235174" + integrity sha512-zw/wn0sj0j0QKbIXfIlnEcTviaCzYOY3V5rAyjR6YtOByFtJiT574+8p9Wlach0lZH9fddD4yb9laEAIl4vXQA== + dependencies: + "@oslojs/binary" "1.0.0" + +"@oslojs/binary@1.0.0": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@oslojs/binary/-/binary-1.0.0.tgz#3e73f9cef0d06731d2aa528066666ccc00d610d6" + integrity sha512-9RCU6OwXU6p67H4NODbuxv2S3eenuQ4/WFLrsq+K/k682xrznH5EVWA7N4VFk9VYVcbFtKqur5YQQZc0ySGhsQ== + +"@oslojs/crypto@^1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@oslojs/crypto/-/crypto-1.0.1.tgz#74cf0d19d9fcda7cf5648cf3188dfeaf1d1b039f" + integrity sha512-7n08G8nWjAr/Yu3vu9zzrd0L9XnrJfpMioQcvCMxBIiF5orECHe5/3J0jmXRVvgfqMm/+4oxlQ+Sq39COYLcNQ== + dependencies: + "@oslojs/asn1" "1.0.0" + "@oslojs/binary" "1.0.0" + +"@oslojs/encoding@^1.0.0", "@oslojs/encoding@^1.1.0": version "1.1.0" resolved "https://registry.yarnpkg.com/@oslojs/encoding/-/encoding-1.1.0.tgz#55f3d9a597430a01f2a5ef63c6b42f769f9ce34e" integrity sha512-70wQhgYmndg4GCPxPPxPGevRKqTIJ2Nh4OkiMWmDAVYsTQ+Ta7Sq+rPevXyXGdzr30/qZBnyOalCszoMxlyldQ== -"@panva/hkdf@^1.0.2", "@panva/hkdf@^1.1.1": - version "1.2.1" - resolved "https://registry.yarnpkg.com/@panva/hkdf/-/hkdf-1.2.1.tgz#cb0d111ef700136f4580349ff0226bf25c853f23" - integrity sha512-6oclG6Y3PiDFcoyk8srjLfVKyMfVCKJ27JwNPViuXziFpmdz+MZnZN/aKY0JGXgYuO/VghU0jcOAZgWXZ1Dmrw== - "@parcel/watcher-android-arm64@2.4.1": version "2.4.1" resolved "https://registry.yarnpkg.com/@parcel/watcher-android-arm64/-/watcher-android-arm64-2.4.1.tgz#c2c19a3c442313ff007d2d7a9c2c1dd3e1c9ca84" @@ -2555,6 +2746,13 @@ mkdirp "^3.0.1" path-browserify "^1.0.1" +"@tybys/wasm-util@^0.8.1": + version "0.8.3" + resolved "https://registry.yarnpkg.com/@tybys/wasm-util/-/wasm-util-0.8.3.tgz#34dc6fd51bdc03524a27359137594bb15c59bba1" + integrity sha512-Z96T/L6dUFFxgFJ+pQtkPpne9q7i6kIPYCFnQBHSgSPV9idTsKfIhCss0h5iM9irweZCatkrdeP8yi5uM1eX6Q== + dependencies: + tslib "^2.4.0" + "@types/aws-lambda@^8.10.83": version "8.10.145" resolved "https://registry.yarnpkg.com/@types/aws-lambda/-/aws-lambda-8.10.145.tgz#b2d31a987f4888e5553ff1819f57cafa475594d9" @@ -2593,13 +2791,6 @@ dependencies: "@babel/types" "^7.20.7" -"@types/bcrypt@^5.0.2": - version "5.0.2" - resolved "https://registry.yarnpkg.com/@types/bcrypt/-/bcrypt-5.0.2.tgz#22fddc11945ea4fbc3655b3e8b8847cc9f811477" - integrity sha512-6atioO8Y75fNcbmj0G7UjI9lXN2pQ/IGJ2FWT4a/btd0Lk9lQalHLKhkgKVZ3r+spnmWUKfbMi1GEe9wyHQfNQ== - dependencies: - "@types/node" "*" - "@types/body-parser@*": version "1.19.5" resolved "https://registry.yarnpkg.com/@types/body-parser/-/body-parser-1.19.5.tgz#04ce9a3b677dc8bd681a17da1ab9835dc9d3ede4" @@ -2620,7 +2811,7 @@ dependencies: "@types/node" "*" -"@types/cookie@0.6.0", "@types/cookie@^0.6.0": +"@types/cookie@^0.6.0": version "0.6.0" resolved "https://registry.yarnpkg.com/@types/cookie/-/cookie-0.6.0.tgz#eac397f28bf1d6ae0ae081363eca2f425bedf0d5" integrity sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA== @@ -3019,11 +3210,6 @@ dependencies: tslib "^2.3.0" -abbrev@1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8" - integrity sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q== - accepts@~1.3.8: version "1.3.8" resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.8.tgz#0bf0be125b67014adcb0b0921e62db7bffe16b2e" @@ -3042,13 +3228,6 @@ acorn@^8.11.2, acorn@^8.11.3, acorn@^8.12.0, acorn@^8.12.1: resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.12.1.tgz#71616bdccbe25e27a54439e0046e89ca76df2248" integrity sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg== -agent-base@6: - version "6.0.2" - resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-6.0.2.tgz#49fff58577cfee3f37176feab4c22e00f86d7f77" - integrity sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ== - dependencies: - debug "4" - agent-base@^7.0.2, agent-base@^7.1.0: version "7.1.1" resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-7.1.1.tgz#bdbded7dfb096b751a2a087eeeb9664725b2e317" @@ -3137,19 +3316,6 @@ apollo-upload-client@^18.0.1: dependencies: extract-files "^13.0.0" -"aproba@^1.0.3 || ^2.0.0": - version "2.0.0" - resolved "https://registry.yarnpkg.com/aproba/-/aproba-2.0.0.tgz#52520b8ae5b569215b354efc0caa3fe1e45a8adc" - integrity sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ== - -are-we-there-yet@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-2.0.0.tgz#372e0e7bd279d8e94c653aaa1f67200884bf3e1c" - integrity sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw== - dependencies: - delegates "^1.0.0" - readable-stream "^3.6.0" - arg@^5.0.2: version "5.0.2" resolved "https://registry.yarnpkg.com/arg/-/arg-5.0.2.tgz#c81433cc427c92c4dcf4865142dbca6f15acd59c" @@ -3420,14 +3586,6 @@ asynckit@^0.4.0: resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q== -auth-astro@^4.1.2: - version "4.1.2" - resolved "https://registry.yarnpkg.com/auth-astro/-/auth-astro-4.1.2.tgz#ff989e3f33d792055b5b558a9ce461b05e3ced22" - integrity sha512-jkbxYZAKtBWVs5mZp+4y7W3WNF0UXlkcz2sOKyy+uBfDLOUWP4av/i9+cXCRflbhbmxBw7sztdzw/pcUE40IRg== - dependencies: - next-auth "^4.18.8" - set-cookie-parser "^2.5.1" - auto-bind@~4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/auto-bind/-/auto-bind-4.0.0.tgz#e3589fc6c2da8f7ca43ba9f84fa52a744fc997fb" @@ -3529,14 +3687,6 @@ base64-js@^1.3.1: resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== -bcrypt@^5.1.1: - version "5.1.1" - resolved "https://registry.yarnpkg.com/bcrypt/-/bcrypt-5.1.1.tgz#0f732c6dcb4e12e5b70a25e326a72965879ba6e2" - integrity sha512-AGBHOG5hPYZ5Xl9KXzU5iKq9516yEmvCKDg3ecP5kX2aB6UqTeXZxk2ELnDgDm6BQSMlLt9rDB4LoSMx0rYwww== - dependencies: - "@mapbox/node-pre-gyp" "^1.0.11" - node-addon-api "^5.0.0" - before-after-hook@^2.2.0: version "2.2.3" resolved "https://registry.yarnpkg.com/before-after-hook/-/before-after-hook-2.2.3.tgz#c51e809c81a4e354084422b9b26bad88249c517c" @@ -3972,11 +4122,6 @@ color-string@^1.9.0: color-name "^1.0.0" simple-swizzle "^0.2.2" -color-support@^1.1.2: - version "1.1.3" - resolved "https://registry.yarnpkg.com/color-support/-/color-support-1.1.3.tgz#93834379a1cc9a0c61f82f52f0d04322251bd5a2" - integrity sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg== - color@^4.2.3: version "4.2.3" resolved "https://registry.yarnpkg.com/color/-/color-4.2.3.tgz#d781ecb5e57224ee43ea9627560107c0e0c6463a" @@ -4057,11 +4202,6 @@ consola@3.2.3: resolved "https://registry.yarnpkg.com/consola/-/consola-3.2.3.tgz#0741857aa88cfa0d6fd53f1cff0375136e98502f" integrity sha512-I5qxpzLv+sJhTVEoLYNcTW+bThDCPsit0vLNKShZx6rLtpilNpmmeTPaeqJb9ZE9dV3DGaeby6Vuhrw38WjeyQ== -console-control-strings@^1.0.0, console-control-strings@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e" - integrity sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ== - constant-case@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/constant-case/-/constant-case-3.0.4.tgz#3b84a9aeaf4cf31ec45e6bf5de91bdfb0589faf1" @@ -4093,20 +4233,15 @@ cookie-signature@1.0.6: resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c" integrity sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ== -cookie@0.6.0, cookie@^0.6.0: - version "0.6.0" - resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.6.0.tgz#2798b04b071b0ecbff0dbb62a505a8efa4e19051" - integrity sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw== - cookie@0.7.1: version "0.7.1" resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.7.1.tgz#2f73c42142d5d5cf71310a74fc4ae61670e5dbc9" integrity sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w== -cookie@^0.5.0: - version "0.5.0" - resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.5.0.tgz#d1f5d71adec6558c58f389987c366aa47e994f8b" - integrity sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw== +cookie@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.6.0.tgz#2798b04b071b0ecbff0dbb62a505a8efa4e19051" + integrity sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw== cors@^2.8.5: version "2.8.5" @@ -4357,11 +4492,6 @@ delayed-stream@~1.0.0: resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ== -delegates@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a" - integrity sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ== - depd@2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/depd/-/depd-2.0.0.tgz#b696163cc757560d09cf22cc8fad1571b79e76df" @@ -4397,7 +4527,7 @@ detect-libc@^1.0.3: resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-1.0.3.tgz#fa137c4bd698edf55cd5cd02ac559f91a4c4ba9b" integrity sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg== -detect-libc@^2.0.0, detect-libc@^2.0.3: +detect-libc@^2.0.3: version "2.0.3" resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-2.0.3.tgz#f0cd503b40f9939b894697d19ad50895e30cf700" integrity sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw== @@ -5309,6 +5439,11 @@ fs-minipass@^2.0.0: dependencies: minipass "^3.0.0" +fs-monkey@^1.0.4: + version "1.0.6" + resolved "https://registry.yarnpkg.com/fs-monkey/-/fs-monkey-1.0.6.tgz#8ead082953e88d992cf3ff844faa907b26756da2" + integrity sha512-b1FMfwetIKymC0eioW7mTywihSQE4oLzQn1dB6rZB5fx/3NpNEdAWeCSMB+60/AeT0TCXsxzAlcYVEFCTAksWg== + fs.realpath@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" @@ -5339,21 +5474,6 @@ functions-have-names@^1.2.3: resolved "https://registry.yarnpkg.com/functions-have-names/-/functions-have-names-1.2.3.tgz#0404fe4ee2ba2f607f0e0ec3c80bae994133b834" integrity sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ== -gauge@^3.0.0: - version "3.0.2" - resolved "https://registry.yarnpkg.com/gauge/-/gauge-3.0.2.tgz#03bf4441c044383908bcfa0656ad91803259b395" - integrity sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q== - dependencies: - aproba "^1.0.3 || ^2.0.0" - color-support "^1.1.2" - console-control-strings "^1.0.0" - has-unicode "^2.0.1" - object-assign "^4.1.1" - signal-exit "^3.0.0" - string-width "^4.2.3" - strip-ansi "^6.0.1" - wide-align "^1.1.2" - generate-password-ts@^1.6.5: version "1.6.5" resolved "https://registry.yarnpkg.com/generate-password-ts/-/generate-password-ts-1.6.5.tgz#e7df96a7710ae46fd8264010309d52428fed7aaf" @@ -5441,7 +5561,7 @@ glob@^10.3.10: package-json-from-dist "^1.0.0" path-scurry "^1.11.1" -glob@^7.1.1, glob@^7.1.3: +glob@^7.1.1: version "7.2.3" resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b" integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q== @@ -5630,11 +5750,6 @@ has-tostringtag@^1.0.0, has-tostringtag@^1.0.2: dependencies: has-symbols "^1.0.3" -has-unicode@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9" - integrity sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ== - hasown@^2.0.0, hasown@^2.0.1, hasown@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/hasown/-/hasown-2.0.2.tgz#003eaf91be7adc372e84ec59dc37252cedb80003" @@ -5818,14 +5933,6 @@ http-proxy-agent@^7.0.0: agent-base "^7.1.0" debug "^4.3.4" -https-proxy-agent@^5.0.0: - version "5.0.1" - resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz#c59ef224a04fe8b754f3db0063a25ea30d0005d6" - integrity sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA== - dependencies: - agent-base "6" - debug "4" - https-proxy-agent@^7.0.0: version "7.0.5" resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-7.0.5.tgz#9e8b5013873299e11fab6fd548405da2d6c602b2" @@ -6286,12 +6393,7 @@ jiti@^1.17.1, jiti@^1.18.2, jiti@^1.21.0: resolved "https://registry.yarnpkg.com/jiti/-/jiti-1.21.6.tgz#6c7f7398dd4b3142767f9a168af2f317a428d268" integrity sha512-2yTgeWTWzMWkHu6Jp9NKgePDaYHbntiwvYuuJLbbN9vl7DC9DvXKOB2BC3ZZ92D3cvV/aflH0osDfwpHepQ53w== -jose@^4.15.5, jose@^4.15.9: - version "4.15.9" - resolved "https://registry.yarnpkg.com/jose/-/jose-4.15.9.tgz#9b68eda29e9a0614c042fa29387196c7dd800100" - integrity sha512-1vUQX+IdDMVPj4k8kOxgUqlcK518yluMuGZwqlr44FS1ppZB/5GWh4rZG89erpOBOJjU/OBsnCVFfapsRz6nEA== - -jose@^5.0.0, jose@^5.1.3: +jose@^5.0.0: version "5.9.3" resolved "https://registry.yarnpkg.com/jose/-/jose-5.9.3.tgz#6eba1ee3f70b42891f0e1883fe0084a46dbbe02c" integrity sha512-egLIoYSpcd+QUF+UHgobt5YzI2Pkw/H39ou9suW687MY6PmCwPmkNV/4TNjn1p2tX5xO3j0d0sq5hiYE24bSlg== @@ -6650,13 +6752,6 @@ lru-cache@^5.1.1: dependencies: yallist "^3.0.2" -lru-cache@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94" - integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA== - dependencies: - yallist "^4.0.0" - lru-cache@^7.10.1, lru-cache@^7.14.1: version "7.18.3" resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-7.18.3.tgz#f793896e0fd0e954a59dfdd82f0773808df6aa89" @@ -6678,13 +6773,6 @@ magicast@^0.3.5: "@babel/types" "^7.25.4" source-map-js "^1.2.0" -make-dir@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-3.1.0.tgz#415e967046b3a7f1d185277d84aa58203726a13f" - integrity sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw== - dependencies: - semver "^6.0.0" - map-cache@^0.2.0: version "0.2.2" resolved "https://registry.yarnpkg.com/map-cache/-/map-cache-0.2.2.tgz#c32abd0bd6525d9b051645bb4f26ac5dc98a0dbf" @@ -6856,6 +6944,20 @@ media-typer@0.3.0: resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" integrity sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ== +memfs-browser@^3.4.13000: + version "3.5.10302" + resolved "https://registry.yarnpkg.com/memfs-browser/-/memfs-browser-3.5.10302.tgz#2067baf616a1b3a8e8023a033e5ead434a7ea0c0" + integrity sha512-JJTc/nh3ig05O0gBBGZjTCPOyydaTxNF0uHYBrcc1gHNnO+KIHIvo0Y1FKCJsaei6FCl8C6xfQomXqu+cuzkIw== + dependencies: + memfs "3.5.3" + +memfs@3.5.3: + version "3.5.3" + resolved "https://registry.yarnpkg.com/memfs/-/memfs-3.5.3.tgz#d9b40fe4f8d5788c5f895bda804cd0d9eeee9f3b" + integrity sha512-UERzLsxzllchadvbPs5aolHh65ISpKpM+ccLbOJ8/vvpBKmAWf+la7dXFy7Mr0ySHbdHrFv5kGFCUHHe6GFEmw== + dependencies: + fs-monkey "^1.0.4" + merge-descriptors@1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.3.tgz#d80319a65f3c7935351e5cfdac8f9318504dbed5" @@ -7322,21 +7424,6 @@ neotraverse@^0.6.18: resolved "https://registry.yarnpkg.com/neotraverse/-/neotraverse-0.6.18.tgz#abcb33dda2e8e713cf6321b29405e822230cdb30" integrity sha512-Z4SmBUweYa09+o6pG+eASabEpP6QkQ70yHj351pQoEXIs8uHbaU2DWVmzBANKgflPa47A50PtB2+NgRpQvr7vA== -next-auth@^4.18.8: - version "4.24.7" - resolved "https://registry.yarnpkg.com/next-auth/-/next-auth-4.24.7.tgz#0a14c1e35b4a2c0c1ecff95c295b74bd48d3817a" - integrity sha512-iChjE8ov/1K/z98gdKbn2Jw+2vLgJtVV39X+rCP5SGnVQuco7QOr19FRNGMIrD8d3LYhHWV9j9sKLzq1aDWWQQ== - dependencies: - "@babel/runtime" "^7.20.13" - "@panva/hkdf" "^1.0.2" - cookie "^0.5.0" - jose "^4.15.5" - oauth "^0.9.15" - openid-client "^5.4.0" - preact "^10.6.3" - preact-render-to-string "^5.1.19" - uuid "^8.3.2" - nlcst-to-string@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/nlcst-to-string/-/nlcst-to-string-4.0.0.tgz#05511e8461ebfb415952eb0b7e9a1a7d40471bd4" @@ -7357,11 +7444,6 @@ node-abort-controller@^3.1.1: resolved "https://registry.yarnpkg.com/node-abort-controller/-/node-abort-controller-3.1.1.tgz#a94377e964a9a37ac3976d848cb5c765833b8548" integrity sha512-AGK2yQKIjRuqnc6VkX2Xj5d+QW8xZ87pa1UK6yA6ouUyuxfHuMP6umE5QK7UmTeOAymo+Zx1Fxiuw9rVx8taHQ== -node-addon-api@^5.0.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-5.1.0.tgz#49da1ca055e109a23d537e9de43c09cca21eb762" - integrity sha512-eh0GgfEkpnoWDq+VY8OyvYhFEzBk6jIYbRKdIlyTiAXIVJ8PyBaKb0rp7oDtoddbdoHWhq8wwr+XZ81F1rpNdA== - node-addon-api@^7.0.0: version "7.1.1" resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-7.1.1.tgz#1aba6693b0f255258a049d621329329322aad558" @@ -7389,13 +7471,6 @@ nodemailer@^6.9.16: resolved "https://registry.yarnpkg.com/nodemailer/-/nodemailer-6.9.16.tgz#3ebdf6c6f477c571c0facb0727b33892635e0b8b" integrity sha512-psAuZdTIRN08HKVd/E8ObdV6NO7NTBY3KsC30F7M4H1OnmLCUNaS56FpYxyb26zWLSyYF9Ozch9KYHhHegsiOQ== -nopt@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/nopt/-/nopt-5.0.0.tgz#530942bb58a512fccafe53fe210f13a25355dc88" - integrity sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ== - dependencies: - abbrev "1" - normalize-path@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-2.1.1.tgz#1ab28b556e198363a8c1a6f7e6fa20137fe6aed9" @@ -7413,16 +7488,6 @@ normalize-range@^0.1.2: resolved "https://registry.yarnpkg.com/normalize-range/-/normalize-range-0.1.2.tgz#2d10c06bdfd312ea9777695a4d28439456b75942" integrity sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA== -npmlog@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-5.0.1.tgz#f06678e80e29419ad67ab964e0fa69959c1eb8b0" - integrity sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw== - dependencies: - are-we-there-yet "^2.0.0" - console-control-strings "^1.1.0" - gauge "^3.0.0" - set-blocking "^2.0.0" - nth-check@^2.0.1: version "2.1.1" resolved "https://registry.yarnpkg.com/nth-check/-/nth-check-2.1.1.tgz#c9eab428effce36cd6b92c924bdb000ef1f1ed1d" @@ -7435,26 +7500,11 @@ nullthrows@^1.1.1: resolved "https://registry.yarnpkg.com/nullthrows/-/nullthrows-1.1.1.tgz#7818258843856ae971eae4208ad7d7eb19a431b1" integrity sha512-2vPPEi+Z7WqML2jZYddDIfy5Dqb0r2fze2zTxNNknZaFpVHU3mFB3R+DWeJWGVx0ecvttSGlJTI+WG+8Z4cDWw== -oauth4webapi@^2.10.4: - version "2.17.0" - resolved "https://registry.yarnpkg.com/oauth4webapi/-/oauth4webapi-2.17.0.tgz#4af65bd4dac6761d8e4f2fb20848e82a879a6725" - integrity sha512-lbC0Z7uzAFNFyzEYRIC+pkSVvDHJTbEW+dYlSBAlCYDe6RxUkJ26bClhk8ocBZip1wfI9uKTe0fm4Ib4RHn6uQ== - -oauth@^0.9.15: - version "0.9.15" - resolved "https://registry.yarnpkg.com/oauth/-/oauth-0.9.15.tgz#bd1fefaf686c96b75475aed5196412ff60cfb9c1" - integrity sha512-a5ERWK1kh38ExDEfoO6qUHJb32rd7aYmPHuyCu3Fta/cnICvYmgd2uhuKXvPD+PXB+gCEYYEaQdIRAjCOwAKNA== - object-assign@^4, object-assign@^4.0.1, object-assign@^4.1.0, object-assign@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" integrity sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg== -object-hash@^2.2.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/object-hash/-/object-hash-2.2.0.tgz#5ad518581eefc443bd763472b8ff2e9c2c0d54a5" - integrity sha512-gScRMn0bS5fH+IuwyIFgnh9zBdo4DV+6GhygmWM9HyNJSgS0hScp1f5vjtm7oIIOiT9trXrShAkLFSc2IqKNgw== - object-hash@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/object-hash/-/object-hash-3.0.0.tgz#73f97f753e7baffc0e2cc9d6e079079744ac82e9" @@ -7532,11 +7582,6 @@ octokit@3.1.2: "@octokit/request-error" "^5.0.0" "@octokit/types" "^12.0.0" -oidc-token-hash@^5.0.3: - version "5.0.3" - resolved "https://registry.yarnpkg.com/oidc-token-hash/-/oidc-token-hash-5.0.3.tgz#9a229f0a1ce9d4fc89bcaee5478c97a889e7b7b6" - integrity sha512-IF4PcGgzAr6XXSff26Sk/+P4KZFJVuHAJZj3wgO3vX2bMdNVp/QXTP3P7CEm9V1IdG8lDLY3HhiqpsE/nOwpPw== - on-finished@2.4.1: version "2.4.1" resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.4.1.tgz#58c8c44116e54845ad57f14ab10b03533184ac3f" @@ -7572,16 +7617,6 @@ oniguruma-to-js@0.4.3: dependencies: regex "^4.3.2" -openid-client@^5.4.0: - version "5.7.0" - resolved "https://registry.yarnpkg.com/openid-client/-/openid-client-5.7.0.tgz#61dbea7251f561e82342278063ce37c5c05347f2" - integrity sha512-4GCCGZt1i2kTHpwvaC/sCpTpQqDnBzDzuJcJMbH+y1Q5qI8U8RBvoSh28svarXszZHR5BAMXbJPX1PGPRE3VOA== - dependencies: - jose "^4.15.9" - lru-cache "^6.0.0" - object-hash "^2.2.0" - oidc-token-hash "^5.0.3" - optimism@^0.18.0: version "0.18.0" resolved "https://registry.yarnpkg.com/optimism/-/optimism-0.18.0.tgz#e7bb38b24715f3fdad8a9a7fc18e999144bbfa63" @@ -7639,6 +7674,14 @@ os-tmpdir@~1.0.2: resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" integrity sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g== +oslo@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/oslo/-/oslo-1.2.1.tgz#aa2b9729c2b9bae74248ba865e9904defe231474" + integrity sha512-HfIhB5ruTdQv0XX2XlncWQiJ5SIHZ7NHZhVyHth0CSZ/xzge00etRyYy/3wp/Dsu+PkxMC+6+B2lS/GcKoewkA== + dependencies: + "@node-rs/argon2" "1.7.0" + "@node-rs/bcrypt" "1.9.0" + p-limit@3.1.0, p-limit@^3.0.2: version "3.1.0" resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b" @@ -7990,30 +8033,6 @@ posthog-node@3.1.3: axios "^1.6.0" rusha "^0.8.14" -preact-render-to-string@5.2.3: - version "5.2.3" - resolved "https://registry.yarnpkg.com/preact-render-to-string/-/preact-render-to-string-5.2.3.tgz#23d17376182af720b1060d5a4099843c7fe92fe4" - integrity sha512-aPDxUn5o3GhWdtJtW0svRC2SS/l8D9MAgo2+AWml+BhDImb27ALf04Q2d+AHqUUOc6RdSXFIBVa2gxzgMKgtZA== - dependencies: - pretty-format "^3.8.0" - -preact-render-to-string@^5.1.19: - version "5.2.6" - resolved "https://registry.yarnpkg.com/preact-render-to-string/-/preact-render-to-string-5.2.6.tgz#0ff0c86cd118d30affb825193f18e92bd59d0604" - integrity sha512-JyhErpYOvBV1hEPwIxc/fHWXPfnEGdRKxc8gFdAZ7XV4tlzyzG847XAyEZqoDnynP88akM4eaHcSOzNcLWFguw== - dependencies: - pretty-format "^3.8.0" - -preact@10.11.3: - version "10.11.3" - resolved "https://registry.yarnpkg.com/preact/-/preact-10.11.3.tgz#8a7e4ba19d3992c488b0785afcc0f8aa13c78d19" - integrity sha512-eY93IVpod/zG3uMF22Unl8h9KkrcKIRs2EGar8hwLZZDU1lkjph303V9HZBwufh2s736U6VXuhD109LYqPoffg== - -preact@^10.6.3: - version "10.24.1" - resolved "https://registry.yarnpkg.com/preact/-/preact-10.24.1.tgz#501e206d0a46667b6d0d8b780c7a65172239b2d0" - integrity sha512-PnBAwFI3Yjxxcxw75n6VId/5TFxNW/81zexzWD9jn1+eSrOP84NdsS38H5IkF/UH3frqRPT+MvuCoVHjTDTnDw== - preferred-pm@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/preferred-pm/-/preferred-pm-4.0.0.tgz#6b256a44d39181fb3829b3abbd9ea2ead6db082b" @@ -8038,11 +8057,6 @@ prettier@^3.3.3: resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.3.3.tgz#30c54fe0be0d8d12e6ae61dbb10109ea00d53105" integrity sha512-i2tDNA0O5IrMO757lfrdQZCc2jPNDVntV0m/+4whiDfWaTKfMNgR7Qz0NAeGz/nRqF4m5/6CLzbP4/liHt12Ew== -pretty-format@^3.8.0: - version "3.8.0" - resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-3.8.0.tgz#bfbed56d5e9a776645f4b1ff7aa1a3ac4fa3c385" - integrity sha512-WuxUnVtlWL1OfZFQFuqvnvs6MiAGk9UNsBostyBOB0Is9wb5uRESevA6rnl/rkksXaGX3GzZhPup5d6Vp1nFew== - prisma@^5.22.0: version "5.22.0" resolved "https://registry.yarnpkg.com/prisma/-/prisma-5.22.0.tgz#1f6717ff487cdef5f5799cc1010459920e2e6197" @@ -8200,7 +8214,7 @@ read-cache@^1.0.0: dependencies: pify "^2.3.0" -readable-stream@^3.4.0, readable-stream@^3.6.0: +readable-stream@^3.4.0: version "3.6.2" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.2.tgz#56a9b36ea965c00c5a93ef31eb111a0f11056967" integrity sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA== @@ -8483,13 +8497,6 @@ rfdc@^1.3.0: resolved "https://registry.yarnpkg.com/rfdc/-/rfdc-1.4.1.tgz#778f76c4fb731d93414e8f925fbecf64cce7f6ca" integrity sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA== -rimraf@^3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" - integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA== - dependencies: - glob "^7.1.3" - rollup@^4.20.0: version "4.22.4" resolved "https://registry.yarnpkg.com/rollup/-/rollup-4.22.4.tgz#4135a6446671cd2a2453e1ad42a45d5973ec3a0f" @@ -8588,12 +8595,12 @@ section-matter@^1.0.0: extend-shallow "^2.0.1" kind-of "^6.0.0" -semver@^6.0.0, semver@^6.3.1: +semver@^6.3.1: version "6.3.1" resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4" integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== -semver@^7.3.5, semver@^7.3.8, semver@^7.5.3, semver@^7.5.4, semver@^7.6.0, semver@^7.6.3: +semver@^7.3.8, semver@^7.5.3, semver@^7.5.4, semver@^7.6.0, semver@^7.6.3: version "7.6.3" resolved "https://registry.yarnpkg.com/semver/-/semver-7.6.3.tgz#980f7b5550bc175fb4dc09403085627f9eb33143" integrity sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A== @@ -8646,11 +8653,6 @@ set-blocking@^2.0.0: resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" integrity sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw== -set-cookie-parser@^2.5.1: - version "2.7.0" - resolved "https://registry.yarnpkg.com/set-cookie-parser/-/set-cookie-parser-2.7.0.tgz#ef5552b56dc01baae102acb5fc9fb8cd060c30f9" - integrity sha512-lXLOiqpkUumhRdFF3k1osNXCy9akgx/dyPZ5p8qAg9seJzXr5ZrlqZuWIMuY6ejOsVLE6flJ5/h3lsn57fQ/PQ== - set-function-length@^1.2.1: version "1.2.2" resolved "https://registry.yarnpkg.com/set-function-length/-/set-function-length-1.2.2.tgz#aac72314198eaed975cf77b2c3b6b880695e5449" @@ -8759,7 +8761,7 @@ side-channel@^1.0.4, side-channel@^1.0.6: get-intrinsic "^1.2.4" object-inspect "^1.13.1" -signal-exit@^3.0.0, signal-exit@^3.0.2: +signal-exit@^3.0.2: version "3.0.7" resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9" integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ== @@ -8887,7 +8889,7 @@ string-env-interpolation@^1.0.1: is-fullwidth-code-point "^3.0.0" strip-ansi "^6.0.1" -"string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: +string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: version "4.2.3" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== @@ -9137,7 +9139,7 @@ tapable@^2.2.0: resolved "https://registry.yarnpkg.com/tapable/-/tapable-2.2.1.tgz#1967a73ef4060a82f12ab96af86d52fdb76eeca0" integrity sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ== -tar@^6.1.11, tar@^6.2.1: +tar@^6.2.1: version "6.2.1" resolved "https://registry.yarnpkg.com/tar/-/tar-6.2.1.tgz#717549c541bc3c2af15751bea94b1dd068d4b03a" integrity sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A== @@ -9583,11 +9585,6 @@ utils-merge@1.0.1: resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713" integrity sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA== -uuid@^8.3.2: - version "8.3.2" - resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2" - integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== - uuid@^9.0.0: version "9.0.1" resolved "https://registry.yarnpkg.com/uuid/-/uuid-9.0.1.tgz#e188d4c8853cc722220392c424cd637f32293f30" @@ -9785,13 +9782,6 @@ which@^2.0.1: dependencies: isexe "^2.0.0" -wide-align@^1.1.2: - version "1.1.5" - resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.5.tgz#df1d4c206854369ecf3c9a4898f1b23fbd9d15d3" - integrity sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg== - dependencies: - string-width "^1.0.2 || 2 || 3 || 4" - widest-line@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/widest-line/-/widest-line-4.0.1.tgz#a0fc673aaba1ea6f0a0d35b3c2795c9a9cc2ebf2"