feat(backend): Add Config Option For Bio Length
This commit is contained in:
parent
a4c0ef824c
commit
df77f339ec
12 changed files with 71 additions and 15 deletions
|
|
@ -341,6 +341,10 @@ id: 'aidx'
|
|||
#maxAltTextLength: 20000
|
||||
# Amount of characters that will be saved for remote media descriptions (alt text). Longer descriptions will be truncated to this length. (minimum: 1)
|
||||
#maxRemoteAltTextLength: 100000
|
||||
# Amount of characters that can be used when writing user bios. Longer descriptions will be rejected. (minimum: 1)
|
||||
#maxBioLength: 1500
|
||||
# Amount of characters that will be saved for remote user bios. Longer descriptions will be truncated to this length. (minimum: 1)
|
||||
#maxRemoteBioLength: 15000
|
||||
|
||||
# Proxy for HTTP/HTTPS
|
||||
#proxy: http://127.0.0.1:3128
|
||||
|
|
|
|||
|
|
@ -344,6 +344,10 @@ id: 'aidx'
|
|||
#maxAltTextLength: 20000
|
||||
# Amount of characters that will be saved for remote media descriptions (alt text). Longer descriptions will be truncated to this length. (minimum: 1)
|
||||
#maxRemoteAltTextLength: 100000
|
||||
# Amount of characters that can be used when writing user bios. Longer descriptions will be truncated to this length. (minimum: 1)
|
||||
#maxBioLength: 1500
|
||||
# Amount of characters that will be saved for remote user bios. Longer descriptions will be truncated to this length. (minimum: 1)
|
||||
#maxRemoteBioLength: 15000
|
||||
|
||||
# Proxy for HTTP/HTTPS
|
||||
#proxy: http://127.0.0.1:3128
|
||||
|
|
|
|||
|
|
@ -0,0 +1,15 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: Lillychan and other Sharkey contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
export class UserDescriptionText1750541176036000 {
|
||||
name = 'UserDescriptionText1750541176036000'
|
||||
async up(queryRunner) {
|
||||
await queryRunner.query(`ALTER TABLE "user_profile" ALTER COLUMN "description" TYPE TEXT USING NULL`);
|
||||
}
|
||||
|
||||
async down(queryRunner) {
|
||||
await queryRunner.query(`ALTER TABLE "user_profile" ALTER COLUMN "description" TYPE character varying(2048)`);
|
||||
}
|
||||
}
|
||||
|
|
@ -96,6 +96,8 @@ type Source = {
|
|||
maxRemoteNoteLength?: number;
|
||||
maxAltTextLength?: number;
|
||||
maxRemoteAltTextLength?: number;
|
||||
maxBioLength?: number;
|
||||
maxRemoteBioLength?: number;
|
||||
|
||||
clusterLimit?: number;
|
||||
|
||||
|
|
@ -261,6 +263,8 @@ export type Config = {
|
|||
maxRemoteCwLength: number;
|
||||
maxAltTextLength: number;
|
||||
maxRemoteAltTextLength: number;
|
||||
maxBioLength: number;
|
||||
maxRemoteBioLength: number;
|
||||
clusterLimit: number | undefined;
|
||||
id: string;
|
||||
outgoingAddress: string | undefined;
|
||||
|
|
@ -461,6 +465,8 @@ export function loadConfig(): Config {
|
|||
maxRemoteCwLength: config.maxRemoteCwLength ?? 5000,
|
||||
maxAltTextLength: config.maxAltTextLength ?? 20000,
|
||||
maxRemoteAltTextLength: config.maxRemoteAltTextLength ?? 100000,
|
||||
maxBioLength: config.maxBioLength ?? 1500,
|
||||
maxRemoteBioLength: config.maxRemoteBioLength ?? 15000,
|
||||
clusterLimit: config.clusterLimit,
|
||||
outgoingAddress: config.outgoingAddress,
|
||||
outgoingAddressFamily: config.outgoingAddressFamily,
|
||||
|
|
@ -658,7 +664,7 @@ function applyEnvOverrides(config: Source) {
|
|||
_apply_top(['sentryForFrontend', 'browserTracingIntegration', 'routeLabel']);
|
||||
_apply_top([['clusterLimit', 'deliverJobConcurrency', 'inboxJobConcurrency', 'relashionshipJobConcurrency', 'deliverJobPerSec', 'inboxJobPerSec', 'relashionshipJobPerSec', 'deliverJobMaxAttempts', 'inboxJobMaxAttempts']]);
|
||||
_apply_top([['outgoingAddress', 'outgoingAddressFamily', 'proxy', 'proxySmtp', 'mediaDirectory', 'mediaProxy', 'proxyRemoteFiles', 'videoThumbnailGenerator']]);
|
||||
_apply_top([['maxFileSize', 'maxNoteLength', 'maxRemoteNoteLength', 'maxAltTextLength', 'maxRemoteAltTextLength', 'pidFile', 'filePermissionBits']]);
|
||||
_apply_top([['maxFileSize', 'maxNoteLength', 'maxRemoteNoteLength', 'maxAltTextLength', 'maxRemoteAltTextLength', 'maxBioLength', 'maxRemoteBioLength', 'pidFile', 'filePermissionBits']]);
|
||||
_apply_top(['import', ['downloadTimeout', 'maxFileSize']]);
|
||||
_apply_top([['signToActivityPubGet', 'checkActivityPubGetSignature', 'setupPassword', 'disallowExternalApRedirect']]);
|
||||
_apply_top(['logging', 'sql', ['disableQueryTruncation', 'enableQueryParamLogging']]);
|
||||
|
|
|
|||
|
|
@ -31,7 +31,6 @@ import type UsersChart from '@/core/chart/charts/users.js';
|
|||
import type InstanceChart from '@/core/chart/charts/instance.js';
|
||||
import type { HashtagService } from '@/core/HashtagService.js';
|
||||
import { MiUserNotePining } from '@/models/UserNotePining.js';
|
||||
import { StatusError } from '@/misc/status-error.js';
|
||||
import type { UtilityService } from '@/core/UtilityService.js';
|
||||
import type { UserEntityService } from '@/core/entities/UserEntityService.js';
|
||||
import { bindThis } from '@/decorators.js';
|
||||
|
|
@ -45,6 +44,7 @@ import { HttpRequestService } from '@/core/HttpRequestService.js';
|
|||
import { verifyFieldLinks } from '@/misc/verify-field-link.js';
|
||||
import { isRetryableError } from '@/misc/is-retryable-error.js';
|
||||
import { renderInlineError } from '@/misc/render-inline-error.js';
|
||||
import { IdentifiableError } from '@/misc/identifiable-error.js';
|
||||
import { getApId, getApType, isActor, isCollection, isCollectionOrOrderedCollection, isPropertyValue } from '../type.js';
|
||||
import { extractApHashtags } from './tag.js';
|
||||
import type { OnModuleInit } from '@nestjs/common';
|
||||
|
|
@ -55,10 +55,8 @@ import type { ApLoggerService } from '../ApLoggerService.js';
|
|||
|
||||
import type { ApImageService } from './ApImageService.js';
|
||||
import type { IActor, ICollection, IObject, IOrderedCollection } from '../type.js';
|
||||
import { IdentifiableError } from '@/misc/identifiable-error.js';
|
||||
|
||||
const nameLength = 128;
|
||||
const summaryLength = 2048;
|
||||
|
||||
type Field = Record<'name' | 'value', string>;
|
||||
|
||||
|
|
@ -220,7 +218,7 @@ export class ApPersonService implements OnModuleInit, OnApplicationShutdown {
|
|||
if (!(typeof x.summary === 'string' && x.summary.length > 0)) {
|
||||
throw new UnrecoverableError(`invalid Actor ${uri}: wrong summary`);
|
||||
}
|
||||
x.summary = truncate(x.summary, summaryLength);
|
||||
x.summary = truncate(x.summary, this.config.maxRemoteBioLength);
|
||||
}
|
||||
|
||||
const idHost = this.utilityService.punyHostPSLDomain(x.id);
|
||||
|
|
@ -458,9 +456,9 @@ export class ApPersonService implements OnModuleInit, OnApplicationShutdown {
|
|||
let _description: string | null = null;
|
||||
|
||||
if (person._misskey_summary) {
|
||||
_description = truncate(person._misskey_summary, summaryLength);
|
||||
_description = truncate(person._misskey_summary, this.config.maxRemoteBioLength);
|
||||
} else if (person.summary) {
|
||||
_description = this.apMfmService.htmlToMfm(truncate(person.summary, summaryLength), person.tag);
|
||||
_description = this.apMfmService.htmlToMfm(truncate(person.summary, this.config.maxRemoteBioLength), person.tag);
|
||||
}
|
||||
|
||||
await transactionalEntityManager.save(new MiUserProfile({
|
||||
|
|
@ -575,7 +573,6 @@ export class ApPersonService implements OnModuleInit, OnApplicationShutdown {
|
|||
if (exist === null) return;
|
||||
//#endregion
|
||||
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
if (resolver == null) resolver = this.apResolverService.createResolver();
|
||||
|
||||
const object = hint ?? await resolver.resolve(uri);
|
||||
|
|
@ -717,9 +714,9 @@ export class ApPersonService implements OnModuleInit, OnApplicationShutdown {
|
|||
let _description: string | null = null;
|
||||
|
||||
if (person._misskey_summary) {
|
||||
_description = truncate(person._misskey_summary, summaryLength);
|
||||
_description = truncate(person._misskey_summary, this.config.maxRemoteBioLength);
|
||||
} else if (person.summary) {
|
||||
_description = this.apMfmService.htmlToMfm(truncate(person.summary, summaryLength), person.tag);
|
||||
_description = this.apMfmService.htmlToMfm(truncate(person.summary, this.config.maxRemoteBioLength), person.tag);
|
||||
}
|
||||
|
||||
await this.userProfilesRepository.update({ userId: exist.id }, {
|
||||
|
|
|
|||
|
|
@ -117,6 +117,8 @@ export class MetaEntityService {
|
|||
maxRemoteCwLength: this.config.maxRemoteCwLength,
|
||||
maxAltTextLength: this.config.maxAltTextLength,
|
||||
maxRemoteAltTextLength: this.config.maxRemoteAltTextLength,
|
||||
maxBioLength: this.config.maxBioLength,
|
||||
maxRemoteBioLength: this.config.maxRemoteBioLength,
|
||||
defaultLightTheme,
|
||||
defaultDarkTheme,
|
||||
defaultLike: instance.defaultLike,
|
||||
|
|
|
|||
|
|
@ -433,7 +433,7 @@ export type MiPartialRemoteUser = Partial<MiUser> & {
|
|||
export const localUsernameSchema = { type: 'string', pattern: /^\w{1,20}$/.toString().slice(1, -1) } as const;
|
||||
export const passwordSchema = { type: 'string', minLength: 1 } as const;
|
||||
export const nameSchema = { type: 'string', minLength: 1, maxLength: 50 } as const;
|
||||
export const descriptionSchema = { type: 'string', minLength: 1, maxLength: 1500 } as const;
|
||||
export const descriptionSchema = { type: 'string', minLength: 1 } as const;
|
||||
export const followedMessageSchema = { type: 'string', minLength: 1, maxLength: 256 } as const;
|
||||
export const locationSchema = { type: 'string', minLength: 1, maxLength: 50 } as const;
|
||||
export const listenbrainzSchema = { type: 'string', minLength: 1, maxLength: 128 } as const;
|
||||
|
|
|
|||
|
|
@ -43,8 +43,8 @@ export class MiUserProfile {
|
|||
})
|
||||
public listenbrainz: string | null;
|
||||
|
||||
@Column('varchar', {
|
||||
length: 2048, nullable: true,
|
||||
@Column('text', {
|
||||
nullable: true,
|
||||
comment: 'The description (bio) of the User.',
|
||||
})
|
||||
public description: string | null;
|
||||
|
|
|
|||
|
|
@ -206,6 +206,14 @@ export const packedMetaLiteSchema = {
|
|||
type: 'number',
|
||||
optional: false, nullable: false,
|
||||
},
|
||||
maxBioLength: {
|
||||
type: 'number',
|
||||
optional: false, nullable: false,
|
||||
},
|
||||
maxRemoteBioLength: {
|
||||
type: 'number',
|
||||
optional: false, nullable: false,
|
||||
},
|
||||
ads: {
|
||||
type: 'array',
|
||||
optional: false, nullable: false,
|
||||
|
|
|
|||
|
|
@ -128,6 +128,8 @@ export class NodeinfoServerService {
|
|||
maxRemoteCwLength: this.config.maxRemoteCwLength,
|
||||
maxAltTextLength: this.config.maxAltTextLength,
|
||||
maxRemoteAltTextLength: this.config.maxRemoteAltTextLength,
|
||||
maxBioLength: this.config.maxBioLength,
|
||||
maxRemoteBioLength: this.config.maxRemoteBioLength,
|
||||
enableEmail: meta.enableEmail,
|
||||
enableServiceWorker: meta.enableServiceWorker,
|
||||
proxyAccountName: proxyAccount.username,
|
||||
|
|
|
|||
|
|
@ -141,6 +141,13 @@ export const meta = {
|
|||
code: 'MAX_CW_LENGTH',
|
||||
id: '7004c478-bda3-4b4f-acb2-4316398c9d52',
|
||||
},
|
||||
|
||||
maxBioLength: {
|
||||
message: 'You tried setting a bio which is too long.',
|
||||
code: 'MAX_BIO_LENGTH',
|
||||
id: 'f3bb3543-8bd1-4e6d-9375-55efaf2b4102',
|
||||
httpStatusCode: 422,
|
||||
},
|
||||
},
|
||||
|
||||
res: {
|
||||
|
|
@ -329,7 +336,12 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
|||
updates.name = trimmedName === '' ? null : trimmedName;
|
||||
}
|
||||
}
|
||||
if (ps.description !== undefined) profileUpdates.description = ps.description;
|
||||
if (ps.description !== undefined) {
|
||||
if (ps.description && ps.description.length > this.config.maxBioLength) {
|
||||
throw new ApiError(meta.errors.maxBioLength);
|
||||
}
|
||||
profileUpdates.description = ps.description;
|
||||
};
|
||||
if (ps.followedMessage !== undefined) profileUpdates.followedMessage = ps.followedMessage;
|
||||
if (ps.lang !== undefined) profileUpdates.lang = ps.lang;
|
||||
if (ps.location !== undefined) profileUpdates.location = ps.location;
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ process.env.NODE_ENV = 'test';
|
|||
|
||||
import * as assert from 'assert';
|
||||
import { inspect } from 'node:util';
|
||||
import { api, post, role, signup, successfulApiCall, uploadFile } from '../utils.js';
|
||||
import { api, failedApiCall, post, role, signup, successfulApiCall, uploadFile } from '../utils.js';
|
||||
import type * as misskey from 'misskey-js';
|
||||
import { DEFAULT_POLICIES } from '@/core/RoleService.js';
|
||||
|
||||
|
|
@ -920,6 +920,12 @@ describe('ユーザー', () => {
|
|||
|
||||
//#endregion
|
||||
|
||||
test('user with to long bio', async () => {
|
||||
await failedApiCall({ endpoint: 'i/update', user: alice, parameters: {
|
||||
description: 'x'.repeat(10000),
|
||||
} }, { status: 422, code: 'MAX_BIO_LENGTH', id: 'f3bb3543-8bd1-4e6d-9375-55efaf2b4102' });
|
||||
});
|
||||
|
||||
test.todo('を管理人として確認することができる(admin/show-user)');
|
||||
test.todo('を管理人として確認することができる(admin/show-users)');
|
||||
test.todo('をサーバー向けに取得することができる(federation/users)');
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue