Compare commits
13 Commits
7d396a5e23
...
c9374adea1
Author | SHA1 | Date | |
---|---|---|---|
|
c9374adea1 | ||
|
6cebb2b9ed | ||
|
9782370e86 | ||
|
6a697785dc | ||
|
ee135b7e3c | ||
|
443335c662 | ||
|
729ad19c3a | ||
|
7d7552e076 | ||
|
e6fb07165e | ||
|
781f60275a | ||
|
122ed3c82d | ||
|
9559fbefe0 | ||
|
fcd75902cc |
@ -6,7 +6,7 @@
|
||||
"type": "git",
|
||||
"url": "https://git.psec.dev/oscar-surf/misskey.git"
|
||||
},
|
||||
"packageManager": "pnpm@9.12.2",
|
||||
"packageManager": "pnpm@9.12.3",
|
||||
"workspaces": [
|
||||
"packages/frontend",
|
||||
"packages/backend",
|
||||
@ -47,13 +47,16 @@
|
||||
},
|
||||
"resolutions": {
|
||||
"@tensorflow/tfjs-core": "4.22.0",
|
||||
"axios": "1.7.7",
|
||||
"chokidar": "4.0.1",
|
||||
"cookie": "1.0.1",
|
||||
"cookie-signature": "1.2.2",
|
||||
"debug": "4.3.7",
|
||||
"esbuild": "0.24.0",
|
||||
"jpeg-js": "0.4.4",
|
||||
"lodash": "4.17.21",
|
||||
"sharp": "0.33.5",
|
||||
"tough-cookie": "5.0.0",
|
||||
"web-streams-polyfill": "4.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
@ -69,7 +72,7 @@
|
||||
"@typescript-eslint/eslint-plugin": "7.10.0",
|
||||
"@typescript-eslint/parser": "7.10.0",
|
||||
"cross-env": "7.0.3",
|
||||
"cypress": "13.15.1",
|
||||
"cypress": "13.15.2",
|
||||
"eslint": "8.57.1",
|
||||
"ncp": "2.0.0",
|
||||
"start-server-and-test": "2.0.8"
|
||||
|
@ -34,16 +34,16 @@
|
||||
"generate-api-json": "pnpm build && node ./scripts/generate_api_json.js"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@swc/core-darwin-arm64": "1.8.0",
|
||||
"@swc/core-darwin-x64": "1.8.0",
|
||||
"@swc/core-linux-arm-gnueabihf": "1.8.0",
|
||||
"@swc/core-linux-arm64-gnu": "1.8.0",
|
||||
"@swc/core-linux-arm64-musl": "1.8.0",
|
||||
"@swc/core-linux-x64-gnu": "1.8.0",
|
||||
"@swc/core-linux-x64-musl": "1.8.0",
|
||||
"@swc/core-win32-arm64-msvc": "1.8.0",
|
||||
"@swc/core-win32-ia32-msvc": "1.8.0",
|
||||
"@swc/core-win32-x64-msvc": "1.8.0",
|
||||
"@swc/core-darwin-arm64": "1.9.1",
|
||||
"@swc/core-darwin-x64": "1.9.1",
|
||||
"@swc/core-linux-arm-gnueabihf": "1.9.1",
|
||||
"@swc/core-linux-arm64-gnu": "1.9.1",
|
||||
"@swc/core-linux-arm64-musl": "1.9.1",
|
||||
"@swc/core-linux-x64-gnu": "1.9.1",
|
||||
"@swc/core-linux-x64-musl": "1.9.1",
|
||||
"@swc/core-win32-arm64-msvc": "1.9.1",
|
||||
"@swc/core-win32-ia32-msvc": "1.9.1",
|
||||
"@swc/core-win32-x64-msvc": "1.9.1",
|
||||
"@tensorflow/tfjs": "4.22.0",
|
||||
"@tensorflow/tfjs-node": "4.22.0",
|
||||
"bufferutil": "4.0.8",
|
||||
@ -64,8 +64,8 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@authenio/samlify-node-xmllint": "2.0.0",
|
||||
"@aws-sdk/client-s3": "3.685.0",
|
||||
"@aws-sdk/lib-storage": "3.685.0",
|
||||
"@aws-sdk/client-s3": "3.687.0",
|
||||
"@aws-sdk/lib-storage": "3.687.0",
|
||||
"@bull-board/api": "6.3.3",
|
||||
"@bull-board/fastify": "6.3.3",
|
||||
"@bull-board/ui": "6.3.3",
|
||||
@ -81,8 +81,8 @@
|
||||
"@fastify/static": "8.0.2",
|
||||
"@fastify/view": "10.0.1",
|
||||
"@misskey-dev/sharp-read-bmp": "1.2.0",
|
||||
"@misskey-dev/summaly": "5.1.0",
|
||||
"@napi-rs/canvas": "0.1.59",
|
||||
"@misskey-dev/summaly": "MisskeyIO/summaly#5.1.1",
|
||||
"@napi-rs/canvas": "0.1.60",
|
||||
"@nestjs/common": "10.4.7",
|
||||
"@nestjs/core": "10.4.7",
|
||||
"@nestjs/testing": "10.4.7",
|
||||
@ -91,7 +91,7 @@
|
||||
"@sinonjs/fake-timers": "11.3.1",
|
||||
"@smithy/node-http-handler": "3.2.5",
|
||||
"@swc/cli": "0.5.0",
|
||||
"@swc/core": "1.8.0",
|
||||
"@swc/core": "1.9.1",
|
||||
"@twemoji/parser": "15.1.1",
|
||||
"accepts": "1.3.8",
|
||||
"ajv": "8.17.1",
|
||||
@ -100,9 +100,9 @@
|
||||
"bcryptjs": "2.4.3",
|
||||
"blurhash": "2.0.5",
|
||||
"body-parser": "1.20.3",
|
||||
"bullmq": "5.24.0",
|
||||
"bullmq": "5.25.2",
|
||||
"cacheable-lookup": "7.0.0",
|
||||
"cbor": "10.0.2",
|
||||
"cbor": "10.0.3",
|
||||
"chalk": "5.3.0",
|
||||
"chalk-template": "1.1.0",
|
||||
"chokidar": "4.0.1",
|
||||
@ -119,7 +119,7 @@
|
||||
"fluent-ffmpeg": "2.1.3",
|
||||
"form-data": "4.0.1",
|
||||
"got": "14.4.4",
|
||||
"happy-dom": "15.9.0",
|
||||
"happy-dom": "15.11.0",
|
||||
"hpagent": "1.2.0",
|
||||
"htmlescape": "1.1.1",
|
||||
"http-link-header": "1.1.3",
|
||||
@ -154,7 +154,7 @@
|
||||
"parse5": "7.2.1",
|
||||
"pg": "8.13.1",
|
||||
"pino": "9.5.0",
|
||||
"pino-pretty": "11.3.0",
|
||||
"pino-pretty": "12.0.0",
|
||||
"pkce-challenge": "4.1.0",
|
||||
"probe-image-size": "7.2.3",
|
||||
"promise-limit": "2.7.0",
|
||||
|
@ -4,5 +4,5 @@
|
||||
*/
|
||||
|
||||
export function sqlLikeEscape(s: string) {
|
||||
return s.replace(/([%_])/g, '\\$1');
|
||||
return s.replace(/([\\%_])/g, '\\$1');
|
||||
}
|
||||
|
@ -495,14 +495,28 @@ export class SAMLIdentifyProviderService {
|
||||
'#text': user.id,
|
||||
},
|
||||
},
|
||||
...(user.name ? [{
|
||||
{
|
||||
'@Name': 'http://schemas.xmlsoap.org/ws/2005/05/identity/claims/upn',
|
||||
'saml:AttributeValue': {
|
||||
'@xsi:type': 'xs:string',
|
||||
'#text': user.id,
|
||||
},
|
||||
},
|
||||
{
|
||||
'@Name': 'firstName',
|
||||
'@NameFormat': 'urn:oasis:names:tc:SAML:2.0:attrname-format:basic',
|
||||
'saml:AttributeValue': {
|
||||
'@xsi:type': 'xs:string',
|
||||
'#text': user.name,
|
||||
'#text': user.name ? user.name : 'Misskey User',
|
||||
},
|
||||
},
|
||||
{
|
||||
'@Name': 'http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname',
|
||||
'saml:AttributeValue': {
|
||||
'@xsi:type': 'xs:string',
|
||||
'#text': user.name ? user.name : 'Misskey User',
|
||||
},
|
||||
},
|
||||
}] : []),
|
||||
{
|
||||
'@Name': 'lastName',
|
||||
'@NameFormat': 'urn:oasis:names:tc:SAML:2.0:attrname-format:basic',
|
||||
@ -511,6 +525,13 @@ export class SAMLIdentifyProviderService {
|
||||
'#text': `@${user.username}`,
|
||||
},
|
||||
},
|
||||
{
|
||||
'@Name': 'http://schemas.xmlsoap.org/ws/2005/05/identity/claims/surname',
|
||||
'saml:AttributeValue': {
|
||||
'@xsi:type': 'xs:string',
|
||||
'#text': `@${user.username}`,
|
||||
},
|
||||
},
|
||||
{
|
||||
'@Name': 'displayName',
|
||||
'@NameFormat': 'urn:oasis:names:tc:SAML:2.0:attrname-format:basic',
|
||||
|
@ -6,7 +6,7 @@
|
||||
import { Inject, Injectable } from '@nestjs/common';
|
||||
import RE2 from 're2';
|
||||
import { summaly } from '@misskey-dev/summaly';
|
||||
import { SummalyResult } from '@misskey-dev/summaly/built/summary.js';
|
||||
import { SummalyResult } from '@misskey-dev/summaly/dist/summary.js';
|
||||
import { DI } from '@/di-symbols.js';
|
||||
import type { Config } from '@/config.js';
|
||||
import { MetaService } from '@/core/MetaService.js';
|
||||
|
@ -41,7 +41,7 @@
|
||||
"chartjs-chart-matrix": "2.0.1",
|
||||
"chartjs-plugin-gradient": "0.6.1",
|
||||
"chartjs-plugin-zoom": "2.0.1",
|
||||
"chromatic": "11.16.5",
|
||||
"chromatic": "11.18.0",
|
||||
"compare-versions": "6.1.1",
|
||||
"cropperjs": "2.0.0-rc.0",
|
||||
"date-fns": "4.1.0",
|
||||
@ -80,7 +80,7 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@misskey-dev/eslint-plugin": "1.0.0",
|
||||
"@misskey-dev/summaly": "5.1.0",
|
||||
"@misskey-dev/summaly": "MisskeyIO/summaly#5.1.1",
|
||||
"@storybook/addon-actions": "8.4.2",
|
||||
"@storybook/addon-essentials": "8.4.2",
|
||||
"@storybook/addon-interactions": "8.4.2",
|
||||
@ -117,15 +117,15 @@
|
||||
"@vue/runtime-core": "3.5.12",
|
||||
"acorn": "8.14.0",
|
||||
"cross-env": "7.0.3",
|
||||
"cypress": "13.15.1",
|
||||
"cypress": "13.15.2",
|
||||
"eslint": "8.57.1",
|
||||
"eslint-plugin-import": "2.31.0",
|
||||
"eslint-plugin-vue": "9.30.0",
|
||||
"fast-glob": "3.3.2",
|
||||
"happy-dom": "15.9.0",
|
||||
"happy-dom": "15.11.0",
|
||||
"intersection-observer": "0.12.2",
|
||||
"micromatch": "4.0.8",
|
||||
"msw": "2.6.0",
|
||||
"msw": "2.6.2",
|
||||
"msw-storybook-addon": "2.0.4",
|
||||
"nodemon": "3.1.7",
|
||||
"prettier": "3.3.3",
|
||||
|
@ -14,7 +14,7 @@ import { apiUrl } from '@/config.js';
|
||||
import { waiting, popup, popupMenu, success, alert } from '@/os.js';
|
||||
import { generateClientTransactionId, misskeyApi } from '@/scripts/misskey-api.js';
|
||||
import { unisonReload, reloadChannel } from '@/scripts/unison-reload.js';
|
||||
import { set as gtagSet } from 'vue-gtag';
|
||||
import { set as gtagSet, time as gtagTime } from 'vue-gtag';
|
||||
import { instance } from '@/instance.js';
|
||||
|
||||
// TODO: 他のタブと永続化されたstateを同期
|
||||
@ -53,6 +53,7 @@ export async function signout() {
|
||||
const registration = await navigator.serviceWorker.ready;
|
||||
const push = await registration.pushManager.getSubscription();
|
||||
if (push) {
|
||||
const initiateTime = Date.now();
|
||||
await window.fetch(`${apiUrl}/sw/unregister`, {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({
|
||||
@ -63,6 +64,14 @@ export async function signout() {
|
||||
'Content-Type': 'application/json',
|
||||
'X-Client-Transaction-Id': generateClientTransactionId('misskey'),
|
||||
},
|
||||
}).then(() => {
|
||||
if (instance.googleAnalyticsId) {
|
||||
gtagTime({
|
||||
name: 'api',
|
||||
event_category: '/sw/unregister',
|
||||
value: Date.now() - initiateTime,
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -105,6 +114,7 @@ export async function removeAccount(idOrToken: Account['id']) {
|
||||
|
||||
function fetchAccount(token: string, id?: string, forceShowDialog?: boolean): Promise<Account> {
|
||||
return new Promise((done, fail) => {
|
||||
const initiateTime = Date.now();
|
||||
window.fetch(`${apiUrl}/i`, {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({
|
||||
@ -114,6 +124,16 @@ function fetchAccount(token: string, id?: string, forceShowDialog?: boolean): Pr
|
||||
'Content-Type': 'application/json',
|
||||
'X-Client-Transaction-Id': generateClientTransactionId('misskey'),
|
||||
},
|
||||
})
|
||||
.then(res => {
|
||||
if (instance.googleAnalyticsId) {
|
||||
gtagTime({
|
||||
name: 'api',
|
||||
event_category: '/i',
|
||||
value: Date.now() - initiateTime,
|
||||
});
|
||||
}
|
||||
return res;
|
||||
})
|
||||
.then(res => new Promise<Account | { error: Record<string, any> }>((done2, fail2) => {
|
||||
if (res.status >= 500 && res.status < 600) {
|
||||
|
@ -285,6 +285,8 @@ export async function common(createVue: () => App<Element>) {
|
||||
app.use(VueGtag, {
|
||||
bootstrap: false,
|
||||
appName: `Misskey v${version}`,
|
||||
pageTrackerEnabled: true,
|
||||
pageTrackerScreenviewEnabled: true,
|
||||
config: {
|
||||
id: instance.googleAnalyticsId,
|
||||
params: {
|
||||
|
@ -29,7 +29,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { computed, onMounted, onUnmounted, provide, ref, shallowRef } from 'vue';
|
||||
import { computed, nextTick, onMounted, onUnmounted, provide, ref, shallowRef } from 'vue';
|
||||
import RouterView from '@/components/global/RouterView.vue';
|
||||
import MkWindow from '@/components/MkWindow.vue';
|
||||
import { popout as _popout } from '@/scripts/popout.js';
|
||||
@ -43,6 +43,8 @@ import { claimAchievement } from '@/scripts/achievements.js';
|
||||
import { getScrollContainer } from '@/scripts/scroll.js';
|
||||
import { useRouterFactory } from '@/router/supplier.js';
|
||||
import { mainRouter } from '@/router/main.js';
|
||||
import { instance } from '@/instance.js';
|
||||
import { pageview } from 'vue-gtag';
|
||||
|
||||
const props = defineProps<{
|
||||
initialPath: string;
|
||||
@ -110,6 +112,21 @@ provide('shouldOmitHeaderTitle', true);
|
||||
provide('shouldHeaderThin', true);
|
||||
provide('forceSpacerMin', true);
|
||||
|
||||
if (instance.googleAnalyticsId) {
|
||||
pageview({
|
||||
page_title: pageMetadata.value?.title,
|
||||
page_path: windowRouter.getCurrentPath(),
|
||||
});
|
||||
windowRouter.afterEach(() =>
|
||||
nextTick(() =>
|
||||
pageview({
|
||||
page_title: pageMetadata.value?.title,
|
||||
page_path: windowRouter.getCurrentPath(),
|
||||
}),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
const contextmenu = computed(() => ([{
|
||||
icon: 'ti ti-player-eject',
|
||||
text: i18n.ts.showInPage,
|
||||
|
@ -62,7 +62,7 @@ function accepted() {
|
||||
state.value = 'accepted';
|
||||
if (session.value && session.value.app.callbackUrl) {
|
||||
const url = new URL(session.value.app.callbackUrl);
|
||||
if (['javascript:', 'file:', 'data:', 'mailto:', 'tel:'].includes(url.protocol)) throw new Error('invalid url');
|
||||
if (['javascript:', 'file:', 'data:', 'mailto:', 'tel:', 'vbscript:'].includes(url.protocol)) throw new Error('invalid url');
|
||||
location.href = `${session.value.app.callbackUrl}?token=${session.value.token}`;
|
||||
}
|
||||
}
|
||||
|
@ -65,7 +65,7 @@ async function onAccept(token: string) {
|
||||
|
||||
if (props.callback && props.callback !== '') {
|
||||
const cbUrl = new URL(props.callback);
|
||||
if (['javascript:', 'file:', 'data:', 'mailto:', 'tel:'].includes(cbUrl.protocol)) throw new Error('invalid url');
|
||||
if (['javascript:', 'file:', 'data:', 'mailto:', 'tel:', 'vbscript:'].includes(cbUrl.protocol)) throw new Error('invalid url');
|
||||
cbUrl.searchParams.set('session', props.session);
|
||||
location.href = cbUrl.toString();
|
||||
} else {
|
||||
|
@ -36,8 +36,10 @@ export async function getNoteClipMenu(props: {
|
||||
|
||||
const isRenote = (
|
||||
props.note.renote != null &&
|
||||
props.note.reply == null &&
|
||||
props.note.text == null &&
|
||||
props.note.fileIds?.length === 0 &&
|
||||
props.note.cw == null &&
|
||||
props.note.fileIds && props.note.fileIds.length === 0 &&
|
||||
props.note.poll == null
|
||||
);
|
||||
|
||||
@ -164,8 +166,10 @@ export function getNoteMenu(props: {
|
||||
}) {
|
||||
const isRenote = (
|
||||
props.note.renote != null &&
|
||||
props.note.reply == null &&
|
||||
props.note.text == null &&
|
||||
props.note.fileIds?.length === 0 &&
|
||||
props.note.cw == null &&
|
||||
props.note.fileIds && props.note.fileIds.length === 0 &&
|
||||
props.note.poll == null
|
||||
);
|
||||
|
||||
@ -509,8 +513,10 @@ export function getRenoteMenu(props: {
|
||||
}) {
|
||||
const isRenote = (
|
||||
props.note.renote != null &&
|
||||
props.note.reply == null &&
|
||||
props.note.text == null &&
|
||||
props.note.fileIds.length === 0 &&
|
||||
props.note.cw == null &&
|
||||
props.note.fileIds && props.note.fileIds.length === 0 &&
|
||||
props.note.poll == null
|
||||
);
|
||||
|
||||
|
@ -8,6 +8,8 @@ import { ref } from 'vue';
|
||||
import { apiUrl } from '@/config.js';
|
||||
import { $i } from '@/account.js';
|
||||
import { miLocalStorage } from '@/local-storage.js';
|
||||
import { time as gtagTime } from 'vue-gtag';
|
||||
import { instance } from '@/instance.js';
|
||||
export const pendingApiRequestsCount = ref(0);
|
||||
|
||||
let id: string | null = miLocalStorage.getItem('id');
|
||||
@ -66,6 +68,7 @@ export function misskeyApi<
|
||||
if (token !== undefined) (data as any).i = token;
|
||||
|
||||
// Send request
|
||||
const initiateTime = Date.now();
|
||||
window.fetch(`${apiUrl}/${endpoint}`, {
|
||||
method: 'POST',
|
||||
body: JSON.stringify(data),
|
||||
@ -76,6 +79,15 @@ export function misskeyApi<
|
||||
'X-Client-Transaction-Id': generateClientTransactionId(initiator),
|
||||
},
|
||||
signal,
|
||||
}).then(res => {
|
||||
if (instance.googleAnalyticsId) {
|
||||
gtagTime({
|
||||
name: 'api',
|
||||
event_category: `/${endpoint}`,
|
||||
value: Date.now() - initiateTime,
|
||||
});
|
||||
}
|
||||
return res;
|
||||
}).then(handleResponse(resolve, reject)).catch(reject);
|
||||
});
|
||||
|
||||
@ -105,6 +117,7 @@ export function misskeyApiGet<
|
||||
|
||||
const promise = new Promise<_ResT>((resolve, reject) => {
|
||||
// Send request
|
||||
const initiateTime = Date.now();
|
||||
window.fetch(`${apiUrl}/${endpoint}?${query}`, {
|
||||
method: 'GET',
|
||||
credentials: 'omit',
|
||||
@ -112,6 +125,15 @@ export function misskeyApiGet<
|
||||
headers: {
|
||||
'X-Client-Transaction-Id': generateClientTransactionId(initiator),
|
||||
},
|
||||
}).then(res => {
|
||||
if (instance.googleAnalyticsId) {
|
||||
gtagTime({
|
||||
name: 'api-get',
|
||||
event_category: `/${endpoint}?${query}`,
|
||||
value: Date.now() - initiateTime,
|
||||
});
|
||||
}
|
||||
return res;
|
||||
}).then(handleResponse(resolve, reject)).catch(reject);
|
||||
});
|
||||
|
||||
|
@ -11,6 +11,15 @@ import { RateLimiter } from '@/scripts/rate-limiter.js';
|
||||
let ctx: AudioContext;
|
||||
const cache = new Map<string, AudioBuffer>();
|
||||
|
||||
function isValidUrl(url: string): boolean {
|
||||
try {
|
||||
new URL(url);
|
||||
return true;
|
||||
} catch (_) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
export const soundsTypes = [
|
||||
// 音声なし
|
||||
null,
|
||||
@ -260,8 +269,12 @@ export function createSourceNode(buffer: AudioBuffer, opts: {
|
||||
*/
|
||||
export async function getSoundDuration(file: string): Promise<number> {
|
||||
const audioEl = document.createElement('audio');
|
||||
audioEl.src = file;
|
||||
return new Promise((resolve) => {
|
||||
audioEl.src = isValidUrl(file) ? file : '';
|
||||
return new Promise((resolve, reject) => {
|
||||
if (!audioEl.src) {
|
||||
reject(new Error('Invalid URL'));
|
||||
return;
|
||||
}
|
||||
const si = setInterval(() => {
|
||||
if (audioEl.readyState > 0) {
|
||||
resolve(audioEl.duration * 1000);
|
||||
|
@ -27,7 +27,7 @@ export function usageReport(data: UsageReport) {
|
||||
|
||||
if (usageReportBuffer.length > 0) {
|
||||
const last = usageReportBuffer[usageReportBuffer.length - 1];
|
||||
if (last.t === data.t && last.e === data.e && last.a === data.a) return;
|
||||
if (last.t === data.t && last.e === data.e && last.i === data.i && last.a === data.a) return;
|
||||
}
|
||||
|
||||
usageReportBuffer.push(data);
|
||||
|
@ -59,7 +59,7 @@
|
||||
],
|
||||
"dependencies": {
|
||||
"@swc/cli": "0.5.0",
|
||||
"@swc/core": "1.8.0",
|
||||
"@swc/core": "1.9.1",
|
||||
"eventemitter3": "5.0.1",
|
||||
"reconnecting-websocket": "4.4.0"
|
||||
}
|
||||
|
1193
pnpm-lock.yaml
1193
pnpm-lock.yaml
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user