diff --git a/.eslintrc.cjs b/.eslintrc.cjs index 5c1bf2f..44ee294 100644 --- a/.eslintrc.cjs +++ b/.eslintrc.cjs @@ -13,6 +13,7 @@ module.exports = { root: true, rules: { 'no-var': 'error', + '@typescript-eslint/no-unsafe-return': 'error', /* Note: the following rules have been set to off so that linting can pass with the current code, but we need to gradually @@ -29,7 +30,6 @@ module.exports = { '@typescript-eslint/require-await': 'off', '@typescript-eslint/restrict-template-expressions': 'off', '@typescript-eslint/no-misused-promises': 'off', - '@typescript-eslint/no-unsafe-return': 'off', '@typescript-eslint/no-unnecessary-type-assertion': 'off', 'no-console': 'off', '@typescript-eslint/no-explicit-any': 'off', diff --git a/backend/src/access/index.ts b/backend/src/access/index.ts index defbbb5..173e33f 100644 --- a/backend/src/access/index.ts +++ b/backend/src/access/index.ts @@ -78,7 +78,7 @@ export function getPayload(jwt: string): JWTPayload { } const [, payload] = parts - const payloadObj = JSON.parse(textDecoder.decode(base64URLDecode(payload))) + const payloadObj: JWTPayload = JSON.parse(textDecoder.decode(base64URLDecode(payload))) return payloadObj } diff --git a/backend/src/activitypub/activities/handle.ts b/backend/src/activitypub/activities/handle.ts index 39ac499..1947eb3 100644 --- a/backend/src/activitypub/activities/handle.ts +++ b/backend/src/activitypub/activities/handle.ts @@ -61,7 +61,7 @@ export async function handle( } if (activity.object instanceof URL) { // This is used for testing only. - return activity.object + return activity.object as URL } if (url === null) { throw new Error('unknown value: ' + JSON.stringify(activity.object)) @@ -85,7 +85,7 @@ export async function handle( } if (activity.actor instanceof URL) { // This is used for testing only. - return activity.actor + return activity.actor as URL } if (url === null) { throw new Error('unknown value: ' + JSON.stringify(activity.actor)) diff --git a/backend/src/activitypub/actors/index.ts b/backend/src/activitypub/actors/index.ts index d0e60f7..ca1cf3b 100644 --- a/backend/src/activitypub/actors/index.ts +++ b/backend/src/activitypub/actors/index.ts @@ -233,5 +233,5 @@ export function personFromRow(row: any): Person { // It's very possible that properties override the values set above. // Almost guaranteed for remote user. ...JSON.parse(row.properties), - } + } as Person } diff --git a/backend/src/activitypub/objects/index.ts b/backend/src/activitypub/objects/index.ts index 77c3281..8db4afd 100644 --- a/backend/src/activitypub/objects/index.ts +++ b/backend/src/activitypub/objects/index.ts @@ -56,7 +56,7 @@ export async function createObject( mastodonId: row.mastodon_id, published: new Date(row.cdate).toISOString(), originalActorId: row.original_actor_id, - } + } as Object } export async function get(url: URL): Promise { @@ -114,7 +114,7 @@ export async function cacheObject( mastodonId: row.mastodon_id, originalActorId: row.original_actor_id, originalObjectId: row.original_object_id, - } + } as Object } } @@ -168,5 +168,5 @@ WHERE objects.${key}=? mastodonId: result.mastodon_id, originalActorId: result.original_actor_id, originalObjectId: result.original_object_id, - } + } as Object } diff --git a/backend/src/config/index.ts b/backend/src/config/index.ts index 405dd17..c20c4be 100644 --- a/backend/src/config/index.ts +++ b/backend/src/config/index.ts @@ -51,7 +51,7 @@ export async function generateVAPIDKeys(db: D1Database) { } export async function get(db: D1Database, name: string): Promise { - const row: any = await db.prepare('SELECT value FROM instance_config WHERE key = ?').bind(name).first() + const row: { value: string } = await db.prepare('SELECT value FROM instance_config WHERE key = ?').bind(name).first() if (!row) { throw new Error(`configuration not found: ${name}`) } diff --git a/backend/src/mastodon/follow.ts b/backend/src/mastodon/follow.ts index 127086d..1547081 100644 --- a/backend/src/mastodon/follow.ts +++ b/backend/src/mastodon/follow.ts @@ -1,4 +1,5 @@ import type { Actor } from 'wildebeest/backend/src/activitypub/actors' +import { getResultsField } from './utils' const STATE_PENDING = 'pending' const STATE_ACCEPTED = 'accepted' @@ -8,9 +9,9 @@ export async function addFollowing(db: D1Database, actor: Actor, target: Actor, const id = crypto.randomUUID() const query = ` - INSERT INTO actor_following (id, actor_id, target_actor_id, state, target_actor_acct) - VALUES (?, ?, ?, ?, ?) - ` + INSERT INTO actor_following (id, actor_id, target_actor_id, state, target_actor_acct) + VALUES (?, ?, ?, ?, ?) + ` const out = await db .prepare(query) @@ -27,8 +28,8 @@ export async function acceptFollowing(db: D1Database, actor: Actor, target: Acto const id = crypto.randomUUID() const query = ` - UPDATE actor_following SET state=? WHERE actor_id=? AND target_actor_id=? AND state=? - ` + UPDATE actor_following SET state=? WHERE actor_id=? AND target_actor_id=? AND state=? + ` const out = await db .prepare(query) @@ -41,8 +42,8 @@ export async function acceptFollowing(db: D1Database, actor: Actor, target: Acto export async function removeFollowing(db: D1Database, actor: Actor, target: Actor) { const query = ` - DELETE FROM actor_following WHERE actor_id=? AND target_actor_id=? - ` + DELETE FROM actor_following WHERE actor_id=? AND target_actor_id=? + ` const out = await db.prepare(query).bind(actor.id.toString(), target.id.toString()).run() if (!out.success) { @@ -50,70 +51,41 @@ export async function removeFollowing(db: D1Database, actor: Actor, target: Acto } } -export async function getFollowingAcct(db: D1Database, actor: Actor): Promise> { +export function getFollowingAcct(db: D1Database, actor: Actor): Promise> { const query = ` - SELECT target_actor_acct FROM actor_following WHERE actor_id=? AND state=? - ` + SELECT target_actor_acct FROM actor_following WHERE actor_id=? AND state=? + ` + const statement = db.prepare(query).bind(actor.id.toString(), STATE_ACCEPTED) - const out: any = await db.prepare(query).bind(actor.id.toString(), STATE_ACCEPTED).all() - if (!out.success) { - throw new Error('SQL error: ' + out.error) - } - - if (out.results !== null) { - return out.results.map((x: any) => x.target_actor_acct) - } else { - return [] - } + return getResultsField(statement, 'target_actor_acct') } -export async function getFollowingRequestedAcct(db: D1Database, actor: Actor): Promise> { +export function getFollowingRequestedAcct(db: D1Database, actor: Actor): Promise> { const query = ` - SELECT target_actor_acct FROM actor_following WHERE actor_id=? AND state=? - ` + SELECT target_actor_acct FROM actor_following WHERE actor_id=? AND state=? + ` - const out: any = await db.prepare(query).bind(actor.id.toString(), STATE_PENDING).all() - if (!out.success) { - throw new Error('SQL error: ' + out.error) - } + const statement = db.prepare(query).bind(actor.id.toString(), STATE_PENDING) - if (out.results !== null) { - return out.results.map((x: any) => x.target_actor_acct) - } else { - return [] - } + return getResultsField(statement, 'target_actor_acct') } -export async function getFollowingId(db: D1Database, actor: Actor): Promise> { +export function getFollowingId(db: D1Database, actor: Actor): Promise> { const query = ` - SELECT target_actor_id FROM actor_following WHERE actor_id=? AND state=? - ` + SELECT target_actor_id FROM actor_following WHERE actor_id=? AND state=? + ` - const out: any = await db.prepare(query).bind(actor.id.toString(), STATE_ACCEPTED).all() - if (!out.success) { - throw new Error('SQL error: ' + out.error) - } + const statement = db.prepare(query).bind(actor.id.toString(), STATE_ACCEPTED) - if (out.results !== null) { - return out.results.map((x: any) => x.target_actor_id) - } else { - return [] - } + return getResultsField(statement, 'target_actor_id') } -export async function getFollowers(db: D1Database, actor: Actor): Promise> { +export function getFollowers(db: D1Database, actor: Actor): Promise> { const query = ` - SELECT actor_id FROM actor_following WHERE target_actor_id=? AND state=? - ` + SELECT actor_id FROM actor_following WHERE target_actor_id=? AND state=? + ` - const out: any = await db.prepare(query).bind(actor.id.toString(), STATE_ACCEPTED).all() - if (!out.success) { - throw new Error('SQL error: ' + out.error) - } + const statement = db.prepare(query).bind(actor.id.toString(), STATE_ACCEPTED) - if (out.results !== null) { - return out.results.map((x: any) => x.actor_id) - } else { - return [] - } + return getResultsField(statement, 'actor_id') } diff --git a/backend/src/mastodon/like.ts b/backend/src/mastodon/like.ts index 623f582..e17a704 100644 --- a/backend/src/mastodon/like.ts +++ b/backend/src/mastodon/like.ts @@ -1,32 +1,27 @@ import type { Object } from 'wildebeest/backend/src/activitypub/objects' import type { Actor } from 'wildebeest/backend/src/activitypub/actors' +import { getResultsField } from './utils' export async function insertLike(db: D1Database, actor: Actor, obj: Object) { const id = crypto.randomUUID() const query = ` - INSERT INTO actor_favourites (id, actor_id, object_id) - VALUES (?, ?, ?) -` + INSERT INTO actor_favourites (id, actor_id, object_id) + VALUES (?, ?, ?) + ` + const out = await db.prepare(query).bind(id, actor.id.toString(), obj.id.toString()).run() if (!out.success) { throw new Error('SQL error: ' + out.error) } } -export async function getLikes(db: D1Database, obj: Object): Promise> { +export function getLikes(db: D1Database, obj: Object): Promise> { const query = ` - SELECT actor_id FROM actor_favourites WHERE object_id=? - ` + SELECT actor_id FROM actor_favourites WHERE object_id=? + ` - const out: any = await db.prepare(query).bind(obj.id.toString()).all() - if (!out.success) { - throw new Error('SQL error: ' + out.error) - } + const statement = db.prepare(query).bind(obj.id.toString()) - if (out.results !== null) { - return out.results.map((x: any) => x.actor_id) - } else { - return [] - } + return getResultsField(statement, 'actor_id') } diff --git a/backend/src/mastodon/notification.ts b/backend/src/mastodon/notification.ts index d945b01..90492ca 100644 --- a/backend/src/mastodon/notification.ts +++ b/backend/src/mastodon/notification.ts @@ -26,7 +26,7 @@ export async function createNotification( VALUES (?, ?, ?, ?) RETURNING id ` - const row: any = await db + const row: { id: string } = await db .prepare(query) .bind(type, actor.id.toString(), fromActor.id.toString(), obj.id.toString()) .first() @@ -41,7 +41,7 @@ export async function insertFollowNotification(db: D1Database, actor: Actor, fro VALUES (?, ?, ?) RETURNING id ` - const row: any = await db.prepare(query).bind(type, actor.id.toString(), fromActor.id.toString()).first() + const row: { id: string } = await db.prepare(query).bind(type, actor.id.toString(), fromActor.id.toString()).first() return row.id } diff --git a/backend/src/mastodon/reblog.ts b/backend/src/mastodon/reblog.ts index f89b95c..b77dc2c 100644 --- a/backend/src/mastodon/reblog.ts +++ b/backend/src/mastodon/reblog.ts @@ -2,33 +2,28 @@ import type { Object } from 'wildebeest/backend/src/activitypub/objects' import type { Actor } from 'wildebeest/backend/src/activitypub/actors' +import { getResultsField } from './utils' export async function insertReblog(db: D1Database, actor: Actor, obj: Object) { const id = crypto.randomUUID() const query = ` - INSERT INTO actor_reblogs (id, actor_id, object_id) - VALUES (?, ?, ?) -` + INSERT INTO actor_reblogs (id, actor_id, object_id) + VALUES (?, ?, ?) + ` + const out = await db.prepare(query).bind(id, actor.id.toString(), obj.id.toString()).run() if (!out.success) { throw new Error('SQL error: ' + out.error) } } -export async function getReblogs(db: D1Database, obj: Object): Promise> { +export function getReblogs(db: D1Database, obj: Object): Promise> { const query = ` - SELECT actor_id FROM actor_reblogs WHERE object_id=? - ` + SELECT actor_id FROM actor_reblogs WHERE object_id=? + ` - const out: any = await db.prepare(query).bind(obj.id.toString()).all() - if (!out.success) { - throw new Error('SQL error: ' + out.error) - } + const statement = db.prepare(query).bind(obj.id.toString()) - if (out.results !== null) { - return out.results.map((x: any) => x.actor_id) - } else { - return [] - } + return getResultsField(statement, 'actor_id') } diff --git a/backend/src/mastodon/subscription.ts b/backend/src/mastodon/subscription.ts index 6c4cf5e..e8dfac0 100644 --- a/backend/src/mastodon/subscription.ts +++ b/backend/src/mastodon/subscription.ts @@ -129,7 +129,7 @@ export async function getVAPIDKeys(db: D1Database): Promise { if (!row) { throw new Error('missing VAPID keys') } - const value = JSON.parse(row.value) + const value: JWK = JSON.parse(row.value) return value } diff --git a/backend/src/mastodon/utils.ts b/backend/src/mastodon/utils.ts new file mode 100644 index 0000000..14b2726 --- /dev/null +++ b/backend/src/mastodon/utils.ts @@ -0,0 +1,9 @@ +export async function getResultsField(statement: D1PreparedStatement, fieldName: string): Promise> { + const out: D1Result> = await statement.all() + + if (!out.success) { + throw new Error('SQL error: ' + out.error) + } + + return (out.results ?? []).map((x) => x[fieldName]) +} diff --git a/backend/src/utils/httpsigjs/utils.ts b/backend/src/utils/httpsigjs/utils.ts index 3493a9b..95860eb 100644 --- a/backend/src/utils/httpsigjs/utils.ts +++ b/backend/src/utils/httpsigjs/utils.ts @@ -31,7 +31,7 @@ export class InvalidAlgorithmError extends HttpSignatureError { * @returns {[string, string]} */ export function validateAlgorithm(algorithm: string, publicKeyType?: string): [string, string] { - var alg = algorithm.toLowerCase().split('-') + const alg = algorithm.toLowerCase().split('-') if (alg[0] === 'hs2019') { return publicKeyType !== undefined ? validateAlgorithm(publicKeyType + '-sha256') : ['hs2019', 'sha256'] diff --git a/backend/src/utils/key-ops.ts b/backend/src/utils/key-ops.ts index cc34302..507006c 100644 --- a/backend/src/utils/key-ops.ts +++ b/backend/src/utils/key-ops.ts @@ -157,7 +157,8 @@ const DEC = { '-': '+', _: '/', '.': '=', -} +} as const +type KeyOfDEC = keyof typeof DEC export function urlsafeBase64Decode(v: string) { - return atob(v.replace(/[-_.]/g, (m: string) => (DEC as any)[m])) + return atob(v.replace(/[-_.]/g, (m: string) => DEC[m as KeyOfDEC])) } diff --git a/backend/test/activitypub/inbox.spec.ts b/backend/test/activitypub/inbox.spec.ts index f876147..88f2c18 100644 --- a/backend/test/activitypub/inbox.spec.ts +++ b/backend/test/activitypub/inbox.spec.ts @@ -13,7 +13,7 @@ const kv_cache: any = { async put() {}, } -const waitUntil = async (p: Promise) => await p +const waitUntil = async (p: Promise) => await p describe('ActivityPub', () => { test('send Note to non existant user', async () => {