Merge branch 'develop' into reimplement-rate-limit
This commit is contained in:
commit
fce656b5e9
|
@ -2,3 +2,7 @@ contact_links:
|
||||||
- name: 💬 Misskey official Discord
|
- name: 💬 Misskey official Discord
|
||||||
url: https://discord.gg/Wp8gVStHW3
|
url: https://discord.gg/Wp8gVStHW3
|
||||||
about: Chat freely about Misskey
|
about: Chat freely about Misskey
|
||||||
|
# 仮
|
||||||
|
- name: 💬 Start discussion
|
||||||
|
url: https://github.com/misskey-dev/misskey/discussions
|
||||||
|
about: The official forum to join conversation and ask question
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
- Fix: 配信停止したインスタンス一覧が見れなくなる問題を修正
|
- Fix: 配信停止したインスタンス一覧が見れなくなる問題を修正
|
||||||
|
|
||||||
### Client
|
### Client
|
||||||
|
- `/about#federation` ページなどで各インスタンスのチャートが表示されなくなっていた問題を修正
|
||||||
- Fix: ユーザーページの追加情報のラベルを投稿者のサーバーの絵文字で表示する (#13968)
|
- Fix: ユーザーページの追加情報のラベルを投稿者のサーバーの絵文字で表示する (#13968)
|
||||||
|
|
||||||
### Server
|
### Server
|
||||||
|
|
|
@ -82,34 +82,14 @@ import { MiReversiGame } from '@/models/ReversiGame.js';
|
||||||
import type { QueryDeepPartialEntity } from 'typeorm/query-builder/QueryPartialEntity.js';
|
import type { QueryDeepPartialEntity } from 'typeorm/query-builder/QueryPartialEntity.js';
|
||||||
|
|
||||||
export interface MiRepository<T extends ObjectLiteral> {
|
export interface MiRepository<T extends ObjectLiteral> {
|
||||||
createTableColumnNames(this: Repository<T> & MiRepository<T>, queryBuilder: InsertQueryBuilder<T>): string[];
|
createTableColumnNames(this: Repository<T> & MiRepository<T>): string[];
|
||||||
createTableColumnNamesWithPrimaryKey(this: Repository<T> & MiRepository<T>, queryBuilder: InsertQueryBuilder<T>): string[];
|
|
||||||
insertOne(this: Repository<T> & MiRepository<T>, entity: QueryDeepPartialEntity<T>, findOptions?: Pick<FindOneOptions<T>, 'relations'>): Promise<T>;
|
insertOne(this: Repository<T> & MiRepository<T>, entity: QueryDeepPartialEntity<T>, findOptions?: Pick<FindOneOptions<T>, 'relations'>): Promise<T>;
|
||||||
selectAliasColumnNames(this: Repository<T> & MiRepository<T>, queryBuilder: InsertQueryBuilder<T>, builder: SelectQueryBuilder<T>): void;
|
selectAliasColumnNames(this: Repository<T> & MiRepository<T>, queryBuilder: InsertQueryBuilder<T>, builder: SelectQueryBuilder<T>): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const miRepository = {
|
export const miRepository = {
|
||||||
createTableColumnNames(queryBuilder) {
|
createTableColumnNames() {
|
||||||
// @ts-expect-error -- protected
|
return this.metadata.columns.filter(column => column.isSelect && !column.isVirtual).map(column => column.databaseName);
|
||||||
const insertedColumns = queryBuilder.getInsertedColumns();
|
|
||||||
if (insertedColumns.length) {
|
|
||||||
return insertedColumns.map(column => column.databaseName);
|
|
||||||
}
|
|
||||||
if (!queryBuilder.expressionMap.mainAlias?.hasMetadata && !queryBuilder.expressionMap.insertColumns.length) {
|
|
||||||
// @ts-expect-error -- protected
|
|
||||||
const valueSets = queryBuilder.getValueSets();
|
|
||||||
if (valueSets.length === 1) {
|
|
||||||
return Object.keys(valueSets[0]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return queryBuilder.expressionMap.insertColumns;
|
|
||||||
},
|
|
||||||
createTableColumnNamesWithPrimaryKey(queryBuilder) {
|
|
||||||
const columnNames = this.createTableColumnNames(queryBuilder);
|
|
||||||
if (!columnNames.includes('id')) {
|
|
||||||
columnNames.unshift('id');
|
|
||||||
}
|
|
||||||
return columnNames;
|
|
||||||
},
|
},
|
||||||
async insertOne(entity, findOptions?) {
|
async insertOne(entity, findOptions?) {
|
||||||
const queryBuilder = this.createQueryBuilder().insert().values(entity);
|
const queryBuilder = this.createQueryBuilder().insert().values(entity);
|
||||||
|
@ -117,7 +97,7 @@ export const miRepository = {
|
||||||
const mainAlias = queryBuilder.expressionMap.mainAlias!;
|
const mainAlias = queryBuilder.expressionMap.mainAlias!;
|
||||||
const name = mainAlias.name;
|
const name = mainAlias.name;
|
||||||
mainAlias.name = 't';
|
mainAlias.name = 't';
|
||||||
const columnNames = this.createTableColumnNamesWithPrimaryKey(queryBuilder);
|
const columnNames = this.createTableColumnNames();
|
||||||
queryBuilder.returning(columnNames.reduce((a, c) => `${a}, ${queryBuilder.escape(c)}`, '').slice(2));
|
queryBuilder.returning(columnNames.reduce((a, c) => `${a}, ${queryBuilder.escape(c)}`, '').slice(2));
|
||||||
const builder = this.createQueryBuilder().addCommonTableExpression(queryBuilder, 'cte', { columnNames });
|
const builder = this.createQueryBuilder().addCommonTableExpression(queryBuilder, 'cte', { columnNames });
|
||||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||||
|
@ -138,7 +118,7 @@ export const miRepository = {
|
||||||
selectOrAddSelect = (selection, selectionAliasName) => builder.addSelect(selection, selectionAliasName);
|
selectOrAddSelect = (selection, selectionAliasName) => builder.addSelect(selection, selectionAliasName);
|
||||||
return builder.select(selection, selectionAliasName);
|
return builder.select(selection, selectionAliasName);
|
||||||
};
|
};
|
||||||
for (const columnName of this.createTableColumnNamesWithPrimaryKey(queryBuilder)) {
|
for (const columnName of this.createTableColumnNames()) {
|
||||||
selectOrAddSelect(`${builder.alias}.${columnName}`, `${builder.alias}_${columnName}`);
|
selectOrAddSelect(`${builder.alias}.${columnName}`, `${builder.alias}_${columnName}`);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
@ -125,6 +125,35 @@ export function file(isSensitive = false) {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function federationInstance(): entities.FederationInstance {
|
||||||
|
return {
|
||||||
|
id: 'someinstanceid',
|
||||||
|
firstRetrievedAt: '2021-01-01T00:00:00.000Z',
|
||||||
|
host: 'misskey-hub.net',
|
||||||
|
usersCount: 10,
|
||||||
|
notesCount: 20,
|
||||||
|
followingCount: 5,
|
||||||
|
followersCount: 15,
|
||||||
|
isNotResponding: false,
|
||||||
|
isSuspended: false,
|
||||||
|
suspensionState: 'none',
|
||||||
|
isBlocked: false,
|
||||||
|
softwareName: 'misskey',
|
||||||
|
softwareVersion: '2024.5.0',
|
||||||
|
openRegistrations: false,
|
||||||
|
name: 'Misskey Hub',
|
||||||
|
description: '',
|
||||||
|
maintainerName: '',
|
||||||
|
maintainerEmail: '',
|
||||||
|
isSilenced: false,
|
||||||
|
iconUrl: 'https://github.com/misskey-dev/misskey/blob/master/packages/frontend/assets/about-icon.png?raw=true',
|
||||||
|
faviconUrl: '',
|
||||||
|
themeColor: '',
|
||||||
|
infoUpdatedAt: '',
|
||||||
|
latestRequestReceivedAt: '',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
export function userDetailed(id = 'someuserid', username = 'miskist', host = 'misskey-hub.net', name = 'Misskey User'): entities.UserDetailed {
|
export function userDetailed(id = 'someuserid', username = 'miskist', host = 'misskey-hub.net', name = 'Misskey User'): entities.UserDetailed {
|
||||||
return {
|
return {
|
||||||
id,
|
id,
|
||||||
|
|
|
@ -403,6 +403,7 @@ function toStories(component: string): Promise<string> {
|
||||||
glob('src/components/MkSignupServerRules.vue'),
|
glob('src/components/MkSignupServerRules.vue'),
|
||||||
glob('src/components/MkUserSetupDialog.vue'),
|
glob('src/components/MkUserSetupDialog.vue'),
|
||||||
glob('src/components/MkUserSetupDialog.*.vue'),
|
glob('src/components/MkUserSetupDialog.*.vue'),
|
||||||
|
glob('src/components/MkInstanceCardMini.vue'),
|
||||||
glob('src/components/MkInviteCode.vue'),
|
glob('src/components/MkInviteCode.vue'),
|
||||||
glob('src/pages/user/home.vue'),
|
glob('src/pages/user/home.vue'),
|
||||||
]);
|
]);
|
||||||
|
|
|
@ -29,7 +29,7 @@ function getChartArray(seed: string, limit: number, option?: { accumulate?: bool
|
||||||
return array;
|
return array;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getChartResolver(fields: string[], option?: { accumulate?: boolean, mulMap?: Record<string, number> }): HttpResponseResolver<PathParams, DefaultBodyType, JsonBodyType> {
|
export function getChartResolver(fields: string[], option?: { accumulate?: boolean, mulMap?: Record<string, number> }): HttpResponseResolver<PathParams, DefaultBodyType, JsonBodyType> {
|
||||||
return ({ request }) => {
|
return ({ request }) => {
|
||||||
action(`GET ${request.url}`)();
|
action(`GET ${request.url}`)();
|
||||||
const limitParam = new URL(request.url).searchParams.get('limit');
|
const limitParam = new URL(request.url).searchParams.get('limit');
|
||||||
|
|
|
@ -0,0 +1,64 @@
|
||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: syuilo and misskey-project
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* eslint-disable @typescript-eslint/explicit-function-return-type */
|
||||||
|
import { StoryObj } from '@storybook/vue3';
|
||||||
|
import { HttpResponse, http } from 'msw';
|
||||||
|
import { federationInstance } from '../../.storybook/fakes.js';
|
||||||
|
import { commonHandlers } from '../../.storybook/mocks.js';
|
||||||
|
import MkInstanceCardMini from './MkInstanceCardMini.vue';
|
||||||
|
import { getChartResolver } from './MkChart.stories.impl.js';
|
||||||
|
export const Default = {
|
||||||
|
render(args) {
|
||||||
|
return {
|
||||||
|
components: {
|
||||||
|
MkInstanceCardMini,
|
||||||
|
},
|
||||||
|
setup() {
|
||||||
|
return {
|
||||||
|
args,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
props() {
|
||||||
|
return {
|
||||||
|
...this.args,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
},
|
||||||
|
template: '<MkInstanceCardMini v-bind="props" />',
|
||||||
|
};
|
||||||
|
},
|
||||||
|
args: {
|
||||||
|
instance: federationInstance(),
|
||||||
|
},
|
||||||
|
parameters: {
|
||||||
|
layout: 'centered',
|
||||||
|
msw: {
|
||||||
|
handlers: [
|
||||||
|
...commonHandlers,
|
||||||
|
http.get('/undefined/preview.webp', async ({ request }) => {
|
||||||
|
const urlStr = new URL(request.url).searchParams.get('url');
|
||||||
|
if (urlStr == null) {
|
||||||
|
return new HttpResponse(null, { status: 404 });
|
||||||
|
}
|
||||||
|
const url = new URL(urlStr);
|
||||||
|
|
||||||
|
if (url.href.startsWith('https://github.com/misskey-dev/misskey/blob/master/packages/frontend/assets/')) {
|
||||||
|
const image = await (await fetch(`client-assets/${url.pathname.split('/').pop()}`)).blob();
|
||||||
|
return new HttpResponse(image, {
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'image/jpeg',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
return new HttpResponse(null, { status: 404 });
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
http.get('/api/charts/instance', getChartResolver(['requests.received'])),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
} satisfies StoryObj<typeof MkInstanceCardMini>;
|
|
@ -29,8 +29,8 @@ const chartValues = ref<number[] | null>(null);
|
||||||
|
|
||||||
misskeyApiGet('charts/instance', { host: props.instance.host, limit: 16 + 1, span: 'day' }).then(res => {
|
misskeyApiGet('charts/instance', { host: props.instance.host, limit: 16 + 1, span: 'day' }).then(res => {
|
||||||
// 今日のぶんの値はまだ途中の値であり、それも含めると大抵の場合前日よりも下降しているようなグラフになってしまうため今日は弾く
|
// 今日のぶんの値はまだ途中の値であり、それも含めると大抵の場合前日よりも下降しているようなグラフになってしまうため今日は弾く
|
||||||
res['requests.received'].splice(0, 1);
|
res.requests.received.splice(0, 1);
|
||||||
chartValues.value = res['requests.received'];
|
chartValues.value = res.requests.received;
|
||||||
});
|
});
|
||||||
|
|
||||||
function getInstanceIcon(instance): string {
|
function getInstanceIcon(instance): string {
|
||||||
|
|
Loading…
Reference in New Issue