mirror of
https://github.com/Drop-OSS/drop.git
synced 2026-01-31 15:37:09 +01:00
Merge pull request #28 from Huskydog9988/auth-overhaul
overhaul auth and futureproof it with argon2
This commit is contained in:
35
.vscode/settings.json
vendored
35
.vscode/settings.json
vendored
@@ -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$"]
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -85,7 +85,7 @@ const navigation: NavigationItem[] = [
|
||||
},
|
||||
{
|
||||
label: "Sign out",
|
||||
route: "/signout",
|
||||
route: "/auth/signout",
|
||||
prefix: "",
|
||||
},
|
||||
].filter((e) => e !== undefined);
|
||||
|
||||
@@ -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)}`,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -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 &&
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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";
|
||||
@@ -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>
|
||||
@@ -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 };
|
||||
}
|
||||
});
|
||||
|
||||
@@ -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;
|
||||
});
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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)}`
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -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");
|
||||
});
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
{
|
||||
"extends": "../.nuxt/tsconfig.server.json"
|
||||
"extends": "../.nuxt/tsconfig.server.json",
|
||||
"compilerOptions": {
|
||||
"exactOptionalPropertyTypes": true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
197
yarn.lock
@@ -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==
|
||||
|
||||
Reference in New Issue
Block a user