mirror of
https://github.com/versia-pub/server.git
synced 2026-03-13 05:49:16 +01:00
refactor: 🚨 Turn every linter rule on and fix issues (there were a LOT :3)
This commit is contained in:
parent
2e98859153
commit
a1e02d0d78
177 changed files with 1826 additions and 1248 deletions
176
utils/api.ts
176
utils/api.ts
|
|
@ -20,13 +20,13 @@ import {
|
|||
import { parse } from "qs";
|
||||
import type { z } from "zod";
|
||||
import { fromZodError } from "zod-validation-error";
|
||||
import type { Application } from "~/database/entities/Application";
|
||||
import { getFromHeader } from "~/database/entities/User";
|
||||
import type { Application } from "~/database/entities/application";
|
||||
import { type AuthData, getFromHeader } from "~/database/entities/user";
|
||||
import type { User } from "~/packages/database-interface/user";
|
||||
import { LogLevel, LogManager } from "~/packages/log-manager";
|
||||
import type { APIRouteMetadata, HttpVerb } from "~/types/api";
|
||||
import type { ApiRouteMetadata, HttpVerb } from "~/types/api";
|
||||
|
||||
export const applyConfig = (routeMeta: APIRouteMetadata) => {
|
||||
export const applyConfig = (routeMeta: ApiRouteMetadata) => {
|
||||
const newMeta = routeMeta;
|
||||
|
||||
// Apply ratelimits from config
|
||||
|
|
@ -94,16 +94,113 @@ export const handleZodError = (
|
|||
result:
|
||||
| { success: true; data?: object }
|
||||
| { success: false; error: z.ZodError<z.AnyZodObject>; data?: object },
|
||||
context: Context,
|
||||
_context: Context,
|
||||
) => {
|
||||
if (!result.success) {
|
||||
return errorResponse(fromZodError(result.error).message, 422);
|
||||
}
|
||||
};
|
||||
|
||||
const getAuth = async (value: Record<string, string>) => {
|
||||
return value.authorization
|
||||
? await getFromHeader(value.authorization)
|
||||
: null;
|
||||
};
|
||||
|
||||
const checkPermissions = (
|
||||
auth: AuthData | null,
|
||||
permissionData: ApiRouteMetadata["permissions"],
|
||||
context: Context,
|
||||
) => {
|
||||
const userPerms = auth?.user
|
||||
? auth.user.getAllPermissions()
|
||||
: config.permissions.anonymous;
|
||||
const requiredPerms =
|
||||
permissionData?.methodOverrides?.[context.req.method as HttpVerb] ??
|
||||
permissionData?.required ??
|
||||
[];
|
||||
const error = errorResponse("Unauthorized", 401);
|
||||
|
||||
if (!requiredPerms.every((perm) => userPerms.includes(perm))) {
|
||||
const missingPerms = requiredPerms.filter(
|
||||
(perm) => !userPerms.includes(perm),
|
||||
);
|
||||
return context.json(
|
||||
{
|
||||
error: `You do not have the required permissions to access this route. Missing: ${missingPerms.join(", ")}`,
|
||||
},
|
||||
403,
|
||||
error.headers.toJSON(),
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
const checkRouteNeedsAuth = (
|
||||
auth: AuthData | null,
|
||||
authData: ApiRouteMetadata["auth"],
|
||||
context: Context,
|
||||
) => {
|
||||
const error = errorResponse("Unauthorized", 401);
|
||||
|
||||
if (auth?.user) {
|
||||
return {
|
||||
user: auth.user as User,
|
||||
token: auth.token as string,
|
||||
application: auth.application as Application | null,
|
||||
};
|
||||
}
|
||||
if (authData.required) {
|
||||
return context.json(
|
||||
{
|
||||
error: "Unauthorized",
|
||||
},
|
||||
401,
|
||||
error.headers.toJSON(),
|
||||
);
|
||||
}
|
||||
|
||||
if (authData.requiredOnMethods?.includes(context.req.method as HttpVerb)) {
|
||||
return context.json(
|
||||
{
|
||||
error: "Unauthorized",
|
||||
},
|
||||
401,
|
||||
error.headers.toJSON(),
|
||||
);
|
||||
}
|
||||
|
||||
return {
|
||||
user: null,
|
||||
token: null,
|
||||
application: null,
|
||||
};
|
||||
};
|
||||
|
||||
export const auth = (
|
||||
authData: APIRouteMetadata["auth"],
|
||||
permissionData?: APIRouteMetadata["permissions"],
|
||||
authData: ApiRouteMetadata["auth"],
|
||||
permissionData?: ApiRouteMetadata["permissions"],
|
||||
) =>
|
||||
validator("header", async (value, context) => {
|
||||
const auth = await getAuth(value);
|
||||
|
||||
// Permissions check
|
||||
if (permissionData) {
|
||||
const permissionCheck = checkPermissions(
|
||||
auth,
|
||||
permissionData,
|
||||
context,
|
||||
);
|
||||
if (permissionCheck) {
|
||||
return permissionCheck;
|
||||
}
|
||||
}
|
||||
|
||||
return checkRouteNeedsAuth(auth, authData, context);
|
||||
});
|
||||
|
||||
/* export const auth = (
|
||||
authData: ApiRouteMetadata["auth"],
|
||||
permissionData?: ApiRouteMetadata["permissions"],
|
||||
) =>
|
||||
validator("header", async (value, context) => {
|
||||
const auth = value.authorization
|
||||
|
|
@ -140,46 +237,41 @@ export const auth = (
|
|||
}
|
||||
}
|
||||
|
||||
if (!auth?.user) {
|
||||
if (authData.required) {
|
||||
return context.json(
|
||||
{
|
||||
error: "Unauthorized",
|
||||
},
|
||||
401,
|
||||
error.headers.toJSON(),
|
||||
);
|
||||
}
|
||||
|
||||
if (
|
||||
authData.requiredOnMethods?.includes(
|
||||
context.req.method as HttpVerb,
|
||||
)
|
||||
) {
|
||||
return context.json(
|
||||
{
|
||||
error: "Unauthorized",
|
||||
},
|
||||
401,
|
||||
error.headers.toJSON(),
|
||||
);
|
||||
}
|
||||
|
||||
// Check role permissions
|
||||
} else {
|
||||
if (auth?.user) {
|
||||
return {
|
||||
user: auth.user as User,
|
||||
token: auth.token as string,
|
||||
application: auth.application as Application | null,
|
||||
};
|
||||
}
|
||||
if (authData.required) {
|
||||
return context.json(
|
||||
{
|
||||
error: "Unauthorized",
|
||||
},
|
||||
401,
|
||||
error.headers.toJSON(),
|
||||
);
|
||||
}
|
||||
|
||||
if (
|
||||
authData.requiredOnMethods?.includes(context.req.method as HttpVerb)
|
||||
) {
|
||||
return context.json(
|
||||
{
|
||||
error: "Unauthorized",
|
||||
},
|
||||
401,
|
||||
error.headers.toJSON(),
|
||||
);
|
||||
}
|
||||
|
||||
return {
|
||||
user: null,
|
||||
token: null,
|
||||
application: null,
|
||||
};
|
||||
});
|
||||
}); */
|
||||
|
||||
/**
|
||||
* Middleware to magically unfuck forms
|
||||
|
|
@ -200,12 +292,12 @@ export const qs = () => {
|
|||
for (const val of value) {
|
||||
urlparams.append(key, val);
|
||||
}
|
||||
} else if (!(value instanceof File)) {
|
||||
urlparams.append(key, String(value));
|
||||
} else {
|
||||
} else if (value instanceof File) {
|
||||
if (!files.has(key)) {
|
||||
files.set(key, value);
|
||||
}
|
||||
} else {
|
||||
urlparams.append(key, String(value));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -297,12 +389,12 @@ export const jsonOrForm = () => {
|
|||
for (const val of value) {
|
||||
urlparams.append(key, val);
|
||||
}
|
||||
} else if (!(value instanceof File)) {
|
||||
urlparams.append(key, String(value));
|
||||
} else {
|
||||
} else if (value instanceof File) {
|
||||
if (!files.has(key)) {
|
||||
files.set(key, value);
|
||||
}
|
||||
} else {
|
||||
urlparams.append(key, String(value));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -341,7 +433,7 @@ export const debugRequest = async (
|
|||
) => {
|
||||
const body = await req.clone().text();
|
||||
await logger.log(
|
||||
LogLevel.DEBUG,
|
||||
LogLevel.Debug,
|
||||
"RequestDebugger",
|
||||
`\n${chalk.green(req.method)} ${chalk.blue(req.url)}\n${chalk.bold(
|
||||
"Hash",
|
||||
|
|
|
|||
|
|
@ -4,7 +4,9 @@ import { lookup } from "mime-types";
|
|||
export const getBestContentType = (
|
||||
content?: typeof EntityValidator.$ContentFormat,
|
||||
) => {
|
||||
if (!content) return { content: "", format: "text/plain" };
|
||||
if (!content) {
|
||||
return { content: "", format: "text/plain" };
|
||||
}
|
||||
|
||||
const bestFormatsRanked = [
|
||||
"text/x.misskeymarkdown",
|
||||
|
|
@ -14,8 +16,9 @@ export const getBestContentType = (
|
|||
];
|
||||
|
||||
for (const format of bestFormatsRanked) {
|
||||
if (content[format])
|
||||
if (content[format]) {
|
||||
return { content: content[format].content, format };
|
||||
}
|
||||
}
|
||||
|
||||
return { content: "", format: "text/plain" };
|
||||
|
|
@ -24,7 +27,9 @@ export const getBestContentType = (
|
|||
export const urlToContentFormat = (
|
||||
url?: string,
|
||||
): typeof EntityValidator.$ContentFormat | null => {
|
||||
if (!url) return null;
|
||||
if (!url) {
|
||||
return null;
|
||||
}
|
||||
if (url.startsWith("https://api.dicebear.com/")) {
|
||||
return {
|
||||
"image/svg+xml": {
|
||||
|
|
@ -46,7 +51,9 @@ export const urlToContentFormat = (
|
|||
export const mimeLookup = async (url: string) => {
|
||||
const naiveLookup = lookup(url.replace(new URL(url).search, ""));
|
||||
|
||||
if (naiveLookup) return naiveLookup;
|
||||
if (naiveLookup) {
|
||||
return naiveLookup;
|
||||
}
|
||||
|
||||
const fetchLookup = fetch(url, { method: "HEAD" }).then(
|
||||
(response) => response.headers.get("content-type") || "",
|
||||
|
|
|
|||
|
|
@ -4,11 +4,11 @@ import { config } from "~/packages/config-manager";
|
|||
const noColors = process.env.NO_COLORS === "true";
|
||||
const noFancyDates = process.env.NO_FANCY_DATES === "true";
|
||||
|
||||
const requests_log = Bun.file(config.logging.storage.requests);
|
||||
const requestsLog = Bun.file(config.logging.storage.requests);
|
||||
const isEntry = true;
|
||||
|
||||
export const logger = new LogManager(
|
||||
isEntry ? requests_log : Bun.file("/dev/null"),
|
||||
isEntry ? requestsLog : Bun.file("/dev/null"),
|
||||
);
|
||||
|
||||
export const consoleLogger = new LogManager(
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { markdownParse } from "~/database/entities/Status";
|
||||
import { markdownParse } from "~/database/entities/status";
|
||||
import { LogLevel } from "~/packages/log-manager";
|
||||
import { dualLogger } from "./loggers";
|
||||
|
||||
|
|
@ -9,19 +9,19 @@ export const renderMarkdownInPath = async (
|
|||
let content = await markdownParse(defaultText ?? "");
|
||||
let lastModified = new Date(1970, 0, 0);
|
||||
|
||||
const extended_description_file = Bun.file(path || "");
|
||||
const extendedDescriptionFile = Bun.file(path || "");
|
||||
|
||||
if (path && (await extended_description_file.exists())) {
|
||||
if (path && (await extendedDescriptionFile.exists())) {
|
||||
content =
|
||||
(await markdownParse(
|
||||
(await extended_description_file.text().catch(async (e) => {
|
||||
await dualLogger.logError(LogLevel.ERROR, "Routes", e);
|
||||
(await extendedDescriptionFile.text().catch(async (e) => {
|
||||
await dualLogger.logError(LogLevel.Error, "Routes", e);
|
||||
return "";
|
||||
})) ||
|
||||
defaultText ||
|
||||
"",
|
||||
)) || "";
|
||||
lastModified = new Date(extended_description_file.lastModified);
|
||||
lastModified = new Date(extendedDescriptionFile.lastModified);
|
||||
}
|
||||
|
||||
return {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
import chalk from "chalk";
|
||||
import { config } from "config-manager";
|
||||
import { count } from "drizzle-orm";
|
||||
import { LogLevel, type LogManager, type MultiLogManager } from "log-manager";
|
||||
|
|
@ -13,7 +12,9 @@ export const meilisearch = new Meilisearch({
|
|||
});
|
||||
|
||||
export const connectMeili = async (logger: MultiLogManager | LogManager) => {
|
||||
if (!config.meilisearch.enabled) return;
|
||||
if (!config.meilisearch.enabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (await meilisearch.isHealthy()) {
|
||||
await meilisearch
|
||||
|
|
@ -33,13 +34,13 @@ export const connectMeili = async (logger: MultiLogManager | LogManager) => {
|
|||
.updateSearchableAttributes(["content"]);
|
||||
|
||||
await logger.log(
|
||||
LogLevel.INFO,
|
||||
LogLevel.Info,
|
||||
"Meilisearch",
|
||||
"Connected to Meilisearch",
|
||||
);
|
||||
} else {
|
||||
await logger.log(
|
||||
LogLevel.CRITICAL,
|
||||
LogLevel.Critical,
|
||||
"Meilisearch",
|
||||
"Error while connecting to Meilisearch",
|
||||
);
|
||||
|
|
@ -53,7 +54,9 @@ export enum MeiliIndexType {
|
|||
}
|
||||
|
||||
export const addUserToMeilisearch = async (user: User) => {
|
||||
if (!config.meilisearch.enabled) return;
|
||||
if (!config.meilisearch.enabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
await meilisearch.index(MeiliIndexType.Accounts).addDocuments([
|
||||
{
|
||||
|
|
@ -116,25 +119,19 @@ export const rebuildSearchIndexes = async (
|
|||
for (let i = 0; i < accountCount / batchSize; i++) {
|
||||
const accounts = await getNthDatabaseAccountBatch(i, batchSize);
|
||||
|
||||
const progress = Math.round((i / (accountCount / batchSize)) * 100);
|
||||
|
||||
console.log(`${chalk.green("✓")} ${progress}%`);
|
||||
|
||||
/* const _progress = Math.round(
|
||||
(i / (accountCount / batchSize)) * 100,
|
||||
);
|
||||
*/
|
||||
// Sync with Meilisearch
|
||||
await meilisearch
|
||||
.index(MeiliIndexType.Accounts)
|
||||
.addDocuments(accounts);
|
||||
}
|
||||
|
||||
const meiliAccountCount = (
|
||||
/* const _meiliAccountCount = (
|
||||
await meilisearch.index(MeiliIndexType.Accounts).getStats()
|
||||
).numberOfDocuments;
|
||||
|
||||
console.log(
|
||||
`${chalk.green("✓")} ${chalk.bold(
|
||||
`Done! ${meiliAccountCount} accounts indexed`,
|
||||
)}`,
|
||||
);
|
||||
).numberOfDocuments; */
|
||||
}
|
||||
|
||||
if (indexes.includes(MeiliIndexType.Statuses)) {
|
||||
|
|
@ -149,9 +146,7 @@ export const rebuildSearchIndexes = async (
|
|||
for (let i = 0; i < statusCount / batchSize; i++) {
|
||||
const statuses = await getNthDatabaseStatusBatch(i, batchSize);
|
||||
|
||||
const progress = Math.round((i / (statusCount / batchSize)) * 100);
|
||||
|
||||
console.log(`${chalk.green("✓")} ${progress}%`);
|
||||
/* const _progress = Math.round((i / (statusCount / batchSize)) * 100); */
|
||||
|
||||
// Sync with Meilisearch
|
||||
await meilisearch
|
||||
|
|
@ -159,14 +154,8 @@ export const rebuildSearchIndexes = async (
|
|||
.addDocuments(statuses);
|
||||
}
|
||||
|
||||
const meiliStatusCount = (
|
||||
/* const _meiliStatusCount = (
|
||||
await meilisearch.index(MeiliIndexType.Statuses).getStats()
|
||||
).numberOfDocuments;
|
||||
|
||||
console.log(
|
||||
`${chalk.green("✓")} ${chalk.bold(
|
||||
`Done! ${meiliStatusCount} statuses indexed`,
|
||||
)}`,
|
||||
);
|
||||
).numberOfDocuments; */
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import type { Application } from "~/database/entities/Application";
|
||||
import type { Application } from "~/database/entities/application";
|
||||
|
||||
/**
|
||||
* Check if an OAuth application is valid for a route
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ export const sanitizedHtmlStrip = (html: string) => {
|
|||
});
|
||||
};
|
||||
|
||||
export const sanitizeHtmlInline = async (
|
||||
export const sanitizeHtmlInline = (
|
||||
html: string,
|
||||
extraConfig?: IFilterXSSOptions,
|
||||
) => {
|
||||
|
|
|
|||
|
|
@ -2,9 +2,9 @@ import { config } from "config-manager";
|
|||
import type {
|
||||
Notification,
|
||||
findManyNotifications,
|
||||
} from "~/database/entities/Notification";
|
||||
import type { Status, findManyNotes } from "~/database/entities/Status";
|
||||
import type { UserType, findManyUsers } from "~/database/entities/User";
|
||||
} from "~/database/entities/notification";
|
||||
import type { Status, findManyNotes } from "~/database/entities/status";
|
||||
import type { UserType, findManyUsers } from "~/database/entities/user";
|
||||
import type { db } from "~/drizzle/db";
|
||||
|
||||
export async function fetchTimeline<T extends UserType | Status | Notification>(
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue