Merge pull request #28 from Huskydog9988/auth-overhaul

overhaul auth and futureproof it with argon2
This commit is contained in:
DecDuck
2025-03-24 12:27:14 +11:00
committed by GitHub
17 changed files with 380 additions and 132 deletions

35
.vscode/settings.json vendored
View File

@@ -1,18 +1,21 @@
{
"spellchecker.ignoreWordsList": [
"mTLS",
"Wireguard"
],
"sqltools.connections": [
{
"previewLimit": 50,
"server": "localhost",
"port": 5432,
"driver": "PostgreSQL",
"name": "drop",
"database": "drop",
"username": "drop",
"password": "drop"
}
]
"spellchecker.ignoreWordsList": ["mTLS", "Wireguard"],
"sqltools.connections": [
{
"previewLimit": 50,
"server": "localhost",
"port": 5432,
"driver": "PostgreSQL",
"name": "drop",
"database": "drop",
"username": "drop",
"password": "drop"
}
],
// allow autocomplete for ArkType expressions like "string | num"
"editor.quickSuggestions": {
"strings": "on"
},
// prioritize ArkType's "type" for autoimports
"typescript.preferences.autoImportSpecifierExcludeRegexes": ["^(node:)?os$"]
}

View File

@@ -32,16 +32,18 @@ To just deploy Drop, we've set up a simple docker compose file in deploy-templat
3. Edit the compose.yml file (`nano compose.yml`) and copy your GiamtBomb API Key into the GIANT_BOMB_API_KEY environment variable
4. Run `docker compose up -d`
Your drop server should now be running. To register the admin user, navigate to http://your.drop.server.ip:3000/register?id=admin
Your drop server should now be running. To register the admin user, navigate to http://your.drop.server.ip:3000/register?id=admin
and fill in the required forms
### Adding a game
To add a game to the drop library, do as follows:
1. Ensure that the current user owns the library folder with `sudo chown -R $(id -u $(whoami)) library`
2. `cd library`
3. `mkdir <GAME_NAME>` with the name of the game which you would like to register
4. `cd <GAME_NAME>`
5. `mkdir <VERSION_NAME>` Upload files for the specific game version to this folder
5. `mkdir <VERSION_NAME>` Upload files for the specific game version to this folder
6. Navigate to http://your.drop.server.ip:3000/
7. Import game metadata (uses GiantBomb API Key) by selecting the game and specifying which entry to import
8. Navigate to http://your.drop.server.ip:3000/admin/library
@@ -73,7 +75,7 @@ Steps:
As part of the first-time bootstrap, Drop creates an invitation with the fixed id of 'admin'. So, to create an admin account, go to:
http://localhost:3000/register?id=admin
http://localhost:3000/auth/register?id=admin
## Contributing

View File

@@ -85,7 +85,7 @@ const navigation: NavigationItem[] = [
},
{
label: "Sign out",
route: "/signout",
route: "/auth/signout",
prefix: "",
},
].filter((e) => e !== undefined);

View File

@@ -16,7 +16,7 @@ const showSignIn = statusCode ? statusCode == 403 || statusCode == 401 : false;
async function signIn() {
clearError({
redirect: `/signin?redirect=${encodeURIComponent(route.fullPath)}`,
redirect: `/auth/signin?redirect=${encodeURIComponent(route.fullPath)}`,
});
}

View File

@@ -1,4 +1,4 @@
const whitelistedPrefixes = ["/signin", "/register", "/api", "/setup"];
const whitelistedPrefixes = ["/auth/signin", "/register", "/api", "/setup"];
const requireAdmin = ["/admin"];
export default defineNuxtRouteMiddleware(async (to, from) => {
@@ -13,7 +13,10 @@ export default defineNuxtRouteMiddleware(async (to, from) => {
await updateUser();
}
if (!user.value) {
return navigateTo({ path: "/signin", query: { redirect: to.fullPath } });
return navigateTo({
path: "/auth/signin",
query: { redirect: to.fullPath },
});
}
if (
requireAdmin.findIndex((e) => to.fullPath.startsWith(e)) != -1 &&

View File

@@ -19,6 +19,8 @@
"@nuxtjs/tailwindcss": "^6.12.2",
"@prisma/client": "^6.1.0",
"@tailwindcss/vite": "^4.0.6",
"argon2": "^0.41.1",
"arktype": "^2.1.10",
"axios": "^1.7.7",
"bcryptjs": "^2.4.3",
"cookie-es": "^1.2.2",
@@ -31,6 +33,7 @@
"nuxt-security": "2.2.0",
"prisma": "^6.1.0",
"sanitize-filename": "^1.6.3",
"sharp": "^0.33.5",
"stream": "^0.0.3",
"stream-mime-type": "^2.0.0",
"turndown": "^7.2.0",

View File

@@ -188,6 +188,7 @@
<script setup lang="ts">
import { XCircleIcon } from "@heroicons/vue/24/solid";
import { type } from "arktype";
const route = useRoute();
const router = useRouter();
@@ -208,14 +209,20 @@ const username = ref(invitation.data.value?.username);
const password = ref("");
const confirmPassword = ref(undefined);
const mailRegex = /^\S+@\S+\.\S+$/;
const validEmail = computed(() => mailRegex.test(email.value ?? ""));
const validUsername = computed(
() =>
(username.value?.length ?? 0) >= 5 &&
username.value?.toLowerCase() == username.value
const emailValidator = type("string.email");
const validEmail = computed(
() => !(emailValidator(email.value) instanceof type.errors)
);
const usernameValidator = type("string.lower.preformatted >= 5");
const validUsername = computed(
() => !(usernameValidator(username.value) instanceof type.errors)
);
const passwordValidator = type("string >= 14");
const validPassword = computed(
() => !(passwordValidator(password.value) instanceof type.errors)
);
const validPassword = computed(() => (password.value?.length ?? 0) >= 14);
const validConfirmPassword = computed(
() => password.value == confirmPassword.value
);
@@ -248,7 +255,7 @@ function register_wrapper() {
loading.value = true;
register()
.then(() => {
router.push("/signin");
router.push("/auth/signin");
})
.catch((response) => {
const message = response.statusMessage || "An unknown error occurred";

View File

@@ -42,6 +42,6 @@ const user = useUser();
user.value = null;
// Redirect to signin page after signout
await $dropFetch("/signout");
router.push("/signin");
await $dropFetch("/api/v1/auth/signout"); //TODO: add signout api route
router.push("/auth/signin");
</script>

View File

@@ -1,7 +1,12 @@
import { AuthMec } from "@prisma/client";
import { JsonArray } from "@prisma/client/runtime/library";
import { type } from "arktype";
import prisma from "~/server/internal/db/database";
import { checkHash } from "~/server/internal/security/simple";
import {
checkHashArgon2,
checkHashBcrypt,
simpleAuth,
} from "~/server/internal/security/simple";
import sessionHandler from "~/server/internal/session";
export default defineEventHandler(async (h3) => {
@@ -19,10 +24,10 @@ export default defineEventHandler(async (h3) => {
const authMek = await prisma.linkedAuthMec.findFirst({
where: {
mec: AuthMec.Simple,
credentials: {
array_starts_with: username,
},
enabled: true,
user: {
username,
},
},
include: {
user: {
@@ -38,24 +43,56 @@ export default defineEventHandler(async (h3) => {
statusCode: 401,
statusMessage: "Invalid username or password.",
});
const credentials = authMek.credentials as JsonArray;
const hash = credentials.at(1);
if (!hash || !authMek.user.enabled)
else if (!authMek.user.enabled)
throw createError({
statusCode: 403,
statusMessage:
"Invalid or disabled account. Please contact the server administrator.",
});
if (!(await checkHash(password, hash.toString())))
throw createError({
statusCode: 401,
statusMessage: "Invalid username or password.",
});
// if using old auth schema
if (Array.isArray(authMek.credentials)) {
const hash = authMek.credentials.at(1)?.toString();
await sessionHandler.setUserId(h3, authMek.userId, rememberMe);
if (!hash)
throw createError({
statusCode: 403,
statusMessage:
"Invalid password state. Please contact the server administrator.",
});
return { result: true, userId: authMek.userId };
if (!(await checkHashBcrypt(password, hash)))
throw createError({
statusCode: 401,
statusMessage: "Invalid username or password.",
});
// TODO: send user to forgot password screen or something to force them to change their password to new system
await sessionHandler.setUserId(h3, authMek.userId, rememberMe);
return { result: true, userId: authMek.userId };
} else {
// using new (modern) login flow
const creds = simpleAuth(authMek.credentials);
if (creds instanceof type.errors) {
// hover out.summary to see validation errors
console.error(creds.summary);
throw createError({
statusCode: 403,
statusMessage:
"Invalid password state. Please contact the server administrator.",
});
}
if (!(await checkHashArgon2(password, creds.password)))
throw createError({
statusCode: 401,
statusMessage: "Invalid username or password.",
});
await sessionHandler.setUserId(h3, authMek.userId, rememberMe);
return { result: true, userId: authMek.userId };
}
});

View File

@@ -1,12 +1,22 @@
import { AuthMec, Invitation } from "@prisma/client";
import prisma from "~/server/internal/db/database";
import { createHash } from "~/server/internal/security/simple";
import {
createHashArgon2,
simpleAuth,
SimpleAuthType,
} from "~/server/internal/security/simple";
import { v4 as uuidv4 } from "uuid";
import * as jdenticon from "jdenticon";
import objectHandler from "~/server/internal/objects";
import { type } from "arktype";
import { writeNonLiteralDefaultMessage } from "arktype/internal/parser/shift/operator/default.ts";
// Only really a simple test, in case people mistype their emails
const mailRegex = /^\S+@\S+\.\S+$/;
const userValidator = type({
username: "string >= 5",
email: "string.email",
password: "string >= 14",
"displayName?": "string | undefined",
});
export default defineEventHandler(async (h3) => {
const body = await readBody(h3);
@@ -27,59 +37,24 @@ export default defineEventHandler(async (h3) => {
statusMessage: "Invalid or expired invitation.",
});
const useInvitationOrBodyRequirement = (
field: keyof Invitation,
check: (v: string) => boolean
) => {
if (invitation[field]) {
return invitation[field].toString();
}
const user = userValidator(body);
if (user instanceof type.errors) {
// hover out.summary to see validation errors
console.error(user.summary);
const v: string = body[field]?.toString();
const valid = check(v);
return valid ? v : undefined;
};
const username = useInvitationOrBodyRequirement(
"username",
(e) => e.length >= 5
);
const email = useInvitationOrBodyRequirement("email", (e) =>
mailRegex.test(e)
);
const password = body.password;
const displayName = body.displayName || username;
if (username === undefined)
throw createError({
statusCode: 400,
statusMessage: "Username is invalid. Must be more than 5 characters.",
});
if (username.toLowerCase() != username)
throw createError({
statusCode: 400,
statusMessage: "Username must be all lowercase",
statusMessage: user.summary,
});
}
if (email === undefined)
throw createError({
statusCode: 400,
statusMessage: "Invalid email. Must follow the format you@example.com",
});
// reuse items from invite
if (invitation.username !== null) user.username = invitation.username;
if (invitation.email !== null) user.email = invitation.email;
if (!password)
throw createError({
statusCode: 400,
statusMessage: "Password empty or missing.",
});
if (password.length < 14)
throw createError({
statusCode: 400,
statusMessage: "Password must be 14 or more characters.",
});
const existing = await prisma.user.count({ where: { username: username } });
const existing = await prisma.user.count({
where: { username: user.username },
});
if (existing > 0)
throw createError({
statusCode: 400,
@@ -91,30 +66,38 @@ export default defineEventHandler(async (h3) => {
const profilePictureId = uuidv4();
await objectHandler.createFromSource(
profilePictureId,
async () => jdenticon.toPng(username, 256),
async () => jdenticon.toPng(user.username, 256),
{},
[`internal:read`, `${userId}:write`]
);
const user = await prisma.user.create({
data: {
username,
displayName,
email,
profilePicture: profilePictureId,
admin: invitation.isAdmin,
},
});
const hash = await createHash(password);
await prisma.linkedAuthMec.create({
data: {
mec: AuthMec.Simple,
credentials: [username, hash],
userId: user.id,
},
});
const creds: SimpleAuthType = {
version: "v1.0.0",
password: await createHashArgon2(user.password),
};
await prisma.invitation.delete({ where: { id: invitationId } });
const [linkMec] = await prisma.$transaction([
prisma.linkedAuthMec.create({
data: {
mec: AuthMec.Simple,
credentials: creds,
user: {
create: {
id: userId,
username: user.username,
displayName: user.displayName ?? user.username,
email: user.email,
profilePicture: profilePictureId,
admin: invitation.isAdmin,
},
},
},
select: {
user: true,
},
}),
prisma.invitation.delete({ where: { id: invitationId } }),
]);
return user;
return linkMec.user;
});

View File

@@ -1,11 +1,22 @@
import bcrypt from 'bcryptjs';
import bcrypt from "bcryptjs";
import * as argon2 from "argon2";
import { type } from "arktype";
const rounds = 10;
export const simpleAuth = type({
version: "string.semver",
password: "string",
});
export async function createHash(password: string) {
return bcrypt.hashSync(password, rounds);
export type SimpleAuthType = typeof simpleAuth.infer;
export async function checkHashBcrypt(password: string, hash: string) {
return await bcrypt.compare(password, hash);
}
export async function checkHash(password: string, hash: string) {
return bcrypt.compareSync(password, hash);
}
export async function createHashArgon2(password: string) {
return await argon2.hash(password);
}
export async function checkHashArgon2(password: string, hash: string) {
return await argon2.verify(hash, password);
}

View File

@@ -18,7 +18,7 @@ export default defineNitroPlugin((nitro) => {
if (userId) break;
return sendRedirect(
event,
`/signin?redirect=${encodeURIComponent(event.path)}`
`/auth/signin?redirect=${encodeURIComponent(event.path)}`
);
}
});

View File

@@ -3,5 +3,5 @@ import sessionHandler from "../internal/session";
export default defineEventHandler(async (h3) => {
await sessionHandler.clearSession(h3);
return sendRedirect(h3, "/signin");
return sendRedirect(h3, "/auth/signin");
});

View File

@@ -1,3 +1,6 @@
{
"extends": "../.nuxt/tsconfig.server.json"
"extends": "../.nuxt/tsconfig.server.json",
"compilerOptions": {
"exactOptionalPropertyTypes": true
}
}

View File

@@ -1,4 +1,7 @@
{
// https://nuxt.com/docs/guide/concepts/typescript
"extends": "./.nuxt/tsconfig.json"
"extends": "./.nuxt/tsconfig.json",
"compilerOptions": {
"exactOptionalPropertyTypes": true
}
}

197
yarn.lock
View File

@@ -20,6 +20,18 @@
resolved "https://registry.yarnpkg.com/@antfu/utils/-/utils-0.7.10.tgz#ae829f170158e297a9b6a28f161a8e487d00814d"
integrity sha512-+562v9k4aI80m1+VuMHehNJWLOFjBnXn3tdOitzD0il5b7smkSBal4+a3oKiQTbrwMmN/TBUMDvbdoWDehgOww==
"@ark/schema@0.45.0":
version "0.45.0"
resolved "https://registry.yarnpkg.com/@ark/schema/-/schema-0.45.0.tgz#5d5da5dfe94ca45d36d54513fe0c53566483a1e7"
integrity sha512-3XlMWkZbEjh0YsF92vnnRNCWNRNhRKDTf6XhugyCXH0YRFuM+w1vFLDbB2JLfZloEd7i5cbqsLaDLzyBZbPrSg==
dependencies:
"@ark/util" "0.45.0"
"@ark/util@0.45.0":
version "0.45.0"
resolved "https://registry.yarnpkg.com/@ark/util/-/util-0.45.0.tgz#2c55394a6af7865aeeb22924f301e28084aea4c0"
integrity sha512-Z1gHEGbpPzLtPmYb932t2B++6YonlUi1Fa14IQ4vhsGMWhd81Mi1miUmdZXW4fNI/wg1saT7H2/5cAuONgTXhg==
"@babel/code-frame@^7.12.13", "@babel/code-frame@^7.22.13", "@babel/code-frame@^7.24.7", "@babel/code-frame@^7.25.9", "@babel/code-frame@^7.26.0", "@babel/code-frame@^7.26.2":
version "7.26.2"
resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.26.2.tgz#4b5fab97d33338eff916235055f0ebc21e573a85"
@@ -423,6 +435,13 @@
"@drop/droplet-linux-x64-gnu" "0.7.0"
"@drop/droplet-win32-x64-msvc" "0.7.0"
"@emnapi/runtime@^1.2.0":
version "1.3.1"
resolved "https://registry.yarnpkg.com/@emnapi/runtime/-/runtime-1.3.1.tgz#0fcaa575afc31f455fd33534c19381cfce6c6f60"
integrity sha512-kEBmG8KyqtxJZv+ygbEim+KCGtIq1fC22Ms3S4ziXmYKm8uyoLX0MHONVKwp+9opg390VaKRNt4a7A9NwmpNhw==
dependencies:
tslib "^2.4.0"
"@esbuild/aix-ppc64@0.21.5":
version "0.21.5"
resolved "https://registry.yarnpkg.com/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz#c7184a326533fcdf1b8ee0733e21c713b975575f"
@@ -800,6 +819,119 @@
resolved "https://registry.yarnpkg.com/@heroicons/vue/-/vue-2.1.5.tgz#efbf090d11acebbcefa4badcf3bed42d7ceee219"
integrity sha512-IpqR72sFqFs55kyKfFS7tN+Ww6odFNeH/7UxycIOrlVYfj4WUGAdzQtLBnJspucSeqWFQsKM0g0YrgU655BEfA==
"@img/sharp-darwin-arm64@0.33.5":
version "0.33.5"
resolved "https://registry.yarnpkg.com/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.33.5.tgz#ef5b5a07862805f1e8145a377c8ba6e98813ca08"
integrity sha512-UT4p+iz/2H4twwAoLCqfA9UH5pI6DggwKEGuaPy7nCVQ8ZsiY5PIcrRvD1DzuY3qYL07NtIQcWnBSY/heikIFQ==
optionalDependencies:
"@img/sharp-libvips-darwin-arm64" "1.0.4"
"@img/sharp-darwin-x64@0.33.5":
version "0.33.5"
resolved "https://registry.yarnpkg.com/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.33.5.tgz#e03d3451cd9e664faa72948cc70a403ea4063d61"
integrity sha512-fyHac4jIc1ANYGRDxtiqelIbdWkIuQaI84Mv45KvGRRxSAa7o7d1ZKAOBaYbnepLC1WqxfpimdeWfvqqSGwR2Q==
optionalDependencies:
"@img/sharp-libvips-darwin-x64" "1.0.4"
"@img/sharp-libvips-darwin-arm64@1.0.4":
version "1.0.4"
resolved "https://registry.yarnpkg.com/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.0.4.tgz#447c5026700c01a993c7804eb8af5f6e9868c07f"
integrity sha512-XblONe153h0O2zuFfTAbQYAX2JhYmDHeWikp1LM9Hul9gVPjFY427k6dFEcOL72O01QxQsWi761svJ/ev9xEDg==
"@img/sharp-libvips-darwin-x64@1.0.4":
version "1.0.4"
resolved "https://registry.yarnpkg.com/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.0.4.tgz#e0456f8f7c623f9dbfbdc77383caa72281d86062"
integrity sha512-xnGR8YuZYfJGmWPvmlunFaWJsb9T/AO2ykoP3Fz/0X5XV2aoYBPkX6xqCQvUTKKiLddarLaxpzNe+b1hjeWHAQ==
"@img/sharp-libvips-linux-arm64@1.0.4":
version "1.0.4"
resolved "https://registry.yarnpkg.com/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.0.4.tgz#979b1c66c9a91f7ff2893556ef267f90ebe51704"
integrity sha512-9B+taZ8DlyyqzZQnoeIvDVR/2F4EbMepXMc/NdVbkzsJbzkUjhXv/70GQJ7tdLA4YJgNP25zukcxpX2/SueNrA==
"@img/sharp-libvips-linux-arm@1.0.5":
version "1.0.5"
resolved "https://registry.yarnpkg.com/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.0.5.tgz#99f922d4e15216ec205dcb6891b721bfd2884197"
integrity sha512-gvcC4ACAOPRNATg/ov8/MnbxFDJqf/pDePbBnuBDcjsI8PssmjoKMAz4LtLaVi+OnSb5FK/yIOamqDwGmXW32g==
"@img/sharp-libvips-linux-s390x@1.0.4":
version "1.0.4"
resolved "https://registry.yarnpkg.com/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.0.4.tgz#f8a5eb1f374a082f72b3f45e2fb25b8118a8a5ce"
integrity sha512-u7Wz6ntiSSgGSGcjZ55im6uvTrOxSIS8/dgoVMoiGE9I6JAfU50yH5BoDlYA1tcuGS7g/QNtetJnxA6QEsCVTA==
"@img/sharp-libvips-linux-x64@1.0.4":
version "1.0.4"
resolved "https://registry.yarnpkg.com/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.0.4.tgz#d4c4619cdd157774906e15770ee119931c7ef5e0"
integrity sha512-MmWmQ3iPFZr0Iev+BAgVMb3ZyC4KeFc3jFxnNbEPas60e1cIfevbtuyf9nDGIzOaW9PdnDciJm+wFFaTlj5xYw==
"@img/sharp-libvips-linuxmusl-arm64@1.0.4":
version "1.0.4"
resolved "https://registry.yarnpkg.com/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.0.4.tgz#166778da0f48dd2bded1fa3033cee6b588f0d5d5"
integrity sha512-9Ti+BbTYDcsbp4wfYib8Ctm1ilkugkA/uscUn6UXK1ldpC1JjiXbLfFZtRlBhjPZ5o1NCLiDbg8fhUPKStHoTA==
"@img/sharp-libvips-linuxmusl-x64@1.0.4":
version "1.0.4"
resolved "https://registry.yarnpkg.com/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.0.4.tgz#93794e4d7720b077fcad3e02982f2f1c246751ff"
integrity sha512-viYN1KX9m+/hGkJtvYYp+CCLgnJXwiQB39damAO7WMdKWlIhmYTfHjwSbQeUK/20vY154mwezd9HflVFM1wVSw==
"@img/sharp-linux-arm64@0.33.5":
version "0.33.5"
resolved "https://registry.yarnpkg.com/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.33.5.tgz#edb0697e7a8279c9fc829a60fc35644c4839bb22"
integrity sha512-JMVv+AMRyGOHtO1RFBiJy/MBsgz0x4AWrT6QoEVVTyh1E39TrCUpTRI7mx9VksGX4awWASxqCYLCV4wBZHAYxA==
optionalDependencies:
"@img/sharp-libvips-linux-arm64" "1.0.4"
"@img/sharp-linux-arm@0.33.5":
version "0.33.5"
resolved "https://registry.yarnpkg.com/@img/sharp-linux-arm/-/sharp-linux-arm-0.33.5.tgz#422c1a352e7b5832842577dc51602bcd5b6f5eff"
integrity sha512-JTS1eldqZbJxjvKaAkxhZmBqPRGmxgu+qFKSInv8moZ2AmT5Yib3EQ1c6gp493HvrvV8QgdOXdyaIBrhvFhBMQ==
optionalDependencies:
"@img/sharp-libvips-linux-arm" "1.0.5"
"@img/sharp-linux-s390x@0.33.5":
version "0.33.5"
resolved "https://registry.yarnpkg.com/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.33.5.tgz#f5c077926b48e97e4a04d004dfaf175972059667"
integrity sha512-y/5PCd+mP4CA/sPDKl2961b+C9d+vPAveS33s6Z3zfASk2j5upL6fXVPZi7ztePZ5CuH+1kW8JtvxgbuXHRa4Q==
optionalDependencies:
"@img/sharp-libvips-linux-s390x" "1.0.4"
"@img/sharp-linux-x64@0.33.5":
version "0.33.5"
resolved "https://registry.yarnpkg.com/@img/sharp-linux-x64/-/sharp-linux-x64-0.33.5.tgz#d806e0afd71ae6775cc87f0da8f2d03a7c2209cb"
integrity sha512-opC+Ok5pRNAzuvq1AG0ar+1owsu842/Ab+4qvU879ippJBHvyY5n2mxF1izXqkPYlGuP/M556uh53jRLJmzTWA==
optionalDependencies:
"@img/sharp-libvips-linux-x64" "1.0.4"
"@img/sharp-linuxmusl-arm64@0.33.5":
version "0.33.5"
resolved "https://registry.yarnpkg.com/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.33.5.tgz#252975b915894fb315af5deea174651e208d3d6b"
integrity sha512-XrHMZwGQGvJg2V/oRSUfSAfjfPxO+4DkiRh6p2AFjLQztWUuY/o8Mq0eMQVIY7HJ1CDQUJlxGGZRw1a5bqmd1g==
optionalDependencies:
"@img/sharp-libvips-linuxmusl-arm64" "1.0.4"
"@img/sharp-linuxmusl-x64@0.33.5":
version "0.33.5"
resolved "https://registry.yarnpkg.com/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.33.5.tgz#3f4609ac5d8ef8ec7dadee80b560961a60fd4f48"
integrity sha512-WT+d/cgqKkkKySYmqoZ8y3pxx7lx9vVejxW/W4DOFMYVSkErR+w7mf2u8m/y4+xHe7yY9DAXQMWQhpnMuFfScw==
optionalDependencies:
"@img/sharp-libvips-linuxmusl-x64" "1.0.4"
"@img/sharp-wasm32@0.33.5":
version "0.33.5"
resolved "https://registry.yarnpkg.com/@img/sharp-wasm32/-/sharp-wasm32-0.33.5.tgz#6f44f3283069d935bb5ca5813153572f3e6f61a1"
integrity sha512-ykUW4LVGaMcU9lu9thv85CbRMAwfeadCJHRsg2GmeRa/cJxsVY9Rbd57JcMxBkKHag5U/x7TSBpScF4U8ElVzg==
dependencies:
"@emnapi/runtime" "^1.2.0"
"@img/sharp-win32-ia32@0.33.5":
version "0.33.5"
resolved "https://registry.yarnpkg.com/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.33.5.tgz#1a0c839a40c5351e9885628c85f2e5dfd02b52a9"
integrity sha512-T36PblLaTwuVJ/zw/LaH0PdZkRz5rd3SmMHX8GSmR7vtNSP5Z6bQkExdSK7xGWyxLw4sUknBuugTelgw2faBbQ==
"@img/sharp-win32-x64@0.33.5":
version "0.33.5"
resolved "https://registry.yarnpkg.com/@img/sharp-win32-x64/-/sharp-win32-x64-0.33.5.tgz#56f00962ff0c4e0eb93d34a047d29fa995e3e342"
integrity sha512-MpY/o8/8kj+EcnxwvrP4aTJSWw/aZ7JIGR4aBeZkZw5B7/Jn+tY9/VNwtcoGmdT7GfggGIU4kygOMSbYnOrAbg==
"@ioredis/commands@^1.1.1":
version "1.2.0"
resolved "https://registry.yarnpkg.com/@ioredis/commands/-/commands-1.2.0.tgz#6d61b3097470af1fdbbe622795b8921d42018e11"
@@ -1365,6 +1497,11 @@
"@parcel/watcher-win32-ia32" "2.5.0"
"@parcel/watcher-win32-x64" "2.5.0"
"@phc/format@^1.0.0":
version "1.0.0"
resolved "https://registry.yarnpkg.com/@phc/format/-/format-1.0.0.tgz#b5627003b3216dc4362125b13f48a4daa76680e4"
integrity sha512-m7X9U6BG2+J+R1lSOdCiITLLrxm+cWlNI3HUFA92oLO77ObGNzaKdh8pMLqdZcshtkKuV84olNNXDfMc4FezBQ==
"@pkgjs/parseargs@^0.11.0":
version "0.11.0"
resolved "https://registry.yarnpkg.com/@pkgjs/parseargs/-/parseargs-0.11.0.tgz#a77ea742fab25775145434eb1d2328cf5013ac33"
@@ -2416,11 +2553,28 @@ arg@^5.0.2:
resolved "https://registry.yarnpkg.com/arg/-/arg-5.0.2.tgz#c81433cc427c92c4dcf4865142dbca6f15acd59c"
integrity sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==
argon2@^0.41.1:
version "0.41.1"
resolved "https://registry.yarnpkg.com/argon2/-/argon2-0.41.1.tgz#30ce6b013e273bc7e92c558d40e66d35e5e8c63b"
integrity sha512-dqCW8kJXke8Ik+McUcMDltrbuAWETPyU6iq+4AhxqKphWi7pChB/Zgd/Tp/o8xRLbg8ksMj46F/vph9wnxpTzQ==
dependencies:
"@phc/format" "^1.0.0"
node-addon-api "^8.1.0"
node-gyp-build "^4.8.1"
argparse@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38"
integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==
arktype@^2.1.10:
version "2.1.10"
resolved "https://registry.yarnpkg.com/arktype/-/arktype-2.1.10.tgz#7a2fb85d1e8fbb22077134993a12e9b12d34ef6e"
integrity sha512-KqbrzI9qIGrQUClifyS1HpUp/oTSRtGDvnMKzwg2TAvxRpynY1mn/ubXaxAAdGPOM8V3pBqwb01Z6TcXqhBxzQ==
dependencies:
"@ark/schema" "0.45.0"
"@ark/util" "0.45.0"
ast-kit@^1.0.1, ast-kit@^1.3.0:
version "1.3.0"
resolved "https://registry.yarnpkg.com/ast-kit/-/ast-kit-1.3.0.tgz#37c8b7418b6c59b1e593d7790dc6c2b1c0814761"
@@ -3343,7 +3497,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.2:
detect-libc@^2.0.0, detect-libc@^2.0.2, 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==
@@ -5619,6 +5773,11 @@ node-addon-api@^7.0.0:
resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-7.1.1.tgz#1aba6693b0f255258a049d621329329322aad558"
integrity sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==
node-addon-api@^8.1.0:
version "8.3.1"
resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-8.3.1.tgz#53bc8a4f8dbde3de787b9828059da94ba9fd4eed"
integrity sha512-lytcDEdxKjGJPTLEfW4mYMigRezMlyJY8W4wxJK8zE533Jlb8L8dRuObJFWg2P+AuOIxoCgKF+2Oq4d4Zd0OUA==
node-fetch-native@^1.6.3, node-fetch-native@^1.6.4:
version "1.6.4"
resolved "https://registry.yarnpkg.com/node-fetch-native/-/node-fetch-native-1.6.4.tgz#679fc8fd8111266d47d7e72c379f1bed9acff06e"
@@ -5646,6 +5805,11 @@ node-gyp-build@^4.2.2:
resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.8.2.tgz#4f802b71c1ab2ca16af830e6c1ea7dd1ad9496fa"
integrity sha512-IRUxE4BVsHWXkV/SFOut4qTlagw2aM8T5/vnTsmrHJvVoKueJHRc/JaFND7QDDc61kLYUJ6qlZM3sqTSyx2dTw==
node-gyp-build@^4.8.1:
version "4.8.4"
resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.8.4.tgz#8a70ee85464ae52327772a90d66c6077a900cfc8"
integrity sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ==
node-mock-http@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/node-mock-http/-/node-mock-http-1.0.0.tgz#4b32cd509c7f46d844e68ea93fb8be405a18a42a"
@@ -6892,6 +7056,35 @@ sharp@^0.32.6:
tar-fs "^3.0.4"
tunnel-agent "^0.6.0"
sharp@^0.33.5:
version "0.33.5"
resolved "https://registry.yarnpkg.com/sharp/-/sharp-0.33.5.tgz#13e0e4130cc309d6a9497596715240b2ec0c594e"
integrity sha512-haPVm1EkS9pgvHrQ/F3Xy+hgcuMV0Wm9vfIBSiwZ05k+xgb0PkBQpGsAA/oWdDobNaZTH5ppvHtzCFbnSEwHVw==
dependencies:
color "^4.2.3"
detect-libc "^2.0.3"
semver "^7.6.3"
optionalDependencies:
"@img/sharp-darwin-arm64" "0.33.5"
"@img/sharp-darwin-x64" "0.33.5"
"@img/sharp-libvips-darwin-arm64" "1.0.4"
"@img/sharp-libvips-darwin-x64" "1.0.4"
"@img/sharp-libvips-linux-arm" "1.0.5"
"@img/sharp-libvips-linux-arm64" "1.0.4"
"@img/sharp-libvips-linux-s390x" "1.0.4"
"@img/sharp-libvips-linux-x64" "1.0.4"
"@img/sharp-libvips-linuxmusl-arm64" "1.0.4"
"@img/sharp-libvips-linuxmusl-x64" "1.0.4"
"@img/sharp-linux-arm" "0.33.5"
"@img/sharp-linux-arm64" "0.33.5"
"@img/sharp-linux-s390x" "0.33.5"
"@img/sharp-linux-x64" "0.33.5"
"@img/sharp-linuxmusl-arm64" "0.33.5"
"@img/sharp-linuxmusl-x64" "0.33.5"
"@img/sharp-wasm32" "0.33.5"
"@img/sharp-win32-ia32" "0.33.5"
"@img/sharp-win32-x64" "0.33.5"
shebang-command@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea"
@@ -7487,7 +7680,7 @@ ts-interface-checker@^0.1.9:
resolved "https://registry.yarnpkg.com/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz#784fd3d679722bc103b1b4b8030bcddb5db2a699"
integrity sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==
tslib@^2.8.0:
tslib@^2.4.0, tslib@^2.8.0:
version "2.8.1"
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.8.1.tgz#612efe4ed235d567e8aba5f2a5fab70280ade83f"
integrity sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==