diff --git a/neko/index.js b/neko/index.js index 36d829c1..d527c2c8 100644 --- a/neko/index.js +++ b/neko/index.js @@ -266,6 +266,8 @@ if (!nativeBinding) { } const { + stringToAcct, + acctToString, nativeRandomStr, IdConvertType, convertId, @@ -274,6 +276,8 @@ const { nativeInitIdGenerator, } = nativeBinding; +module.exports.stringToAcct = stringToAcct; +module.exports.acctToString = acctToString; module.exports.nativeRandomStr = nativeRandomStr; module.exports.IdConvertType = IdConvertType; module.exports.convertId = convertId; diff --git a/packages/backend/native-utils/Cargo.toml b/packages/backend/native-utils/Cargo.toml index f84d9998..c797692b 100644 --- a/packages/backend/native-utils/Cargo.toml +++ b/packages/backend/native-utils/Cargo.toml @@ -32,7 +32,7 @@ tokio = { version = "1.35.1", features = ["full"] } utoipa = "4.1.0" # Default enable napi4 feature, see https://nodejs.org/api/n-api.html#node-api-version-matrix -napi = { version = "2.14.1", default-features = false, features = ["napi6", "tokio_rt"], optional = true } +napi = { version = "2.14.1", default-features = false, features = ["napi9", "tokio_rt"], optional = true } napi-derive = { version = "2.14.5", optional = true } basen = "0.1.0" diff --git a/packages/backend/native-utils/src/util/acct.rs b/packages/backend/native-utils/src/util/acct.rs new file mode 100644 index 00000000..65cd7ddc --- /dev/null +++ b/packages/backend/native-utils/src/util/acct.rs @@ -0,0 +1,36 @@ +use napi_derive::napi; + +#[derive(Clone, Eq, PartialEq, Debug)] +#[napi(object)] +pub struct Acct { + pub username: String, + pub host: Option, +} + +#[napi] +pub fn string_to_acct(acct: String) -> Acct { + let split: Vec<&str> = if let Some(stripped) = acct.strip_prefix('@') { + stripped + } else { + &acct + } + .split('@') + .collect(); + + Acct { + username: split[0].to_string(), + host: if split.len() == 1 { + None + } else { + Some(split[1].to_string()) + }, + } +} + +#[napi] +pub fn acct_to_string(acct: Acct) -> String { + match acct.host { + Some(host) => format!("{}@{}", acct.username, host), + None => acct.username, + } +} diff --git a/packages/backend/native-utils/src/util/mod.rs b/packages/backend/native-utils/src/util/mod.rs index 1be5a7fd..6f286a47 100644 --- a/packages/backend/native-utils/src/util/mod.rs +++ b/packages/backend/native-utils/src/util/mod.rs @@ -1,2 +1,3 @@ +pub mod acct; pub mod id; pub mod random; diff --git a/packages/backend/src/misc/acct.ts b/packages/backend/src/misc/acct.ts deleted file mode 100644 index cb6808b4..00000000 --- a/packages/backend/src/misc/acct.ts +++ /dev/null @@ -1,14 +0,0 @@ -export type Acct = { - username: string; - host: string | null; -}; - -export function parse(acct: string): Acct { - if (acct.startsWith("@")) acct = acct.slice(1); - const split = acct.split("@", 2); - return { username: split[0], host: split[1] || null }; -} - -export function toString(acct: Acct): string { - return acct.host == null ? acct.username : `${acct.username}@${acct.host}`; -} diff --git a/packages/backend/src/misc/check-hit-antenna.ts b/packages/backend/src/misc/check-hit-antenna.ts index d34ad084..23e409d1 100644 --- a/packages/backend/src/misc/check-hit-antenna.ts +++ b/packages/backend/src/misc/check-hit-antenna.ts @@ -3,7 +3,7 @@ import type { Note } from "@/models/entities/note.js"; import type { User } from "@/models/entities/user.js"; import { Blockings, Followings, UserProfiles } from "@/models/index.js"; import { getFullApAccount } from "@/misc/convert-host.js"; -import * as Acct from "@/misc/acct.js"; +import { stringToAcct } from "native-utils/built/index.js"; import type { Packed } from "@/misc/schema.js"; import { Cache } from "@/misc/cache.js"; import { getWordHardMute } from "@/misc/check-word-mute.js"; @@ -25,8 +25,8 @@ export async function checkHitAntenna( if (antenna.src === "users") { const accts = antenna.users.map((x) => { - const { username, host } = Acct.parse(x); - return getFullApAccount(username, host).toLowerCase(); + const { username, host } = stringToAcct(x); + return getFullApAccount(username, host ?? null).toLowerCase(); }); if ( !accts.includes( diff --git a/packages/backend/src/queue/processors/db/import-blocking.ts b/packages/backend/src/queue/processors/db/import-blocking.ts index 290c9dbc..cac91af7 100644 --- a/packages/backend/src/queue/processors/db/import-blocking.ts +++ b/packages/backend/src/queue/processors/db/import-blocking.ts @@ -1,7 +1,7 @@ import type Bull from "bull"; import { queueLogger } from "../../logger.js"; -import * as Acct from "@/misc/acct.js"; +import { stringToAcct } from "native-utils/built/index.js"; import { resolveUser } from "@/remote/resolve-user.js"; import { downloadTextFile } from "@/misc/download-text-file.js"; import { isSelfHost, toPuny } from "@/misc/convert-host.js"; @@ -41,7 +41,7 @@ export async function importBlocking( try { const acct = line.split(",")[0].trim(); - const { username, host } = Acct.parse(acct); + const { username, host } = stringToAcct(acct); let target = isSelfHost(host!) ? await Users.findOneBy({ diff --git a/packages/backend/src/queue/processors/db/import-following.ts b/packages/backend/src/queue/processors/db/import-following.ts index b1a7cd2c..c2e59739 100644 --- a/packages/backend/src/queue/processors/db/import-following.ts +++ b/packages/backend/src/queue/processors/db/import-following.ts @@ -1,7 +1,7 @@ import { IsNull } from "typeorm"; import follow from "@/services/following/create.js"; -import * as Acct from "@/misc/acct.js"; +import { stringToAcct } from "native-utils/built/index.js"; import { resolveUser } from "@/remote/resolve-user.js"; import { downloadTextFile } from "@/misc/download-text-file.js"; import { isSelfHost, toPuny } from "@/misc/convert-host.js"; @@ -39,7 +39,7 @@ export async function importFollowing( if (file.type.endsWith("json")) { for (const acct of JSON.parse(csv)) { try { - const { username, host } = Acct.parse(acct); + const { username, host } = stringToAcct(acct); let target = isSelfHost(host!) ? await Users.findOneBy({ @@ -77,7 +77,7 @@ export async function importFollowing( try { const acct = line.split(",")[0].trim(); - const { username, host } = Acct.parse(acct); + const { username, host } = stringToAcct(acct); let target = isSelfHost(host!) ? await Users.findOneBy({ diff --git a/packages/backend/src/queue/processors/db/import-muting.ts b/packages/backend/src/queue/processors/db/import-muting.ts index 80e05673..83c9f5d4 100644 --- a/packages/backend/src/queue/processors/db/import-muting.ts +++ b/packages/backend/src/queue/processors/db/import-muting.ts @@ -1,7 +1,7 @@ import type Bull from "bull"; import { queueLogger } from "../../logger.js"; -import * as Acct from "@/misc/acct.js"; +import { stringToAcct } from "native-utils/built/index.js"; import { resolveUser } from "@/remote/resolve-user.js"; import { downloadTextFile } from "@/misc/download-text-file.js"; import { isSelfHost, toPuny } from "@/misc/convert-host.js"; @@ -42,7 +42,7 @@ export async function importMuting( try { const acct = line.split(",")[0].trim(); - const { username, host } = Acct.parse(acct); + const { username, host } = stringToAcct(acct); let target = isSelfHost(host!) ? await Users.findOneBy({ diff --git a/packages/backend/src/queue/processors/db/import-user-lists.ts b/packages/backend/src/queue/processors/db/import-user-lists.ts index bfa47ead..6661b315 100644 --- a/packages/backend/src/queue/processors/db/import-user-lists.ts +++ b/packages/backend/src/queue/processors/db/import-user-lists.ts @@ -1,7 +1,7 @@ import type Bull from "bull"; import { queueLogger } from "../../logger.js"; -import * as Acct from "@/misc/acct.js"; +import { stringToAcct } from "native-utils/built/index.js"; import { resolveUser } from "@/remote/resolve-user.js"; import { pushUserToUserList } from "@/services/user-list/push.js"; import { downloadTextFile } from "@/misc/download-text-file.js"; @@ -47,7 +47,7 @@ export async function importUserLists( try { const listName = line.split(",")[0].trim(); - const { username, host } = Acct.parse(line.split(",")[1].trim()); + const { username, host } = stringToAcct(line.split(",")[1].trim()); let list = await UserLists.findOneBy({ userId: user.id, diff --git a/packages/backend/src/server/api/endpoints/i/known-as.ts b/packages/backend/src/server/api/endpoints/i/known-as.ts index 39bf6fff..4f0f87c8 100644 --- a/packages/backend/src/server/api/endpoints/i/known-as.ts +++ b/packages/backend/src/server/api/endpoints/i/known-as.ts @@ -8,7 +8,7 @@ import { DAY } from "@/const.js"; import { apiLogger } from "@/server/api/logger.js"; import define from "@/server/api/define.js"; import { ApiError } from "@/server/api/error.js"; -import { parse } from "@/misc/acct.js"; +import { stringToAcct } from "native-utils/built/index.js"; export const meta = { tags: ["users"], @@ -71,9 +71,9 @@ export default define(meta, paramDef, async (ps, user) => { for (const line of ps.alsoKnownAs) { if (!line) throw new ApiError(meta.errors.noSuchUser); - const { username, host } = parse(line); + const { username, host } = stringToAcct(line); - const aka = await resolveUser(username, host).catch((e) => { + const aka = await resolveUser(username, host ?? null).catch((e) => { apiLogger.warn(`failed to resolve remote user: ${e}`); throw new ApiError(meta.errors.noSuchUser); }); diff --git a/packages/backend/src/server/api/endpoints/i/move.ts b/packages/backend/src/server/api/endpoints/i/move.ts index 31182c14..303b4318 100644 --- a/packages/backend/src/server/api/endpoints/i/move.ts +++ b/packages/backend/src/server/api/endpoints/i/move.ts @@ -12,7 +12,7 @@ import { getUser } from "@/server/api/common/getters.js"; import { Followings, Users } from "@/models/index.js"; import config from "@/config/index.js"; import { publishMainStream } from "@/services/stream.js"; -import { parse } from "@/misc/acct.js"; +import { stringToAcct } from "native-utils/built/index.js"; export const meta = { tags: ["users"], @@ -89,10 +89,10 @@ export default define(meta, paramDef, async (ps, user) => { if (!ps.moveToAccount) throw new ApiError(meta.errors.noSuchMoveTarget); if (user.movedToUri) throw new ApiError(meta.errors.alreadyMoved); - const { username, host } = parse(ps.moveToAccount); + const { username, host } = stringToAcct(ps.moveToAccount); if (!host) throw new ApiError(meta.errors.notRemote); - const moveTo: User = await resolveUser(username, host).catch((e) => { + const moveTo: User = await resolveUser(username, host ?? null).catch((e) => { apiLogger.warn(`failed to resolve remote user: ${e}`); throw new ApiError(meta.errors.noSuchMoveTarget); }); diff --git a/packages/backend/src/server/api/endpoints/pinned-users.ts b/packages/backend/src/server/api/endpoints/pinned-users.ts index b3273787..e8d2e9a1 100644 --- a/packages/backend/src/server/api/endpoints/pinned-users.ts +++ b/packages/backend/src/server/api/endpoints/pinned-users.ts @@ -1,7 +1,7 @@ import { IsNull } from "typeorm"; import { Users } from "@/models/index.js"; import { fetchMeta } from "@/misc/fetch-meta.js"; -import * as Acct from "@/misc/acct.js"; +import { stringToAcct } from "native-utils/built/index.js"; import type { User } from "@/models/entities/user.js"; import define from "@/server/api/define.js"; @@ -35,7 +35,7 @@ export default define(meta, paramDef, async (ps, me) => { const users = await Promise.all( meta.pinnedUsers - .map((acct) => Acct.parse(acct)) + .map((acct) => stringToAcct(acct)) .map((acct) => Users.findOneBy({ usernameLower: acct.username.toLowerCase(), diff --git a/packages/backend/src/server/index.ts b/packages/backend/src/server/index.ts index a2d8e7db..ad14d9ab 100644 --- a/packages/backend/src/server/index.ts +++ b/packages/backend/src/server/index.ts @@ -19,7 +19,7 @@ import { Users } from "@/models/index.js"; import { fetchMeta } from "@/misc/fetch-meta.js"; import { genIdenticon } from "@/misc/gen-identicon.js"; import { createTemp } from "@/misc/create-temp.js"; -import * as Acct from "@/misc/acct.js"; +import { stringToAcct } from "native-utils/built/index.js"; import { envOption } from "@/env.js"; import megalodon, { MegalodonInterface } from "megalodon"; import activityPub from "./activitypub.js"; @@ -107,7 +107,7 @@ router.use(nodeinfo.routes()); router.use(wellKnown.routes()); router.get("/avatar/@:acct", async (ctx) => { - const { username, host } = Acct.parse(ctx.params.acct); + const { username, host } = stringToAcct(ctx.params.acct); const user = await Users.findOne({ where: { usernameLower: username.toLowerCase(), diff --git a/packages/backend/src/server/web/index.ts b/packages/backend/src/server/web/index.ts index 1b7ba5ac..0e6ad57d 100644 --- a/packages/backend/src/server/web/index.ts +++ b/packages/backend/src/server/web/index.ts @@ -28,7 +28,7 @@ import { Emojis, GalleryPosts, } from "@/models/index.js"; -import * as Acct from "@/misc/acct.js"; +import { stringToAcct } from "native-utils/built/index.js"; import { getNoteSummary } from "@/misc/get-note-summary.js"; import { queues } from "@/queue/queues.js"; import { genOpenapiSpec } from "../api/openapi/gen-spec.js"; @@ -328,7 +328,7 @@ const getFeed = async ( if (meta.privateMode) { return; } - const { username, host } = Acct.parse(acct); + const { username, host } = stringToAcct(acct); const user = await Users.findOneBy({ usernameLower: username.toLowerCase(), host: host ?? IsNull(), @@ -459,7 +459,7 @@ const jsonFeed: Router.Middleware = async (ctx) => { const userPage: Router.Middleware = async (ctx, next) => { const userParam = ctx.params.user; const subParam = ctx.params.sub; - const { username, host } = Acct.parse(userParam); + const { username, host } = stringToAcct(userParam); const user = await Users.findOneBy({ usernameLower: username.toLowerCase(), @@ -578,7 +578,7 @@ router.get("/posts/:note", async (ctx, next) => { // Page router.get("/@:user/pages/:page", async (ctx, next) => { - const { username, host } = Acct.parse(ctx.params.user); + const { username, host } = stringToAcct(ctx.params.user); const user = await Users.findOneBy({ usernameLower: username.toLowerCase(), host: host ?? IsNull(), diff --git a/packages/backend/src/server/well-known.ts b/packages/backend/src/server/well-known.ts index 999e6d4a..eae8c833 100644 --- a/packages/backend/src/server/well-known.ts +++ b/packages/backend/src/server/well-known.ts @@ -1,7 +1,8 @@ import Router from "@koa/router"; import config from "@/config/index.js"; -import * as Acct from "@/misc/acct.js"; +import { stringToAcct } from "native-utils/built/index.js"; +import type { Acct } from "native-utils/built/index.js"; import { links } from "./nodeinfo.js"; import { escapeAttribute, escapeValue } from "@/prelude/xml.js"; import { Users } from "@/models/index.js"; @@ -110,7 +111,7 @@ router.get(webFingerPath, async (ctx) => { resource.startsWith(`${config.url.toLowerCase()}/users/`) ? fromId(resource.split("/").pop()!) : fromAcct( - Acct.parse( + stringToAcct( resource.startsWith(`${config.url.toLowerCase()}/@`) ? resource.split("/").pop()! : resource.startsWith("acct:") @@ -119,7 +120,7 @@ router.get(webFingerPath, async (ctx) => { ), ); - const fromAcct = (acct: Acct.Acct): FindOptionsWhere | number => + const fromAcct = (acct: Acct): FindOptionsWhere | number => !acct.host || acct.host === config.host.toLowerCase() ? { usernameLower: acct.username, diff --git a/packages/backend/src/services/send-email-notification.ts b/packages/backend/src/services/send-email-notification.ts index a2f422a9..78243b3e 100644 --- a/packages/backend/src/services/send-email-notification.ts +++ b/packages/backend/src/services/send-email-notification.ts @@ -2,7 +2,7 @@ import type { User } from "@/models/entities/user.js"; // import { sendEmail } from "./send-email.js"; // import { I18n } from "@/misc/i18n.js"; -// import * as Acct from "@/misc/acct.js"; +// import { acctToString } from "native-utils/built/index.js"; // TODO //const locales = await import('../../../../locales/index.js'); @@ -15,7 +15,7 @@ async function follow(userId: User["id"], follower: User) { const locale = locales[userProfile.lang || 'ja-JP']; const i18n = new I18n(locale); // TODO: render user information html - sendEmail(userProfile.email, i18n.t('_email._follow.title'), `${follower.name} (@${Acct.toString(follower)})`, `${follower.name} (@${Acct.toString(follower)})`); + sendEmail(userProfile.email, i18n.t('_email._follow.title'), `${follower.name} (@${acctToString(follower)})`, `${follower.name} (@${acctToString(follower)})`); */ } @@ -26,7 +26,7 @@ async function receiveFollowRequest(userId: User["id"], follower: User) { const locale = locales[userProfile.lang || 'ja-JP']; const i18n = new I18n(locale); // TODO: render user information html - sendEmail(userProfile.email, i18n.t('_email._receiveFollowRequest.title'), `${follower.name} (@${Acct.toString(follower)})`, `${follower.name} (@${Acct.toString(follower)})`); + sendEmail(userProfile.email, i18n.t('_email._receiveFollowRequest.title'), `${follower.name} (@${acctToString(follower)})`, `${follower.name} (@${acctToString(follower)})`); */ }