summaryrefslogtreecommitdiff
path: root/packages/backend
diff options
context:
space:
mode:
authorsyuilo <4439005+syuilo@users.noreply.github.com>2025-05-09 17:44:35 +0900
committerGitHub <noreply@github.com>2025-05-09 17:44:35 +0900
commitebf291084f9490f0345a488592e2b4cd2ffb9d91 (patch)
tree243df153ec60c16e495f344e9643bf0fc7668230 /packages/backend
parentFeat: No websocket mode (#15851) (diff)
downloadmisskey-ebf291084f9490f0345a488592e2b4cd2ffb9d91.tar.gz
misskey-ebf291084f9490f0345a488592e2b4cd2ffb9d91.tar.bz2
misskey-ebf291084f9490f0345a488592e2b4cd2ffb9d91.zip
Feat: UGCの公開範囲設定機能 (#15938)
* wip * Update CHANGELOG.md * wip * wip * Update show.ts
Diffstat (limited to 'packages/backend')
-rw-r--r--packages/backend/migration/1746330901644-visibleUserGeneratedContentsForNonLoggedInVisitors.js16
-rw-r--r--packages/backend/src/models/Meta.ts6
-rw-r--r--packages/backend/src/server/api/endpoints/admin/meta.ts6
-rw-r--r--packages/backend/src/server/api/endpoints/admin/update-meta.ts8
-rw-r--r--packages/backend/src/server/api/endpoints/notes/show.ts15
-rw-r--r--packages/backend/src/server/api/endpoints/users/show.ts17
-rw-r--r--packages/backend/src/server/web/ClientServerService.ts15
7 files changed, 79 insertions, 4 deletions
diff --git a/packages/backend/migration/1746330901644-visibleUserGeneratedContentsForNonLoggedInVisitors.js b/packages/backend/migration/1746330901644-visibleUserGeneratedContentsForNonLoggedInVisitors.js
new file mode 100644
index 0000000000..115698a420
--- /dev/null
+++ b/packages/backend/migration/1746330901644-visibleUserGeneratedContentsForNonLoggedInVisitors.js
@@ -0,0 +1,16 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+export class VisibleUserGeneratedContentsForNonLoggedInVisitors1746330901644 {
+ name = 'VisibleUserGeneratedContentsForNonLoggedInVisitors1746330901644'
+
+ async up(queryRunner) {
+ await queryRunner.query(`ALTER TABLE "meta" ADD "ugcVisibilityForVisitor" character varying(128) NOT NULL DEFAULT 'local'`);
+ }
+
+ async down(queryRunner) {
+ await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "ugcVisibilityForVisitor"`);
+ }
+}
diff --git a/packages/backend/src/models/Meta.ts b/packages/backend/src/models/Meta.ts
index 46f3b2e3c0..95d19c8075 100644
--- a/packages/backend/src/models/Meta.ts
+++ b/packages/backend/src/models/Meta.ts
@@ -660,6 +660,12 @@ export class MiMeta {
public federationHosts: string[];
@Column('varchar', {
+ length: 128,
+ default: 'local',
+ })
+ public ugcVisibilityForVisitor: 'all' | 'local' | 'none';
+
+ @Column('varchar', {
length: 64,
nullable: true,
})
diff --git a/packages/backend/src/server/api/endpoints/admin/meta.ts b/packages/backend/src/server/api/endpoints/admin/meta.ts
index 4a106e7175..cb48a1bc95 100644
--- a/packages/backend/src/server/api/endpoints/admin/meta.ts
+++ b/packages/backend/src/server/api/endpoints/admin/meta.ts
@@ -546,6 +546,11 @@ export const meta = {
},
},
},
+ ugcVisibilityForVisitor: {
+ type: 'string',
+ enum: ['all', 'local', 'none'],
+ optional: false, nullable: false,
+ },
},
},
} as const;
@@ -691,6 +696,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
federation: instance.federation,
federationHosts: instance.federationHosts,
deliverSuspendedSoftware: instance.deliverSuspendedSoftware,
+ ugcVisibilityForVisitor: instance.ugcVisibilityForVisitor,
};
});
}
diff --git a/packages/backend/src/server/api/endpoints/admin/update-meta.ts b/packages/backend/src/server/api/endpoints/admin/update-meta.ts
index 31eeaa5e38..fc6b890b69 100644
--- a/packages/backend/src/server/api/endpoints/admin/update-meta.ts
+++ b/packages/backend/src/server/api/endpoints/admin/update-meta.ts
@@ -196,6 +196,10 @@ export const paramDef = {
required: ['software', 'versionRange'],
},
},
+ ugcVisibilityForVisitor: {
+ type: 'string',
+ enum: ['all', 'local', 'none'],
+ },
},
required: [],
} as const;
@@ -690,6 +694,10 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
set.federationHosts = ps.federationHosts.filter(Boolean).map(x => x.toLowerCase());
}
+ if (ps.ugcVisibilityForVisitor !== undefined) {
+ set.ugcVisibilityForVisitor = ps.ugcVisibilityForVisitor;
+ }
+
const before = await this.metaService.fetch(true);
await this.metaService.update(set);
diff --git a/packages/backend/src/server/api/endpoints/notes/show.ts b/packages/backend/src/server/api/endpoints/notes/show.ts
index 11839bce36..b93c73b0c5 100644
--- a/packages/backend/src/server/api/endpoints/notes/show.ts
+++ b/packages/backend/src/server/api/endpoints/notes/show.ts
@@ -3,10 +3,12 @@
* SPDX-License-Identifier: AGPL-3.0-only
*/
-import { Injectable } from '@nestjs/common';
+import { Inject, Injectable } from '@nestjs/common';
import { Endpoint } from '@/server/api/endpoint-base.js';
import { NoteEntityService } from '@/core/entities/NoteEntityService.js';
import { GetterService } from '@/server/api/GetterService.js';
+import { DI } from '@/di-symbols.js';
+import { MiMeta } from '@/models/Meta.js';
import { ApiError } from '../../error.js';
export const meta = {
@@ -46,6 +48,9 @@ export const paramDef = {
@Injectable()
export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export
constructor(
+ @Inject(DI.meta)
+ private serverSettings: MiMeta,
+
private noteEntityService: NoteEntityService,
private getterService: GetterService,
) {
@@ -59,6 +64,14 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
throw new ApiError(meta.errors.signinRequired);
}
+ if (this.serverSettings.ugcVisibilityForVisitor === 'none' && me == null) {
+ throw new ApiError(meta.errors.signinRequired);
+ }
+
+ if (this.serverSettings.ugcVisibilityForVisitor === 'local' && note.userHost != null && me == null) {
+ throw new ApiError(meta.errors.signinRequired);
+ }
+
return await this.noteEntityService.pack(note, me, {
detail: true,
});
diff --git a/packages/backend/src/server/api/endpoints/users/show.ts b/packages/backend/src/server/api/endpoints/users/show.ts
index 062326e28d..431869d47f 100644
--- a/packages/backend/src/server/api/endpoints/users/show.ts
+++ b/packages/backend/src/server/api/endpoints/users/show.ts
@@ -5,7 +5,7 @@
import { In, IsNull } from 'typeorm';
import { Inject, Injectable } from '@nestjs/common';
-import type { UsersRepository } from '@/models/_.js';
+import type { MiMeta, UsersRepository } from '@/models/_.js';
import type { MiUser } from '@/models/User.js';
import { Endpoint } from '@/server/api/endpoint-base.js';
import { UserEntityService } from '@/core/entities/UserEntityService.js';
@@ -82,6 +82,9 @@ export const paramDef = {
@Injectable()
export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export
constructor(
+ @Inject(DI.meta)
+ private serverSettings: MiMeta,
+
@Inject(DI.usersRepository)
private usersRepository: UsersRepository,
@@ -92,6 +95,10 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
private apiLoggerService: ApiLoggerService,
) {
super(meta, paramDef, async (ps, me, _1, _2, _3, ip) => {
+ if (this.serverSettings.ugcVisibilityForVisitor === 'none' && me == null) {
+ throw new ApiError(meta.errors.noSuchUser);
+ }
+
let user;
const isModerator = await this.roleService.isModerator(me);
@@ -123,6 +130,10 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
} else {
// Lookup user
if (typeof ps.host === 'string' && typeof ps.username === 'string') {
+ if (this.serverSettings.ugcVisibilityForVisitor === 'local' && me == null) {
+ throw new ApiError(meta.errors.noSuchUser);
+ }
+
user = await this.remoteUserResolveService.resolveUser(ps.username, ps.host).catch(err => {
this.apiLoggerService.logger.warn(`failed to resolve remote user: ${err}`);
throw new ApiError(meta.errors.failedToResolveRemoteUser);
@@ -139,6 +150,10 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
throw new ApiError(meta.errors.noSuchUser);
}
+ if (this.serverSettings.ugcVisibilityForVisitor === 'local' && user.host != null && me == null) {
+ throw new ApiError(meta.errors.noSuchUser);
+ }
+
if (user.host == null) {
if (me == null && ip != null) {
this.perUserPvChart.commitByVisitor(user, ip);
diff --git a/packages/backend/src/server/web/ClientServerService.ts b/packages/backend/src/server/web/ClientServerService.ts
index 30a911088e..9a33d27d86 100644
--- a/packages/backend/src/server/web/ClientServerService.ts
+++ b/packages/backend/src/server/web/ClientServerService.ts
@@ -513,7 +513,12 @@ export class ClientServerService {
vary(reply.raw, 'Accept');
- if (user != null) {
+ if (
+ user != null && (
+ this.meta.ugcVisibilityForVisitor === 'all' ||
+ (this.meta.ugcVisibilityForVisitor === 'local' && user.host == null)
+ )
+ ) {
const profile = await this.userProfilesRepository.findOneByOrFail({ userId: user.id });
const me = profile.fields
? profile.fields
@@ -577,7 +582,13 @@ export class ClientServerService {
relations: ['user'],
});
- if (note && !note.user!.requireSigninToViewContents) {
+ if (
+ note &&
+ !note.user!.requireSigninToViewContents &&
+ (this.meta.ugcVisibilityForVisitor === 'all' ||
+ (this.meta.ugcVisibilityForVisitor === 'local' && note.userHost == null)
+ )
+ ) {
const _note = await this.noteEntityService.pack(note);
const profile = await this.userProfilesRepository.findOneByOrFail({ userId: note.userId });
reply.header('Cache-Control', 'public, max-age=15');