From 91cfd8142237d10d03a0313ef7d91ab1a8236504 Mon Sep 17 00:00:00 2001 From: Thilo Billerbeck Date: Thu, 6 Feb 2025 00:50:23 +0100 Subject: [PATCH] refactor: adapt biome code style, add git hooks --- devenv.nix | 12 ++++++ drizzle.config.ts | 2 +- index.ts | 13 +++--- lib/bluesky.ts | 47 +++++++++++---------- lib/db.ts | 75 ++++++++++++++++++++++------------ lib/mastodon.ts | 47 ++++++++++----------- lib/migration.ts | 12 +++--- lib/tasks/cleanup.ts | 13 +++--- lib/tasks/mastodonToBluesky.ts | 37 +++++++++-------- lib/tasks/scheduler.ts | 2 +- lib/utils.ts | 9 ++-- package.json | 3 +- routes/root.ts | 29 +++++++------ routes/user.ts | 17 ++++---- tailwind.config.js | 2 + 15 files changed, 185 insertions(+), 135 deletions(-) diff --git a/devenv.nix b/devenv.nix index 0fdee80..f754be2 100644 --- a/devenv.nix +++ b/devenv.nix @@ -47,4 +47,16 @@ }; pre-commit.hooks.commitizen.enable = true; + git-hooks.hooks.lint = { + enable = true; + name = "Lint code"; + entry = "pnpm run lint"; + pass_filenames = false; + }; + git-hooks.hooks.format-check = { + enable = true; + name = "Check formatting"; + entry = "pnpm run format:check"; + pass_filenames = false; + }; } diff --git a/drizzle.config.ts b/drizzle.config.ts index f30feb9..31ebcac 100644 --- a/drizzle.config.ts +++ b/drizzle.config.ts @@ -4,7 +4,7 @@ export default defineConfig({ out: "./drizzle", schema: "./drizzle/schema.ts", dbCredentials: { - url: process.env.POSTGRES_URL!, + url: process.env.POSTGRES_URL || "", }, // Print all statements verbose: true, diff --git a/index.ts b/index.ts index f3eba0b..66e9667 100644 --- a/index.ts +++ b/index.ts @@ -6,11 +6,10 @@ import fastifyFormbody from "@fastify/formbody"; import fastifyJwt from "@fastify/jwt"; import { routesRoot } from "./routes/root"; import { routesUser } from "./routes/user"; -import { join } from "path"; +import { join } from "node:path"; import * as Sentry from "@sentry/node"; import { nodeProfilingIntegration } from "@sentry/profiling-node"; -import { readFileSync } from "fs"; -import { join as pathJoin } from "path"; +import { existsSync, readFileSync } from "node:fs"; import { printInfo } from "./lib/utils"; import { client, db } from "./lib/db"; import { migrationHelper } from "./lib/migration"; @@ -47,8 +46,8 @@ migrationHelper() let version = "development"; - const gitRevPath = pathJoin(__dirname, ".git-rev"); - if (require("fs").existsSync(gitRevPath)) { + const gitRevPath = join(__dirname, ".git-rev"); + if (existsSync(gitRevPath)) { version = readFileSync(gitRevPath, "utf-8").trim(); } @@ -88,8 +87,8 @@ migrationHelper() app.register(routesUser); app.listen( - { host: ADDRESS, port: parseInt(PORT, 10) }, - function (err, address) { + { host: ADDRESS, port: Number.parseInt(PORT, 10) }, + (err, address) => { if (err) { app.log.error(err); } diff --git a/lib/bluesky.ts b/lib/bluesky.ts index 81b68d4..4335449 100644 --- a/lib/bluesky.ts +++ b/lib/bluesky.ts @@ -2,11 +2,11 @@ import { AtpAgent, AppBskyFeedPost, RichText, - BlobRef, - AtpSessionData, - AtpSessionEvent, + type BlobRef, + type AtpSessionData, + type AtpSessionEvent, } from "@atproto/api"; -import { Entity } from "megalodon"; +import type { Entity } from "megalodon"; import { fetchImageToBytes, logSchedulerEvent, @@ -14,20 +14,24 @@ import { splitTextBluesky, } from "./utils"; import sharp from "sharp"; -import { Attachment } from "megalodon/lib/src/entities/attachment"; +import type { Attachment } from "megalodon/lib/src/entities/attachment"; import { clearBlueskyCreds, clearBluskySession, db, persistBlueskySession, } from "./db"; -import { ResponseType, XRPCError } from "@atproto/xrpc"; +import { ResponseType, type XRPCError } from "@atproto/xrpc"; +import type { InferSelectModel } from "drizzle-orm"; +import type { mastodonInstance, user as User } from "../drizzle/schema"; export async function intiBlueskyAgent( url: string, handle: string, password: string, - user: any, + user: InferSelectModel & { + mastodonInstance: InferSelectModel; + }, ): Promise { let session: AtpSessionData | undefined = undefined; session = user.blueskySession as unknown as AtpSessionData; @@ -72,14 +76,14 @@ export async function intiBlueskyAgent( "resuming session", ); return agent; - } else { - logSchedulerEvent( - user.name, - user.mastodonInstance.url, - "AGENT", - "could not resume session", - ); } + + logSchedulerEvent( + user.name, + user.mastodonInstance.url, + "AGENT", + "could not resume session", + ); } else { logSchedulerEvent( user.name, @@ -93,7 +97,7 @@ export async function intiBlueskyAgent( await agent.login({ identifier: handle, password: password }); return agent; } catch (err) { - if ((err as XRPCError).status == ResponseType.AuthRequired) { + if ((err as XRPCError).status === ResponseType.AuthRequired) { // invalidate creds to prevent further login attempts resulting in rate limiting logSchedulerEvent( user.name, @@ -102,7 +106,7 @@ export async function intiBlueskyAgent( "invalid creds", ); clearBlueskyCreds(user); - } else if ((err as XRPCError).status == ResponseType.RateLimitExceeded) { + } else if ((err as XRPCError).status === ResponseType.RateLimitExceeded) { logSchedulerEvent( user.name, user.mastodonInstance.url, @@ -126,13 +130,13 @@ export async function generateBlueskyPostsFromMastodon( status: Entity.Status, client: AtpAgent, ): Promise> { - let posts: Array = []; + const posts: Array = []; const spoiler = status.sensitive ? `CW: ${status.spoiler_text}\n\n` : ""; const conv = mastodonHtmlToText(status.content); const split = splitTextBluesky(conv, spoiler); for (const [idx, text] of split.entries()) { - let post = await generateBlueskyPostFromMastodon( + const post = await generateBlueskyPostFromMastodon( text, client, idx === 0 ? status.media_attachments : undefined, @@ -192,8 +196,10 @@ export async function generateBlueskyPostFromMastodon( arr = new Uint8Array(result.buffer); } + if (!mimeType) continue; + const res = await client.uploadBlob(arr, { - encoding: mimeType!, + encoding: mimeType, }); let width = 1200; @@ -232,9 +238,8 @@ export async function generateBlueskyPostFromMastodon( const res = AppBskyFeedPost.validateRecord(post); if (res.success) { return post; - } else { - console.log(res); } + console.log(res); } return undefined; } diff --git a/lib/db.ts b/lib/db.ts index 1914dbe..88abd1b 100644 --- a/lib/db.ts +++ b/lib/db.ts @@ -1,11 +1,19 @@ -import { ReplyRef } from "@atproto/api/dist/client/types/app/bsky/feed/post"; +import type { ReplyRef } from "@atproto/api/dist/client/types/app/bsky/feed/post"; import { logSchedulerEvent } from "./utils"; import { drizzle } from "drizzle-orm/node-postgres"; import { Client } from "pg"; import * as schema from "./../drizzle/schema"; import * as relations from "./../drizzle/relations"; -import { count, eq } from "drizzle-orm"; -import { AtpSessionData } from "@atproto/api"; +import { + count, + eq, + type InferInsertModel, + type InferSelectModel, +} from "drizzle-orm"; +import type { AtpSessionData } from "@atproto/api"; +import type { OAuth } from "megalodon"; +import type Response from "megalodon/lib/src/response"; +import type { Account } from "megalodon/lib/src/entities/account"; export const client = new Client({ connectionString: process.env.POSTGRES_URL, @@ -88,7 +96,9 @@ export async function findParentToot( } export async function persistBlueskySession( - user: any, + user: InferSelectModel & { + mastodonInstance: InferSelectModel; + }, evt: string, sess?: AtpSessionData, ) { @@ -119,7 +129,11 @@ export async function persistBlueskySession( }); } -export async function clearBluskySession(user: any) { +export async function clearBluskySession( + user: InferSelectModel & { + mastodonInstance: InferSelectModel; + }, +) { return await db .update(schema.user) .set({ @@ -147,7 +161,11 @@ export async function clearBluskySession(user: any) { }); } -export async function clearBlueskyCreds(user: any) { +export async function clearBlueskyCreds( + user: InferSelectModel & { + mastodonInstance: InferSelectModel; + }, +) { return await db .update(schema.user) .set({ @@ -186,8 +204,8 @@ export async function findUsers() { } export async function findUserById( - userid: any, - withMastodonInstance: boolean = false, + userid: string, + withMastodonInstance = false, ) { return await db.query.user.findFirst({ where: (user, { eq }) => eq(user.id, userid), @@ -213,16 +231,16 @@ export async function getAllUserInformation(userId: string) { }); } -export async function deleteUser(user: any) { +export async function deleteUser(userId: string, userName: string) { return await db .delete(schema.user) - .where(eq(schema.user.id, user.id)) + .where(eq(schema.user.id, userId)) .then(() => { - logSchedulerEvent(user.name, "---", "CREDENTIAL_CHECK", "User deleted"); + logSchedulerEvent(userName, "---", "CREDENTIAL_CHECK", "User deleted"); }) .catch((err) => { logSchedulerEvent( - user.name, + userName, "---", "CREDENTIAL_CHECK", "Could not delete user", @@ -246,14 +264,17 @@ export async function getMastodonInstanceUsers() { .groupBy(schema.mastodonInstance.id); } -export async function deleteMastodonInstance(instance: any) { +export async function deleteMastodonInstance( + instanceId: string, + instanceUrl: string, +) { return await db .delete(schema.mastodonInstance) - .where(eq(schema.mastodonInstance.id, instance.id)) + .where(eq(schema.mastodonInstance.id, instanceId)) .then(() => { logSchedulerEvent( "SYSTEM", - instance.url, + instanceUrl, "INSTANCE_USERS", "Instance deleted", ); @@ -261,7 +282,7 @@ export async function deleteMastodonInstance(instance: any) { .catch((err) => { logSchedulerEvent( "SYSTEM", - instance.url, + instanceUrl, "INSTANCE_USERS", "Could not delete instance", ); @@ -270,7 +291,7 @@ export async function deleteMastodonInstance(instance: any) { } export async function persistBlueskyCreds( - userId: any, + userId: string, handle: string, token: string, pds: string, @@ -288,10 +309,10 @@ export async function persistBlueskyCreds( } export async function updateRelaySettings( - userId: any, - relayCriteria: any, + userId: string, + relayCriteria: InferInsertModel["relayCriteria"], relayMarker: string, - relayVisibility: any, + relayVisibility: InferInsertModel["relayVisibility"], ) { return await db .update(schema.user) @@ -307,7 +328,7 @@ export async function updateRelaySettings( export async function createMastodonInstance( instanceDomain: string, - appData: any, + appData: OAuth.AppData, ) { return await db .insert(schema.mastodonInstance) @@ -322,9 +343,9 @@ export async function createMastodonInstance( } export async function createUser( - instanceId: any, - verifiedCredentials: any, - token: any, + instanceId: string, + verifiedCredentials: Response, + token: OAuth.TokenData, ) { return await db .insert(schema.user) @@ -339,7 +360,11 @@ export async function createUser( .returning(); } -export async function updateUser(userId: any, token: any, name: string) { +export async function updateUser( + userId: string, + token: OAuth.TokenData, + name: string, +) { return await db .update(schema.user) .set({ diff --git a/lib/mastodon.ts b/lib/mastodon.ts index ced7dc5..3af3f12 100644 --- a/lib/mastodon.ts +++ b/lib/mastodon.ts @@ -1,15 +1,9 @@ -import { MegalodonInterface, Mastodon } from "megalodon"; -import { Status } from "megalodon/lib/src/entities/status"; -import { Constraint } from "./constraint"; +import type { MegalodonInterface, Mastodon } from "megalodon"; +import type { Status } from "megalodon/lib/src/entities/status"; +import type { Constraint } from "./constraint"; import { convert } from "html-to-text"; - -export function initMastodonAgent() { - return new Mastodon( - "mastodon", - process.env.MASTODON_URL!, - process.env.MSTODON_TOKEN!, - ); -} +import type { InferInsertModel } from "drizzle-orm"; +import type { user } from "../drizzle/schema"; export async function getUserIdFromMastodonHandle( handle: string, @@ -24,31 +18,32 @@ function verifyThread( uid: string, status: Status, searchSpace: Status[], - relayVisibility: string[], - initialCall: boolean = false, + relayVisibility: InferInsertModel["relayVisibility"], + initialCall = false, ): boolean { - if (!status) return false; + if (!status || !relayVisibility) return false; if ( status.in_reply_to_account_id === uid && (status.visibility === "unlisted" || relayVisibility.includes(status.visibility)) ) { - return verifyThread( - uid, - searchSpace.find((s) => s.id === status.in_reply_to_id)!, - searchSpace, - relayVisibility, - false, + const parentStatus = searchSpace.find( + (s) => s.id === status.in_reply_to_id, ); - } else if ( + + if (!parentStatus) return false; + + return verifyThread(uid, parentStatus, searchSpace, relayVisibility, false); + } + if ( !status.in_reply_to_account_id && !initialCall && status.visibility === "public" ) { return true; - } else { - return false; } + + return false; } export async function getNewToots( @@ -56,7 +51,7 @@ export async function getNewToots( uid: string, lastTootTime: Date, constraint: Constraint, - relayVisibility: string[], + relayVisibility: InferInsertModel["relayVisibility"], ) { const statuses = await client.getAccountStatuses(uid, { limit: 50, @@ -67,7 +62,9 @@ export async function getNewToots( const statuses_data = await statuses.data; const statuses_filtered = statuses_data.filter((status) => { const newPost = new Date(status.created_at) > lastTootTime; - const isInVisibilityScope = relayVisibility.includes(status.visibility); + const isInVisibilityScope = relayVisibility + ? relayVisibility.includes(status.visibility) + : false; const isNotMention = status.mentions.length === 0; const text = convert(status.content ?? "", { wordwrap: false, diff --git a/lib/migration.ts b/lib/migration.ts index c70c8d3..2110a58 100644 --- a/lib/migration.ts +++ b/lib/migration.ts @@ -3,7 +3,7 @@ import { migrate } from "drizzle-orm/node-postgres/migrator"; import { sql } from "drizzle-orm"; import { db } from "./db"; -const runPrismaToDrizzleMigrationScript = async (db: any) => { +const runPrismaToDrizzleMigrationScript = async () => { return db.execute(sql` CREATE SCHEMA drizzle; @@ -32,7 +32,7 @@ const runPrismaToDrizzleMigrationScript = async (db: any) => { `); }; -const checkPrismaMigrationsTable = async (db: any) => { +const checkPrismaMigrationsTable = async () => { try { const result = await db.execute(sql` SELECT FROM information_schema.tables @@ -45,7 +45,7 @@ const checkPrismaMigrationsTable = async (db: any) => { } }; -const checkLastMigrationApplied = async (db: any) => { +const checkLastMigrationApplied = async () => { const result = await db.execute(sql` SELECT * FROM _prisma_migrations WHERE migration_name = '20241223001124_orphan_user_settings'; `); @@ -55,14 +55,14 @@ const checkLastMigrationApplied = async (db: any) => { export async function migrationHelper() { console.log("Migration helper started."); - const prismaTablesExist = await checkPrismaMigrationsTable(db); + const prismaTablesExist = await checkPrismaMigrationsTable(); const lastPrismaMigrationApplied = prismaTablesExist - ? await checkLastMigrationApplied(db) + ? await checkLastMigrationApplied() : false; if (prismaTablesExist && lastPrismaMigrationApplied) { console.log("Migrating from Prisma to Drizzle!"); - await runPrismaToDrizzleMigrationScript(db); + await runPrismaToDrizzleMigrationScript(); console.log("Created Drizzle migrations table."); } else if (prismaTablesExist && !lastPrismaMigrationApplied) { console.error( diff --git a/lib/tasks/cleanup.ts b/lib/tasks/cleanup.ts index 6209fce..5e55e48 100644 --- a/lib/tasks/cleanup.ts +++ b/lib/tasks/cleanup.ts @@ -1,7 +1,6 @@ import { Mastodon } from "megalodon"; import { domainToUrl, logSchedulerEvent } from "../utils"; import { - db, deleteMastodonInstance, deleteUser, findUsers, @@ -13,7 +12,7 @@ export default async function cleanupJob() { const users = await findUsers(); - users.forEach((user: any) => { + for (const user of users) { const userClient = new Mastodon( domainToUrl(user.mastodonInstance.url), user.mastodonToken, @@ -44,14 +43,14 @@ export default async function cleanupJob() { "CREDENTIAL_CHECK", "Deleting user due to invalid credentials", ); - deleteUser(user); + deleteUser(user.id, user.name); } }); - }); + } const instances = await getMastodonInstanceUsers(); - instances.forEach((instance) => { + for (const instance of instances) { logSchedulerEvent( "SYSTEM", instance.url, @@ -67,7 +66,7 @@ export default async function cleanupJob() { "Deleting instance due to no users", ); - deleteMastodonInstance(instance); + deleteMastodonInstance(instance.id, instance.url); } - }); + } } diff --git a/lib/tasks/mastodonToBluesky.ts b/lib/tasks/mastodonToBluesky.ts index a31fe60..94e0c94 100644 --- a/lib/tasks/mastodonToBluesky.ts +++ b/lib/tasks/mastodonToBluesky.ts @@ -15,14 +15,15 @@ import { findUsers, clearBlueskyCreds, } from "../db"; -import { ReplyRef } from "@atproto/api/dist/client/types/app/bsky/feed/post"; +import type { ReplyRef } from "@atproto/api/dist/client/types/app/bsky/feed/post"; +import { XRPCError } from "@atproto/xrpc"; export default async function taskMastodonToBluesky() { console.log("Running scheduled job: reposting to bluesky..."); const users = await findUsers(); - users.forEach(async (user: any) => { + for await (const user of users) { if (!user.blueskyHandle || !user.blueskyToken) { logSchedulerEvent( user.name, @@ -102,7 +103,9 @@ export default async function taskMastodonToBluesky() { if (postsBsky.length === 0) continue; let repRef: ReplyRef = { + // biome-ignore lint/style/noNonNullAssertion: root: undefined!, + // biome-ignore lint/style/noNonNullAssertion: parent: undefined!, }; @@ -175,22 +178,24 @@ export default async function taskMastodonToBluesky() { } try { - let result = await blueskyClient.post(postBsky); + const result = await blueskyClient.post(postBsky); if (repRef.root === undefined) repRef.root = result; repRef.parent = result; - } catch (err: any) { - if (err.error === "AccountDeactivated") { - logSchedulerEvent( - user.name, - user.mastodonInstance.url, - "REPOSTER", - `Account deactivated, invalidating creds`, - ); - - clearBlueskyCreds(user); - - return; + } catch (err: unknown) { + if (err instanceof XRPCError) { + if (err.error === "AccountDeactivated") { + logSchedulerEvent( + user.name, + user.mastodonInstance.url, + "REPOSTER", + "Account deactivated, invalidating creds", + ); + + clearBlueskyCreds(user); + + return; + } } } @@ -231,5 +236,5 @@ export default async function taskMastodonToBluesky() { console.error(err); } } - }); + } } diff --git a/lib/tasks/scheduler.ts b/lib/tasks/scheduler.ts index e55cb95..abe32dd 100644 --- a/lib/tasks/scheduler.ts +++ b/lib/tasks/scheduler.ts @@ -18,7 +18,7 @@ if (process.env.SENTRY_DSN) { const job = new SimpleIntervalJob( { - seconds: parseInt(process.env.POLL_INTERVAL ?? "60"), + seconds: Number.parseInt(process.env.POLL_INTERVAL ?? "60"), runImmediately: true, }, new AsyncTask("taskMastodonToBluesky", taskMastodonToBluesky), diff --git a/lib/utils.ts b/lib/utils.ts index 822a838..e38d768 100644 --- a/lib/utils.ts +++ b/lib/utils.ts @@ -45,14 +45,17 @@ export function validateDomain(domain: string) { } export function genCallBackUrl(instanceDomain: string) { - if (process.env.NODE_ENV == "development") { + if (process.env.NODE_ENV === "development") { const { ADDRESS = "localhost", PORT = "3000" } = process.env; return `http://${ADDRESS}:${PORT}/auth/callback/${btoa(instanceDomain)}`; } return `${process.env.APP_URL}/auth/callback/${btoa(instanceDomain)}`; } -export const authenticateJWT = async (req: any, res: any) => { +export const authenticateJWT = async ( + req: { jwtVerify: () => unknown }, + res: { redirect: (arg0: string) => void }, +) => { try { await req.jwtVerify(); } catch (err) { @@ -91,7 +94,7 @@ export function logSchedulerEvent( // due to the read after write system of bluesky, we need to wait a bit before fetching the post export function getBlueskyApiWaittime(): number { return process.env.BLUESKY_API_WAITTIME - ? parseInt(process.env.BLUESKY_API_WAITTIME) + ? Number.parseInt(process.env.BLUESKY_API_WAITTIME) : 5000; } diff --git a/package.json b/package.json index f3d617b..88db0a7 100644 --- a/package.json +++ b/package.json @@ -10,7 +10,8 @@ "tailwind:css": "concurrently \"tailwindcss -i ${PWD}/public/styles/tailwind.css -o ${PWD}/public/styles/style.css --watch\"", "tailwind:build": "tailwindcss -i ${PWD}/public/styles/tailwind.css -o ${PWD}/public/styles/style.css", "format": "biome format --write", - "format:check": "biome format" + "format:check": "biome format", + "lint": "biome lint" }, "dependencies": { "@atproto/api": "^0.13.31", diff --git a/routes/root.ts b/routes/root.ts index 87bb619..2517cd5 100644 --- a/routes/root.ts +++ b/routes/root.ts @@ -1,29 +1,32 @@ import { - intiBlueskyAgent, validateBlueskyAppPassword, validateBlueskyCredentials, validateBlueskyHandle, } from "./../lib/bluesky"; import { authenticateJWT, checkValidHttpsUrl } from "./../lib/utils"; import { - db, findUserById, persistBlueskyCreds, updateRelaySettings, } from "./../lib/db"; -import { FastifyInstance } from "fastify"; +import type { FastifyInstance } from "fastify"; +import type { InferInsertModel } from "drizzle-orm"; +import type { user as User } from "./../drizzle/schema"; -export const routesRoot = async (app: FastifyInstance, options: Object) => { +export const routesRoot = async (app: FastifyInstance) => { app.get("/", { onRequest: [authenticateJWT] }, async (req, res) => { const user = await findUserById(req.user.id); + let hasBlueskyToken = false; + if (user?.blueskyToken) hasBlueskyToken = true; + return res.view("index", { userName: req.user.mastodonHandle, instance: req.user.instance, blueskyHandle: user?.blueskyHandle, blueskyPDS: user?.blueskyPDS, - hasBlueskyToken: user?.blueskyToken ? true : false, - pollingInterval: parseInt(process.env.POLL_INTERVAL ?? "60"), + hasBlueskyToken: hasBlueskyToken, + pollingInterval: Number.parseInt(process.env.POLL_INTERVAL ?? "60"), relayCriteria: user?.relayCriteria, relayMarker: user?.relayMarker, relayVisibility: user?.relayVisibility, @@ -42,14 +45,14 @@ export const routesRoot = async (app: FastifyInstance, options: Object) => { async (req, res) => { const user = await findUserById(req.user.id, true); - let response_data: any = { + const response_data = { err: undefined, blueskyPDS: req.body.blueskyPDS, userName: req.user.mastodonHandle, instance: req.user.instance, relayCriteria: user?.relayCriteria, relayMarker: user?.relayMarker, - pollingInterval: parseInt(process.env.POLL_INTERVAL ?? "60"), + pollingInterval: Number.parseInt(process.env.POLL_INTERVAL ?? "60"), }; if (!validateBlueskyAppPassword(req.body.blueskyToken)) @@ -95,21 +98,21 @@ export const routesRoot = async (app: FastifyInstance, options: Object) => { app.post<{ Body: { - relayCriteria: any; + relayCriteria: string; relayMarker: string; relayVisibility: string[]; }; }>("/settings/repost", { onRequest: [authenticateJWT] }, async (req, res) => { const user = await findUserById(req.user.id, true); - let response_data: any = { + const response_data = { err: undefined, blueskyPDS: user?.blueskyPDS, userName: req.user.mastodonHandle, instance: req.user.instance, relayCriteria: user?.relayCriteria, relayMarker: user?.relayMarker, - pollingInterval: parseInt(process.env.POLL_INTERVAL ?? "60"), + pollingInterval: Number.parseInt(process.env.POLL_INTERVAL ?? "60"), }; if (req.body.relayVisibility === undefined) @@ -126,9 +129,9 @@ export const routesRoot = async (app: FastifyInstance, options: Object) => { await updateRelaySettings( req.user.id, - req.body.relayCriteria, + req.body.relayCriteria as InferInsertModel["relayCriteria"], req.body.relayMarker, - relayVisibility, + relayVisibility as InferInsertModel["relayVisibility"], ); return res.redirect("/"); diff --git a/routes/user.ts b/routes/user.ts index 09e2491..e08ed9b 100644 --- a/routes/user.ts +++ b/routes/user.ts @@ -1,4 +1,4 @@ -import { FastifyInstance } from "fastify"; +import type { FastifyInstance } from "fastify"; import { Mastodon } from "megalodon"; import { authenticateJWT, @@ -9,16 +9,15 @@ import { import { createMastodonInstance, createUser, - db, deleteUser, getAllUserInformation, getInstanceByDomain, getUserByMastodonUid, updateUser, } from "./../lib/db"; -import { AtpSessionData } from "@atproto/api"; +import type { AtpSessionData } from "@atproto/api"; -export const routesUser = async (app: FastifyInstance, options: Object) => { +export const routesUser = async (app: FastifyInstance) => { app.get("/login", async (req, res) => { return res.view("login", {}); }); @@ -31,7 +30,7 @@ export const routesUser = async (app: FastifyInstance, options: Object) => { "/account/delete", { onRequest: [authenticateJWT] }, async (req, res) => { - deleteUser(req.user); + deleteUser(req.user.id, req.user.mastodonHandle); return res.clearCookie("token").redirect("/login"); }, ); @@ -40,7 +39,7 @@ export const routesUser = async (app: FastifyInstance, options: Object) => { "/account/downloadData", { onRequest: [authenticateJWT] }, async (req, res) => { - let user = await getAllUserInformation(req.user.id); + const user = await getAllUserInformation(req.user.id); if (user) { if (user.blueskySession) { const blueskySession = @@ -85,7 +84,7 @@ export const routesUser = async (app: FastifyInstance, options: Object) => { } const url = domainToUrl(instanceDomain); - let client = new Mastodon(url); + const client = new Mastodon(url); let knownInstance = await getInstanceByDomain(instanceDomain); @@ -135,7 +134,7 @@ export const routesUser = async (app: FastifyInstance, options: Object) => { }); } - let client = new Mastodon(domainToUrl(instance.url)); + const client = new Mastodon(domainToUrl(instance.url)); const token = await client.fetchAccessToken( instance.applicationId, @@ -150,7 +149,7 @@ export const routesUser = async (app: FastifyInstance, options: Object) => { ); const verifiedCredentials = await userClient.verifyAccountCredentials(); - let user: any = await getUserByMastodonUid( + let user = await getUserByMastodonUid( verifiedCredentials.data.id, instance.id, ); diff --git a/tailwind.config.js b/tailwind.config.js index ad1619d..b0a7ef0 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -1,4 +1,5 @@ /** @type {import('tailwindcss').Config} */ + module.exports = { content: ["./views/**/*.liquid"], theme: { @@ -11,6 +12,7 @@ module.exports = { themes: [ { black: { + // biome-ignore lint/complexity/useLiteralKeys: ...require("daisyui/src/theming/themes")["dark"], primary: "#6b21ff", secondary: "#1cd6ff",