Merge branch 'develop' into mahjong
This commit is contained in:
commit
4ae591a2c7
|
@ -0,0 +1,113 @@
|
||||||
|
name: Storybook
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- master
|
||||||
|
- develop
|
||||||
|
- dev/storybook8 # for testing
|
||||||
|
pull_request_target:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
env:
|
||||||
|
NODE_OPTIONS: "--max_old_space_size=7168"
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3.6.0
|
||||||
|
if: github.event_name != 'pull_request_target'
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
submodules: true
|
||||||
|
- uses: actions/checkout@v3.6.0
|
||||||
|
if: github.event_name == 'pull_request_target'
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
submodules: true
|
||||||
|
ref: "refs/pull/${{ github.event.number }}/merge"
|
||||||
|
- name: Checkout actual HEAD
|
||||||
|
if: github.event_name == 'pull_request_target'
|
||||||
|
id: rev
|
||||||
|
run: |
|
||||||
|
echo "base=$(git rev-list --parents -n1 HEAD | cut -d" " -f2)" >> $GITHUB_OUTPUT
|
||||||
|
git checkout $(git rev-list --parents -n1 HEAD | cut -d" " -f3)
|
||||||
|
- name: Install pnpm
|
||||||
|
uses: pnpm/action-setup@v2
|
||||||
|
with:
|
||||||
|
version: 8
|
||||||
|
run_install: false
|
||||||
|
- name: Use Node.js 20.x
|
||||||
|
uses: actions/setup-node@v3.8.1
|
||||||
|
with:
|
||||||
|
node-version-file: '.node-version'
|
||||||
|
cache: 'pnpm'
|
||||||
|
- run: corepack enable
|
||||||
|
- run: pnpm i --frozen-lockfile
|
||||||
|
- name: Check pnpm-lock.yaml
|
||||||
|
run: git diff --exit-code pnpm-lock.yaml
|
||||||
|
- name: Build misskey-js
|
||||||
|
run: pnpm --filter misskey-js build
|
||||||
|
- name: Build storybook
|
||||||
|
run: pnpm --filter frontend build-storybook
|
||||||
|
- name: Publish to Chromatic
|
||||||
|
if: github.event_name != 'pull_request_target' && github.ref == 'refs/heads/master'
|
||||||
|
run: pnpm --filter frontend chromatic --exit-once-uploaded -d storybook-static
|
||||||
|
env:
|
||||||
|
CHROMATIC_PROJECT_TOKEN: ${{ secrets.CHROMATIC_PROJECT_TOKEN }}
|
||||||
|
- name: Publish to Chromatic
|
||||||
|
if: github.event_name != 'pull_request_target' && github.ref != 'refs/heads/master'
|
||||||
|
id: chromatic_push
|
||||||
|
run: |
|
||||||
|
DIFF="${{ github.event.before }} HEAD"
|
||||||
|
if [ "$DIFF" = "0000000000000000000000000000000000000000 HEAD" ]; then
|
||||||
|
DIFF="HEAD"
|
||||||
|
fi
|
||||||
|
CHROMATIC_PARAMETER="$(node packages/frontend/.storybook/changes.js $(git diff-tree --no-commit-id --name-only -r $(echo "$DIFF") | xargs))"
|
||||||
|
if [ "$CHROMATIC_PARAMETER" = " --skip" ]; then
|
||||||
|
echo "skip=true" >> $GITHUB_OUTPUT
|
||||||
|
fi
|
||||||
|
if pnpm --filter frontend chromatic -d storybook-static $(echo "$CHROMATIC_PARAMETER"); then
|
||||||
|
echo "success=true" >> $GITHUB_OUTPUT
|
||||||
|
else
|
||||||
|
echo "success=false" >> $GITHUB_OUTPUT
|
||||||
|
fi
|
||||||
|
env:
|
||||||
|
CHROMATIC_PROJECT_TOKEN: ${{ secrets.CHROMATIC_PROJECT_TOKEN }}
|
||||||
|
- name: Publish to Chromatic
|
||||||
|
if: github.event_name == 'pull_request_target'
|
||||||
|
id: chromatic_pull_request
|
||||||
|
run: |
|
||||||
|
DIFF="${{ steps.rev.outputs.base }} HEAD"
|
||||||
|
if [ "$DIFF" = "0000000000000000000000000000000000000000 HEAD" ]; then
|
||||||
|
DIFF="HEAD"
|
||||||
|
fi
|
||||||
|
CHROMATIC_PARAMETER="$(node packages/frontend/.storybook/changes.js $(git diff-tree --no-commit-id --name-only -r $(echo "$DIFF") | xargs))"
|
||||||
|
if [ "$CHROMATIC_PARAMETER" = " --skip" ]; then
|
||||||
|
echo "skip=true" >> $GITHUB_OUTPUT
|
||||||
|
fi
|
||||||
|
BRANCH="${{ github.event.pull_request.head.user.login }}:${{ github.event.pull_request.head.ref }}"
|
||||||
|
if [ "$BRANCH" = "misskey-dev:${{ github.event.pull_request.head.ref }}" ]; then
|
||||||
|
BRANCH="${{ github.event.pull_request.head.ref }}"
|
||||||
|
fi
|
||||||
|
pnpm --filter frontend chromatic --exit-once-uploaded -d storybook-static --branch-name $BRANCH $(echo "$CHROMATIC_PARAMETER")
|
||||||
|
env:
|
||||||
|
CHROMATIC_PROJECT_TOKEN: ${{ secrets.CHROMATIC_PROJECT_TOKEN }}
|
||||||
|
- name: Notify that Chromatic detects changes
|
||||||
|
uses: actions/github-script@v6.4.0
|
||||||
|
if: github.event_name != 'pull_request_target' && steps.chromatic_push.outputs.success == 'false'
|
||||||
|
with:
|
||||||
|
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
script: |
|
||||||
|
github.rest.repos.createCommitComment({
|
||||||
|
owner: context.repo.owner,
|
||||||
|
repo: context.repo.repo,
|
||||||
|
commit_sha: context.sha,
|
||||||
|
body: 'Chromatic detects changes. Please [review the changes on Chromatic](https://www.chromatic.com/builds?appId=6428f7d7b962f0b79f97d6e4).'
|
||||||
|
})
|
||||||
|
- name: Upload Artifacts
|
||||||
|
uses: actions/upload-artifact@v3
|
||||||
|
with:
|
||||||
|
name: storybook
|
||||||
|
path: packages/frontend/storybook-static
|
|
@ -115,6 +115,7 @@ jobs:
|
||||||
run: pnpm exec cypress install
|
run: pnpm exec cypress install
|
||||||
- name: Cypress run
|
- name: Cypress run
|
||||||
uses: cypress-io/github-action@v6
|
uses: cypress-io/github-action@v6
|
||||||
|
timeout-minutes: 15
|
||||||
with:
|
with:
|
||||||
install: false
|
install: false
|
||||||
start: pnpm start:test
|
start: pnpm start:test
|
||||||
|
|
|
@ -24,7 +24,7 @@
|
||||||
- Fix: リモートユーザーのリアクション一覧がすべて見えてしまうのを修正
|
- Fix: リモートユーザーのリアクション一覧がすべて見えてしまうのを修正
|
||||||
* すべてのリモートユーザーのリアクション一覧を見えないようにします
|
* すべてのリモートユーザーのリアクション一覧を見えないようにします
|
||||||
- Enhance: モデレーターはすべてのユーザーのリアクション一覧を見られるように
|
- Enhance: モデレーターはすべてのユーザーのリアクション一覧を見られるように
|
||||||
- Fix: 特定のキーワードを含むノートが投稿された際、エラーに出来るような設定項目を追加 #13207
|
- Fix: 特定のキーワード及び正規表現にマッチする文字列を含むノートが投稿された際、エラーに出来るような設定項目を追加 #13207
|
||||||
* デフォルトは空欄なので適用前と同等の動作になります
|
* デフォルトは空欄なので適用前と同等の動作になります
|
||||||
|
|
||||||
### Client
|
### Client
|
||||||
|
|
|
@ -122,6 +122,19 @@ command.
|
||||||
If you have not changed it from the default, it will be "http://localhost:3000".
|
If you have not changed it from the default, it will be "http://localhost:3000".
|
||||||
If "port" in .config/default.yml is set to something other than 3000, you need to change the proxy settings in packages/frontend/vite.config.local-dev.ts.
|
If "port" in .config/default.yml is set to something other than 3000, you need to change the proxy settings in packages/frontend/vite.config.local-dev.ts.
|
||||||
|
|
||||||
|
### `MK_DEV_PREFER=backend pnpm dev`
|
||||||
|
pnpm dev has another mode with `MK_DEV_PREFER=backend`.
|
||||||
|
|
||||||
|
```
|
||||||
|
MK_DEV_PREFER=backend pnpm dev
|
||||||
|
```
|
||||||
|
|
||||||
|
- This mode is closer to the production environment than the default mode.
|
||||||
|
- Vite runs behind the backend (the backend will proxy Vite at /vite).
|
||||||
|
- You can see Misskey by accessing `http://localhost:3000` (Replace `3000` with the port configured with `port` in .config/default.yml).
|
||||||
|
- To change the port of Vite, specify with `VITE_PORT` environment variable.
|
||||||
|
- HMR may not work in some environments such as Windows.
|
||||||
|
|
||||||
### Dev Container
|
### Dev Container
|
||||||
Instead of running `pnpm` locally, you can use Dev Container to set up your development environment.
|
Instead of running `pnpm` locally, you can use Dev Container to set up your development environment.
|
||||||
To use Dev Container, open the project directory on VSCode with Dev Containers installed.
|
To use Dev Container, open the project directory on VSCode with Dev Containers installed.
|
||||||
|
|
|
@ -162,12 +162,12 @@ describe('After user signed in', () => {
|
||||||
|
|
||||||
it('successfully loads', () => {
|
it('successfully loads', () => {
|
||||||
// 表示に時間がかかるのでデフォルト秒数だとタイムアウトする
|
// 表示に時間がかかるのでデフォルト秒数だとタイムアウトする
|
||||||
cy.get('[data-cy-user-setup-continue]', { timeout: 12000 }).should('be.visible');
|
cy.get('[data-cy-user-setup-continue]', { timeout: 30000 }).should('be.visible');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('account setup wizard', () => {
|
it('account setup wizard', () => {
|
||||||
// 表示に時間がかかるのでデフォルト秒数だとタイムアウトする
|
// 表示に時間がかかるのでデフォルト秒数だとタイムアウトする
|
||||||
cy.get('[data-cy-user-setup-continue]', { timeout: 12000 }).click();
|
cy.get('[data-cy-user-setup-continue]', { timeout: 30000 }).click();
|
||||||
|
|
||||||
cy.get('[data-cy-user-setup-user-name] input').type('ありす');
|
cy.get('[data-cy-user-setup-user-name] input').type('ありす');
|
||||||
cy.get('[data-cy-user-setup-user-description] textarea').type('ほげ');
|
cy.get('[data-cy-user-setup-user-description] textarea').type('ほげ');
|
||||||
|
@ -205,7 +205,7 @@ describe('After user setup', () => {
|
||||||
|
|
||||||
// アカウント初期設定ウィザード
|
// アカウント初期設定ウィザード
|
||||||
// 表示に時間がかかるのでデフォルト秒数だとタイムアウトする
|
// 表示に時間がかかるのでデフォルト秒数だとタイムアウトする
|
||||||
cy.get('[data-cy-user-setup] [data-cy-modal-window-close]', { timeout: 12000 }).click();
|
cy.get('[data-cy-user-setup] [data-cy-modal-window-close]', { timeout: 30000 }).click();
|
||||||
cy.get('[data-cy-modal-dialog-ok]').click();
|
cy.get('[data-cy-modal-dialog-ok]').click();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -14,7 +14,7 @@ describe('Router transition', () => {
|
||||||
|
|
||||||
// アカウント初期設定ウィザード
|
// アカウント初期設定ウィザード
|
||||||
// 表示に時間がかかるのでデフォルト秒数だとタイムアウトする
|
// 表示に時間がかかるのでデフォルト秒数だとタイムアウトする
|
||||||
cy.get('[data-cy-user-setup] [data-cy-modal-window-close]', { timeout: 12000 }).click();
|
cy.get('[data-cy-user-setup] [data-cy-modal-window-close]', { timeout: 30000 }).click();
|
||||||
cy.wait(500);
|
cy.wait(500);
|
||||||
cy.get('[data-cy-modal-dialog-ok]').click();
|
cy.get('[data-cy-modal-dialog-ok]').click();
|
||||||
});
|
});
|
||||||
|
|
|
@ -14,9 +14,9 @@ export type FanoutTimelineName =
|
||||||
| `homeTimeline:${string}`
|
| `homeTimeline:${string}`
|
||||||
| `homeTimelineWithFiles:${string}` // only notes with files are included
|
| `homeTimelineWithFiles:${string}` // only notes with files are included
|
||||||
// local timeline
|
// local timeline
|
||||||
| 'localTimeline' // replies are not included
|
| `localTimeline` // replies are not included
|
||||||
| 'localTimelineWithFiles' // only non-reply notes with files are included
|
| `localTimelineWithFiles` // only non-reply notes with files are included
|
||||||
| 'localTimelineWithReplies' // only replies are included
|
| `localTimelineWithReplies` // only replies are included
|
||||||
| `localTimelineWithReplyTo:${string}` // Only replies to specific local user are included. Parameter is reply user id.
|
| `localTimelineWithReplyTo:${string}` // Only replies to specific local user are included. Parameter is reply user id.
|
||||||
|
|
||||||
// antenna
|
// antenna
|
||||||
|
|
|
@ -263,11 +263,9 @@ export class NoteCreateService implements OnApplicationShutdown {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!user.host) {
|
|
||||||
if (this.utilityService.isKeyWordIncluded(data.cw ?? data.text ?? '', meta.prohibitedWords)) {
|
if (this.utilityService.isKeyWordIncluded(data.cw ?? data.text ?? '', meta.prohibitedWords)) {
|
||||||
throw new NoteCreateService.ContainsProhibitedWordsError();
|
throw new NoteCreateService.ContainsProhibitedWordsError();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
const inSilencedInstance = this.utilityService.isSilencedHost(meta.silencedHosts, user.host);
|
const inSilencedInstance = this.utilityService.isSilencedHost(meta.silencedHosts, user.host);
|
||||||
|
|
||||||
|
|
|
@ -12,11 +12,11 @@ import type { Config } from '@/config.js';
|
||||||
import { DI } from '@/di-symbols.js';
|
import { DI } from '@/di-symbols.js';
|
||||||
import { bindThis } from '@/decorators.js';
|
import { bindThis } from '@/decorators.js';
|
||||||
import type { Antenna } from '@/server/api/endpoints/i/import-antennas.js';
|
import type { Antenna } from '@/server/api/endpoints/i/import-antennas.js';
|
||||||
import { ApRequestCreator } from '@/core/activitypub/ApRequestService.js';
|
|
||||||
import type { DbQueue, DeliverQueue, EndedPollNotificationQueue, InboxQueue, ObjectStorageQueue, RelationshipQueue, SystemQueue, WebhookDeliverQueue } from './QueueModule.js';
|
import type { DbQueue, DeliverQueue, EndedPollNotificationQueue, InboxQueue, ObjectStorageQueue, RelationshipQueue, SystemQueue, WebhookDeliverQueue } from './QueueModule.js';
|
||||||
import type { DbJobData, DeliverJobData, RelationshipJobData, ThinUser } from '../queue/types.js';
|
import type { DbJobData, DeliverJobData, RelationshipJobData, ThinUser } from '../queue/types.js';
|
||||||
import type httpSignature from '@peertube/http-signature';
|
import type httpSignature from '@peertube/http-signature';
|
||||||
import type * as Bull from 'bullmq';
|
import type * as Bull from 'bullmq';
|
||||||
|
import { ApRequestCreator } from '@/core/activitypub/ApRequestService.js';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class QueueService {
|
export class QueueService {
|
||||||
|
|
|
@ -31,7 +31,7 @@ export class ApMfmService {
|
||||||
|
|
||||||
const parsed = mfm.parse(srcMfm);
|
const parsed = mfm.parse(srcMfm);
|
||||||
|
|
||||||
if (!apAppend && parsed.every(n => ['text', 'unicodeEmoji', 'emojiCode', 'mention', 'hashtag', 'url'].includes(n.type))) {
|
if (!apAppend && parsed?.every(n => ['text', 'unicodeEmoji', 'emojiCode', 'mention', 'hashtag', 'url'].includes(n.type))) {
|
||||||
noMisskeyContent = true;
|
noMisskeyContent = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
import type { onRequestHookHandler } from 'fastify';
|
||||||
|
|
||||||
|
export const handleRequestRedirectToOmitSearch: onRequestHookHandler = (request, reply, done) => {
|
||||||
|
const index = request.url.indexOf('?');
|
||||||
|
if (~index) {
|
||||||
|
reply.redirect(301, request.url.slice(0, index));
|
||||||
|
}
|
||||||
|
done();
|
||||||
|
};
|
|
@ -34,7 +34,7 @@ export class RelationshipProcessorService {
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public async processFollow(job: Bull.Job<RelationshipJobData>): Promise<string> {
|
public async processFollow(job: Bull.Job<RelationshipJobData>): Promise<string> {
|
||||||
this.logger.info(`${job.data.from.id} is trying to follow ${job.data.to.id} ${job.data.withReplies ? 'with replies' : 'without replies'}`);
|
this.logger.info(`${job.data.from.id} is trying to follow ${job.data.to.id} ${job.data.withReplies ? "with replies" : "without replies"}`);
|
||||||
await this.userFollowingService.follow(job.data.from, job.data.to, {
|
await this.userFollowingService.follow(job.data.from, job.data.to, {
|
||||||
requestId: job.data.requestId,
|
requestId: job.data.requestId,
|
||||||
silent: job.data.silent,
|
silent: job.data.silent,
|
||||||
|
|
|
@ -27,6 +27,7 @@ import { LoggerService } from '@/core/LoggerService.js';
|
||||||
import { bindThis } from '@/decorators.js';
|
import { bindThis } from '@/decorators.js';
|
||||||
import { isMimeImage } from '@/misc/is-mime-image.js';
|
import { isMimeImage } from '@/misc/is-mime-image.js';
|
||||||
import { correctFilename } from '@/misc/correct-filename.js';
|
import { correctFilename } from '@/misc/correct-filename.js';
|
||||||
|
import { handleRequestRedirectToOmitSearch } from '@/misc/fastify-hook-handlers.js';
|
||||||
import type { FastifyInstance, FastifyRequest, FastifyReply, FastifyPluginOptions } from 'fastify';
|
import type { FastifyInstance, FastifyRequest, FastifyReply, FastifyPluginOptions } from 'fastify';
|
||||||
|
|
||||||
const _filename = fileURLToPath(import.meta.url);
|
const _filename = fileURLToPath(import.meta.url);
|
||||||
|
@ -67,6 +68,8 @@ export class FileServerService {
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
fastify.register((fastify, options, done) => {
|
||||||
|
fastify.addHook('onRequest', handleRequestRedirectToOmitSearch);
|
||||||
fastify.get('/files/app-default.jpg', (request, reply) => {
|
fastify.get('/files/app-default.jpg', (request, reply) => {
|
||||||
const file = fs.createReadStream(`${_dirname}/assets/dummy.png`);
|
const file = fs.createReadStream(`${_dirname}/assets/dummy.png`);
|
||||||
reply.header('Content-Type', 'image/jpeg');
|
reply.header('Content-Type', 'image/jpeg');
|
||||||
|
@ -79,8 +82,9 @@ export class FileServerService {
|
||||||
.catch(err => this.errorHandler(request, reply, err));
|
.catch(err => this.errorHandler(request, reply, err));
|
||||||
});
|
});
|
||||||
fastify.get<{ Params: { key: string; } }>('/files/:key/*', async (request, reply) => {
|
fastify.get<{ Params: { key: string; } }>('/files/:key/*', async (request, reply) => {
|
||||||
return await this.sendDriveFile(request, reply)
|
return await reply.redirect(301, `${this.config.url}/files/${request.params.key}`);
|
||||||
.catch(err => this.errorHandler(request, reply, err));
|
});
|
||||||
|
done();
|
||||||
});
|
});
|
||||||
|
|
||||||
fastify.get<{
|
fastify.get<{
|
||||||
|
|
|
@ -38,7 +38,7 @@ export class NodeinfoServerService {
|
||||||
public getLinks() {
|
public getLinks() {
|
||||||
return [{
|
return [{
|
||||||
rel: 'http://nodeinfo.diaspora.software/ns/schema/2.1',
|
rel: 'http://nodeinfo.diaspora.software/ns/schema/2.1',
|
||||||
href: this.config.url + nodeinfo2_1path,
|
href: this.config.url + nodeinfo2_1path
|
||||||
}, {
|
}, {
|
||||||
rel: 'http://nodeinfo.diaspora.software/ns/schema/2.0',
|
rel: 'http://nodeinfo.diaspora.software/ns/schema/2.0',
|
||||||
href: this.config.url + nodeinfo2_0path,
|
href: this.config.url + nodeinfo2_0path,
|
||||||
|
|
|
@ -25,8 +25,8 @@ export const meta = {
|
||||||
items: {
|
items: {
|
||||||
type: 'object',
|
type: 'object',
|
||||||
},
|
},
|
||||||
},
|
}
|
||||||
},
|
}
|
||||||
},
|
},
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
|
|
|
@ -71,7 +71,7 @@ export const paramDef = {
|
||||||
type: 'object',
|
type: 'object',
|
||||||
properties: {
|
properties: {
|
||||||
userId: { type: 'string', format: 'misskey:id' },
|
userId: { type: 'string', format: 'misskey:id' },
|
||||||
withReplies: { type: 'boolean' },
|
withReplies: { type: 'boolean' }
|
||||||
},
|
},
|
||||||
required: ['userId'],
|
required: ['userId'],
|
||||||
} as const;
|
} as const;
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
import { Inject, Injectable } from '@nestjs/common';
|
import { Inject, Injectable } from '@nestjs/common';
|
||||||
import { Endpoint } from '@/server/api/endpoint-base.js';
|
import { Endpoint } from '@/server/api/endpoint-base.js';
|
||||||
import type { UsersRepository } from '@/models/_.js';
|
import type { UsersRepository } from '@/models/_.js';
|
||||||
import { safeForSql } from '@/misc/safe-for-sql.js';
|
import { safeForSql } from "@/misc/safe-for-sql.js";
|
||||||
import { normalizeForSearch } from '@/misc/normalize-for-search.js';
|
import { normalizeForSearch } from '@/misc/normalize-for-search.js';
|
||||||
import { UserEntityService } from '@/core/entities/UserEntityService.js';
|
import { UserEntityService } from '@/core/entities/UserEntityService.js';
|
||||||
import { DI } from '@/di-symbols.js';
|
import { DI } from '@/di-symbols.js';
|
||||||
|
|
|
@ -14,7 +14,7 @@ export const meta = {
|
||||||
tags: ['account'],
|
tags: ['account'],
|
||||||
|
|
||||||
requireCredential: true,
|
requireCredential: true,
|
||||||
kind: 'read:account',
|
kind: "read:account",
|
||||||
|
|
||||||
res: {
|
res: {
|
||||||
type: 'object',
|
type: 'object',
|
||||||
|
|
|
@ -38,9 +38,9 @@ export const meta = {
|
||||||
type: 'array',
|
type: 'array',
|
||||||
uniqueItems: true,
|
uniqueItems: true,
|
||||||
items: {
|
items: {
|
||||||
type: 'string',
|
type: 'string'
|
||||||
},
|
|
||||||
},
|
},
|
||||||
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
@ -35,7 +35,7 @@ export const meta = {
|
||||||
type: 'array',
|
type: 'array',
|
||||||
uniqueItems: true,
|
uniqueItems: true,
|
||||||
items: {
|
items: {
|
||||||
type: 'string',
|
type: 'string'
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
isAuthorized: {
|
isAuthorized: {
|
||||||
|
|
|
@ -22,7 +22,7 @@ export const meta = {
|
||||||
|
|
||||||
res: {
|
res: {
|
||||||
type: 'object',
|
type: 'object',
|
||||||
},
|
}
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
export const paramDef = {
|
export const paramDef = {
|
||||||
|
|
|
@ -22,7 +22,7 @@ export const meta = {
|
||||||
|
|
||||||
res: {
|
res: {
|
||||||
type: 'object',
|
type: 'object',
|
||||||
},
|
}
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
export const paramDef = {
|
export const paramDef = {
|
||||||
|
|
|
@ -22,8 +22,8 @@ export const meta = {
|
||||||
type: 'array',
|
type: 'array',
|
||||||
items: {
|
items: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
},
|
}
|
||||||
},
|
}
|
||||||
},
|
},
|
||||||
domain: {
|
domain: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
|
@ -31,7 +31,7 @@ export const meta = {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
}
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
export const paramDef = {
|
export const paramDef = {
|
||||||
|
|
|
@ -33,7 +33,7 @@ export const meta = {
|
||||||
properties: {
|
properties: {
|
||||||
id: {
|
id: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
format: 'misskey:id',
|
format: 'misskey:id'
|
||||||
},
|
},
|
||||||
userId: {
|
userId: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
|
@ -45,7 +45,7 @@ export const meta = {
|
||||||
items: {
|
items: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
enum: webhookEventTypes,
|
enum: webhookEventTypes,
|
||||||
},
|
}
|
||||||
},
|
},
|
||||||
url: { type: 'string' },
|
url: { type: 'string' },
|
||||||
secret: { type: 'string' },
|
secret: { type: 'string' },
|
||||||
|
|
|
@ -23,7 +23,7 @@ export const meta = {
|
||||||
properties: {
|
properties: {
|
||||||
id: {
|
id: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
format: 'misskey:id',
|
format: 'misskey:id'
|
||||||
},
|
},
|
||||||
userId: {
|
userId: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
|
@ -35,7 +35,7 @@ export const meta = {
|
||||||
items: {
|
items: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
enum: webhookEventTypes,
|
enum: webhookEventTypes,
|
||||||
},
|
}
|
||||||
},
|
},
|
||||||
url: { type: 'string' },
|
url: { type: 'string' },
|
||||||
secret: { type: 'string' },
|
secret: { type: 'string' },
|
||||||
|
@ -43,8 +43,8 @@ export const meta = {
|
||||||
latestSentAt: { type: 'string', format: 'date-time', nullable: true },
|
latestSentAt: { type: 'string', format: 'date-time', nullable: true },
|
||||||
latestStatus: { type: 'integer', nullable: true },
|
latestStatus: { type: 'integer', nullable: true },
|
||||||
},
|
},
|
||||||
},
|
}
|
||||||
},
|
}
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
export const paramDef = {
|
export const paramDef = {
|
||||||
|
|
|
@ -30,7 +30,7 @@ export const meta = {
|
||||||
properties: {
|
properties: {
|
||||||
id: {
|
id: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
format: 'misskey:id',
|
format: 'misskey:id'
|
||||||
},
|
},
|
||||||
userId: {
|
userId: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
|
@ -42,7 +42,7 @@ export const meta = {
|
||||||
items: {
|
items: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
enum: webhookEventTypes,
|
enum: webhookEventTypes,
|
||||||
},
|
}
|
||||||
},
|
},
|
||||||
url: { type: 'string' },
|
url: { type: 'string' },
|
||||||
secret: { type: 'string' },
|
secret: { type: 'string' },
|
||||||
|
|
|
@ -47,7 +47,7 @@ export const meta = {
|
||||||
bothWithRepliesAndWithFiles: {
|
bothWithRepliesAndWithFiles: {
|
||||||
message: 'Specifying both withReplies and withFiles is not supported',
|
message: 'Specifying both withReplies and withFiles is not supported',
|
||||||
code: 'BOTH_WITH_REPLIES_AND_WITH_FILES',
|
code: 'BOTH_WITH_REPLIES_AND_WITH_FILES',
|
||||||
id: 'dfaa3eb7-8002-4cb7-bcc4-1095df46656f',
|
id: 'dfaa3eb7-8002-4cb7-bcc4-1095df46656f'
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
} as const;
|
} as const;
|
||||||
|
|
|
@ -18,7 +18,7 @@ export const meta = {
|
||||||
properties: {
|
properties: {
|
||||||
id: {
|
id: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
format: 'misskey:id',
|
format: 'misskey:id'
|
||||||
},
|
},
|
||||||
required: {
|
required: {
|
||||||
type: 'boolean',
|
type: 'boolean',
|
||||||
|
@ -34,8 +34,8 @@ export const meta = {
|
||||||
default: 'hello',
|
default: 'hello',
|
||||||
nullable: true,
|
nullable: true,
|
||||||
},
|
},
|
||||||
},
|
}
|
||||||
},
|
}
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
export const paramDef = {
|
export const paramDef = {
|
||||||
|
|
|
@ -34,6 +34,7 @@ import { ChannelEntityService } from '@/core/entities/ChannelEntityService.js';
|
||||||
import type { ChannelsRepository, ClipsRepository, FlashsRepository, GalleryPostsRepository, MiMeta, NotesRepository, PagesRepository, ReversiGamesRepository, UserProfilesRepository, UsersRepository } from '@/models/_.js';
|
import type { ChannelsRepository, ClipsRepository, FlashsRepository, GalleryPostsRepository, MiMeta, NotesRepository, PagesRepository, ReversiGamesRepository, UserProfilesRepository, UsersRepository } from '@/models/_.js';
|
||||||
import type Logger from '@/logger.js';
|
import type Logger from '@/logger.js';
|
||||||
import { deepClone } from '@/misc/clone.js';
|
import { deepClone } from '@/misc/clone.js';
|
||||||
|
import { handleRequestRedirectToOmitSearch } from '@/misc/fastify-hook-handlers.js';
|
||||||
import { bindThis } from '@/decorators.js';
|
import { bindThis } from '@/decorators.js';
|
||||||
import { FlashEntityService } from '@/core/entities/FlashEntityService.js';
|
import { FlashEntityService } from '@/core/entities/FlashEntityService.js';
|
||||||
import { RoleService } from '@/core/RoleService.js';
|
import { RoleService } from '@/core/RoleService.js';
|
||||||
|
@ -253,12 +254,17 @@ export class ClientServerService {
|
||||||
|
|
||||||
//#region vite assets
|
//#region vite assets
|
||||||
if (this.config.clientManifestExists) {
|
if (this.config.clientManifestExists) {
|
||||||
|
fastify.register((fastify, options, done) => {
|
||||||
fastify.register(fastifyStatic, {
|
fastify.register(fastifyStatic, {
|
||||||
root: viteOut,
|
root: viteOut,
|
||||||
prefix: '/vite/',
|
prefix: '/vite/',
|
||||||
maxAge: ms('30 days'),
|
maxAge: ms('30 days'),
|
||||||
|
immutable: true,
|
||||||
decorateReply: false,
|
decorateReply: false,
|
||||||
});
|
});
|
||||||
|
fastify.addHook('onRequest', handleRequestRedirectToOmitSearch);
|
||||||
|
done();
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
const port = (process.env.VITE_PORT ?? '5173');
|
const port = (process.env.VITE_PORT ?? '5173');
|
||||||
fastify.register(fastifyProxy, {
|
fastify.register(fastifyProxy, {
|
||||||
|
@ -292,12 +298,17 @@ export class ClientServerService {
|
||||||
decorateReply: false,
|
decorateReply: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
fastify.register((fastify, options, done) => {
|
||||||
fastify.register(fastifyStatic, {
|
fastify.register(fastifyStatic, {
|
||||||
root: tarball,
|
root: tarball,
|
||||||
prefix: '/tarball/',
|
prefix: '/tarball/',
|
||||||
|
maxAge: ms('30 days'),
|
||||||
immutable: true,
|
immutable: true,
|
||||||
decorateReply: false,
|
decorateReply: false,
|
||||||
});
|
});
|
||||||
|
fastify.addHook('onRequest', handleRequestRedirectToOmitSearch);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
fastify.get('/favicon.ico', async (request, reply) => {
|
fastify.get('/favicon.ico', async (request, reply) => {
|
||||||
return reply.sendFile('/favicon.ico', staticAssets);
|
return reply.sendFile('/favicon.ico', staticAssets);
|
||||||
|
|
|
@ -7,10 +7,11 @@ process.env.NODE_ENV = 'test';
|
||||||
|
|
||||||
import * as assert from 'assert';
|
import * as assert from 'assert';
|
||||||
import { MiNote } from '@/models/Note.js';
|
import { MiNote } from '@/models/Note.js';
|
||||||
import type { Packed } from '@/misc/json-schema.js';
|
|
||||||
import { api, initTestDb, makeStreamCatcher, post, signup, uploadFile } from '../utils.js';
|
import { api, initTestDb, makeStreamCatcher, post, signup, uploadFile } from '../utils.js';
|
||||||
import type * as misskey from 'misskey-js';
|
import type * as misskey from 'misskey-js';
|
||||||
import type{ Repository } from 'typeorm';
|
import type{ Repository } from 'typeorm'
|
||||||
|
import type { Packed } from '@/misc/json-schema.js';
|
||||||
|
|
||||||
|
|
||||||
describe('Drive', () => {
|
describe('Drive', () => {
|
||||||
let Notes: Repository<MiNote>;
|
let Notes: Repository<MiNote>;
|
||||||
|
@ -30,7 +31,7 @@ describe('Drive', () => {
|
||||||
|
|
||||||
const marker = Math.random().toString();
|
const marker = Math.random().toString();
|
||||||
|
|
||||||
const url = 'https://raw.githubusercontent.com/misskey-dev/misskey/develop/packages/backend/test/resources/Lenna.jpg';
|
const url = 'https://raw.githubusercontent.com/misskey-dev/misskey/develop/packages/backend/test/resources/Lenna.jpg'
|
||||||
|
|
||||||
const catcher = makeStreamCatcher(
|
const catcher = makeStreamCatcher(
|
||||||
alice,
|
alice,
|
||||||
|
@ -50,7 +51,7 @@ describe('Drive', () => {
|
||||||
assert.strictEqual(res.status, 204);
|
assert.strictEqual(res.status, 204);
|
||||||
assert.strictEqual(file.name, 'Lenna.jpg');
|
assert.strictEqual(file.name, 'Lenna.jpg');
|
||||||
assert.strictEqual(file.type, 'image/jpeg');
|
assert.strictEqual(file.type, 'image/jpeg');
|
||||||
});
|
})
|
||||||
|
|
||||||
test('ローカルからアップロードできる', async () => {
|
test('ローカルからアップロードできる', async () => {
|
||||||
// APIレスポンスを直接使用するので utils.js uploadFile が通過することで成功とする
|
// APIレスポンスを直接使用するので utils.js uploadFile が通過することで成功とする
|
||||||
|
@ -58,27 +59,27 @@ describe('Drive', () => {
|
||||||
const res = await uploadFile(alice, { path: 'Lenna.jpg', name: 'テスト画像' });
|
const res = await uploadFile(alice, { path: 'Lenna.jpg', name: 'テスト画像' });
|
||||||
|
|
||||||
assert.strictEqual(res.body?.name, 'テスト画像.jpg');
|
assert.strictEqual(res.body?.name, 'テスト画像.jpg');
|
||||||
assert.strictEqual(res.body.type, 'image/jpeg');
|
assert.strictEqual(res.body?.type, 'image/jpeg');
|
||||||
});
|
})
|
||||||
|
|
||||||
test('添付ノート一覧を取得できる', async () => {
|
test('添付ノート一覧を取得できる', async () => {
|
||||||
const ids = (await Promise.all([uploadFile(alice), uploadFile(alice), uploadFile(alice)])).map(elm => elm.body!.id);
|
const ids = (await Promise.all([uploadFile(alice), uploadFile(alice), uploadFile(alice)])).map(elm => elm.body!.id)
|
||||||
|
|
||||||
const note0 = await post(alice, { fileIds: [ids[0]] });
|
const note0 = await post(alice, { fileIds: [ids[0]] });
|
||||||
const note1 = await post(alice, { fileIds: [ids[0], ids[1]] });
|
const note1 = await post(alice, { fileIds: [ids[0], ids[1]] });
|
||||||
|
|
||||||
const attached0 = await api('drive/files/attached-notes', { fileId: ids[0] }, alice);
|
const attached0 = await api('drive/files/attached-notes', { fileId: ids[0] }, alice);
|
||||||
assert.strictEqual(attached0.body.length, 2);
|
assert.strictEqual(attached0.body.length, 2);
|
||||||
assert.strictEqual(attached0.body[0].id, note1.id);
|
assert.strictEqual(attached0.body[0].id, note1.id)
|
||||||
assert.strictEqual(attached0.body[1].id, note0.id);
|
assert.strictEqual(attached0.body[1].id, note0.id)
|
||||||
|
|
||||||
const attached1 = await api('drive/files/attached-notes', { fileId: ids[1] }, alice);
|
const attached1 = await api('drive/files/attached-notes', { fileId: ids[1] }, alice);
|
||||||
assert.strictEqual(attached1.body.length, 1);
|
assert.strictEqual(attached1.body.length, 1);
|
||||||
assert.strictEqual(attached1.body[0].id, note1.id);
|
assert.strictEqual(attached1.body[0].id, note1.id)
|
||||||
|
|
||||||
const attached2 = await api('drive/files/attached-notes', { fileId: ids[2] }, alice);
|
const attached2 = await api('drive/files/attached-notes', { fileId: ids[2] }, alice);
|
||||||
assert.strictEqual(attached2.body.length, 0);
|
assert.strictEqual(attached2.body.length, 0)
|
||||||
});
|
})
|
||||||
|
|
||||||
test('添付ノート一覧は他の人から見えない', async () => {
|
test('添付ノート一覧は他の人から見えない', async () => {
|
||||||
const file = await uploadFile(alice);
|
const file = await uploadFile(alice);
|
||||||
|
@ -88,6 +89,7 @@ describe('Drive', () => {
|
||||||
const res = await api('drive/files/attached-notes', { fileId: file.body!.id }, bob);
|
const res = await api('drive/files/attached-notes', { fileId: file.body!.id }, bob);
|
||||||
assert.strictEqual(res.status, 400);
|
assert.strictEqual(res.status, 400);
|
||||||
assert.strictEqual('error' in res.body, true);
|
assert.strictEqual('error' in res.body, true);
|
||||||
});
|
|
||||||
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -663,7 +663,7 @@ describe('Note', () => {
|
||||||
assert.strictEqual(note2.body.error.code, 'CONTAINS_PROHIBITED_WORDS');
|
assert.strictEqual(note2.body.error.code, 'CONTAINS_PROHIBITED_WORDS');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('禁止ワードを含んでいてもリモートノートはエラーにならない', async () => {
|
test('禁止ワードを含んでるリモートノートもエラーになる', async () => {
|
||||||
const prohibited = await api('admin/update-meta', {
|
const prohibited = await api('admin/update-meta', {
|
||||||
prohibitedWords: [
|
prohibitedWords: [
|
||||||
'test',
|
'test',
|
||||||
|
@ -678,7 +678,7 @@ describe('Note', () => {
|
||||||
text: 'hogetesthuge',
|
text: 'hogetesthuge',
|
||||||
}, tom);
|
}, tom);
|
||||||
|
|
||||||
assert.strictEqual(note1.status, 200);
|
assert.strictEqual(note1.status, 400);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -100,6 +100,7 @@ describe('ActivityPub', () => {
|
||||||
perRemoteUserUserTimelineCacheMax: 100,
|
perRemoteUserUserTimelineCacheMax: 100,
|
||||||
blockedHosts: [] as string[],
|
blockedHosts: [] as string[],
|
||||||
sensitiveWords: [] as string[],
|
sensitiveWords: [] as string[],
|
||||||
|
prohibitedWords: [] as string[],
|
||||||
} as MiMeta;
|
} as MiMeta;
|
||||||
let meta = metaInitial;
|
let meta = metaInitial;
|
||||||
|
|
||||||
|
|
|
@ -13,10 +13,10 @@ import fetch, { File, RequestInit } from 'node-fetch';
|
||||||
import { DataSource } from 'typeorm';
|
import { DataSource } from 'typeorm';
|
||||||
import { JSDOM } from 'jsdom';
|
import { JSDOM } from 'jsdom';
|
||||||
import { DEFAULT_POLICIES } from '@/core/RoleService.js';
|
import { DEFAULT_POLICIES } from '@/core/RoleService.js';
|
||||||
import { Packed } from '@/misc/json-schema.js';
|
|
||||||
import { entities } from '../src/postgres.js';
|
import { entities } from '../src/postgres.js';
|
||||||
import { loadConfig } from '../src/config.js';
|
import { loadConfig } from '../src/config.js';
|
||||||
import type * as misskey from 'misskey-js';
|
import type * as misskey from 'misskey-js';
|
||||||
|
import { Packed } from '@/misc/json-schema.js';
|
||||||
|
|
||||||
export { server as startServer, jobQueue as startJobQueue } from '@/boot/common.js';
|
export { server as startServer, jobQueue as startJobQueue } from '@/boot/common.js';
|
||||||
|
|
||||||
|
@ -123,9 +123,9 @@ export function randomString(chars = 'abcdefghijklmnopqrstuvwxyz0123456789', len
|
||||||
function timeoutPromise<T>(p: Promise<T>, timeout: number): Promise<T> {
|
function timeoutPromise<T>(p: Promise<T>, timeout: number): Promise<T> {
|
||||||
return Promise.race([
|
return Promise.race([
|
||||||
p,
|
p,
|
||||||
new Promise((reject) => {
|
new Promise((reject) =>{
|
||||||
setTimeout(() => { reject(new Error('timed out')); }, timeout);
|
setTimeout(() => { reject(new Error('timed out')); }, timeout)
|
||||||
}) as never,
|
}) as never
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -343,7 +343,7 @@ export const uploadUrl = async (user: UserToken, url: string): Promise<Packed<'D
|
||||||
'main',
|
'main',
|
||||||
(msg) => msg.type === 'urlUploadFinished' && msg.body.marker === marker,
|
(msg) => msg.type === 'urlUploadFinished' && msg.body.marker === marker,
|
||||||
(msg) => msg.body.file as Packed<'DriveFile'>,
|
(msg) => msg.body.file as Packed<'DriveFile'>,
|
||||||
60 * 1000,
|
60 * 1000
|
||||||
);
|
);
|
||||||
|
|
||||||
await api('drive/files/upload-from-url', {
|
await api('drive/files/upload-from-url', {
|
||||||
|
@ -439,15 +439,15 @@ export function makeStreamCatcher<T>(
|
||||||
cond: (message: Record<string, any>) => boolean,
|
cond: (message: Record<string, any>) => boolean,
|
||||||
extractor: (message: Record<string, any>) => T,
|
extractor: (message: Record<string, any>) => T,
|
||||||
timeout = 60 * 1000): Promise<T> {
|
timeout = 60 * 1000): Promise<T> {
|
||||||
let ws: WebSocket;
|
let ws: WebSocket
|
||||||
const p = new Promise<T>(async (resolve) => {
|
const p = new Promise<T>(async (resolve) => {
|
||||||
ws = await connectStream(user, channel, (msg) => {
|
ws = await connectStream(user, channel, (msg) => {
|
||||||
if (cond(msg)) {
|
if (cond(msg)) {
|
||||||
resolve(extractor(msg));
|
resolve(extractor(msg))
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}).finally(() => {
|
}).finally(() => {
|
||||||
ws.close();
|
ws?.close();
|
||||||
});
|
});
|
||||||
|
|
||||||
return timeoutPromise(p, timeout);
|
return timeoutPromise(p, timeout);
|
||||||
|
|
|
@ -3,25 +3,28 @@
|
||||||
* SPDX-License-Identifier: AGPL-3.0-only
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { resolve } from 'node:path';
|
import { createRequire } from 'node:module';
|
||||||
|
import { dirname, join, resolve } from 'node:path';
|
||||||
import { fileURLToPath } from 'node:url';
|
import { fileURLToPath } from 'node:url';
|
||||||
import type { StorybookConfig } from '@storybook/vue3-vite';
|
import type { StorybookConfig } from '@storybook/vue3-vite';
|
||||||
import { type Plugin, mergeConfig } from 'vite';
|
import { type Plugin, mergeConfig } from 'vite';
|
||||||
import turbosnap from 'vite-plugin-turbosnap';
|
import turbosnap from 'vite-plugin-turbosnap';
|
||||||
|
|
||||||
const dirname = fileURLToPath(new URL('.', import.meta.url));
|
const require = createRequire(import.meta.url);
|
||||||
|
const _dirname = fileURLToPath(new URL('.', import.meta.url));
|
||||||
|
|
||||||
const config = {
|
const config = {
|
||||||
stories: ['../src/**/*.mdx', '../src/**/*.stories.@(js|jsx|ts|tsx)'],
|
stories: ['../src/**/*.mdx', '../src/**/*.stories.@(js|jsx|ts|tsx)'],
|
||||||
addons: [
|
addons: [
|
||||||
'@storybook/addon-essentials',
|
getAbsolutePath('@storybook/addon-essentials'),
|
||||||
'@storybook/addon-interactions',
|
getAbsolutePath('@storybook/addon-interactions'),
|
||||||
'@storybook/addon-links',
|
getAbsolutePath('@storybook/addon-links'),
|
||||||
'@storybook/addon-storysource',
|
getAbsolutePath('@storybook/addon-storysource'),
|
||||||
resolve(dirname, '../node_modules/storybook-addon-misskey-theme'),
|
getAbsolutePath('@storybook/addon-mdx-gfm'),
|
||||||
|
resolve(_dirname, '../node_modules/storybook-addon-misskey-theme'),
|
||||||
],
|
],
|
||||||
framework: {
|
framework: {
|
||||||
name: '@storybook/vue3-vite',
|
name: getAbsolutePath('@storybook/vue3-vite') as '@storybook/vue3-vite',
|
||||||
options: {},
|
options: {},
|
||||||
},
|
},
|
||||||
docs: {
|
docs: {
|
||||||
|
@ -37,10 +40,13 @@ const config = {
|
||||||
}
|
}
|
||||||
return mergeConfig(config, {
|
return mergeConfig(config, {
|
||||||
plugins: [
|
plugins: [
|
||||||
|
{
|
||||||
// XXX: https://github.com/IanVS/vite-plugin-turbosnap/issues/8
|
// XXX: https://github.com/IanVS/vite-plugin-turbosnap/issues/8
|
||||||
(turbosnap as any as typeof turbosnap['default'])({
|
...(turbosnap as any as typeof turbosnap['default'])({
|
||||||
rootDir: config.root ?? process.cwd(),
|
rootDir: config.root ?? process.cwd(),
|
||||||
}),
|
}),
|
||||||
|
name: 'fake-turbosnap',
|
||||||
|
},
|
||||||
],
|
],
|
||||||
build: {
|
build: {
|
||||||
target: [
|
target: [
|
||||||
|
@ -53,3 +59,7 @@ const config = {
|
||||||
},
|
},
|
||||||
} satisfies StorybookConfig;
|
} satisfies StorybookConfig;
|
||||||
export default config;
|
export default config;
|
||||||
|
|
||||||
|
function getAbsolutePath(value: string): string {
|
||||||
|
return dirname(require.resolve(join(value, 'package.json')));
|
||||||
|
}
|
||||||
|
|
|
@ -3,8 +3,8 @@
|
||||||
* SPDX-License-Identifier: AGPL-3.0-only
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { addons } from '@storybook/addons';
|
|
||||||
import { FORCE_REMOUNT } from '@storybook/core-events';
|
import { FORCE_REMOUNT } from '@storybook/core-events';
|
||||||
|
import { addons } from '@storybook/preview-api';
|
||||||
import { type Preview, setup } from '@storybook/vue3';
|
import { type Preview, setup } from '@storybook/vue3';
|
||||||
import isChromatic from 'chromatic/isChromatic';
|
import isChromatic from 'chromatic/isChromatic';
|
||||||
import { initialize, mswDecorator } from 'msw-storybook-addon';
|
import { initialize, mswDecorator } from 'msw-storybook-addon';
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
"build": "vite build",
|
"build": "vite build",
|
||||||
"storybook-dev": "nodemon --verbose --watch src --ext \"mdx,ts,vue\" --ignore \"*.stories.ts\" --exec \"pnpm build-storybook-pre && pnpm exec storybook dev -p 6006 --ci\"",
|
"storybook-dev": "nodemon --verbose --watch src --ext \"mdx,ts,vue\" --ignore \"*.stories.ts\" --exec \"pnpm build-storybook-pre && pnpm exec storybook dev -p 6006 --ci\"",
|
||||||
"build-storybook-pre": "(tsc -p .storybook || echo done.) && node .storybook/generate.js && node .storybook/preload-locale.js && node .storybook/preload-theme.js",
|
"build-storybook-pre": "(tsc -p .storybook || echo done.) && node .storybook/generate.js && node .storybook/preload-locale.js && node .storybook/preload-theme.js",
|
||||||
"build-storybook": "pnpm build-storybook-pre && storybook build",
|
"build-storybook": "pnpm build-storybook-pre && storybook build --webpack-stats-json storybook-static",
|
||||||
"chromatic": "chromatic",
|
"chromatic": "chromatic",
|
||||||
"test": "vitest --run --globals",
|
"test": "vitest --run --globals",
|
||||||
"test-and-coverage": "vitest --run --coverage --globals",
|
"test-and-coverage": "vitest --run --coverage --globals",
|
||||||
|
@ -28,7 +28,7 @@
|
||||||
"@tabler/icons-webfont": "2.44.0",
|
"@tabler/icons-webfont": "2.44.0",
|
||||||
"@twemoji/parser": "15.0.0",
|
"@twemoji/parser": "15.0.0",
|
||||||
"@vitejs/plugin-vue": "5.0.3",
|
"@vitejs/plugin-vue": "5.0.3",
|
||||||
"@vue/compiler-sfc": "3.4.18",
|
"@vue/compiler-sfc": "3.4.15",
|
||||||
"aiscript-vscode": "github:aiscript-dev/aiscript-vscode#v0.1.2",
|
"aiscript-vscode": "github:aiscript-dev/aiscript-vscode#v0.1.2",
|
||||||
"astring": "1.8.6",
|
"astring": "1.8.6",
|
||||||
"broadcast-channel": "7.0.0",
|
"broadcast-channel": "7.0.0",
|
||||||
|
@ -73,30 +73,30 @@
|
||||||
"uuid": "9.0.1",
|
"uuid": "9.0.1",
|
||||||
"v-code-diff": "1.7.2",
|
"v-code-diff": "1.7.2",
|
||||||
"vite": "5.1.0",
|
"vite": "5.1.0",
|
||||||
"vue": "3.4.18",
|
"vue": "3.4.15",
|
||||||
"vuedraggable": "next"
|
"vuedraggable": "next"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@misskey-dev/eslint-plugin": "1.0.0",
|
"@misskey-dev/eslint-plugin": "1.0.0",
|
||||||
"@misskey-dev/summaly": "5.0.3",
|
"@misskey-dev/summaly": "5.0.3",
|
||||||
"@storybook/addon-actions": "7.6.10",
|
"@storybook/addon-actions": "8.0.0-beta.2",
|
||||||
"@storybook/addon-essentials": "7.6.10",
|
"@storybook/addon-essentials": "8.0.0-beta.2",
|
||||||
"@storybook/addon-interactions": "7.6.10",
|
"@storybook/addon-interactions": "8.0.0-beta.2",
|
||||||
"@storybook/addon-links": "7.6.10",
|
"@storybook/addon-links": "8.0.0-beta.2",
|
||||||
"@storybook/addon-storysource": "7.6.10",
|
"@storybook/addon-mdx-gfm": "8.0.0-beta.2",
|
||||||
"@storybook/addons": "7.6.10",
|
"@storybook/addon-storysource": "8.0.0-beta.2",
|
||||||
"@storybook/blocks": "7.6.10",
|
"@storybook/blocks": "8.0.0-beta.2",
|
||||||
"@storybook/core-events": "7.6.10",
|
"@storybook/components": "8.0.0-beta.2",
|
||||||
"@storybook/jest": "0.2.3",
|
"@storybook/core-events": "8.0.0-beta.2",
|
||||||
"@storybook/manager-api": "7.6.10",
|
"@storybook/manager-api": "8.0.0-beta.2",
|
||||||
"@storybook/preview-api": "7.6.10",
|
"@storybook/preview-api": "8.0.0-beta.2",
|
||||||
"@storybook/react": "7.6.10",
|
"@storybook/react": "8.0.0-beta.2",
|
||||||
"@storybook/react-vite": "7.6.10",
|
"@storybook/react-vite": "8.0.0-beta.2",
|
||||||
"@storybook/testing-library": "0.2.2",
|
"@storybook/test": "8.0.0-beta.2",
|
||||||
"@storybook/theming": "7.6.10",
|
"@storybook/theming": "8.0.0-beta.2",
|
||||||
"@storybook/types": "7.6.10",
|
"@storybook/types": "8.0.0-beta.2",
|
||||||
"@storybook/vue3": "7.6.10",
|
"@storybook/vue3": "8.0.0-beta.2",
|
||||||
"@storybook/vue3-vite": "7.6.10",
|
"@storybook/vue3-vite": "8.0.0-beta.2",
|
||||||
"@testing-library/vue": "8.0.2",
|
"@testing-library/vue": "8.0.2",
|
||||||
"@types/escape-regexp": "0.0.3",
|
"@types/escape-regexp": "0.0.3",
|
||||||
"@types/estree": "1.0.5",
|
"@types/estree": "1.0.5",
|
||||||
|
@ -130,12 +130,12 @@
|
||||||
"react": "18.2.0",
|
"react": "18.2.0",
|
||||||
"react-dom": "18.2.0",
|
"react-dom": "18.2.0",
|
||||||
"start-server-and-test": "2.0.3",
|
"start-server-and-test": "2.0.3",
|
||||||
"storybook": "7.6.10",
|
"storybook": "8.0.0-beta.2",
|
||||||
"storybook-addon-misskey-theme": "github:misskey-dev/storybook-addon-misskey-theme",
|
"storybook-addon-misskey-theme": "github:misskey-dev/storybook-addon-misskey-theme",
|
||||||
"vite-plugin-turbosnap": "1.0.3",
|
"vite-plugin-turbosnap": "1.0.3",
|
||||||
"vitest": "0.34.6",
|
"vitest": "0.34.6",
|
||||||
"vitest-fetch-mock": "0.2.2",
|
"vitest-fetch-mock": "0.2.2",
|
||||||
"vue-component-type-helpers": "^1.8.27",
|
"vue-component-type-helpers": "1.8.27",
|
||||||
"vue-eslint-parser": "9.4.2",
|
"vue-eslint-parser": "9.4.2",
|
||||||
"vue-tsc": "1.8.27"
|
"vue-tsc": "1.8.27"
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,8 +5,7 @@
|
||||||
|
|
||||||
/* eslint-disable @typescript-eslint/explicit-function-return-type */
|
/* eslint-disable @typescript-eslint/explicit-function-return-type */
|
||||||
import { action } from '@storybook/addon-actions';
|
import { action } from '@storybook/addon-actions';
|
||||||
import { expect } from '@storybook/jest';
|
import { expect, userEvent, waitFor, within } from '@storybook/test';
|
||||||
import { userEvent, waitFor, within } from '@storybook/testing-library';
|
|
||||||
import { StoryObj } from '@storybook/vue3';
|
import { StoryObj } from '@storybook/vue3';
|
||||||
import { HttpResponse, http } from 'msw';
|
import { HttpResponse, http } from 'msw';
|
||||||
import { userDetailed } from '../../.storybook/fakes.js';
|
import { userDetailed } from '../../.storybook/fakes.js';
|
||||||
|
|
|
@ -123,7 +123,7 @@ function callback(response?: string) {
|
||||||
function onReceivedMessage(message: MessageEvent) {
|
function onReceivedMessage(message: MessageEvent) {
|
||||||
if (message.data.token) {
|
if (message.data.token) {
|
||||||
if (props.instanceUrl && new URL(message.origin).host === new URL(props.instanceUrl).host) {
|
if (props.instanceUrl && new URL(message.origin).host === new URL(props.instanceUrl).host) {
|
||||||
callback(<string>message.data.token);
|
callback(message.data.token);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,8 +4,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* eslint-disable @typescript-eslint/explicit-function-return-type */
|
/* eslint-disable @typescript-eslint/explicit-function-return-type */
|
||||||
import { expect } from '@storybook/jest';
|
import { expect, userEvent, waitFor, within } from '@storybook/test';
|
||||||
import { userEvent, waitFor, within } from '@storybook/testing-library';
|
|
||||||
import { StoryObj } from '@storybook/vue3';
|
import { StoryObj } from '@storybook/vue3';
|
||||||
import { galleryPost } from '../../.storybook/fakes.js';
|
import { galleryPost } from '../../.storybook/fakes.js';
|
||||||
import MkGalleryPostPreview from './MkGalleryPostPreview.vue';
|
import MkGalleryPostPreview from './MkGalleryPostPreview.vue';
|
||||||
|
|
|
@ -4,8 +4,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* eslint-disable @typescript-eslint/explicit-function-return-type */
|
/* eslint-disable @typescript-eslint/explicit-function-return-type */
|
||||||
import { expect } from '@storybook/jest';
|
import { expect, userEvent, waitFor, within } from '@storybook/test';
|
||||||
import { userEvent, waitFor, within } from '@storybook/testing-library';
|
|
||||||
import { StoryObj } from '@storybook/vue3';
|
import { StoryObj } from '@storybook/vue3';
|
||||||
import { onBeforeUnmount } from 'vue';
|
import { onBeforeUnmount } from 'vue';
|
||||||
import MkSignupServerRules from './MkSignupDialog.rules.vue';
|
import MkSignupServerRules from './MkSignupDialog.rules.vue';
|
||||||
|
|
|
@ -4,8 +4,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* eslint-disable @typescript-eslint/explicit-function-return-type */
|
/* eslint-disable @typescript-eslint/explicit-function-return-type */
|
||||||
import { expect } from '@storybook/jest';
|
import { expect, userEvent, within } from '@storybook/test';
|
||||||
import { userEvent, within } from '@storybook/testing-library';
|
|
||||||
import { StoryObj } from '@storybook/vue3';
|
import { StoryObj } from '@storybook/vue3';
|
||||||
import MkA from './MkA.vue';
|
import MkA from './MkA.vue';
|
||||||
import { tick } from '@/scripts/test-utils.js';
|
import { tick } from '@/scripts/test-utils.js';
|
||||||
|
|
|
@ -5,8 +5,7 @@
|
||||||
|
|
||||||
/* eslint-disable @typescript-eslint/explicit-function-return-type */
|
/* eslint-disable @typescript-eslint/explicit-function-return-type */
|
||||||
import { action } from '@storybook/addon-actions';
|
import { action } from '@storybook/addon-actions';
|
||||||
import { expect } from '@storybook/jest';
|
import { expect, waitFor } from '@storybook/test';
|
||||||
import { waitFor } from '@storybook/testing-library';
|
|
||||||
import { StoryObj } from '@storybook/vue3';
|
import { StoryObj } from '@storybook/vue3';
|
||||||
import MkError from './MkError.vue';
|
import MkError from './MkError.vue';
|
||||||
export const Default = {
|
export const Default = {
|
||||||
|
|
|
@ -5,8 +5,7 @@
|
||||||
|
|
||||||
/* eslint-disable @typescript-eslint/explicit-function-return-type */
|
/* eslint-disable @typescript-eslint/explicit-function-return-type */
|
||||||
import { StoryObj } from '@storybook/vue3';
|
import { StoryObj } from '@storybook/vue3';
|
||||||
import { within } from '@storybook/testing-library';
|
import { expect, within } from '@storybook/test';
|
||||||
import { expect } from '@storybook/jest';
|
|
||||||
import MkMisskeyFlavoredMarkdown from './MkMisskeyFlavoredMarkdown.js';
|
import MkMisskeyFlavoredMarkdown from './MkMisskeyFlavoredMarkdown.js';
|
||||||
export const Default = {
|
export const Default = {
|
||||||
render(args) {
|
render(args) {
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* eslint-disable @typescript-eslint/explicit-function-return-type */
|
/* eslint-disable @typescript-eslint/explicit-function-return-type */
|
||||||
import { waitFor } from '@storybook/testing-library';
|
import { waitFor } from '@storybook/test';
|
||||||
import { StoryObj } from '@storybook/vue3';
|
import { StoryObj } from '@storybook/vue3';
|
||||||
import MkPageHeader from './MkPageHeader.vue';
|
import MkPageHeader from './MkPageHeader.vue';
|
||||||
export const Empty = {
|
export const Empty = {
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* eslint-disable @typescript-eslint/explicit-function-return-type */
|
/* eslint-disable @typescript-eslint/explicit-function-return-type */
|
||||||
import { expect } from '@storybook/jest';
|
import { expect } from '@storybook/test';
|
||||||
import { StoryObj } from '@storybook/vue3';
|
import { StoryObj } from '@storybook/vue3';
|
||||||
import MkTime from './MkTime.vue';
|
import MkTime from './MkTime.vue';
|
||||||
import { i18n } from '@/i18n.js';
|
import { i18n } from '@/i18n.js';
|
||||||
|
|
|
@ -4,8 +4,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* eslint-disable @typescript-eslint/explicit-function-return-type */
|
/* eslint-disable @typescript-eslint/explicit-function-return-type */
|
||||||
import { expect } from '@storybook/jest';
|
import { expect, userEvent, waitFor, within } from '@storybook/test';
|
||||||
import { userEvent, waitFor, within } from '@storybook/testing-library';
|
|
||||||
import { StoryObj } from '@storybook/vue3';
|
import { StoryObj } from '@storybook/vue3';
|
||||||
import { HttpResponse, http } from 'msw';
|
import { HttpResponse, http } from 'msw';
|
||||||
import { commonHandlers } from '../../../.storybook/mocks.js';
|
import { commonHandlers } from '../../../.storybook/mocks.js';
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* eslint-disable @typescript-eslint/explicit-function-return-type */
|
/* eslint-disable @typescript-eslint/explicit-function-return-type */
|
||||||
import { expect } from '@storybook/jest';
|
import { expect } from '@storybook/test';
|
||||||
import { StoryObj } from '@storybook/vue3';
|
import { StoryObj } from '@storybook/vue3';
|
||||||
import { userDetailed } from '../../../.storybook/fakes.js';
|
import { userDetailed } from '../../../.storybook/fakes.js';
|
||||||
import MkUserName from './MkUserName.vue';
|
import MkUserName from './MkUserName.vue';
|
||||||
|
|
|
@ -17,7 +17,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
<div>{{ i18n.ts.youShouldUpgradeClient }}</div>
|
<div>{{ i18n.ts.youShouldUpgradeClient }}</div>
|
||||||
<MkButton style="margin: 8px auto;" @click="reload">{{ i18n.ts.reload }}</MkButton>
|
<MkButton style="margin: 8px auto;" @click="reload">{{ i18n.ts.reload }}</MkButton>
|
||||||
</template>
|
</template>
|
||||||
<div><MkA to="/docs/general/troubleshooting" class="_link">{{ i18n.ts.troubleshooting }}</MkA></div>
|
<div><MkLink url="https://misskey-hub.net/docs/for-users/resources/troubleshooting/" target="_blank">{{ i18n.ts.troubleshooting }}</MkLink></div>
|
||||||
<div v-if="error" style="opacity: 0.7;">ERROR: {{ error }}</div>
|
<div v-if="error" style="opacity: 0.7;">ERROR: {{ error }}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -28,6 +28,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
import { ref, computed } from 'vue';
|
import { ref, computed } from 'vue';
|
||||||
import * as Misskey from 'misskey-js';
|
import * as Misskey from 'misskey-js';
|
||||||
import MkButton from '@/components/MkButton.vue';
|
import MkButton from '@/components/MkButton.vue';
|
||||||
|
import MkLink from '@/components/MkLink.vue';
|
||||||
import { version } from '@/config.js';
|
import { version } from '@/config.js';
|
||||||
import { misskeyApi } from '@/scripts/misskey-api.js';
|
import { misskeyApi } from '@/scripts/misskey-api.js';
|
||||||
import { unisonReload } from '@/scripts/unison-reload.js';
|
import { unisonReload } from '@/scripts/unison-reload.js';
|
||||||
|
|
|
@ -101,7 +101,7 @@ const announcements = {
|
||||||
limit: 10,
|
limit: 10,
|
||||||
};
|
};
|
||||||
|
|
||||||
const isTimelineAvailable = ref(instance.policies.ltlAvailable || instance.policies.gtlAvailable);
|
const isTimelineAvailable = ref(instance.policies?.ltlAvailable || instance.policies?.gtlAvailable);
|
||||||
|
|
||||||
const showMenu = ref(false);
|
const showMenu = ref(false);
|
||||||
const isDesktop = ref(window.innerWidth >= DESKTOP_THRESHOLD);
|
const isDesktop = ref(window.innerWidth >= DESKTOP_THRESHOLD);
|
||||||
|
|
3439
pnpm-lock.yaml
3439
pnpm-lock.yaml
File diff suppressed because it is too large
Load Diff
|
@ -70,7 +70,7 @@ execa('pnpm', ['--filter', 'backend', 'dev'], {
|
||||||
stderr: process.stderr,
|
stderr: process.stderr,
|
||||||
});
|
});
|
||||||
|
|
||||||
execa('pnpm', ['--filter', 'frontend', 'dev'], {
|
execa('pnpm', ['--filter', 'frontend', process.env.MK_DEV_PREFER === 'backend' ? 'dev' : 'watch'], {
|
||||||
cwd: _dirname + '/../',
|
cwd: _dirname + '/../',
|
||||||
stdout: process.stdout,
|
stdout: process.stdout,
|
||||||
stderr: process.stderr,
|
stderr: process.stderr,
|
||||||
|
|
Loading…
Reference in New Issue