diff options
| author | Hazelnoot <acomputerdog@gmail.com> | 2025-05-07 16:33:18 +0000 |
|---|---|---|
| committer | Hazelnoot <acomputerdog@gmail.com> | 2025-05-07 16:33:18 +0000 |
| commit | d39a56c1b7d74dd07cc78b4c82a6fb6e51036252 (patch) | |
| tree | 24f9c6baa07fadc11c791f1a59bee2c3149cbf56 /packages/backend/src/misc | |
| parent | merge: Add BunnyCDN Edge Storage support (!952) (diff) | |
| parent | isNotUserHome > isUserHome (diff) | |
| download | sharkey-d39a56c1b7d74dd07cc78b4c82a6fb6e51036252.tar.gz sharkey-d39a56c1b7d74dd07cc78b4c82a6fb6e51036252.tar.bz2 sharkey-d39a56c1b7d74dd07cc78b4c82a6fb6e51036252.zip | |
merge: Merge upstream 2025.4.1 (!955)
View MR for information: https://activitypub.software/TransFem-org/Sharkey/-/merge_requests/955
Closes #638, #1037, #734, and #766
Approved-by: dakkar <dakkar@thenautilus.net>
Approved-by: Marie <github@yuugi.dev>
Diffstat (limited to 'packages/backend/src/misc')
| -rw-r--r-- | packages/backend/src/misc/FileWriterStream.ts | 1 | ||||
| -rw-r--r-- | packages/backend/src/misc/bigint.ts | 40 | ||||
| -rw-r--r-- | packages/backend/src/misc/gen-identicon.ts | 4 | ||||
| -rw-r--r-- | packages/backend/src/misc/id/aid.ts | 7 | ||||
| -rw-r--r-- | packages/backend/src/misc/id/aidx.ts | 8 | ||||
| -rw-r--r-- | packages/backend/src/misc/id/meid.ts | 9 | ||||
| -rw-r--r-- | packages/backend/src/misc/id/meidg.ts | 9 | ||||
| -rw-r--r-- | packages/backend/src/misc/id/object-id.ts | 9 | ||||
| -rw-r--r-- | packages/backend/src/misc/id/ulid.ts | 20 | ||||
| -rw-r--r-- | packages/backend/src/misc/is-native-token.ts | 7 | ||||
| -rw-r--r-- | packages/backend/src/misc/is-renote.ts | 2 | ||||
| -rw-r--r-- | packages/backend/src/misc/json-schema.ts | 87 | ||||
| -rw-r--r-- | packages/backend/src/misc/json-value.ts | 2 | ||||
| -rw-r--r-- | packages/backend/src/misc/token.ts (renamed from packages/backend/src/misc/generate-native-user-token.ts) | 5 |
14 files changed, 159 insertions, 51 deletions
diff --git a/packages/backend/src/misc/FileWriterStream.ts b/packages/backend/src/misc/FileWriterStream.ts index 367a8eb560..27c67cb5df 100644 --- a/packages/backend/src/misc/FileWriterStream.ts +++ b/packages/backend/src/misc/FileWriterStream.ts @@ -4,6 +4,7 @@ */ import * as fs from 'node:fs/promises'; +import { WritableStream } from 'node:stream/web'; import type { PathLike } from 'node:fs'; /** diff --git a/packages/backend/src/misc/bigint.ts b/packages/backend/src/misc/bigint.ts new file mode 100644 index 0000000000..efa1527ec9 --- /dev/null +++ b/packages/backend/src/misc/bigint.ts @@ -0,0 +1,40 @@ +/* + * SPDX-FileCopyrightText: syuilo and misskey-project + * SPDX-License-Identifier: AGPL-3.0-only + */ + +function parseBigIntChunked(str: string, base: number, chunkSize: number, powerOfChunkSize: bigint): bigint { + const chunks = []; + while (str.length > 0) { + chunks.unshift(str.slice(-chunkSize)); + str = str.slice(0, -chunkSize); + } + let result = 0n; + for (const chunk of chunks) { + result *= powerOfChunkSize; + const int = parseInt(chunk, base); + if (Number.isNaN(int)) { + throw new Error('Invalid base36 string'); + } + result += BigInt(int); + } + return result; +} + +export function parseBigInt36(str: string): bigint { + // log_36(Number.MAX_SAFE_INTEGER) => 10.251599391715352 + // so we process 10 chars at once + return parseBigIntChunked(str, 36, 10, 36n ** 10n); +} + +export function parseBigInt16(str: string): bigint { + // log_16(Number.MAX_SAFE_INTEGER) => 13.25 + // so we process 13 chars at once + return parseBigIntChunked(str, 16, 13, 16n ** 13n); +} + +export function parseBigInt32(str: string): bigint { + // log_32(Number.MAX_SAFE_INTEGER) => 10.6 + // so we process 10 chars at once + return parseBigIntChunked(str, 32, 10, 32n ** 10n); +} diff --git a/packages/backend/src/misc/gen-identicon.ts b/packages/backend/src/misc/gen-identicon.ts index f3c08cc76e..ac7db82f2e 100644 --- a/packages/backend/src/misc/gen-identicon.ts +++ b/packages/backend/src/misc/gen-identicon.ts @@ -44,7 +44,7 @@ const sideN = Math.floor(n / 2); /** * Generate buffer of an identicon by seed */ -export async function genIdenticon(seed: string): Promise<Buffer> { +export function genIdenticon(seed: string): Buffer { const rand = gen.create(seed); const canvas = createCanvas(size, size); const ctx = canvas.getContext('2d'); @@ -100,5 +100,5 @@ export async function genIdenticon(seed: string): Promise<Buffer> { } } - return await canvas.toBuffer('image/png'); + return canvas.toBuffer('image/png'); } diff --git a/packages/backend/src/misc/id/aid.ts b/packages/backend/src/misc/id/aid.ts index 60ba788e44..c0e8478db5 100644 --- a/packages/backend/src/misc/id/aid.ts +++ b/packages/backend/src/misc/id/aid.ts @@ -7,6 +7,7 @@ // 長さ8の[2000年1月1日からの経過ミリ秒をbase36でエンコードしたもの] + 長さ2の[ノイズ文字列] import * as crypto from 'node:crypto'; +import { parseBigInt36 } from '@/misc/bigint.js'; export const aidRegExp = /^[0-9a-z]{10}$/; @@ -35,6 +36,12 @@ export function parseAid(id: string): { date: Date; } { return { date: new Date(time) }; } +export function parseAidFull(id: string): { date: number; additional: bigint; } { + const date = parseInt(id.slice(0, 8), 36) + TIME2000; + const additional = parseBigInt36(id.slice(8, 10)); + return { date, additional }; +} + export function isSafeAidT(t: number): boolean { return t > TIME2000; } diff --git a/packages/backend/src/misc/id/aidx.ts b/packages/backend/src/misc/id/aidx.ts index 1b087e70af..006673a6d0 100644 --- a/packages/backend/src/misc/id/aidx.ts +++ b/packages/backend/src/misc/id/aidx.ts @@ -9,6 +9,7 @@ // https://misskey.m544.net/notes/71899acdcc9859ec5708ac24 import { customAlphabet } from 'nanoid'; +import { parseBigInt36 } from '@/misc/bigint.js'; export const aidxRegExp = /^[0-9a-z]{16}$/; @@ -16,6 +17,7 @@ const TIME2000 = 946684800000; const TIME_LENGTH = 8; const NODE_LENGTH = 4; const NOISE_LENGTH = 4; +const AIDX_LENGTH = TIME_LENGTH + NODE_LENGTH + NOISE_LENGTH; const nodeId = customAlphabet('0123456789abcdefghijklmnopqrstuvwxyz', NODE_LENGTH)(); let counter = 0; @@ -42,6 +44,12 @@ export function parseAidx(id: string): { date: Date; } { return { date: new Date(time) }; } +export function parseAidxFull(id: string): { date: number; additional: bigint; } { + const date = parseInt(id.slice(0, TIME_LENGTH), 36) + TIME2000; + const additional = parseBigInt36(id.slice(TIME_LENGTH, AIDX_LENGTH)); + return { date, additional }; +} + export function isSafeAidxT(t: number): boolean { return t > TIME2000; } diff --git a/packages/backend/src/misc/id/meid.ts b/packages/backend/src/misc/id/meid.ts index dfab48a369..563e07ed8f 100644 --- a/packages/backend/src/misc/id/meid.ts +++ b/packages/backend/src/misc/id/meid.ts @@ -3,6 +3,8 @@ * SPDX-License-Identifier: AGPL-3.0-only */ +import { parseBigInt16 } from '@/misc/bigint.js'; + const CHARS = '0123456789abcdef'; // same as object-id @@ -39,6 +41,13 @@ export function parseMeid(id: string): { date: Date; } { }; } +export function parseMeidFull(id: string): { date: number; additional: bigint; } { + return { + date: parseInt(id.slice(0, 12), 16) - 0x800000000000, + additional: parseBigInt16(id.slice(12, 24)), + }; +} + export function isSafeMeidT(t: number): boolean { return t > 0; } diff --git a/packages/backend/src/misc/id/meidg.ts b/packages/backend/src/misc/id/meidg.ts index b9c0cc3dda..b825807114 100644 --- a/packages/backend/src/misc/id/meidg.ts +++ b/packages/backend/src/misc/id/meidg.ts @@ -3,6 +3,8 @@ * SPDX-License-Identifier: AGPL-3.0-only */ +import { parseBigInt16 } from '@/misc/bigint.js'; + const CHARS = '0123456789abcdef'; // 4bit Fixed hex value 'g' @@ -39,6 +41,13 @@ export function parseMeidg(id: string): { date: Date; } { }; } +export function parseMeidgFull(id: string): { date: number; additional: bigint; } { + return { + date: parseInt(id.slice(1, 12), 16), + additional: parseBigInt16(id.slice(12, 24)), + }; +} + export function isSafeMeidgT(t: number): boolean { return t > 0; } diff --git a/packages/backend/src/misc/id/object-id.ts b/packages/backend/src/misc/id/object-id.ts index 243f92bbac..68409c7a61 100644 --- a/packages/backend/src/misc/id/object-id.ts +++ b/packages/backend/src/misc/id/object-id.ts @@ -3,6 +3,8 @@ * SPDX-License-Identifier: AGPL-3.0-only */ +import { parseBigInt16 } from '@/misc/bigint.js'; + const CHARS = '0123456789abcdef'; // same as meid @@ -39,6 +41,13 @@ export function parseObjectId(id: string): { date: Date; } { }; } +export function parseObjectIdFull(id: string): { date: number; additional: bigint; } { + return { + date: parseInt(id.slice(0, 8), 16) * 1000, + additional: parseBigInt16(id.slice(8, 24)), + }; +} + export function isSafeObjectIdT(t: number): boolean { return t > 0; } diff --git a/packages/backend/src/misc/id/ulid.ts b/packages/backend/src/misc/id/ulid.ts index fc3654d6d2..8b81702d19 100644 --- a/packages/backend/src/misc/id/ulid.ts +++ b/packages/backend/src/misc/id/ulid.ts @@ -5,15 +5,27 @@ // Crockford's Base32 // https://github.com/ulid/spec#encoding +import { parseBigInt32 } from '@/misc/bigint.js'; + const CHARS = '0123456789ABCDEFGHJKMNPQRSTVWXYZ'; export const ulidRegExp = /^[0123456789ABCDEFGHJKMNPQRSTVWXYZ]{26}$/; -export function parseUlid(id: string): { date: Date; } { - const timestamp = id.slice(0, 10); +function parseBase32(timestamp: string) { let time = 0; - for (let i = 0; i < 10; i++) { + for (let i = 0; i < timestamp.length; i++) { time = time * 32 + CHARS.indexOf(timestamp[i]); } - return { date: new Date(time) }; + return time; +} + +export function parseUlid(id: string): { date: Date; } { + return { date: new Date(parseBase32(id.slice(0, 10))) }; +} + +export function parseUlidFull(id: string): { date: number; additional: bigint; } { + return { + date: parseBase32(id.slice(0, 10)), + additional: parseBigInt32(id.slice(10, 26)), + }; } diff --git a/packages/backend/src/misc/is-native-token.ts b/packages/backend/src/misc/is-native-token.ts deleted file mode 100644 index 300c4c05b3..0000000000 --- a/packages/backend/src/misc/is-native-token.ts +++ /dev/null @@ -1,7 +0,0 @@ -/* - * SPDX-FileCopyrightText: syuilo and misskey-project - * SPDX-License-Identifier: AGPL-3.0-only - */ - -// eslint-disable-next-line import/no-default-export -export default (token: string) => token.length === 16; diff --git a/packages/backend/src/misc/is-renote.ts b/packages/backend/src/misc/is-renote.ts index d6872de46a..fcaafaf95a 100644 --- a/packages/backend/src/misc/is-renote.ts +++ b/packages/backend/src/misc/is-renote.ts @@ -77,7 +77,7 @@ type PackedPureRenote = PackedRenote & { replyId: NonNullable<Packed<'Note'>['replyId']>; poll: NonNullable<Packed<'Note'>['poll']>; fileIds: NonNullable<Packed<'Note'>['fileIds']>; -} +}; export function isRenotePacked(note: Packed<'Note'>): note is PackedRenote { return note.renoteId != null; diff --git a/packages/backend/src/misc/json-schema.ts b/packages/backend/src/misc/json-schema.ts index f612591eda..27aa3d89de 100644 --- a/packages/backend/src/misc/json-schema.ts +++ b/packages/backend/src/misc/json-schema.ts @@ -63,6 +63,10 @@ import { } from '@/models/json-schema/meta.js'; import { packedSystemWebhookSchema } from '@/models/json-schema/system-webhook.js'; import { packedAbuseReportNotificationRecipientSchema } from '@/models/json-schema/abuse-report-notification-recipient.js'; +import { packedChatMessageSchema, packedChatMessageLiteSchema, packedChatMessageLiteForRoomSchema, packedChatMessageLiteFor1on1Schema } from '@/models/json-schema/chat-message.js'; +import { packedChatRoomSchema } from '@/models/json-schema/chat-room.js'; +import { packedChatRoomInvitationSchema } from '@/models/json-schema/chat-room-invitation.js'; +import { packedChatRoomMembershipSchema } from '@/models/json-schema/chat-room-membership.js'; export const refs = { UserLite: packedUserLiteSchema, @@ -120,6 +124,13 @@ export const refs = { MetaDetailed: packedMetaDetailedSchema, SystemWebhook: packedSystemWebhookSchema, AbuseReportNotificationRecipient: packedAbuseReportNotificationRecipientSchema, + ChatMessage: packedChatMessageSchema, + ChatMessageLite: packedChatMessageLiteSchema, + ChatMessageLiteFor1on1: packedChatMessageLiteFor1on1Schema, + ChatMessageLiteForRoom: packedChatMessageLiteForRoomSchema, + ChatRoom: packedChatRoomSchema, + ChatRoomInvitation: packedChatRoomInvitationSchema, + ChatRoomMembership: packedChatRoomMembershipSchema, }; export type Packed<x extends keyof typeof refs> = SchemaType<typeof refs[x]>; @@ -143,7 +154,7 @@ type OfSchema = { readonly anyOf?: ReadonlyArray<Schema>; readonly oneOf?: ReadonlyArray<Schema>; readonly allOf?: ReadonlyArray<Schema>; -} +}; export interface Schema extends OfSchema { readonly type?: TypeStringef; @@ -166,15 +177,16 @@ export interface Schema extends OfSchema { readonly maximum?: number; readonly minimum?: number; readonly pattern?: string; + readonly additionalProperties?: Schema | boolean; } type RequiredPropertyNames<s extends Obj> = { [K in keyof s]: - // K is not optional - s[K]['optional'] extends false ? K : - // K has default value - s[K]['default'] extends null | string | number | boolean | Record<string, unknown> ? K : - never + // K is not optional + s[K]['optional'] extends false ? K : + // K has default value + s[K]['default'] extends null | string | number | boolean | Record<string, unknown> ? K : + never }[keyof s]; export type Obj = Record<string, Schema>; @@ -213,11 +225,18 @@ type ObjectSchemaTypeDef<p extends Schema> = p['anyOf'] extends ReadonlyArray<Schema> ? p['anyOf'][number]['required'] extends ReadonlyArray<keyof p['properties']> ? UnionObjType<p['properties'], NonNullable<p['anyOf'][number]['required']>> & ObjType<p['properties'], NonNullable<p['required']>> : never - : ObjType<p['properties'], NonNullable<p['required']>> - : - p['anyOf'] extends ReadonlyArray<Schema> ? never : // see CONTRIBUTING.md - p['allOf'] extends ReadonlyArray<Schema> ? UnionToIntersection<UnionSchemaType<p['allOf']>> : - any + : ObjType<p['properties'], NonNullable<p['required']>> + : + p['anyOf'] extends ReadonlyArray<Schema> ? never : // see CONTRIBUTING.md + p['allOf'] extends ReadonlyArray<Schema> ? UnionToIntersection<UnionSchemaType<p['allOf']>> : + p['additionalProperties'] extends true ? Record<string, any> : + p['additionalProperties'] extends Schema ? + p['additionalProperties'] extends infer AdditionalProperties ? + AdditionalProperties extends Schema ? + Record<string, SchemaType<AdditionalProperties>> : + never : + never : + any; type ObjectSchemaType<p extends Schema> = NullOrUndefined<p, ObjectSchemaTypeDef<p>>; @@ -227,30 +246,30 @@ export type SchemaTypeDef<p extends Schema> = p['type'] extends 'number' ? number : p['type'] extends 'string' ? ( p['enum'] extends readonly (string | null)[] ? - p['enum'][number] : - p['format'] extends 'date-time' ? string : // Dateにする?? - string + p['enum'][number] : + p['format'] extends 'date-time' ? string : // Dateにする?? + string ) : - p['type'] extends 'boolean' ? boolean : - p['type'] extends 'object' ? ObjectSchemaTypeDef<p> : - p['type'] extends 'array' ? ( - p['items'] extends OfSchema ? ( - p['items']['anyOf'] extends ReadonlyArray<Schema> ? UnionSchemaType<NonNullable<p['items']['anyOf']>>[] : - p['items']['oneOf'] extends ReadonlyArray<Schema> ? ArrayUnion<UnionSchemaType<NonNullable<p['items']['oneOf']>>> : - p['items']['allOf'] extends ReadonlyArray<Schema> ? UnionToIntersection<UnionSchemaType<NonNullable<p['items']['allOf']>>>[] : - never - ) : - p['prefixItems'] extends ReadonlyArray<Schema> ? ( - p['items'] extends NonNullable<Schema> ? [...ArrayToTuple<p['prefixItems']>, ...SchemaType<p['items']>[]] : - p['items'] extends false ? ArrayToTuple<p['prefixItems']> : - p['unevaluatedItems'] extends false ? ArrayToTuple<p['prefixItems']> : - [...ArrayToTuple<p['prefixItems']>, ...unknown[]] + p['type'] extends 'boolean' ? boolean : + p['type'] extends 'object' ? ObjectSchemaTypeDef<p> : + p['type'] extends 'array' ? ( + p['items'] extends OfSchema ? ( + p['items']['anyOf'] extends ReadonlyArray<Schema> ? UnionSchemaType<NonNullable<p['items']['anyOf']>>[] : + p['items']['oneOf'] extends ReadonlyArray<Schema> ? ArrayUnion<UnionSchemaType<NonNullable<p['items']['oneOf']>>> : + p['items']['allOf'] extends ReadonlyArray<Schema> ? UnionToIntersection<UnionSchemaType<NonNullable<p['items']['allOf']>>>[] : + never + ) : + p['prefixItems'] extends ReadonlyArray<Schema> ? ( + p['items'] extends NonNullable<Schema> ? [...ArrayToTuple<p['prefixItems']>, ...SchemaType<p['items']>[]] : + p['items'] extends false ? ArrayToTuple<p['prefixItems']> : + p['unevaluatedItems'] extends false ? ArrayToTuple<p['prefixItems']> : + [...ArrayToTuple<p['prefixItems']>, ...unknown[]] + ) : + p['items'] extends NonNullable<Schema> ? SchemaType<p['items']>[] : + any[] ) : - p['items'] extends NonNullable<Schema> ? SchemaType<p['items']>[] : - any[] - ) : - p['anyOf'] extends ReadonlyArray<Schema> ? UnionSchemaType<p['anyOf']> & PartialIntersection<UnionSchemaType<p['anyOf']>> : - p['oneOf'] extends ReadonlyArray<Schema> ? UnionSchemaType<p['oneOf']> : - any; + p['anyOf'] extends ReadonlyArray<Schema> ? UnionSchemaType<p['anyOf']> & PartialIntersection<UnionSchemaType<p['anyOf']>> : + p['oneOf'] extends ReadonlyArray<Schema> ? UnionSchemaType<p['oneOf']> : + any; export type SchemaType<p extends Schema> = NullOrUndefined<p, SchemaTypeDef<p>>; diff --git a/packages/backend/src/misc/json-value.ts b/packages/backend/src/misc/json-value.ts index bd7fe12058..195f7c4d47 100644 --- a/packages/backend/src/misc/json-value.ts +++ b/packages/backend/src/misc/json-value.ts @@ -4,7 +4,7 @@ */ export type JsonValue = JsonArray | JsonObject | string | number | boolean | null; -export type JsonObject = {[K in string]?: JsonValue}; +export type JsonObject = { [K in string]?: JsonValue }; export type JsonArray = JsonValue[]; export function isJsonObject(value: JsonValue | undefined): value is JsonObject { diff --git a/packages/backend/src/misc/generate-native-user-token.ts b/packages/backend/src/misc/token.ts index 85fb383ba2..5d37cba26d 100644 --- a/packages/backend/src/misc/generate-native-user-token.ts +++ b/packages/backend/src/misc/token.ts @@ -5,5 +5,6 @@ import { secureRndstr } from '@/misc/secure-rndstr.js'; -// eslint-disable-next-line import/no-default-export -export default () => secureRndstr(16); +export const generateNativeUserToken = () => secureRndstr(16); + +export const isNativeUserToken = (token: string) => token.length === 16; |