1
1
mirror of https://github.com/kokonect-link/cherrypick synced 2025-01-09 03:13:57 +09:00

Merge branch 'develop' into feat-13070

This commit is contained in:
かっこかり 2024-02-02 13:03:36 +09:00 committed by GitHub
commit c56abec648
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
16 changed files with 109 additions and 105 deletions

View File

@ -1,66 +1,23 @@
name: Deploy test environment name: deploy-test-environment
on: on:
push: #push:
workflow_dispatch: workflow_dispatch:
inputs: inputs:
repository: repository:
description: 'Repository to deploy (optional)' description: 'Repository to deploy (optional)'
type: string
required: false required: false
branch: branch_or_hash:
description: 'Branch to deploy (optional)' description: 'Branch or Commit hash to deploy (optional)'
type: string
required: false required: false
jobs: jobs:
deploy-test-environment: deploy-test-environment:
runs-on: ubuntu-latest uses: joinmisskey/misskey-tga/.github/workflows/deploy-test-environment.yml@main
steps: with:
- name: Set environment variable (for tput command & pnpm) repository: ${{ github.event.inputs.repository }}
run: | branch_or_hash: ${{ github.event.inputs.branch_or_hash }}
echo "TERM=xterm" >> $GITHUB_ENV secrets:
REPOSITORY=${{ github.event.inputs.repository || github.repository }} DISCORD_WEBHOOK_URL: ${{ secrets.DISCORD_WEBHOOK_URL }}
echo "REPOSITORY=$REPOSITORY" >> $GITHUB_ENV
BRANCH=${{ github.event.inputs.branch || github.ref_name }}
echo "BRANCH=$BRANCH" >> $GITHUB_ENV
- name: Checkout
uses: actions/checkout@v4
with:
repository: ${{ env.REPOSITORY }}
ref: ${{ env.BRANCH }}
- name: Get the latest commit SHA
run: |
SHA=$(git log -1 --format="%H")
echo "SHA=$SHA" >> $GITHUB_ENV
- name: Start cloudflare tunnel (quick)
run: |
wget https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-amd64.deb
sudo dpkg -i cloudflared-linux-amd64.deb
sudo cloudflared tunnel --metrics localhost:55555 --url localhost:3000 > /dev/null 2>&1 &
sleep 15
TUNNEL_RESPONSE=$(curl http://localhost:55555/quicktunnel)
TUNNEL_DOMAIN=$(echo $TUNNEL_RESPONSE | grep -o '"hostname":"[^"]*' | grep -o '[^"]*$')
echo "::add-mask::$TUNNEL_DOMAIN"
echo "TUNNEL_DOMAIN=$TUNNEL_DOMAIN" >> $GITHUB_ENV
- name: Install misskey
run: |
wget https://raw.githubusercontent.com/joinmisskey/bash-install/v4/misskey-install.sh
wget https://raw.githubusercontent.com/joinmisskey/bash-install/v4/testenv_githubactions.txt
sed -i "s/host=127.0.0.1/host=$TUNNEL_DOMAIN/g" testenv_githubactions.txt
sed -i "s|git_repository=https://github.com/misskey-dev/misskey|git_repository=https://github.com/$REPOSITORY|g" testenv_githubactions.txt
sed -i "s|git_branch=master|git_branch=$BRANCH|g" testenv_githubactions.txt
sudo chmod 555 ./misskey-install.sh
sudo bash -x ./misskey-install.sh -c ./testenv_githubactions.txt
- name: Post tunnel info to Discord
run: |
CURRENT_TIME=$(TZ=Asia/Tokyo date +'%Y-%m-%d %H:%M:%S JST')
COMMIT_URL="https://github.com/$REPOSITORY/commit/$SHA"
curl -X POST -H "Content-Type: application/json" -d "{\"content\": \"==============================\nURL: https://$TUNNEL_DOMAIN\nRepository: $REPOSITORY\nBranch: $BRANCH\nCommit: $COMMIT_URL\nTime: $CURRENT_TIME\n==============================\"}" ${{ secrets.DISCORD_WEBHOOK_URL }}
- name: Wait
run: |
timeout 3600 tail -f /var/log/syslog || true

View File

@ -23,7 +23,7 @@ jobs:
private_key: ${{ secrets.DEPLOYBOT_PRIVATE_KEY }} private_key: ${{ secrets.DEPLOYBOT_PRIVATE_KEY }}
- name: Slash Command Dispatch - name: Slash Command Dispatch
uses: peter-evans/slash-command-dispatch@v3 uses: peter-evans/slash-command-dispatch@v4
env: env:
TOKEN: ${{ steps.generate_token.outputs.token }} TOKEN: ${{ steps.generate_token.outputs.token }}
with: with:

View File

@ -61,7 +61,7 @@ jobs:
- name: Test - name: Test
run: pnpm --filter backend test-and-coverage run: pnpm --filter backend test-and-coverage
- name: Upload to Codecov - name: Upload to Codecov
uses: codecov/codecov-action@v3 uses: codecov/codecov-action@v4
with: with:
token: ${{ secrets.CODECOV_TOKEN }} token: ${{ secrets.CODECOV_TOKEN }}
files: ./packages/backend/coverage/coverage-final.json files: ./packages/backend/coverage/coverage-final.json
@ -111,7 +111,7 @@ jobs:
- name: Test - name: Test
run: pnpm --filter backend test-and-coverage:e2e run: pnpm --filter backend test-and-coverage:e2e
- name: Upload to Codecov - name: Upload to Codecov
uses: codecov/codecov-action@v3 uses: codecov/codecov-action@v4
with: with:
token: ${{ secrets.CODECOV_TOKEN }} token: ${{ secrets.CODECOV_TOKEN }}
files: ./packages/backend/coverage/coverage-final.json files: ./packages/backend/coverage/coverage-final.json

View File

@ -53,7 +53,7 @@ jobs:
- name: Test - name: Test
run: pnpm --filter frontend test-and-coverage run: pnpm --filter frontend test-and-coverage
- name: Upload Coverage - name: Upload Coverage
uses: codecov/codecov-action@v3 uses: codecov/codecov-action@v4
with: with:
token: ${{ secrets.CODECOV_TOKEN }} token: ${{ secrets.CODECOV_TOKEN }}
files: ./packages/frontend/coverage/coverage-final.json files: ./packages/frontend/coverage/coverage-final.json

View File

@ -50,7 +50,7 @@ jobs:
CI: true CI: true
- name: Upload Coverage - name: Upload Coverage
uses: codecov/codecov-action@v3 uses: codecov/codecov-action@v4
with: with:
token: ${{ secrets.CODECOV_TOKEN }} token: ${{ secrets.CODECOV_TOKEN }}
files: ./packages/misskey-js/coverage/coverage-final.json files: ./packages/misskey-js/coverage/coverage-final.json

View File

@ -21,6 +21,9 @@
- Feat: [mCaptcha](https://github.com/mCaptcha/mCaptcha)のサポートを追加 - Feat: [mCaptcha](https://github.com/mCaptcha/mCaptcha)のサポートを追加
- Fix: リストライムラインの「リノートを表示」が正しく機能しない問題を修正 - Fix: リストライムラインの「リノートを表示」が正しく機能しない問題を修正
- Feat: Add support for TrueMail - Feat: Add support for TrueMail
- Fix: リモートユーザーのリアクション一覧がすべて見えてしまうのを修正
* すべてのリモートユーザーのリアクション一覧を見えないようにします
- Enhance: モデレーターはすべてのユーザーのリアクション一覧を見られるように
### Client ### Client
- Feat: 新しいゲームを追加 - Feat: 新しいゲームを追加
@ -71,6 +74,7 @@
- Fix: ipv4とipv6の両方が利用可能な環境でallowedPrivateNetworksが設定されていた場合プライベートipの検証ができていなかった問題を修正 - Fix: ipv4とipv6の両方が利用可能な環境でallowedPrivateNetworksが設定されていた場合プライベートipの検証ができていなかった問題を修正
- Fix: properly handle cc followers - Fix: properly handle cc followers
- Fix: ジョブに関する設定の名前を修正 relashionshipJobPerSec -> relationshipJobPerSec - Fix: ジョブに関する設定の名前を修正 relashionshipJobPerSec -> relationshipJobPerSec
- Fix: コントロールパネル->モデレーション->「誰でも新規登録できるようにする」の初期値をONからOFFに変更 #13122
### Service Worker ### Service Worker
- Enhance: オフライン表示のデザインを改善・多言語対応 - Enhance: オフライン表示のデザインを改善・多言語対応

View File

@ -3,6 +3,6 @@ import { genOpenapiSpec } from './built/server/api/openapi/gen-spec.js'
import { writeFileSync } from "node:fs"; import { writeFileSync } from "node:fs";
const config = loadConfig(); const config = loadConfig();
const spec = genOpenapiSpec(config); const spec = genOpenapiSpec(config, true);
writeFileSync('./built/api.json', JSON.stringify(spec), 'utf-8'); writeFileSync('./built/api.json', JSON.stringify(spec), 'utf-8');

View File

@ -0,0 +1,16 @@
/*
* SPDX-FileCopyrightText: syuilo and other misskey contributors
* SPDX-License-Identifier: AGPL-3.0-only
*/
export class FixMetaDisableRegistration1706791962000 {
name = 'FixMetaDisableRegistration1706791962000'
async up(queryRunner) {
await queryRunner.query(`alter table meta alter column "disableRegistration" set default true;`);
}
async down(queryRunner) {
await queryRunner.query(`alter table meta alter column "disableRegistration" set default false;`);
}
}

View File

@ -409,7 +409,7 @@ export class UserEntityService implements OnModuleInit {
}), }),
pinnedPageId: profile!.pinnedPageId, pinnedPageId: profile!.pinnedPageId,
pinnedPage: profile!.pinnedPageId ? this.pageEntityService.pack(profile!.pinnedPageId, me) : null, pinnedPage: profile!.pinnedPageId ? this.pageEntityService.pack(profile!.pinnedPageId, me) : null,
publicReactions: profile!.publicReactions, publicReactions: this.isLocalUser(user) ? profile!.publicReactions : false, // https://github.com/misskey-dev/misskey/issues/12964
followersVisibility: profile!.followersVisibility, followersVisibility: profile!.followersVisibility,
followingVisibility: profile!.followingVisibility, followingVisibility: profile!.followingVisibility,
twoFactorEnabled: profile!.twoFactorEnabled, twoFactorEnabled: profile!.twoFactorEnabled,

View File

@ -119,6 +119,7 @@ export interface Schema extends OfSchema {
readonly example?: any; readonly example?: any;
readonly format?: string; readonly format?: string;
readonly ref?: keyof typeof refs; readonly ref?: keyof typeof refs;
readonly selfRef?: boolean;
readonly enum?: ReadonlyArray<string | null>; readonly enum?: ReadonlyArray<string | null>;
readonly default?: (this['type'] extends TypeStringef ? StringDefToType<this['type']> : any) | null; readonly default?: (this['type'] extends TypeStringef ? StringDefToType<this['type']> : any) | null;
readonly maxLength?: number; readonly maxLength?: number;

View File

@ -53,6 +53,7 @@ const sectionBlockSchema = {
type: 'object', type: 'object',
optional: false, nullable: false, optional: false, nullable: false,
ref: 'PageBlock', ref: 'PageBlock',
selfRef: true,
}, },
}, },
}, },

View File

@ -9,6 +9,9 @@ import { Endpoint } from '@/server/api/endpoint-base.js';
import { QueryService } from '@/core/QueryService.js'; import { QueryService } from '@/core/QueryService.js';
import { NoteReactionEntityService } from '@/core/entities/NoteReactionEntityService.js'; import { NoteReactionEntityService } from '@/core/entities/NoteReactionEntityService.js';
import { DI } from '@/di-symbols.js'; import { DI } from '@/di-symbols.js';
import { CacheService } from '@/core/CacheService.js';
import { UserEntityService } from '@/core/entities/UserEntityService.js';
import { RoleService } from '@/core/RoleService.js';
import { ApiError } from '../../error.js'; import { ApiError } from '../../error.js';
export const meta = { export const meta = {
@ -34,6 +37,11 @@ export const meta = {
code: 'REACTIONS_NOT_PUBLIC', code: 'REACTIONS_NOT_PUBLIC',
id: '673a7dd2-6924-1093-e0c0-e68456ceae5c', id: '673a7dd2-6924-1093-e0c0-e68456ceae5c',
}, },
isRemoteUser: {
message: 'Currently unavailable to display reactions of remote users. See https://github.com/misskey-dev/misskey/issues/12964',
code: 'IS_REMOTE_USER',
id: '6b95fa98-8cf9-2350-e284-f0ffdb54a805',
},
}, },
} as const; } as const;
@ -59,14 +67,24 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
@Inject(DI.noteReactionsRepository) @Inject(DI.noteReactionsRepository)
private noteReactionsRepository: NoteReactionsRepository, private noteReactionsRepository: NoteReactionsRepository,
private cacheService: CacheService,
private userEntityService: UserEntityService,
private noteReactionEntityService: NoteReactionEntityService, private noteReactionEntityService: NoteReactionEntityService,
private queryService: QueryService, private queryService: QueryService,
private roleService: RoleService,
) { ) {
super(meta, paramDef, async (ps, me) => { super(meta, paramDef, async (ps, me) => {
const profile = await this.userProfilesRepository.findOneByOrFail({ userId: ps.userId }); const iAmModerator = me ? await this.roleService.isModerator(me) : false; // Moderators can see reactions of all users
if (!iAmModerator) {
const user = await this.cacheService.findUserById(ps.userId);
if (this.userEntityService.isRemoteUser(user)) {
throw new ApiError(meta.errors.isRemoteUser);
}
if ((me == null || me.id !== ps.userId) && !profile.publicReactions) { const profile = await this.userProfilesRepository.findOneByOrFail({ userId: ps.userId });
throw new ApiError(meta.errors.reactionsNotPublic); if ((me == null || me.id !== ps.userId) && !profile.publicReactions) {
throw new ApiError(meta.errors.reactionsNotPublic);
}
} }
const query = this.queryService.makePaginationQuery(this.noteReactionsRepository.createQueryBuilder('reaction'), const query = this.queryService.makePaginationQuery(this.noteReactionsRepository.createQueryBuilder('reaction'),

View File

@ -6,9 +6,9 @@
import type { Config } from '@/config.js'; import type { Config } from '@/config.js';
import endpoints, { IEndpoint } from '../endpoints.js'; import endpoints, { IEndpoint } from '../endpoints.js';
import { errors as basicErrors } from './errors.js'; import { errors as basicErrors } from './errors.js';
import { schemas, convertSchemaToOpenApiSchema } from './schemas.js'; import { getSchemas, convertSchemaToOpenApiSchema } from './schemas.js';
export function genOpenapiSpec(config: Config) { export function genOpenapiSpec(config: Config, includeSelfRef = false) {
const spec = { const spec = {
openapi: '3.1.0', openapi: '3.1.0',
@ -30,7 +30,7 @@ export function genOpenapiSpec(config: Config) {
paths: {} as any, paths: {} as any,
components: { components: {
schemas: schemas, schemas: getSchemas(includeSelfRef),
securitySchemes: { securitySchemes: {
bearerAuth: { bearerAuth: {
@ -56,7 +56,7 @@ export function genOpenapiSpec(config: Config) {
} }
} }
const resSchema = endpoint.meta.res ? convertSchemaToOpenApiSchema(endpoint.meta.res, 'res') : {}; const resSchema = endpoint.meta.res ? convertSchemaToOpenApiSchema(endpoint.meta.res, 'res', includeSelfRef) : {};
let desc = (endpoint.meta.description ? endpoint.meta.description : 'No description provided.') + '\n\n'; let desc = (endpoint.meta.description ? endpoint.meta.description : 'No description provided.') + '\n\n';
@ -71,7 +71,7 @@ export function genOpenapiSpec(config: Config) {
} }
const requestType = endpoint.meta.requireFile ? 'multipart/form-data' : 'application/json'; const requestType = endpoint.meta.requireFile ? 'multipart/form-data' : 'application/json';
const schema = { ...convertSchemaToOpenApiSchema(endpoint.params, 'param') }; const schema = { ...convertSchemaToOpenApiSchema(endpoint.params, 'param', false) };
if (endpoint.meta.requireFile) { if (endpoint.meta.requireFile) {
schema.properties = { schema.properties = {

View File

@ -6,10 +6,10 @@
import type { Schema } from '@/misc/json-schema.js'; import type { Schema } from '@/misc/json-schema.js';
import { refs } from '@/misc/json-schema.js'; import { refs } from '@/misc/json-schema.js';
export function convertSchemaToOpenApiSchema(schema: Schema, type: 'param' | 'res') { export function convertSchemaToOpenApiSchema(schema: Schema, type: 'param' | 'res', includeSelfRef: boolean): any {
// optional, nullable, refはスキーマ定義に含まれないので分離しておく // optional, nullable, refはスキーマ定義に含まれないので分離しておく
// eslint-disable-next-line @typescript-eslint/no-unused-vars // eslint-disable-next-line @typescript-eslint/no-unused-vars
const { optional, nullable, ref, ...res }: any = schema; const { optional, nullable, ref, selfRef, ...res }: any = schema;
if (schema.type === 'object' && schema.properties) { if (schema.type === 'object' && schema.properties) {
if (type === 'res') { if (type === 'res') {
@ -21,20 +21,20 @@ export function convertSchemaToOpenApiSchema(schema: Schema, type: 'param' | 're
} }
for (const k of Object.keys(schema.properties)) { for (const k of Object.keys(schema.properties)) {
res.properties[k] = convertSchemaToOpenApiSchema(schema.properties[k], type); res.properties[k] = convertSchemaToOpenApiSchema(schema.properties[k], type, includeSelfRef);
} }
} }
if (schema.type === 'array' && schema.items) { if (schema.type === 'array' && schema.items) {
res.items = convertSchemaToOpenApiSchema(schema.items, type); res.items = convertSchemaToOpenApiSchema(schema.items, type, includeSelfRef);
} }
for (const o of ['anyOf', 'oneOf', 'allOf'] as const) { for (const o of ['anyOf', 'oneOf', 'allOf'] as const) {
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
if (o in schema) res[o] = schema[o]!.map(schema => convertSchemaToOpenApiSchema(schema, type)); if (o in schema) res[o] = schema[o]!.map(schema => convertSchemaToOpenApiSchema(schema, type, includeSelfRef));
} }
if (type === 'res' && schema.ref) { if (type === 'res' && schema.ref && (!schema.selfRef || includeSelfRef)) {
const $ref = `#/components/schemas/${schema.ref}`; const $ref = `#/components/schemas/${schema.ref}`;
if (schema.nullable || schema.optional) { if (schema.nullable || schema.optional) {
res.allOf = [{ $ref }]; res.allOf = [{ $ref }];
@ -54,35 +54,37 @@ export function convertSchemaToOpenApiSchema(schema: Schema, type: 'param' | 're
return res; return res;
} }
export const schemas = { export function getSchemas(includeSelfRef: boolean) {
Error: { return {
type: 'object', Error: {
properties: { type: 'object',
error: { properties: {
type: 'object', error: {
description: 'An error object.', type: 'object',
properties: { description: 'An error object.',
code: { properties: {
type: 'string', code: {
description: 'An error code. Unique within the endpoint.', type: 'string',
}, description: 'An error code. Unique within the endpoint.',
message: { },
type: 'string', message: {
description: 'An error message.', type: 'string',
}, description: 'An error message.',
id: { },
type: 'string', id: {
format: 'uuid', type: 'string',
description: 'An error ID. This ID is static.', format: 'uuid',
description: 'An error ID. This ID is static.',
},
}, },
required: ['code', 'id', 'message'],
}, },
required: ['code', 'id', 'message'],
}, },
required: ['error'],
}, },
required: ['error'],
},
...Object.fromEntries( ...Object.fromEntries(
Object.entries(refs).map(([key, schema]) => [key, convertSchemaToOpenApiSchema(schema, 'res')]), Object.entries(refs).map(([key, schema]) => [key, convertSchemaToOpenApiSchema(schema, 'res', includeSelfRef)]),
), ),
}; };
}

View File

@ -263,7 +263,12 @@ const translation = ref<Misskey.entities.NotesTranslateResponse | null>(null);
const translating = ref(false); const translating = ref(false);
const showTicker = (defaultStore.state.instanceTicker === 'always') || (defaultStore.state.instanceTicker === 'remote' && appearNote.value.user.instance); const showTicker = (defaultStore.state.instanceTicker === 'always') || (defaultStore.state.instanceTicker === 'remote' && appearNote.value.user.instance);
const canRenote = computed(() => ['public', 'home'].includes(appearNote.value.visibility) || (appearNote.value.visibility === 'followers' && appearNote.value.userId === $i?.id)); const canRenote = computed(() => ['public', 'home'].includes(appearNote.value.visibility) || (appearNote.value.visibility === 'followers' && appearNote.value.userId === $i?.id));
const renoteCollapsed = ref(defaultStore.state.collapseRenotes && isRenote && (($i && ($i.id === note.value.userId || $i.id === appearNote.value.userId)) ?? (appearNote.value.myReaction != null))); const renoteCollapsed = ref(
defaultStore.state.collapseRenotes && isRenote && (
($i && ($i.id === note.value.userId || $i.id === appearNote.value.userId)) || // `||` must be `||`! See https://github.com/misskey-dev/misskey/issues/13131
(appearNote.value.myReaction != null)
)
);
/* Overload FunctionLint /* Overload FunctionLint
function checkMute(noteToCheck: Misskey.entities.Note, mutedWords: Array<string | string[]> | undefined | null, checkOnly: true): boolean; function checkMute(noteToCheck: Misskey.entities.Note, mutedWords: Array<string | string[]> | undefined | null, checkOnly: true): boolean;

View File

@ -96,7 +96,7 @@ const headerTabs = computed(() => user.value ? [{
key: 'achievements', key: 'achievements',
title: i18n.ts.achievements, title: i18n.ts.achievements,
icon: 'ti ti-medal', icon: 'ti ti-medal',
}] : []), ...($i && ($i.id === user.value.id)) || user.value.publicReactions ? [{ }] : []), ...($i && ($i.id === user.value.id || $i.isAdmin || $i.isModerator)) || user.value.publicReactions ? [{
key: 'reactions', key: 'reactions',
title: i18n.ts.reaction, title: i18n.ts.reaction,
icon: 'ti ti-mood-happy', icon: 'ti ti-mood-happy',