Merge branch 'develop' into sw-notification-action
This commit is contained in:
commit
a1c45e85cf
332 changed files with 1385 additions and 3411 deletions
|
|
@ -1,11 +1,11 @@
|
|||
<script lang="ts">
|
||||
import { defineComponent, h, TransitionGroup } from 'vue';
|
||||
import { defineComponent, h, PropType, TransitionGroup } from 'vue';
|
||||
import MkAd from '@client/components/global/ad.vue';
|
||||
|
||||
export default defineComponent({
|
||||
props: {
|
||||
items: {
|
||||
type: Array,
|
||||
type: Array as PropType<{ id: string; createdAt: string; _shouldInsertAd_: boolean; }[]>,
|
||||
required: true,
|
||||
},
|
||||
direction: {
|
||||
|
|
|
|||
|
|
@ -87,6 +87,10 @@ export default defineComponent({
|
|||
text: this.file.isSensitive ? this.$ts.unmarkAsSensitive : this.$ts.markAsSensitive,
|
||||
icon: this.file.isSensitive ? 'fas fa-eye' : 'fas fa-eye-slash',
|
||||
action: this.toggleSensitive
|
||||
}, {
|
||||
text: this.$ts.describeFile,
|
||||
icon: 'fas fa-i-cursor',
|
||||
action: this.describe
|
||||
}, null, {
|
||||
text: this.$ts.copyUrl,
|
||||
icon: 'fas fa-link',
|
||||
|
|
@ -150,6 +154,26 @@ export default defineComponent({
|
|||
});
|
||||
},
|
||||
|
||||
describe() {
|
||||
os.popup(import('@client/components/media-caption.vue'), {
|
||||
title: this.$ts.describeFile,
|
||||
input: {
|
||||
placeholder: this.$ts.inputNewDescription,
|
||||
default: this.file.comment !== null ? this.file.comment : '',
|
||||
},
|
||||
image: this.file
|
||||
}, {
|
||||
done: result => {
|
||||
if (!result || result.canceled) return;
|
||||
let comment = result.result;
|
||||
os.api('drive/files/update', {
|
||||
fileId: this.file.id,
|
||||
comment: comment.length == 0 ? null : comment
|
||||
});
|
||||
}
|
||||
}, 'closed');
|
||||
},
|
||||
|
||||
toggleSensitive() {
|
||||
os.api('drive/files/update', {
|
||||
fileId: this.file.id,
|
||||
|
|
|
|||
|
|
@ -139,7 +139,7 @@ export default defineComponent({
|
|||
});
|
||||
}
|
||||
|
||||
this.connection = os.stream.useSharedConnection('drive');
|
||||
this.connection = os.stream.useChannel('drive');
|
||||
|
||||
this.connection.on('fileCreated', this.onStreamDriveFileCreated);
|
||||
this.connection.on('fileUpdated', this.onStreamDriveFileUpdated);
|
||||
|
|
@ -301,7 +301,7 @@ export default defineComponent({
|
|||
}
|
||||
}).then(({ canceled, result: url }) => {
|
||||
if (canceled) return;
|
||||
os.api('drive/files/upload_from_url', {
|
||||
os.api('drive/files/upload-from-url', {
|
||||
url: url,
|
||||
folderId: this.folder ? this.folder.id : undefined
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,7 +1,5 @@
|
|||
<template>
|
||||
<div class="xfbouadm" v-if="meta" :style="{ backgroundImage: `url(${ meta.backgroundImageUrl })` }">
|
||||
|
||||
</div>
|
||||
<div class="xfbouadm" v-if="meta" :style="{ backgroundImage: `url(${ meta.backgroundImageUrl })` }"></div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
|
|
|
|||
|
|
@ -71,7 +71,7 @@ export default defineComponent({
|
|||
},
|
||||
|
||||
mounted() {
|
||||
this.connection = os.stream.useSharedConnection('main');
|
||||
this.connection = os.stream.useChannel('main');
|
||||
|
||||
this.connection.on('follow', this.onFollowChange);
|
||||
this.connection.on('unfollow', this.onFollowChange);
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
<MkModal ref="modal" @click="$refs.modal.close()" @closed="$emit('closed')">
|
||||
<div class="xubzgfga">
|
||||
<header>{{ image.name }}</header>
|
||||
<img :src="image.url" :alt="image.name" :title="image.name" @click="$refs.modal.close()"/>
|
||||
<img :src="image.url" :alt="image.comment" :title="image.comment" @click="$refs.modal.close()"/>
|
||||
<footer>
|
||||
<span>{{ image.type }}</span>
|
||||
<span>{{ bytes(image.size) }}</span>
|
||||
|
|
|
|||
|
|
@ -87,8 +87,6 @@ export default defineComponent({
|
|||
> .icon {
|
||||
padding-left: 2px;
|
||||
font-size: .9em;
|
||||
font-weight: 400;
|
||||
font-style: normal;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
238
src/client/components/media-caption.vue
Normal file
238
src/client/components/media-caption.vue
Normal file
|
|
@ -0,0 +1,238 @@
|
|||
<template>
|
||||
<MkModal ref="modal" @click="done(true)" @closed="$emit('closed')">
|
||||
<div class="container">
|
||||
<div class="fullwidth top-caption">
|
||||
<div class="mk-dialog">
|
||||
<header v-if="title"><Mfm :text="title"/></header>
|
||||
<textarea autofocus v-model="inputValue" :placeholder="input.placeholder" @keydown="onInputKeydown"></textarea>
|
||||
<div class="buttons" v-if="(showOkButton || showCancelButton)">
|
||||
<MkButton inline @click="ok" primary>{{ $ts.ok }}</MkButton>
|
||||
<MkButton inline @click="cancel" >{{ $ts.cancel }}</MkButton>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="hdrwpsaf fullwidth">
|
||||
<header>{{ image.name }}</header>
|
||||
<img :src="image.url" :alt="image.comment" :title="image.comment" @click="$refs.modal.close()"/>
|
||||
<footer>
|
||||
<span>{{ image.type }}</span>
|
||||
<span>{{ bytes(image.size) }}</span>
|
||||
<span v-if="image.properties && image.properties.width">{{ number(image.properties.width) }}px × {{ number(image.properties.height) }}px</span>
|
||||
</footer>
|
||||
</div>
|
||||
</div>
|
||||
</MkModal>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
import MkModal from '@client/components/ui/modal.vue';
|
||||
import MkButton from '@client/components/ui/button.vue';
|
||||
import bytes from '@client/filters/bytes';
|
||||
import number from '@client/filters/number';
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
MkModal,
|
||||
MkButton,
|
||||
},
|
||||
|
||||
props: {
|
||||
image: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
title: {
|
||||
type: String,
|
||||
required: false
|
||||
},
|
||||
input: {
|
||||
required: true
|
||||
},
|
||||
showOkButton: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
showCancelButton: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
cancelableByBgClick: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
},
|
||||
|
||||
emits: ['done', 'closed'],
|
||||
|
||||
data() {
|
||||
return {
|
||||
inputValue: this.input.default ? this.input.default : null
|
||||
};
|
||||
},
|
||||
|
||||
mounted() {
|
||||
document.addEventListener('keydown', this.onKeydown);
|
||||
},
|
||||
|
||||
beforeUnmount() {
|
||||
document.removeEventListener('keydown', this.onKeydown);
|
||||
},
|
||||
|
||||
methods: {
|
||||
bytes,
|
||||
number,
|
||||
|
||||
done(canceled, result?) {
|
||||
this.$emit('done', { canceled, result });
|
||||
this.$refs.modal.close();
|
||||
},
|
||||
|
||||
async ok() {
|
||||
if (!this.showOkButton) return;
|
||||
|
||||
const result = this.inputValue;
|
||||
this.done(false, result);
|
||||
},
|
||||
|
||||
cancel() {
|
||||
this.done(true);
|
||||
},
|
||||
|
||||
onBgClick() {
|
||||
if (this.cancelableByBgClick) {
|
||||
this.cancel();
|
||||
}
|
||||
},
|
||||
|
||||
onKeydown(e) {
|
||||
if (e.which === 27) { // ESC
|
||||
this.cancel();
|
||||
}
|
||||
},
|
||||
|
||||
onInputKeydown(e) {
|
||||
if (e.which === 13) { // Enter
|
||||
if (e.ctrlKey) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
this.ok();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.container {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
flex-direction: row;
|
||||
}
|
||||
@media (max-width: 850px) {
|
||||
.container {
|
||||
flex-direction: column;
|
||||
}
|
||||
.top-caption {
|
||||
padding-bottom: 8px;
|
||||
}
|
||||
}
|
||||
.fullwidth {
|
||||
width: 100%;
|
||||
margin: auto;
|
||||
}
|
||||
.mk-dialog {
|
||||
position: relative;
|
||||
padding: 32px;
|
||||
min-width: 320px;
|
||||
max-width: 480px;
|
||||
box-sizing: border-box;
|
||||
text-align: center;
|
||||
background: var(--panel);
|
||||
border-radius: var(--radius);
|
||||
margin: auto;
|
||||
|
||||
> header {
|
||||
margin: 0 0 8px 0;
|
||||
font-weight: bold;
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
> .buttons {
|
||||
margin-top: 16px;
|
||||
|
||||
> * {
|
||||
margin: 0 8px;
|
||||
}
|
||||
}
|
||||
|
||||
> textarea {
|
||||
display: block;
|
||||
box-sizing: border-box;
|
||||
padding: 0 24px;
|
||||
margin: 0;
|
||||
width: 100%;
|
||||
font-size: 16px;
|
||||
border: none;
|
||||
border-radius: 0;
|
||||
background: transparent;
|
||||
color: var(--fg);
|
||||
font-family: inherit;
|
||||
max-width: 100%;
|
||||
min-width: 100%;
|
||||
min-height: 90px;
|
||||
|
||||
&:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
&:disabled {
|
||||
opacity: 0.5;
|
||||
}
|
||||
}
|
||||
}
|
||||
.hdrwpsaf {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
|
||||
> header,
|
||||
> footer {
|
||||
align-self: center;
|
||||
display: inline-block;
|
||||
padding: 6px 9px;
|
||||
font-size: 90%;
|
||||
background: rgba(0, 0, 0, 0.5);
|
||||
border-radius: 6px;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
> header {
|
||||
margin-bottom: 8px;
|
||||
opacity: 0.9;
|
||||
}
|
||||
|
||||
> img {
|
||||
display: block;
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
object-fit: contain;
|
||||
width: 100%;
|
||||
cursor: zoom-out;
|
||||
image-orientation: from-image;
|
||||
}
|
||||
|
||||
> footer {
|
||||
margin-top: 8px;
|
||||
opacity: 0.8;
|
||||
|
||||
> span + span {
|
||||
margin-left: 0.5em;
|
||||
padding-left: 0.5em;
|
||||
border-left: solid 1px rgba(255, 255, 255, 0.5);
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
<template>
|
||||
<div class="qjewsnkg" v-if="hide" @click="hide = false">
|
||||
<ImgWithBlurhash class="bg" :hash="image.blurhash" :title="image.name"/>
|
||||
<ImgWithBlurhash class="bg" :hash="image.blurhash" :title="image.comment" :alt="image.comment"/>
|
||||
<div class="text">
|
||||
<div>
|
||||
<b><i class="fas fa-exclamation-triangle"></i> {{ $ts.sensitive }}</b>
|
||||
|
|
@ -14,7 +14,7 @@
|
|||
:title="image.name"
|
||||
@click.prevent="onClick"
|
||||
>
|
||||
<ImgWithBlurhash :hash="image.blurhash" :src="url" :alt="image.name" :title="image.name" :cover="false"/>
|
||||
<ImgWithBlurhash :hash="image.blurhash" :src="url" :alt="image.comment" :title="image.comment" :cover="false"/>
|
||||
<div class="gif" v-if="image.type === 'image/gif'">GIF</div>
|
||||
</a>
|
||||
<i class="fas fa-eye-slash" @click="hide = true"></i>
|
||||
|
|
|
|||
|
|
@ -12,10 +12,10 @@
|
|||
<XNotification v-else :notification="notification" :with-time="true" :full="true" class="_panel notification" :key="notification.id"/>
|
||||
</XList>
|
||||
|
||||
<button class="_buttonPrimary" v-appear="$store.state.enableInfiniteScroll ? fetchMore : null" @click="fetchMore" v-show="more" :disabled="moreFetching" :style="{ cursor: moreFetching ? 'wait' : 'pointer' }">
|
||||
<MkButton primary style="margin: var(--margin) auto;" v-appear="$store.state.enableInfiniteScroll ? fetchMore : null" @click="fetchMore" v-show="more" :disabled="moreFetching" :style="{ cursor: moreFetching ? 'wait' : 'pointer' }">
|
||||
<template v-if="!moreFetching">{{ $ts.loadMore }}</template>
|
||||
<template v-if="moreFetching"><MkLoading inline/></template>
|
||||
</button>
|
||||
</MkButton>
|
||||
</div>
|
||||
</transition>
|
||||
</template>
|
||||
|
|
@ -29,12 +29,14 @@ import XList from './date-separated-list.vue';
|
|||
import XNote from './note.vue';
|
||||
import { notificationTypes } from '../../types';
|
||||
import * as os from '@client/os';
|
||||
import MkButton from '@client/components/ui/button.vue';
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
XNotification,
|
||||
XList,
|
||||
XNote,
|
||||
MkButton,
|
||||
},
|
||||
|
||||
mixins: [
|
||||
|
|
@ -88,7 +90,7 @@ export default defineComponent({
|
|||
},
|
||||
|
||||
mounted() {
|
||||
this.connection = os.stream.useSharedConnection('main');
|
||||
this.connection = os.stream.useChannel('main');
|
||||
this.connection.on('notification', this.onNotification);
|
||||
|
||||
this.connection.on('readAllNotifications', () => {
|
||||
|
|
|
|||
|
|
@ -89,6 +89,27 @@ export default defineComponent({
|
|||
file.name = result;
|
||||
});
|
||||
},
|
||||
|
||||
async describe(file) {
|
||||
os.popup(import("@client/components/media-caption.vue"), {
|
||||
title: this.$ts.describeFile,
|
||||
input: {
|
||||
placeholder: this.$ts.inputNewDescription,
|
||||
default: file.comment !== null ? file.comment : "",
|
||||
},
|
||||
image: file
|
||||
}, {
|
||||
done: result => {
|
||||
if (!result || result.canceled) return;
|
||||
let comment = result.result;
|
||||
os.api('drive/files/update', {
|
||||
fileId: file.id,
|
||||
comment: comment.length == 0 ? null : comment
|
||||
});
|
||||
}
|
||||
}, 'closed');
|
||||
},
|
||||
|
||||
showFileMenu(file, ev: MouseEvent) {
|
||||
if (this.menu) return;
|
||||
this.menu = os.modalMenu([{
|
||||
|
|
@ -99,6 +120,10 @@ export default defineComponent({
|
|||
text: file.isSensitive ? this.$ts.unmarkAsSensitive : this.$ts.markAsSensitive,
|
||||
icon: file.isSensitive ? 'fas fa-eye-slash' : 'fas fa-eye',
|
||||
action: () => { this.toggleSensitive(file) }
|
||||
}, {
|
||||
text: this.$ts.describeFile,
|
||||
icon: 'fas fa-i-cursor',
|
||||
action: () => { this.describe(file) }
|
||||
}, {
|
||||
text: this.$ts.attachCancel,
|
||||
icon: 'fas fa-times-circle',
|
||||
|
|
|
|||
|
|
@ -92,33 +92,33 @@ export default defineComponent({
|
|||
this.query = {
|
||||
antennaId: this.antenna
|
||||
};
|
||||
this.connection = os.stream.connectToChannel('antenna', {
|
||||
this.connection = os.stream.useChannel('antenna', {
|
||||
antennaId: this.antenna
|
||||
});
|
||||
this.connection.on('note', prepend);
|
||||
} else if (this.src == 'home') {
|
||||
endpoint = 'notes/timeline';
|
||||
this.connection = os.stream.useSharedConnection('homeTimeline');
|
||||
this.connection = os.stream.useChannel('homeTimeline');
|
||||
this.connection.on('note', prepend);
|
||||
|
||||
this.connection2 = os.stream.useSharedConnection('main');
|
||||
this.connection2 = os.stream.useChannel('main');
|
||||
this.connection2.on('follow', onChangeFollowing);
|
||||
this.connection2.on('unfollow', onChangeFollowing);
|
||||
} else if (this.src == 'local') {
|
||||
endpoint = 'notes/local-timeline';
|
||||
this.connection = os.stream.useSharedConnection('localTimeline');
|
||||
this.connection = os.stream.useChannel('localTimeline');
|
||||
this.connection.on('note', prepend);
|
||||
} else if (this.src == 'social') {
|
||||
endpoint = 'notes/hybrid-timeline';
|
||||
this.connection = os.stream.useSharedConnection('hybridTimeline');
|
||||
this.connection = os.stream.useChannel('hybridTimeline');
|
||||
this.connection.on('note', prepend);
|
||||
} else if (this.src == 'global') {
|
||||
endpoint = 'notes/global-timeline';
|
||||
this.connection = os.stream.useSharedConnection('globalTimeline');
|
||||
this.connection = os.stream.useChannel('globalTimeline');
|
||||
this.connection.on('note', prepend);
|
||||
} else if (this.src == 'mentions') {
|
||||
endpoint = 'notes/mentions';
|
||||
this.connection = os.stream.useSharedConnection('main');
|
||||
this.connection = os.stream.useChannel('main');
|
||||
this.connection.on('mention', prepend);
|
||||
} else if (this.src == 'directs') {
|
||||
endpoint = 'notes/mentions';
|
||||
|
|
@ -130,14 +130,14 @@ export default defineComponent({
|
|||
prepend(note);
|
||||
}
|
||||
};
|
||||
this.connection = os.stream.useSharedConnection('main');
|
||||
this.connection = os.stream.useChannel('main');
|
||||
this.connection.on('mention', onNote);
|
||||
} else if (this.src == 'list') {
|
||||
endpoint = 'notes/user-list-timeline';
|
||||
this.query = {
|
||||
listId: this.list
|
||||
};
|
||||
this.connection = os.stream.connectToChannel('userList', {
|
||||
this.connection = os.stream.useChannel('userList', {
|
||||
listId: this.list
|
||||
});
|
||||
this.connection.on('note', prepend);
|
||||
|
|
@ -148,7 +148,7 @@ export default defineComponent({
|
|||
this.query = {
|
||||
channelId: this.channel
|
||||
};
|
||||
this.connection = os.stream.connectToChannel('channel', {
|
||||
this.connection = os.stream.useChannel('channel', {
|
||||
channelId: this.channel
|
||||
});
|
||||
this.connection.on('note', prepend);
|
||||
|
|
|
|||
|
|
@ -224,8 +224,6 @@ fetchInstance().then(() => {
|
|||
initializeSw();
|
||||
});
|
||||
|
||||
stream.init($i);
|
||||
|
||||
const app = createApp(await (
|
||||
window.location.search === '?zen' ? import('@client/ui/zen.vue') :
|
||||
!$i ? import('@client/ui/visitor.vue') :
|
||||
|
|
@ -357,7 +355,7 @@ if ($i) {
|
|||
}
|
||||
}
|
||||
|
||||
const main = stream.useSharedConnection('main', 'System');
|
||||
const main = stream.useChannel('main', null, 'System');
|
||||
|
||||
// 自分の情報が更新されたとき
|
||||
main.on('meUpdated', i => {
|
||||
|
|
@ -419,10 +417,6 @@ if ($i) {
|
|||
sound.play('channel');
|
||||
});
|
||||
|
||||
main.on('readAllAnnouncements', () => {
|
||||
updateAccount({ hasUnreadAnnouncement: false });
|
||||
});
|
||||
|
||||
// トークンが再生成されたとき
|
||||
// このままではMisskeyが利用できないので強制的にサインアウトさせる
|
||||
main.on('myTokenRegenerated', () => {
|
||||
|
|
|
|||
|
|
@ -1,26 +1,14 @@
|
|||
import { computed, reactive } from 'vue';
|
||||
import * as Misskey from 'misskey-js';
|
||||
import { api } from './os';
|
||||
|
||||
// TODO: 他のタブと永続化されたstateを同期
|
||||
|
||||
export type Instance = {
|
||||
emojis: {
|
||||
category: string;
|
||||
}[];
|
||||
ads: {
|
||||
id: string;
|
||||
ratio: number;
|
||||
place: string;
|
||||
url: string;
|
||||
imageUrl: string;
|
||||
}[];
|
||||
};
|
||||
|
||||
const data = localStorage.getItem('instance');
|
||||
|
||||
// TODO: instanceをリアクティブにするかは再考の余地あり
|
||||
|
||||
export const instance: Instance = reactive(data ? JSON.parse(data) : {
|
||||
export const instance: Misskey.entities.InstanceMetadata = reactive(data ? JSON.parse(data) : {
|
||||
// TODO: set default values
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -3,16 +3,16 @@
|
|||
import { Component, defineAsyncComponent, markRaw, reactive, Ref, ref } from 'vue';
|
||||
import { EventEmitter } from 'eventemitter3';
|
||||
import insertTextAtCursor from 'insert-text-at-cursor';
|
||||
import * as Misskey from 'misskey-js';
|
||||
import * as Sentry from '@sentry/browser';
|
||||
import Stream from '@client/scripts/stream';
|
||||
import { apiUrl, debug } from '@client/config';
|
||||
import { apiUrl, debug, url } from '@client/config';
|
||||
import MkPostFormDialog from '@client/components/post-form-dialog.vue';
|
||||
import MkWaitingDialog from '@client/components/waiting-dialog.vue';
|
||||
import { resolve } from '@client/router';
|
||||
import { $i } from '@client/account';
|
||||
import { defaultStore } from '@client/store';
|
||||
|
||||
export const stream = markRaw(new Stream());
|
||||
export const stream = markRaw(new Misskey.Stream(url, $i));
|
||||
|
||||
export const pendingApiRequestsCount = ref(0);
|
||||
let apiRequestsCount = 0; // for debug
|
||||
|
|
@ -20,7 +20,11 @@ export const apiRequests = ref([]); // for debug
|
|||
|
||||
export const windows = new Map();
|
||||
|
||||
export function api(endpoint: string, data: Record<string, any> = {}, token?: string | null | undefined) {
|
||||
const apiClient = new Misskey.api.APIClient({
|
||||
origin: url,
|
||||
});
|
||||
|
||||
export const api = ((endpoint: string, data: Record<string, any> = {}, token?: string | null | undefined) => {
|
||||
pendingApiRequestsCount.value++;
|
||||
|
||||
const onFinally = () => {
|
||||
|
|
@ -56,7 +60,7 @@ export function api(endpoint: string, data: Record<string, any> = {}, token?: st
|
|||
if (res.status === 200) {
|
||||
resolve(body);
|
||||
if (debug) {
|
||||
log!.res = markRaw(body);
|
||||
log!.res = markRaw(JSON.parse(JSON.stringify(body)));
|
||||
log!.state = 'success';
|
||||
}
|
||||
} else if (res.status === 204) {
|
||||
|
|
@ -90,17 +94,15 @@ export function api(endpoint: string, data: Record<string, any> = {}, token?: st
|
|||
promise.then(onFinally, onFinally);
|
||||
|
||||
return promise;
|
||||
}
|
||||
}) as typeof apiClient.request;
|
||||
|
||||
export function apiWithDialog(
|
||||
export const apiWithDialog = ((
|
||||
endpoint: string,
|
||||
data: Record<string, any> = {},
|
||||
token?: string | null | undefined,
|
||||
onSuccess?: (res: any) => void,
|
||||
onFailure?: (e: Error) => void,
|
||||
) {
|
||||
) => {
|
||||
const promise = api(endpoint, data, token);
|
||||
promiseDialog(promise, onSuccess, onFailure ? onFailure : (e) => {
|
||||
promiseDialog(promise, null, (e) => {
|
||||
dialog({
|
||||
type: 'error',
|
||||
text: e.message + '\n' + (e as any).id,
|
||||
|
|
@ -108,7 +110,7 @@ export function apiWithDialog(
|
|||
});
|
||||
|
||||
return promise;
|
||||
}
|
||||
}) as typeof api;
|
||||
|
||||
export function promiseDialog<T extends Promise<any>>(
|
||||
promise: T,
|
||||
|
|
|
|||
|
|
@ -90,7 +90,7 @@ export default defineComponent({
|
|||
stats: null,
|
||||
serverInfo: null,
|
||||
connection: null,
|
||||
queueConnection: os.stream.useSharedConnection('queueStats'),
|
||||
queueConnection: os.stream.useChannel('queueStats'),
|
||||
memUsage: 0,
|
||||
chartCpuMem: null,
|
||||
chartNet: null,
|
||||
|
|
@ -121,7 +121,7 @@ export default defineComponent({
|
|||
os.api('admin/server-info', {}).then(res => {
|
||||
this.serverInfo = res;
|
||||
|
||||
this.connection = os.stream.useSharedConnection('serverStats');
|
||||
this.connection = os.stream.useChannel('serverStats');
|
||||
this.connection.on('stats', this.onStats);
|
||||
this.connection.on('statsLog', this.onStatsLog);
|
||||
this.connection.send('requestLog', {
|
||||
|
|
|
|||
|
|
@ -92,6 +92,7 @@ export default defineComponent({
|
|||
version,
|
||||
url,
|
||||
stats: null,
|
||||
meta: null,
|
||||
fetchStats: () => os.api('stats', {}),
|
||||
fetchServerInfo: () => os.api('admin/server-info', {}),
|
||||
fetchJobs: () => os.api('admin/queue/deliver-delayed', {}),
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ export default defineComponent({
|
|||
title: this.$ts.jobQueue,
|
||||
icon: 'fas fa-clipboard-list',
|
||||
},
|
||||
connection: os.stream.useSharedConnection('queueStats'),
|
||||
connection: os.stream.useChannel('queueStats'),
|
||||
}
|
||||
},
|
||||
|
||||
|
|
|
|||
|
|
@ -19,6 +19,11 @@
|
|||
<span>{{ $ts.bannerUrl }}</span>
|
||||
</FormInput>
|
||||
|
||||
<FormInput v-model:value="backgroundImageUrl">
|
||||
<template #prefix><i class="fas fa-link"></i></template>
|
||||
<span>{{ $ts.backgroundImageUrl }}</span>
|
||||
</FormInput>
|
||||
|
||||
<FormInput v-model:value="tosUrl">
|
||||
<template #prefix><i class="fas fa-link"></i></template>
|
||||
<span>{{ $ts.tosUrl }}</span>
|
||||
|
|
@ -88,6 +93,7 @@ export default defineComponent({
|
|||
maintainerEmail: null,
|
||||
iconUrl: null,
|
||||
bannerUrl: null,
|
||||
backgroundImageUrl: null,
|
||||
maxNoteTextLength: 0,
|
||||
enableLocalTimeline: false,
|
||||
enableGlobalTimeline: false,
|
||||
|
|
@ -106,6 +112,7 @@ export default defineComponent({
|
|||
this.tosUrl = meta.tosUrl;
|
||||
this.iconUrl = meta.iconUrl;
|
||||
this.bannerUrl = meta.bannerUrl;
|
||||
this.backgroundImageUrl = meta.backgroundImageUrl;
|
||||
this.maintainerName = meta.maintainerName;
|
||||
this.maintainerEmail = meta.maintainerEmail;
|
||||
this.maxNoteTextLength = meta.maxNoteTextLength;
|
||||
|
|
@ -120,6 +127,7 @@ export default defineComponent({
|
|||
tosUrl: this.tosUrl,
|
||||
iconUrl: this.iconUrl,
|
||||
bannerUrl: this.bannerUrl,
|
||||
backgroundImageUrl: this.backgroundImageUrl,
|
||||
maintainerName: this.maintainerName,
|
||||
maintainerEmail: this.maintainerEmail,
|
||||
maxNoteTextLength: this.maxNoteTextLength,
|
||||
|
|
|
|||
|
|
@ -63,7 +63,7 @@ export default defineComponent({
|
|||
},
|
||||
|
||||
mounted() {
|
||||
this.connection = os.stream.useSharedConnection('messagingIndex');
|
||||
this.connection = os.stream.useChannel('messagingIndex');
|
||||
|
||||
this.connection.on('message', this.onMessage);
|
||||
this.connection.on('read', this.onRead);
|
||||
|
|
|
|||
|
|
@ -141,7 +141,7 @@ const Component = defineComponent({
|
|||
this.group = group;
|
||||
}
|
||||
|
||||
this.connection = os.stream.connectToChannel('messaging', {
|
||||
this.connection = os.stream.useChannel('messaging', {
|
||||
otherparty: this.user ? this.user.id : undefined,
|
||||
group: this.group ? this.group.id : undefined,
|
||||
});
|
||||
|
|
|
|||
|
|
@ -350,6 +350,9 @@ export default defineComponent({
|
|||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
@use "sass:math";
|
||||
|
||||
.xqnhankfuuilcwvhgsopeqncafzsquya {
|
||||
text-align: center;
|
||||
|
||||
|
|
@ -388,11 +391,11 @@ export default defineComponent({
|
|||
font-size: 0.8em;
|
||||
|
||||
&:first-child {
|
||||
margin-left: -($gap / 2);
|
||||
margin-left: -(math.div($gap, 2));
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
margin-right: -($gap / 2);
|
||||
margin-right: -(math.div($gap, 2));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -413,11 +416,11 @@ export default defineComponent({
|
|||
font-size: 12px;
|
||||
|
||||
&:first-child {
|
||||
margin-top: -($gap / 2);
|
||||
margin-top: -(math.div($gap, 2));
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: -($gap / 2);
|
||||
margin-bottom: -(math.div($gap, 2));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -61,7 +61,7 @@ export default defineComponent({
|
|||
if (this.connection) {
|
||||
this.connection.dispose();
|
||||
}
|
||||
this.connection = os.stream.connectToChannel('gamesReversiGame', {
|
||||
this.connection = os.stream.useChannel('gamesReversiGame', {
|
||||
gameId: this.game.id
|
||||
});
|
||||
this.connection.on('started', this.onStarted);
|
||||
|
|
|
|||
|
|
@ -92,7 +92,7 @@ export default defineComponent({
|
|||
|
||||
mounted() {
|
||||
if (this.$i) {
|
||||
this.connection = os.stream.useSharedConnection('gamesReversi');
|
||||
this.connection = os.stream.useChannel('gamesReversi');
|
||||
|
||||
this.connection.on('invited', this.onInvited);
|
||||
|
||||
|
|
|
|||
|
|
@ -220,6 +220,9 @@ export default defineComponent({
|
|||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
@use "sass:math";
|
||||
|
||||
.uawsfosz {
|
||||
> div {
|
||||
padding: 24px;
|
||||
|
|
@ -227,12 +230,12 @@ export default defineComponent({
|
|||
> .meter {
|
||||
$size: 12px;
|
||||
background: rgba(0, 0, 0, 0.1);
|
||||
border-radius: ($size / 2);
|
||||
border-radius: math.div($size, 2);
|
||||
overflow: hidden;
|
||||
|
||||
> div {
|
||||
height: $size;
|
||||
border-radius: ($size / 2);
|
||||
border-radius: math.div($size, 2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,8 +4,8 @@
|
|||
<div class="_formLabel"><i class="fab fa-twitter"></i> Twitter</div>
|
||||
<div class="_formPanel" style="padding: 16px;">
|
||||
<p v-if="integrations.twitter">{{ $ts.connectedTo }}: <a :href="`https://twitter.com/${integrations.twitter.screenName}`" rel="nofollow noopener" target="_blank">@{{ integrations.twitter.screenName }}</a></p>
|
||||
<MkButton v-if="integrations.twitter" @click="disconnectTwitter" danger>{{ $ts.disconnectSerice }}</MkButton>
|
||||
<MkButton v-else @click="connectTwitter" primary>{{ $ts.connectSerice }}</MkButton>
|
||||
<MkButton v-if="integrations.twitter" @click="disconnectTwitter" danger>{{ $ts.disconnectService }}</MkButton>
|
||||
<MkButton v-else @click="connectTwitter" primary>{{ $ts.connectService }}</MkButton>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
@ -13,8 +13,8 @@
|
|||
<div class="_formLabel"><i class="fab fa-discord"></i> Discord</div>
|
||||
<div class="_formPanel" style="padding: 16px;">
|
||||
<p v-if="integrations.discord">{{ $ts.connectedTo }}: <a :href="`https://discord.com/users/${integrations.discord.id}`" rel="nofollow noopener" target="_blank">@{{ integrations.discord.username }}#{{ integrations.discord.discriminator }}</a></p>
|
||||
<MkButton v-if="integrations.discord" @click="disconnectDiscord" danger>{{ $ts.disconnectSerice }}</MkButton>
|
||||
<MkButton v-else @click="connectDiscord" primary>{{ $ts.connectSerice }}</MkButton>
|
||||
<MkButton v-if="integrations.discord" @click="disconnectDiscord" danger>{{ $ts.disconnectService }}</MkButton>
|
||||
<MkButton v-else @click="connectDiscord" primary>{{ $ts.connectService }}</MkButton>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
@ -22,8 +22,8 @@
|
|||
<div class="_formLabel"><i class="fab fa-github"></i> GitHub</div>
|
||||
<div class="_formPanel" style="padding: 16px;">
|
||||
<p v-if="integrations.github">{{ $ts.connectedTo }}: <a :href="`https://github.com/${integrations.github.login}`" rel="nofollow noopener" target="_blank">@{{ integrations.github.login }}</a></p>
|
||||
<MkButton v-if="integrations.github" @click="disconnectGithub" danger>{{ $ts.disconnectSerice }}</MkButton>
|
||||
<MkButton v-else @click="connectGithub" primary>{{ $ts.connectSerice }}</MkButton>
|
||||
<MkButton v-if="integrations.github" @click="disconnectGithub" danger>{{ $ts.disconnectService }}</MkButton>
|
||||
<MkButton v-else @click="connectGithub" primary>{{ $ts.connectService }}</MkButton>
|
||||
</div>
|
||||
</div>
|
||||
</FormBase>
|
||||
|
|
|
|||
|
|
@ -71,7 +71,7 @@
|
|||
<FormButton primary v-else @click="wallpaper = null">{{ $ts.removeWallpaper }}</FormButton>
|
||||
|
||||
<FormGroup>
|
||||
<FormLink to="https://assets.msky.cafe/theme/list" external><template #icon><i class="fas fa-globe"></i></template>{{ $ts._theme.explore }}</FormLink>
|
||||
<FormLink to="https://assets.misskey.io/theme/list" external><template #icon><i class="fas fa-globe"></i></template>{{ $ts._theme.explore }}</FormLink>
|
||||
<FormLink to="/settings/theme/install"><template #icon><i class="fas fa-download"></i></template>{{ $ts._theme.install }}</FormLink>
|
||||
</FormGroup>
|
||||
|
||||
|
|
|
|||
|
|
@ -3,6 +3,11 @@ import * as url from '../../prelude/url';
|
|||
|
||||
export function getStaticImageUrl(baseUrl: string): string {
|
||||
const u = new URL(baseUrl);
|
||||
if (u.href.startsWith(`${instanceUrl}/proxy/`)) {
|
||||
// もう既にproxyっぽそうだったらsearchParams付けるだけ
|
||||
u.searchParams.set('static', '1');
|
||||
return u.href;
|
||||
}
|
||||
const dummy = `${u.host}${u.pathname}`; // 拡張子がないとキャッシュしてくれないCDNがあるので
|
||||
return `${instanceUrl}/proxy/${dummy}?${url.query({
|
||||
url: u.href,
|
||||
|
|
|
|||
|
|
@ -47,7 +47,7 @@ export function selectFile(src: any, label: string | null, multiple = false) {
|
|||
|
||||
const marker = Math.random().toString(); // TODO: UUIDとか使う
|
||||
|
||||
const connection = os.stream.useSharedConnection('main');
|
||||
const connection = os.stream.useChannel('main');
|
||||
connection.on('urlUploadFinished', data => {
|
||||
if (data.marker === marker) {
|
||||
res(multiple ? [data.file] : data.file);
|
||||
|
|
@ -55,7 +55,7 @@ export function selectFile(src: any, label: string | null, multiple = false) {
|
|||
}
|
||||
});
|
||||
|
||||
os.api('drive/files/upload_from_url', {
|
||||
os.api('drive/files/upload-from-url', {
|
||||
url: url,
|
||||
marker
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,312 +0,0 @@
|
|||
import autobind from 'autobind-decorator';
|
||||
import { EventEmitter } from 'eventemitter3';
|
||||
import ReconnectingWebsocket from 'reconnecting-websocket';
|
||||
import { markRaw } from 'vue';
|
||||
import { debug, wsUrl } from '@client/config';
|
||||
import { query as urlQuery } from '../../prelude/url';
|
||||
|
||||
/**
|
||||
* Misskey stream connection
|
||||
*/
|
||||
export default class Stream extends EventEmitter {
|
||||
private stream: ReconnectingWebsocket;
|
||||
public state: 'initializing' | 'reconnecting' | 'connected' = 'initializing';
|
||||
private sharedConnectionPools: Pool[] = [];
|
||||
private sharedConnections: SharedConnection[] = [];
|
||||
private nonSharedConnections: NonSharedConnection[] = [];
|
||||
|
||||
@autobind
|
||||
public init(user): void {
|
||||
const query = urlQuery({
|
||||
i: user?.token,
|
||||
_t: Date.now(),
|
||||
});
|
||||
|
||||
this.stream = new ReconnectingWebsocket(`${wsUrl}?${query}`, '', { minReconnectionDelay: 1 }); // https://github.com/pladaria/reconnecting-websocket/issues/91
|
||||
this.stream.addEventListener('open', this.onOpen);
|
||||
this.stream.addEventListener('close', this.onClose);
|
||||
this.stream.addEventListener('message', this.onMessage);
|
||||
}
|
||||
|
||||
@autobind
|
||||
public useSharedConnection(channel: string, name?: string): SharedConnection {
|
||||
let pool = this.sharedConnectionPools.find(p => p.channel === channel);
|
||||
|
||||
if (pool == null) {
|
||||
pool = new Pool(this, channel);
|
||||
this.sharedConnectionPools.push(pool);
|
||||
}
|
||||
|
||||
const connection = markRaw(new SharedConnection(this, channel, pool, name));
|
||||
this.sharedConnections.push(connection);
|
||||
return connection;
|
||||
}
|
||||
|
||||
@autobind
|
||||
public removeSharedConnection(connection: SharedConnection) {
|
||||
this.sharedConnections = this.sharedConnections.filter(c => c !== connection);
|
||||
}
|
||||
|
||||
@autobind
|
||||
public removeSharedConnectionPool(pool: Pool) {
|
||||
this.sharedConnectionPools = this.sharedConnectionPools.filter(p => p !== pool);
|
||||
}
|
||||
|
||||
@autobind
|
||||
public connectToChannel(channel: string, params?: any): NonSharedConnection {
|
||||
const connection = markRaw(new NonSharedConnection(this, channel, params));
|
||||
this.nonSharedConnections.push(connection);
|
||||
return connection;
|
||||
}
|
||||
|
||||
@autobind
|
||||
public disconnectToChannel(connection: NonSharedConnection) {
|
||||
this.nonSharedConnections = this.nonSharedConnections.filter(c => c !== connection);
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback of when open connection
|
||||
*/
|
||||
@autobind
|
||||
private onOpen() {
|
||||
const isReconnect = this.state === 'reconnecting';
|
||||
|
||||
this.state = 'connected';
|
||||
this.emit('_connected_');
|
||||
|
||||
// チャンネル再接続
|
||||
if (isReconnect) {
|
||||
for (const p of this.sharedConnectionPools)
|
||||
p.connect();
|
||||
for (const c of this.nonSharedConnections)
|
||||
c.connect();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback of when close connection
|
||||
*/
|
||||
@autobind
|
||||
private onClose() {
|
||||
if (this.state === 'connected') {
|
||||
this.state = 'reconnecting';
|
||||
this.emit('_disconnected_');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback of when received a message from connection
|
||||
*/
|
||||
@autobind
|
||||
private onMessage(message) {
|
||||
const { type, body } = JSON.parse(message.data);
|
||||
|
||||
if (type === 'channel') {
|
||||
const id = body.id;
|
||||
|
||||
let connections: Connection[];
|
||||
|
||||
connections = this.sharedConnections.filter(c => c.id === id);
|
||||
|
||||
if (connections.length === 0) {
|
||||
connections = [this.nonSharedConnections.find(c => c.id === id)];
|
||||
}
|
||||
|
||||
for (const c of connections.filter(c => c != null)) {
|
||||
c.emit(body.type, Object.freeze(body.body));
|
||||
if (debug) c.inCount++;
|
||||
}
|
||||
} else {
|
||||
this.emit(type, Object.freeze(body));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a message to connection
|
||||
*/
|
||||
@autobind
|
||||
public send(typeOrPayload, payload?) {
|
||||
const data = payload === undefined ? typeOrPayload : {
|
||||
type: typeOrPayload,
|
||||
body: payload
|
||||
};
|
||||
|
||||
this.stream.send(JSON.stringify(data));
|
||||
}
|
||||
|
||||
/**
|
||||
* Close this connection
|
||||
*/
|
||||
@autobind
|
||||
public close() {
|
||||
this.stream.removeEventListener('open', this.onOpen);
|
||||
this.stream.removeEventListener('message', this.onMessage);
|
||||
}
|
||||
}
|
||||
|
||||
let idCounter = 0;
|
||||
|
||||
class Pool {
|
||||
public channel: string;
|
||||
public id: string;
|
||||
protected stream: Stream;
|
||||
public users = 0;
|
||||
private disposeTimerId: any;
|
||||
private isConnected = false;
|
||||
|
||||
constructor(stream: Stream, channel: string) {
|
||||
this.channel = channel;
|
||||
this.stream = stream;
|
||||
|
||||
this.id = (++idCounter).toString();
|
||||
|
||||
this.stream.on('_disconnected_', this.onStreamDisconnected);
|
||||
}
|
||||
|
||||
@autobind
|
||||
private onStreamDisconnected() {
|
||||
this.isConnected = false;
|
||||
}
|
||||
|
||||
@autobind
|
||||
public inc() {
|
||||
if (this.users === 0 && !this.isConnected) {
|
||||
this.connect();
|
||||
}
|
||||
|
||||
this.users++;
|
||||
|
||||
// タイマー解除
|
||||
if (this.disposeTimerId) {
|
||||
clearTimeout(this.disposeTimerId);
|
||||
this.disposeTimerId = null;
|
||||
}
|
||||
}
|
||||
|
||||
@autobind
|
||||
public dec() {
|
||||
this.users--;
|
||||
|
||||
// そのコネクションの利用者が誰もいなくなったら
|
||||
if (this.users === 0) {
|
||||
// また直ぐに再利用される可能性があるので、一定時間待ち、
|
||||
// 新たな利用者が現れなければコネクションを切断する
|
||||
this.disposeTimerId = setTimeout(() => {
|
||||
this.disconnect();
|
||||
}, 3000);
|
||||
}
|
||||
}
|
||||
|
||||
@autobind
|
||||
public connect() {
|
||||
if (this.isConnected) return;
|
||||
this.isConnected = true;
|
||||
this.stream.send('connect', {
|
||||
channel: this.channel,
|
||||
id: this.id
|
||||
});
|
||||
}
|
||||
|
||||
@autobind
|
||||
private disconnect() {
|
||||
this.stream.off('_disconnected_', this.onStreamDisconnected);
|
||||
this.stream.send('disconnect', { id: this.id });
|
||||
this.stream.removeSharedConnectionPool(this);
|
||||
}
|
||||
}
|
||||
|
||||
abstract class Connection extends EventEmitter {
|
||||
public channel: string;
|
||||
protected stream: Stream;
|
||||
public abstract id: string;
|
||||
|
||||
public name?: string; // for debug
|
||||
public inCount: number = 0; // for debug
|
||||
public outCount: number = 0; // for debug
|
||||
|
||||
constructor(stream: Stream, channel: string, name?: string) {
|
||||
super();
|
||||
|
||||
this.stream = stream;
|
||||
this.channel = channel;
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
@autobind
|
||||
public send(id: string, typeOrPayload, payload?) {
|
||||
const type = payload === undefined ? typeOrPayload.type : typeOrPayload;
|
||||
const body = payload === undefined ? typeOrPayload.body : payload;
|
||||
|
||||
this.stream.send('ch', {
|
||||
id: id,
|
||||
type: type,
|
||||
body: body
|
||||
});
|
||||
|
||||
if (debug) this.outCount++;
|
||||
}
|
||||
|
||||
public abstract dispose(): void;
|
||||
}
|
||||
|
||||
class SharedConnection extends Connection {
|
||||
private pool: Pool;
|
||||
|
||||
public get id(): string {
|
||||
return this.pool.id;
|
||||
}
|
||||
|
||||
constructor(stream: Stream, channel: string, pool: Pool, name?: string) {
|
||||
super(stream, channel, name);
|
||||
|
||||
this.pool = pool;
|
||||
this.pool.inc();
|
||||
}
|
||||
|
||||
@autobind
|
||||
public send(typeOrPayload, payload?) {
|
||||
super.send(this.pool.id, typeOrPayload, payload);
|
||||
}
|
||||
|
||||
@autobind
|
||||
public dispose() {
|
||||
this.pool.dec();
|
||||
this.removeAllListeners();
|
||||
this.stream.removeSharedConnection(this);
|
||||
}
|
||||
}
|
||||
|
||||
class NonSharedConnection extends Connection {
|
||||
public id: string;
|
||||
protected params: any;
|
||||
|
||||
constructor(stream: Stream, channel: string, params?: any) {
|
||||
super(stream, channel);
|
||||
|
||||
this.params = params;
|
||||
this.id = (++idCounter).toString();
|
||||
|
||||
this.connect();
|
||||
}
|
||||
|
||||
@autobind
|
||||
public connect() {
|
||||
this.stream.send('connect', {
|
||||
channel: this.channel,
|
||||
id: this.id,
|
||||
params: this.params
|
||||
});
|
||||
}
|
||||
|
||||
@autobind
|
||||
public send(typeOrPayload, payload?) {
|
||||
super.send(this.id, typeOrPayload, payload);
|
||||
}
|
||||
|
||||
@autobind
|
||||
public dispose() {
|
||||
this.removeAllListeners();
|
||||
this.stream.send('disconnect', { id: this.id });
|
||||
this.stream.disconnectToChannel(this);
|
||||
}
|
||||
}
|
||||
|
|
@ -146,6 +146,7 @@ hr {
|
|||
width: 100%;
|
||||
height: 100%;
|
||||
background: var(--modalBg);
|
||||
-webkit-backdrop-filter: var(--modalBgFilter);
|
||||
backdrop-filter: var(--modalBgFilter);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -48,7 +48,7 @@ export default defineComponent({
|
|||
};
|
||||
|
||||
if ($i) {
|
||||
const connection = stream.useSharedConnection('main', 'UI');
|
||||
const connection = stream.useChannel('main', null, 'UI');
|
||||
connection.on('notification', onNotification);
|
||||
|
||||
//#region Listen message from SW
|
||||
|
|
|
|||
|
|
@ -121,33 +121,33 @@ export default defineComponent({
|
|||
this.query = {
|
||||
antennaId: this.antenna
|
||||
};
|
||||
this.connection = os.stream.connectToChannel('antenna', {
|
||||
this.connection = os.stream.useChannel('antenna', {
|
||||
antennaId: this.antenna
|
||||
});
|
||||
this.connection.on('note', prepend);
|
||||
} else if (this.src == 'home') {
|
||||
endpoint = 'notes/timeline';
|
||||
this.connection = os.stream.useSharedConnection('homeTimeline');
|
||||
this.connection = os.stream.useChannel('homeTimeline');
|
||||
this.connection.on('note', prepend);
|
||||
|
||||
this.connection2 = os.stream.useSharedConnection('main');
|
||||
this.connection2 = os.stream.useChannel('main');
|
||||
this.connection2.on('follow', onChangeFollowing);
|
||||
this.connection2.on('unfollow', onChangeFollowing);
|
||||
} else if (this.src == 'local') {
|
||||
endpoint = 'notes/local-timeline';
|
||||
this.connection = os.stream.useSharedConnection('localTimeline');
|
||||
this.connection = os.stream.useChannel('localTimeline');
|
||||
this.connection.on('note', prepend);
|
||||
} else if (this.src == 'social') {
|
||||
endpoint = 'notes/hybrid-timeline';
|
||||
this.connection = os.stream.useSharedConnection('hybridTimeline');
|
||||
this.connection = os.stream.useChannel('hybridTimeline');
|
||||
this.connection.on('note', prepend);
|
||||
} else if (this.src == 'global') {
|
||||
endpoint = 'notes/global-timeline';
|
||||
this.connection = os.stream.useSharedConnection('globalTimeline');
|
||||
this.connection = os.stream.useChannel('globalTimeline');
|
||||
this.connection.on('note', prepend);
|
||||
} else if (this.src == 'mentions') {
|
||||
endpoint = 'notes/mentions';
|
||||
this.connection = os.stream.useSharedConnection('main');
|
||||
this.connection = os.stream.useChannel('main');
|
||||
this.connection.on('mention', prepend);
|
||||
} else if (this.src == 'directs') {
|
||||
endpoint = 'notes/mentions';
|
||||
|
|
@ -159,14 +159,14 @@ export default defineComponent({
|
|||
prepend(note);
|
||||
}
|
||||
};
|
||||
this.connection = os.stream.useSharedConnection('main');
|
||||
this.connection = os.stream.useChannel('main');
|
||||
this.connection.on('mention', onNote);
|
||||
} else if (this.src == 'list') {
|
||||
endpoint = 'notes/user-list-timeline';
|
||||
this.query = {
|
||||
listId: this.list
|
||||
};
|
||||
this.connection = os.stream.connectToChannel('userList', {
|
||||
this.connection = os.stream.useChannel('userList', {
|
||||
listId: this.list
|
||||
});
|
||||
this.connection.on('note', prepend);
|
||||
|
|
@ -178,7 +178,7 @@ export default defineComponent({
|
|||
this.query = {
|
||||
channelId: this.channel
|
||||
};
|
||||
this.connection = os.stream.connectToChannel('channel', {
|
||||
this.connection = os.stream.useChannel('channel', {
|
||||
channelId: this.channel
|
||||
});
|
||||
this.connection.on('note', prepend);
|
||||
|
|
|
|||
|
|
@ -241,7 +241,6 @@ export default defineComponent({
|
|||
> .text {
|
||||
display: none;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -309,7 +308,7 @@ export default defineComponent({
|
|||
> .indicator {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 20px;
|
||||
left: 0;
|
||||
color: var(--navIndicator);
|
||||
font-size: 8px;
|
||||
animation: blink 1s infinite;
|
||||
|
|
|
|||
|
|
@ -65,7 +65,7 @@ export default defineComponent({
|
|||
extends: widget,
|
||||
data() {
|
||||
return {
|
||||
connection: os.stream.useSharedConnection('queueStats'),
|
||||
connection: os.stream.useChannel('queueStats'),
|
||||
inbox: {
|
||||
activeSincePrevTick: 0,
|
||||
active: 0,
|
||||
|
|
|
|||
|
|
@ -48,7 +48,7 @@ export default defineComponent({
|
|||
};
|
||||
},
|
||||
mounted() {
|
||||
this.connection = os.stream.useSharedConnection('main');
|
||||
this.connection = os.stream.useChannel('main');
|
||||
|
||||
this.connection.on('driveFileCreated', this.onDriveFileCreated);
|
||||
|
||||
|
|
|
|||
|
|
@ -63,7 +63,7 @@ export default defineComponent({
|
|||
os.api('server-info', {}).then(res => {
|
||||
this.meta = res;
|
||||
});
|
||||
this.connection = os.stream.useSharedConnection('serverStats');
|
||||
this.connection = os.stream.useChannel('serverStats');
|
||||
},
|
||||
unmounted() {
|
||||
this.connection.dispose();
|
||||
|
|
|
|||
|
|
@ -1,25 +1,25 @@
|
|||
# ストリーミングAPI
|
||||
# API de Streaming
|
||||
|
||||
ストリーミングAPIを使うと、リアルタイムで様々な情報(例えばタイムラインに新しい投稿が流れてきた、メッセージが届いた、フォローされた、など)を受け取ったり、様々な操作を行ったりすることができます。
|
||||
Usando la API de streaming, se puede recibir en tiempo real toda clase de información (por ejemplo, los posts nuevos que pasaron por la linea de tiempo, los mensajes recibidos, las notificaciones de seguimiento, etc.) y manejar varias operaciones en estas.
|
||||
|
||||
## ストリームに接続する
|
||||
## Conectarse a streams
|
||||
|
||||
ストリーミングAPIを利用するには、まずMisskeyサーバーに**websocket**接続する必要があります。
|
||||
Para usar la API de streaming, primero hay que conectar un **websocket** al servidor de Misskey
|
||||
|
||||
以下のURLに、`i`というパラメータ名で認証情報を含めて、websocket接続してください。例:
|
||||
Conecte el websocket a la URL mencionada abajo, incluyendo la información de autenticación en el parámetro `i`Ej:
|
||||
```
|
||||
%WS_URL%/streaming?i=xxxxxxxxxxxxxxx
|
||||
```
|
||||
|
||||
認証情報は、自分のAPIキーや、アプリケーションからストリームに接続する際はユーザーのアクセストークンのことを指します。
|
||||
La información de autenticación hace referencia a tu propia clave de la API, o al token de acceso del usuario cuando se conecta al stream desde la aplicación
|
||||
|
||||
<div class="ui info">
|
||||
<p><i class="fas fa-info-circle"></i> 認証情報の取得については、<a href="./api">こちらのドキュメント</a>をご確認ください。</p>
|
||||
<p><i class="fas fa-info-circle"></i> Para obtener la información de la autenticación, consulte <a href="./api">Este documento</a></p>
|
||||
</div>
|
||||
|
||||
---
|
||||
|
||||
認証情報は省略することもできますが、その場合非ログインでの利用ということになり、受信できる情報や可能な操作は限られます。例:
|
||||
La información de autenticación puede omitirse, pero en ese caso de uso sin un login, se restringirá la información que puede ser recibida y las operaciones posibles,Ej:
|
||||
|
||||
```
|
||||
%WS_URL%/streaming
|
||||
|
|
@ -50,7 +50,7 @@ MisskeyのストリーミングAPIにはチャンネルという概念があり
|
|||
}
|
||||
```
|
||||
|
||||
ここで、
|
||||
Aquí
|
||||
* `channel`には接続したいチャンネル名を設定します。チャンネルの種類については後述します。
|
||||
* `id`にはそのチャンネルとやり取りするための任意のIDを設定します。ストリームでは様々なメッセージが流れるので、そのメッセージがどのチャンネルからのものなのか識別する必要があるからです。このIDは、UUIDや、乱数のようなもので構いません。
|
||||
* `params`はチャンネルに接続する際のパラメータです。チャンネルによって接続時に必要とされるパラメータは異なります。パラメータ不要のチャンネルに接続する際は、このプロパティは省略可能です。
|
||||
|
|
@ -76,7 +76,7 @@ MisskeyのストリーミングAPIにはチャンネルという概念があり
|
|||
}
|
||||
```
|
||||
|
||||
ここで、
|
||||
Aquí
|
||||
* `id`には前述したそのチャンネルに接続する際に設定したIDが設定されています。これで、このメッセージがどのチャンネルからのものなのか知ることができます。
|
||||
* `type`にはメッセージの種類が設定されます。チャンネルによって、どのような種類のメッセージが流れてくるかは異なります。
|
||||
* `body`にはメッセージの内容が設定されます。チャンネルによって、どのような内容のメッセージが流れてくるかは異なります。
|
||||
|
|
@ -98,7 +98,7 @@ MisskeyのストリーミングAPIにはチャンネルという概念があり
|
|||
}
|
||||
```
|
||||
|
||||
ここで、
|
||||
Aquí
|
||||
* `id`には前述したそのチャンネルに接続する際に設定したIDを設定します。これで、このメッセージがどのチャンネルに向けたものなのか識別させることができます。
|
||||
* `type`にはメッセージの種類を設定します。チャンネルによって、どのような種類のメッセージを受け付けるかは異なります。
|
||||
* `body`にはメッセージの内容を設定します。チャンネルによって、どのような内容のメッセージを受け付けるかは異なります。
|
||||
|
|
@ -115,7 +115,7 @@ MisskeyのストリーミングAPIにはチャンネルという概念があり
|
|||
}
|
||||
```
|
||||
|
||||
ここで、
|
||||
Aquí
|
||||
* `id`には前述したそのチャンネルに接続する際に設定したIDを設定します。
|
||||
|
||||
## ストリームを経由してAPIリクエストする
|
||||
|
|
@ -136,7 +136,7 @@ MisskeyのストリーミングAPIにはチャンネルという概念があり
|
|||
}
|
||||
```
|
||||
|
||||
ここで、
|
||||
Aquí
|
||||
* `id`には、APIのレスポンスを識別するための、APIリクエストごとの一意なIDを設定する必要があります。UUIDや、簡単な乱数のようなもので構いません。
|
||||
* `endpoint`には、あなたがリクエストしたいAPIのエンドポイントを指定します。
|
||||
* `data`には、エンドポイントのパラメータを含めます。
|
||||
|
|
@ -158,7 +158,7 @@ APIへリクエストすると、レスポンスがストリームから次の
|
|||
}
|
||||
```
|
||||
|
||||
ここで、
|
||||
Aquí
|
||||
* `xxxxxxxxxxxxxxxx`の部分には、リクエストの際に設定された`id`が含まれています。これにより、どのリクエストに対するレスポンスなのか判別することができます。
|
||||
* `body`には、レスポンスが含まれています。
|
||||
|
||||
|
|
@ -185,7 +185,7 @@ Misskeyは投稿のキャプチャと呼ばれる仕組みを提供していま
|
|||
}
|
||||
```
|
||||
|
||||
ここで、
|
||||
Aquí
|
||||
* `id`にキャプチャしたい投稿の`id`を設定します。
|
||||
|
||||
このメッセージを送信すると、Misskeyにキャプチャを要請したことになり、以後、その投稿に関するイベントが流れてくるようになります。
|
||||
|
|
@ -206,7 +206,7 @@ Misskeyは投稿のキャプチャと呼ばれる仕組みを提供していま
|
|||
}
|
||||
```
|
||||
|
||||
ここで、
|
||||
Aquí
|
||||
* `body`内の`id`に、イベントを発生させた投稿のIDが設定されます。
|
||||
* `body`内の`type`に、イベントの種類が設定されます。
|
||||
* `body`内の`body`に、イベントの詳細が設定されます。
|
||||
|
|
@ -219,7 +219,7 @@ Misskeyは投稿のキャプチャと呼ばれる仕組みを提供していま
|
|||
* `reaction`に、リアクションの種類が設定されます。
|
||||
* `userId`に、リアクションを行ったユーザーのIDが設定されます。
|
||||
|
||||
例:
|
||||
Ej:
|
||||
```json
|
||||
{
|
||||
type: 'noteUpdated',
|
||||
|
|
@ -239,7 +239,7 @@ Misskeyは投稿のキャプチャと呼ばれる仕組みを提供していま
|
|||
|
||||
* `deletedAt`に、削除日時が設定されます。
|
||||
|
||||
例:
|
||||
Ej:
|
||||
```json
|
||||
{
|
||||
type: 'noteUpdated',
|
||||
|
|
@ -259,7 +259,7 @@ Misskeyは投稿のキャプチャと呼ばれる仕組みを提供していま
|
|||
* `choice`に、選択肢IDが設定されます。
|
||||
* `userId`に、投票を行ったユーザーのIDが設定されます。
|
||||
|
||||
例:
|
||||
Ej:
|
||||
```json
|
||||
{
|
||||
type: 'noteUpdated',
|
||||
|
|
@ -289,7 +289,7 @@ Misskeyは投稿のキャプチャと呼ばれる仕組みを提供していま
|
|||
}
|
||||
```
|
||||
|
||||
ここで、
|
||||
Aquí
|
||||
* `id`にキャプチャを解除したい投稿の`id`を設定します。
|
||||
|
||||
このメッセージを送信すると、以後、その投稿に関するイベントは流れてこないようになります。
|
||||
|
|
|
|||
|
|
@ -1,12 +1,12 @@
|
|||
# Tema
|
||||
|
||||
テーマを設定して、Misskeyクライアントの見た目を変更できます。
|
||||
Eligiendo un tema, se puede cambiar la apariencia del cliente de Misskey
|
||||
|
||||
## テーマの設定
|
||||
設定 > テーマ
|
||||
## Configuración del tema
|
||||
Configuración > Tema
|
||||
|
||||
## テーマを作成する
|
||||
テーマコードはJSON5で記述されたテーマオブジェクトです。 テーマは以下のようなオブジェクトです。
|
||||
## Crear tema
|
||||
El código del tema se guarda como un archivo JSON5. Un ejemplo de tema se puede ver aquí:
|
||||
``` js
|
||||
{
|
||||
id: '17587283-dd92-4a2c-a22c-be0637c9e22a',
|
||||
|
|
@ -33,36 +33,36 @@
|
|||
|
||||
```
|
||||
|
||||
* `id` ... テーマの一意なID。UUIDをおすすめします。
|
||||
* `name` ... テーマ名
|
||||
* `author` ... テーマの作者
|
||||
* `desc` ... テーマの説明(オプション)
|
||||
* `base` ... 明るいテーマか、暗いテーマか
|
||||
* `light`にすると明るいテーマになり、`dark`にすると暗いテーマになります。
|
||||
* テーマはここで設定されたベーステーマを継承します。
|
||||
* `props` ... テーマのスタイル定義。これから説明します。
|
||||
* `id` ... Clave única del tema.Se recomienda un código UUID
|
||||
* `name` ... Nombre del tema
|
||||
* `author` ... Autor del tema
|
||||
* `desc` ... Descripción del tema (opcional)
|
||||
* `base` ... Si es un tema claro u oscuro
|
||||
* Si se elige `light`, será un tema claro. Si se elige `dark`, será un tema oscuro.
|
||||
* Aquí el tema hereda los valores por defecto del tema base elegido
|
||||
* `props` ... Definición del estilo del tema. Esto se explica en lo siguiente.
|
||||
|
||||
### テーマのスタイル定義
|
||||
`props`下にはテーマのスタイルを定義します。 キーがCSSの変数名になり、バリューで中身を指定します。 なお、この`props`オブジェクトはベーステーマから継承されます。 ベーステーマは、このテーマの`base`が`light`なら[_light.json5](https://github.com/misskey-dev/misskey/blob/develop/src/client/themes/_light.json5)で、`dark`なら[_dark.json5](https://github.com/misskey-dev/misskey/blob/develop/src/client/themes/_dark.json5)です。 つまり、このテーマ内の`props`に`panel`というキーが無くても、そこにはベーステーマの`panel`があると見なされます。
|
||||
### Definición del estilo del tema
|
||||
Debajo de `props`, se define el estilo del tema. La clave es el nombre de las variables del CSS, y con los valores estos se configuran. Incluso más, este objeto `props` hereda los valores por defecto del tema base. El tema base es [_light.json5](https://github.com/misskey-dev/misskey/blob/develop/src/client/themes/_light.json5) si el `base` de este tema es `light`, y [_dark.json5](https://github.com/misskey-dev/misskey/blob/develop/src/client/themes/_dark.json5) si si el `base` de este tema es `dark` Resumiendo, aunque no haya una clave `panel` en el `props` del tema, se considera el <0>panel</0> del tema base.
|
||||
|
||||
#### バリューで使える構文
|
||||
* 16進数で表された色
|
||||
* 例: `#00ff00`
|
||||
* `rgb(r, g, b)`形式で表された色
|
||||
* 例: `rgb(0, 255, 0)`
|
||||
* `rgb(r, g, b, a)`形式で表された透明度を含む色
|
||||
* 例: `rgba(0, 255, 0, 0.5)`
|
||||
* 他のキーの値の参照
|
||||
* `@{キー名}`と書くと他のキーの値の参照になります。`{キー名}`は参照したいキーの名前に置き換えます。
|
||||
* 例: `@panel`
|
||||
* 定数(後述)の参照
|
||||
* `${定数名}`と書くと定数の参照になります。`{定数名}`は参照したい定数の名前に置き換えます。
|
||||
* 例: `$main`
|
||||
* 関数(後述)
|
||||
* `:{関数名}<{引数}<{色}`
|
||||
#### Sintaxis de las variables
|
||||
* Los colores en base hexadecimal
|
||||
* Ej: `#00ff00`
|
||||
* Los colores con la sintaxis `rgb(r, g, b)`
|
||||
* Ej: `rgb(0, 255, 0)`
|
||||
* Los colores con la sintaxis `rgb(r, g, b, a)` con un grado de transparencia
|
||||
* Ej: `rgba(0, 255, 0, 0.5)`
|
||||
* Referencias a valores de otras claves
|
||||
* Escribiendo `@{nombre de clave}` se hace referencia al valor de la otra clave.Reemplace `{nombre de clave}` por el nombre de la clave al cual quiera hacer referencia.
|
||||
* Ej: `@panel`
|
||||
* Referencia a una constante (ver más abajo)
|
||||
* Escribiendo `${nombre de la constante}` se hace referencia a la constante.Reemplace `{nombre de la constante}` por la constante al cual quiera hacer referencia.
|
||||
* Ej: `$main`
|
||||
* Funciones (ver más abajo)
|
||||
* `:{nombre de la función}<{parámetros}<{color}`
|
||||
|
||||
#### Constante
|
||||
「CSS変数として出力はしたくないが、他のCSS変数の値として使いまわしたい」値があるときは、定数を使うと便利です。 キー名を`$`で始めると、そのキーはCSS変数として出力されません。
|
||||
#### Constantes
|
||||
Cuando hay un valor que no se quiere generar como variable CSS pero sí se quiere reutilizar como valor de otra variable CSS, es conveniente usar constantes. Cuando a un nombre de clave se le añade como prefijo `$`, esa clave no será generada como una variable CSS.
|
||||
|
||||
#### funciones
|
||||
wip
|
||||
|
|
|
|||
|
|
@ -1,15 +1,15 @@
|
|||
# タイムラインの比較
|
||||
# Comparación de las lineas de tiempo
|
||||
|
||||
https://docs.google.com/spreadsheets/d/1lxQ2ugKrhz58Bg96HTDK_2F98BUritkMyIiBkOByjHA/edit?usp=sharing
|
||||
|
||||
## Inicio
|
||||
自分のフォローしているユーザーの投稿
|
||||
Los posts de los usuarios que uno sigue
|
||||
|
||||
## Local
|
||||
全てのローカルユーザーの「ホーム」指定されていない投稿
|
||||
Todos los posts de los usuarios locales que no estén marcados como "Solo inicio"
|
||||
|
||||
## Social
|
||||
自分のフォローしているユーザーの投稿と、全てのローカルユーザーの「ホーム」指定されていない投稿
|
||||
Los posts de los usuarios que uno sigue más todos los posts de los usuarios locales que no estén marcados como "Solo inicio"
|
||||
|
||||
## Global
|
||||
全てのローカルユーザーの「ホーム」指定されていない投稿と、サーバーに届いた全てのリモートユーザーの「ホーム」指定されていない投稿
|
||||
Todos los posts de los usuarios locales que no estén marcados como "Solo inicio" más todos los posts de los usuarios remotos recibidos por el servidor que no estén marcados como "Solo inicio"
|
||||
|
|
|
|||
|
|
@ -7,11 +7,11 @@ API를 사용하려면 먼저 액세스 토큰을 취득해야 합니다. 이
|
|||
## 액세스 토큰 가져오기
|
||||
기본적으로 API는 요청 시에 액세스 토큰이 필요합니다. API에 요청하는 것이 자기 자신인지, 불특정한 유저에게 사용하는 애플리케이션인지에 따라 취득 절차가 달라집니다.
|
||||
|
||||
* 전자의 경우: [ 「자기 자신의 액세스 토큰을 수동으로 발급하기」](#自分自身のアクセストークンを手動発行する)로 진행
|
||||
* 전자의 경우: [「자기 자신의 액세스 토큰을 수동으로 발급하기」](#自分自身のアクセストークンを手動発行する)로 진행
|
||||
* 후자의 경우: [「애플리케이션 사용자에게 액세스 토큰 발급을 요청하기」](#アプリケーション利用者にアクセストークンの発行をリクエストする)로 진행
|
||||
|
||||
### 자기 자신의 액세스 토큰을 수동으로 발급하기
|
||||
「설정 > API」에서 자신의 액세스 토큰을 발급할 수 있습니다.
|
||||
「설정 > API」에서 자신의 액세스 토큰을 발급할 수 있습니다.
|
||||
|
||||
[「API 사용 방법」으로 이동](#APIの使い方)
|
||||
|
||||
|
|
|
|||
|
|
@ -1,2 +1,2 @@
|
|||
# MFM
|
||||
MFM은 Misskey Flavored Markdown의 약자로, Misskey의 다양한 장소에서 사용할 수 있는 전용 마크업 언어입니다. MFM로 사용 가능한 구문은 [MFM 치트 시트 ](/mfm-cheat-sheet)에서 확인할 수 있습니다.
|
||||
MFM은 Misskey Flavored Markdown의 약자로, Misskey의 다양한 장소에서 사용할 수 있는 전용 마크업 언어입니다. MFM로 사용 가능한 구문은 [MFM 치트 시트](/mfm-cheat-sheet)에서 확인할 수 있습니다.
|
||||
|
|
|
|||
|
|
@ -93,6 +93,9 @@
|
|||
{ "category": "face", "char": "🥱", "name": "yawning", "keywords": ["face", "tired", "yawning"] },
|
||||
{ "category": "face", "char": "😴", "name": "sleeping", "keywords": ["face", "tired", "sleepy", "night", "zzz"] },
|
||||
{ "category": "face", "char": "💤", "name": "zzz", "keywords": ["sleepy", "tired", "dream"] },
|
||||
{ "category": "face", "char": "\uD83D\uDE36\u200D\uD83C\uDF2B\uFE0F", "name": "face_in_clouds", "keywords": [] },
|
||||
{ "category": "face", "char": "\uD83D\uDE2E\u200D\uD83D\uDCA8", "name": "face_exhaling", "keywords": [] },
|
||||
{ "category": "face", "char": "\uD83D\uDE35\u200D\uD83D\uDCAB", "name": "face_with_spiral_eyes", "keywords": [] },
|
||||
{ "category": "face", "char": "💩", "name": "poop", "keywords": ["hankey", "shitface", "fail", "turd", "shit"] },
|
||||
{ "category": "face", "char": "😈", "name": "smiling_imp", "keywords": ["devil", "horns"] },
|
||||
{ "category": "face", "char": "👿", "name": "imp", "keywords": ["devil", "angry", "horns"] },
|
||||
|
|
@ -1219,6 +1222,8 @@
|
|||
{ "category": "symbols", "char": "💘", "name": "cupid", "keywords": ["love", "like", "heart", "affection", "valentines"] },
|
||||
{ "category": "symbols", "char": "💝", "name": "gift_heart", "keywords": ["love", "valentines"] },
|
||||
{ "category": "symbols", "char": "💟", "name": "heart_decoration", "keywords": ["purple-square", "love", "like"] },
|
||||
{ "category": "symbols", "char": "\u2764\uFE0F\u200D\uD83D\uDD25", "name": "heart_on_fire", "keywords": [] },
|
||||
{ "category": "symbols", "char": "\u2764\uFE0F\u200D\uD83E\uDE79", "name": "mending_heart", "keywords": [] },
|
||||
{ "category": "symbols", "char": "☮", "name": "peace_symbol", "keywords": ["hippie"] },
|
||||
{ "category": "symbols", "char": "✝", "name": "latin_cross", "keywords": ["christianity"] },
|
||||
{ "category": "symbols", "char": "☪", "name": "star_and_crescent", "keywords": ["islam"] },
|
||||
|
|
|
|||
|
|
@ -56,6 +56,8 @@ const forceExitAfter = timeout => () => {
|
|||
* @param {string} signalOrEvent The exit signal or event name received on the process.
|
||||
*/
|
||||
async function shutdownHandler(signalOrEvent) {
|
||||
if (process.env.NODE_ENV === 'test') return process.exit(0);
|
||||
|
||||
console.warn(`Shutting down: received [${signalOrEvent}] signal`);
|
||||
|
||||
for (const listener of shutdownListeners) {
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
|
|
@ -5,6 +5,8 @@ import { Note } from '../models/entities/note';
|
|||
import { Cache } from './cache';
|
||||
import { isSelfHost, toPunyNullable } from './convert-host';
|
||||
import { decodeReaction } from './reaction-lib';
|
||||
import config from '@/config';
|
||||
import { query } from '@/prelude/url';
|
||||
|
||||
const cache = new Cache<Emoji | null>(1000 * 60 * 60 * 12);
|
||||
|
||||
|
|
@ -59,9 +61,12 @@ export async function populateEmoji(emojiName: string, noteUserHost: string | nu
|
|||
|
||||
if (emoji == null) return null;
|
||||
|
||||
const isLocal = emoji.host == null;
|
||||
const url = isLocal ? emoji.url : `${config.url}/proxy/image.png?${query({url: emoji.url})}`;
|
||||
|
||||
return {
|
||||
name: emojiName,
|
||||
url: emoji.url,
|
||||
url,
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -41,14 +41,12 @@ export const packedBlockingSchema = {
|
|||
type: 'string' as const,
|
||||
optional: false as const, nullable: false as const,
|
||||
format: 'id',
|
||||
description: 'The unique identifier for this blocking.',
|
||||
example: 'xxxxxxxxxx',
|
||||
},
|
||||
createdAt: {
|
||||
type: 'string' as const,
|
||||
optional: false as const, nullable: false as const,
|
||||
format: 'date-time',
|
||||
description: 'The date that the blocking was created.'
|
||||
},
|
||||
blockeeId: {
|
||||
type: 'string' as const,
|
||||
|
|
@ -59,7 +57,6 @@ export const packedBlockingSchema = {
|
|||
type: 'object' as const,
|
||||
optional: false as const, nullable: false as const,
|
||||
ref: 'User',
|
||||
description: 'The blockee.'
|
||||
},
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -51,14 +51,12 @@ export const packedChannelSchema = {
|
|||
type: 'string' as const,
|
||||
optional: false as const, nullable: false as const,
|
||||
format: 'id',
|
||||
description: 'The unique identifier for this Channel.',
|
||||
example: 'xxxxxxxxxx',
|
||||
},
|
||||
createdAt: {
|
||||
type: 'string' as const,
|
||||
optional: false as const, nullable: false as const,
|
||||
format: 'date-time',
|
||||
description: 'The date that the Channel was created.'
|
||||
},
|
||||
lastNotedAt: {
|
||||
type: 'string' as const,
|
||||
|
|
@ -68,7 +66,6 @@ export const packedChannelSchema = {
|
|||
name: {
|
||||
type: 'string' as const,
|
||||
optional: false as const, nullable: false as const,
|
||||
description: 'The name of the Channel.'
|
||||
},
|
||||
description: {
|
||||
type: 'string' as const,
|
||||
|
|
|
|||
|
|
@ -39,14 +39,12 @@ export const packedClipSchema = {
|
|||
type: 'string' as const,
|
||||
optional: false as const, nullable: false as const,
|
||||
format: 'id',
|
||||
description: 'The unique identifier for this Clip.',
|
||||
example: 'xxxxxxxxxx',
|
||||
},
|
||||
createdAt: {
|
||||
type: 'string' as const,
|
||||
optional: false as const, nullable: false as const,
|
||||
format: 'date-time',
|
||||
description: 'The date that the Clip was created.'
|
||||
},
|
||||
userId: {
|
||||
type: 'string' as const,
|
||||
|
|
@ -61,17 +59,14 @@ export const packedClipSchema = {
|
|||
name: {
|
||||
type: 'string' as const,
|
||||
optional: false as const, nullable: false as const,
|
||||
description: 'The name of the Clip.'
|
||||
},
|
||||
description: {
|
||||
type: 'string' as const,
|
||||
optional: false as const, nullable: true as const,
|
||||
description: 'The description of the Clip.'
|
||||
},
|
||||
isPublic: {
|
||||
type: 'boolean' as const,
|
||||
optional: false as const, nullable: false as const,
|
||||
description: 'Whether this Clip is public.',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
|
|
|||
|
|
@ -59,6 +59,7 @@ export class DriveFileRepository extends Repository<DriveFile> {
|
|||
const { sum } = await this
|
||||
.createQueryBuilder('file')
|
||||
.where('file.userId = :id', { id: id })
|
||||
.andWhere('file.isLink = FALSE')
|
||||
.select('SUM(file.size)', 'sum')
|
||||
.getRawOne();
|
||||
|
||||
|
|
@ -69,6 +70,7 @@ export class DriveFileRepository extends Repository<DriveFile> {
|
|||
const { sum } = await this
|
||||
.createQueryBuilder('file')
|
||||
.where('file.userHost = :host', { host: toPuny(host) })
|
||||
.andWhere('file.isLink = FALSE')
|
||||
.select('SUM(file.size)', 'sum')
|
||||
.getRawOne();
|
||||
|
||||
|
|
@ -79,6 +81,7 @@ export class DriveFileRepository extends Repository<DriveFile> {
|
|||
const { sum } = await this
|
||||
.createQueryBuilder('file')
|
||||
.where('file.userHost IS NULL')
|
||||
.andWhere('file.isLink = FALSE')
|
||||
.select('SUM(file.size)', 'sum')
|
||||
.getRawOne();
|
||||
|
||||
|
|
@ -89,6 +92,7 @@ export class DriveFileRepository extends Repository<DriveFile> {
|
|||
const { sum } = await this
|
||||
.createQueryBuilder('file')
|
||||
.where('file.userHost IS NOT NULL')
|
||||
.andWhere('file.isLink = FALSE')
|
||||
.select('SUM(file.size)', 'sum')
|
||||
.getRawOne();
|
||||
|
||||
|
|
@ -150,44 +154,37 @@ export const packedDriveFileSchema = {
|
|||
type: 'string' as const,
|
||||
optional: false as const, nullable: false as const,
|
||||
format: 'id',
|
||||
description: 'The unique identifier for this Drive file.',
|
||||
example: 'xxxxxxxxxx',
|
||||
},
|
||||
createdAt: {
|
||||
type: 'string' as const,
|
||||
optional: false as const, nullable: false as const,
|
||||
format: 'date-time',
|
||||
description: 'The date that the Drive file was created on Misskey.'
|
||||
},
|
||||
name: {
|
||||
type: 'string' as const,
|
||||
optional: false as const, nullable: false as const,
|
||||
description: 'The file name with extension.',
|
||||
example: 'lenna.jpg'
|
||||
},
|
||||
type: {
|
||||
type: 'string' as const,
|
||||
optional: false as const, nullable: false as const,
|
||||
description: 'The MIME type of this Drive file.',
|
||||
example: 'image/jpeg'
|
||||
},
|
||||
md5: {
|
||||
type: 'string' as const,
|
||||
optional: false as const, nullable: false as const,
|
||||
format: 'md5',
|
||||
description: 'The MD5 hash of this Drive file.',
|
||||
example: '15eca7fba0480996e2245f5185bf39f2'
|
||||
},
|
||||
size: {
|
||||
type: 'number' as const,
|
||||
optional: false as const, nullable: false as const,
|
||||
description: 'The size of this Drive file. (bytes)',
|
||||
example: 51469
|
||||
},
|
||||
isSensitive: {
|
||||
type: 'boolean' as const,
|
||||
optional: false as const, nullable: false as const,
|
||||
description: 'Whether this Drive file is sensitive.',
|
||||
},
|
||||
blurhash: {
|
||||
type: 'string' as const,
|
||||
|
|
@ -218,13 +215,11 @@ export const packedDriveFileSchema = {
|
|||
type: 'string' as const,
|
||||
optional: false as const, nullable: true as const,
|
||||
format: 'url',
|
||||
description: 'The URL of this Drive file.',
|
||||
},
|
||||
thumbnailUrl: {
|
||||
type: 'string' as const,
|
||||
optional: false as const, nullable: true as const,
|
||||
format: 'url',
|
||||
description: 'The thumbnail URL of this Drive file.',
|
||||
},
|
||||
comment: {
|
||||
type: 'string' as const,
|
||||
|
|
@ -234,26 +229,22 @@ export const packedDriveFileSchema = {
|
|||
type: 'string' as const,
|
||||
optional: false as const, nullable: true as const,
|
||||
format: 'id',
|
||||
description: 'The parent folder ID of this Drive file.',
|
||||
example: 'xxxxxxxxxx',
|
||||
},
|
||||
folder: {
|
||||
type: 'object' as const,
|
||||
optional: true as const, nullable: true as const,
|
||||
description: 'The parent folder of this Drive file.',
|
||||
ref: 'DriveFolder'
|
||||
},
|
||||
userId: {
|
||||
type: 'string' as const,
|
||||
optional: false as const, nullable: true as const,
|
||||
format: 'id',
|
||||
description: 'Owner ID of this Drive file.',
|
||||
example: 'xxxxxxxxxx',
|
||||
},
|
||||
user: {
|
||||
type: 'object' as const,
|
||||
optional: true as const, nullable: true as const,
|
||||
description: 'Owner of this Drive file.',
|
||||
ref: 'User'
|
||||
}
|
||||
},
|
||||
|
|
|
|||
|
|
@ -59,35 +59,29 @@ export const packedDriveFolderSchema = {
|
|||
type: 'string' as const,
|
||||
optional: false as const, nullable: false as const,
|
||||
format: 'id',
|
||||
description: 'The unique identifier for this Drive folder.',
|
||||
example: 'xxxxxxxxxx',
|
||||
},
|
||||
createdAt: {
|
||||
type: 'string' as const,
|
||||
optional: false as const, nullable: false as const,
|
||||
format: 'date-time',
|
||||
description: 'The date that the Drive folder was created.'
|
||||
},
|
||||
name: {
|
||||
type: 'string' as const,
|
||||
optional: false as const, nullable: false as const,
|
||||
description: 'The folder name.',
|
||||
},
|
||||
foldersCount: {
|
||||
type: 'number' as const,
|
||||
optional: true as const, nullable: false as const,
|
||||
description: 'The count of child folders.',
|
||||
},
|
||||
filesCount: {
|
||||
type: 'number' as const,
|
||||
optional: true as const, nullable: false as const,
|
||||
description: 'The count of child files.',
|
||||
},
|
||||
parentId: {
|
||||
type: 'string' as const,
|
||||
optional: false as const, nullable: true as const,
|
||||
format: 'id',
|
||||
description: 'The parent folder ID of this folder.',
|
||||
example: 'xxxxxxxxxx',
|
||||
},
|
||||
parent: {
|
||||
|
|
|
|||
|
|
@ -95,14 +95,12 @@ export const packedFollowingSchema = {
|
|||
type: 'string' as const,
|
||||
optional: false as const, nullable: false as const,
|
||||
format: 'id',
|
||||
description: 'The unique identifier for this following.',
|
||||
example: 'xxxxxxxxxx',
|
||||
},
|
||||
createdAt: {
|
||||
type: 'string' as const,
|
||||
optional: false as const, nullable: false as const,
|
||||
format: 'date-time',
|
||||
description: 'The date that the following was created.'
|
||||
},
|
||||
followeeId: {
|
||||
type: 'string' as const,
|
||||
|
|
@ -113,7 +111,6 @@ export const packedFollowingSchema = {
|
|||
type: 'object' as const,
|
||||
optional: true as const, nullable: false as const,
|
||||
ref: 'User',
|
||||
description: 'The followee.'
|
||||
},
|
||||
followerId: {
|
||||
type: 'string' as const,
|
||||
|
|
@ -124,7 +121,6 @@ export const packedFollowingSchema = {
|
|||
type: 'object' as const,
|
||||
optional: true as const, nullable: false as const,
|
||||
ref: 'User',
|
||||
description: 'The follower.'
|
||||
},
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -34,38 +34,31 @@ export const packedHashtagSchema = {
|
|||
tag: {
|
||||
type: 'string' as const,
|
||||
optional: false as const, nullable: false as const,
|
||||
description: 'The hashtag name. No # prefixed.',
|
||||
example: 'misskey',
|
||||
},
|
||||
mentionedUsersCount: {
|
||||
type: 'number' as const,
|
||||
optional: false as const, nullable: false as const,
|
||||
description: 'Number of all users using this hashtag.'
|
||||
},
|
||||
mentionedLocalUsersCount: {
|
||||
type: 'number' as const,
|
||||
optional: false as const, nullable: false as const,
|
||||
description: 'Number of local users using this hashtag.'
|
||||
},
|
||||
mentionedRemoteUsersCount: {
|
||||
type: 'number' as const,
|
||||
optional: false as const, nullable: false as const,
|
||||
description: 'Number of remote users using this hashtag.'
|
||||
},
|
||||
attachedUsersCount: {
|
||||
type: 'number' as const,
|
||||
optional: false as const, nullable: false as const,
|
||||
description: 'Number of all users who attached this hashtag to profile.'
|
||||
},
|
||||
attachedLocalUsersCount: {
|
||||
type: 'number' as const,
|
||||
optional: false as const, nullable: false as const,
|
||||
description: 'Number of local users who attached this hashtag to profile.'
|
||||
},
|
||||
attachedRemoteUsersCount: {
|
||||
type: 'number' as const,
|
||||
optional: false as const, nullable: false as const,
|
||||
description: 'Number of remote users who attached this hashtag to profile.'
|
||||
},
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -53,14 +53,12 @@ export const packedMessagingMessageSchema = {
|
|||
type: 'string' as const,
|
||||
optional: false as const, nullable: false as const,
|
||||
format: 'id',
|
||||
description: 'The unique identifier for this MessagingMessage.',
|
||||
example: 'xxxxxxxxxx',
|
||||
},
|
||||
createdAt: {
|
||||
type: 'string' as const,
|
||||
optional: false as const, nullable: false as const,
|
||||
format: 'date-time',
|
||||
description: 'The date that the MessagingMessage was created.'
|
||||
},
|
||||
userId: {
|
||||
type: 'string' as const,
|
||||
|
|
|
|||
|
|
@ -41,14 +41,12 @@ export const packedMutingSchema = {
|
|||
type: 'string' as const,
|
||||
optional: false as const, nullable: false as const,
|
||||
format: 'id',
|
||||
description: 'The unique identifier for this muting.',
|
||||
example: 'xxxxxxxxxx',
|
||||
},
|
||||
createdAt: {
|
||||
type: 'string' as const,
|
||||
optional: false as const, nullable: false as const,
|
||||
format: 'date-time',
|
||||
description: 'The date that the muting was created.'
|
||||
},
|
||||
muteeId: {
|
||||
type: 'string' as const,
|
||||
|
|
@ -59,7 +57,6 @@ export const packedMutingSchema = {
|
|||
type: 'object' as const,
|
||||
optional: false as const, nullable: false as const,
|
||||
ref: 'User',
|
||||
description: 'The mutee.'
|
||||
},
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -35,14 +35,12 @@ export const packedNoteFavoriteSchema = {
|
|||
type: 'string' as const,
|
||||
optional: false as const, nullable: false as const,
|
||||
format: 'id',
|
||||
description: 'The unique identifier for this favorite.',
|
||||
example: 'xxxxxxxxxx',
|
||||
},
|
||||
createdAt: {
|
||||
type: 'string' as const,
|
||||
optional: false as const, nullable: false as const,
|
||||
format: 'date-time',
|
||||
description: 'The date that the favorite was created.'
|
||||
},
|
||||
note: {
|
||||
type: 'object' as const,
|
||||
|
|
|
|||
|
|
@ -32,25 +32,21 @@ export const packedNoteReactionSchema = {
|
|||
type: 'string' as const,
|
||||
optional: false as const, nullable: false as const,
|
||||
format: 'id',
|
||||
description: 'The unique identifier for this reaction.',
|
||||
example: 'xxxxxxxxxx',
|
||||
},
|
||||
createdAt: {
|
||||
type: 'string' as const,
|
||||
optional: false as const, nullable: false as const,
|
||||
format: 'date-time',
|
||||
description: 'The date that the reaction was created.'
|
||||
},
|
||||
user: {
|
||||
type: 'object' as const,
|
||||
optional: false as const, nullable: false as const,
|
||||
ref: 'User',
|
||||
description: 'User who performed this reaction.'
|
||||
},
|
||||
type: {
|
||||
type: 'string' as const,
|
||||
optional: false as const, nullable: false as const,
|
||||
description: 'The reaction type.'
|
||||
},
|
||||
},
|
||||
};
|
||||
|
|
|
|||
|
|
@ -281,14 +281,12 @@ export const packedNoteSchema = {
|
|||
type: 'string' as const,
|
||||
optional: false as const, nullable: false as const,
|
||||
format: 'id',
|
||||
description: 'The unique identifier for this Note.',
|
||||
example: 'xxxxxxxxxx',
|
||||
},
|
||||
createdAt: {
|
||||
type: 'string' as const,
|
||||
optional: false as const, nullable: false as const,
|
||||
format: 'date-time',
|
||||
description: 'The date that the Note was created on Misskey.'
|
||||
},
|
||||
text: {
|
||||
type: 'string' as const,
|
||||
|
|
@ -426,7 +424,6 @@ export const packedNoteSchema = {
|
|||
reactions: {
|
||||
type: 'object' as const,
|
||||
optional: false as const, nullable: false as const,
|
||||
description: 'Key is either Unicode emoji or custom emoji, value is count of that emoji reaction.',
|
||||
},
|
||||
renoteCount: {
|
||||
type: 'number' as const,
|
||||
|
|
@ -439,18 +436,15 @@ export const packedNoteSchema = {
|
|||
uri: {
|
||||
type: 'string' as const,
|
||||
optional: false as const, nullable: true as const,
|
||||
description: 'The URI of a note. it will be null when the note is local.',
|
||||
},
|
||||
url: {
|
||||
type: 'string' as const,
|
||||
optional: false as const, nullable: true as const,
|
||||
description: 'The human readable url of a note. it will be null when the note is local.',
|
||||
},
|
||||
|
||||
myReaction: {
|
||||
type: 'object' as const,
|
||||
optional: true as const, nullable: true as const,
|
||||
description: 'Key is either Unicode emoji or custom emoji, value is count of that emoji reaction.',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
|
|
|||
|
|
@ -117,20 +117,17 @@ export const packedNotificationSchema = {
|
|||
type: 'string' as const,
|
||||
optional: false as const, nullable: false as const,
|
||||
format: 'id',
|
||||
description: 'The unique identifier for this notification.',
|
||||
example: 'xxxxxxxxxx',
|
||||
},
|
||||
createdAt: {
|
||||
type: 'string' as const,
|
||||
optional: false as const, nullable: false as const,
|
||||
format: 'date-time',
|
||||
description: 'The date that the notification was created.'
|
||||
},
|
||||
type: {
|
||||
type: 'string' as const,
|
||||
optional: false as const, nullable: false as const,
|
||||
enum: ['follow', 'followRequestAccepted', 'receiveFollowRequest', 'mention', 'reply', 'renote', 'quote', 'reaction', 'pollVote'],
|
||||
description: 'The type of the notification.'
|
||||
},
|
||||
userId: {
|
||||
type: 'string' as const,
|
||||
|
|
|
|||
|
|
@ -34,19 +34,16 @@ export const packedUserGroupSchema = {
|
|||
type: 'string' as const,
|
||||
optional: false as const, nullable: false as const,
|
||||
format: 'id',
|
||||
description: 'The unique identifier for this UserGroup.',
|
||||
example: 'xxxxxxxxxx',
|
||||
},
|
||||
createdAt: {
|
||||
type: 'string' as const,
|
||||
optional: false as const, nullable: false as const,
|
||||
format: 'date-time',
|
||||
description: 'The date that the UserGroup was created.'
|
||||
},
|
||||
name: {
|
||||
type: 'string' as const,
|
||||
optional: false as const, nullable: false as const,
|
||||
description: 'The name of the UserGroup.'
|
||||
},
|
||||
ownerId: {
|
||||
type: 'string' as const,
|
||||
|
|
|
|||
|
|
@ -33,19 +33,16 @@ export const packedUserListSchema = {
|
|||
type: 'string' as const,
|
||||
optional: false as const, nullable: false as const,
|
||||
format: 'id',
|
||||
description: 'The unique identifier for this UserList.',
|
||||
example: 'xxxxxxxxxx',
|
||||
},
|
||||
createdAt: {
|
||||
type: 'string' as const,
|
||||
optional: false as const, nullable: false as const,
|
||||
format: 'date-time',
|
||||
description: 'The date that the UserList was created.'
|
||||
},
|
||||
name: {
|
||||
type: 'string' as const,
|
||||
optional: false as const, nullable: false as const,
|
||||
description: 'The name of the UserList.'
|
||||
},
|
||||
userIds: {
|
||||
type: 'array' as const,
|
||||
|
|
|
|||
|
|
@ -342,19 +342,16 @@ export const packedUserSchema = {
|
|||
type: 'string' as const,
|
||||
nullable: false as const, optional: false as const,
|
||||
format: 'id',
|
||||
description: 'The unique identifier for this User.',
|
||||
example: 'xxxxxxxxxx',
|
||||
},
|
||||
name: {
|
||||
type: 'string' as const,
|
||||
nullable: true as const, optional: false as const,
|
||||
description: 'The name of the user, as they’ve defined it.',
|
||||
example: '藍'
|
||||
},
|
||||
username: {
|
||||
type: 'string' as const,
|
||||
nullable: false as const, optional: false as const,
|
||||
description: 'The screen name, handle, or alias that this user identifies themselves with.',
|
||||
example: 'ai'
|
||||
},
|
||||
host: {
|
||||
|
|
@ -379,24 +376,20 @@ export const packedUserSchema = {
|
|||
isAdmin: {
|
||||
type: 'boolean' as const,
|
||||
nullable: false as const, optional: false as const,
|
||||
description: 'Whether this account is the admin.',
|
||||
default: false
|
||||
},
|
||||
isModerator: {
|
||||
type: 'boolean' as const,
|
||||
nullable: false as const, optional: false as const,
|
||||
description: 'Whether this account is a moderator.',
|
||||
default: false
|
||||
},
|
||||
isBot: {
|
||||
type: 'boolean' as const,
|
||||
nullable: false as const, optional: true as const,
|
||||
description: 'Whether this account is a bot.'
|
||||
},
|
||||
isCat: {
|
||||
type: 'boolean' as const,
|
||||
nullable: false as const, optional: true as const,
|
||||
description: 'Whether this account is a cat.'
|
||||
},
|
||||
emojis: {
|
||||
type: 'array' as const,
|
||||
|
|
@ -438,7 +431,6 @@ export const packedUserSchema = {
|
|||
type: 'string' as const,
|
||||
nullable: false as const, optional: true as const,
|
||||
format: 'date-time',
|
||||
description: 'The date that the user account was created on Misskey.'
|
||||
},
|
||||
updatedAt: {
|
||||
type: 'string' as const,
|
||||
|
|
@ -471,7 +463,6 @@ export const packedUserSchema = {
|
|||
description: {
|
||||
type: 'string' as const,
|
||||
nullable: true as const, optional: true as const,
|
||||
description: 'The user-defined UTF-8 string describing their account.',
|
||||
example: 'Hi masters, I am Ai!'
|
||||
},
|
||||
location: {
|
||||
|
|
@ -505,17 +496,14 @@ export const packedUserSchema = {
|
|||
followersCount: {
|
||||
type: 'number' as const,
|
||||
nullable: false as const, optional: true as const,
|
||||
description: 'The number of followers this account currently has.'
|
||||
},
|
||||
followingCount: {
|
||||
type: 'number' as const,
|
||||
nullable: false as const, optional: true as const,
|
||||
description: 'The number of users this account is following.'
|
||||
},
|
||||
notesCount: {
|
||||
type: 'number' as const,
|
||||
nullable: false as const, optional: true as const,
|
||||
description: 'The number of Notes (including renotes) issued by the user.'
|
||||
},
|
||||
pinnedNoteIds: {
|
||||
type: 'array' as const,
|
||||
|
|
|
|||
|
|
@ -1,12 +1,12 @@
|
|||
import Resolver from '../../resolver';
|
||||
import { IRemoteUser } from '../../../../models/entities/user';
|
||||
import acceptFollow from './follow';
|
||||
import { IAccept, IFollow } from '../../type';
|
||||
import { IAccept, isFollow, getApType } from '../../type';
|
||||
import { apLogger } from '../../logger';
|
||||
|
||||
const logger = apLogger;
|
||||
|
||||
export default async (actor: IRemoteUser, activity: IAccept): Promise<void> => {
|
||||
export default async (actor: IRemoteUser, activity: IAccept): Promise<string> => {
|
||||
const uri = activity.id || activity;
|
||||
|
||||
logger.info(`Accept: ${uri}`);
|
||||
|
|
@ -18,13 +18,7 @@ export default async (actor: IRemoteUser, activity: IAccept): Promise<void> => {
|
|||
throw e;
|
||||
});
|
||||
|
||||
switch (object.type) {
|
||||
case 'Follow':
|
||||
acceptFollow(actor, object as IFollow);
|
||||
break;
|
||||
if (isFollow(object)) return await acceptFollow(actor, object);
|
||||
|
||||
default:
|
||||
logger.warn(`Unknown accept type: ${object.type}`);
|
||||
break;
|
||||
}
|
||||
return `skip: Unknown Accept type: ${getApType(object)}`;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import Resolver from '../../resolver';
|
||||
import { IRemoteUser } from '../../../../models/entities/user';
|
||||
import createNote from './note';
|
||||
import { ICreate, getApId, validPost } from '../../type';
|
||||
import { ICreate, getApId, isPost, getApType } from '../../type';
|
||||
import { apLogger } from '../../logger';
|
||||
import { toArray, concat, unique } from '../../../../prelude/array';
|
||||
|
||||
|
|
@ -35,9 +35,9 @@ export default async (actor: IRemoteUser, activity: ICreate): Promise<void> => {
|
|||
throw e;
|
||||
});
|
||||
|
||||
if (validPost.includes(object.type)) {
|
||||
if (isPost(object)) {
|
||||
createNote(resolver, actor, object, false, activity);
|
||||
} else {
|
||||
logger.warn(`Unknown type: ${object.type}`);
|
||||
logger.warn(`Unknown type: ${getApType(object)}`);
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,12 +1,12 @@
|
|||
import Resolver from '../../resolver';
|
||||
import { IRemoteUser } from '../../../../models/entities/user';
|
||||
import rejectFollow from './follow';
|
||||
import { IReject, IFollow } from '../../type';
|
||||
import { IReject, isFollow, getApType } from '../../type';
|
||||
import { apLogger } from '../../logger';
|
||||
|
||||
const logger = apLogger;
|
||||
|
||||
export default async (actor: IRemoteUser, activity: IReject): Promise<void> => {
|
||||
export default async (actor: IRemoteUser, activity: IReject): Promise<string> => {
|
||||
const uri = activity.id || activity;
|
||||
|
||||
logger.info(`Reject: ${uri}`);
|
||||
|
|
@ -18,13 +18,7 @@ export default async (actor: IRemoteUser, activity: IReject): Promise<void> => {
|
|||
throw e;
|
||||
});
|
||||
|
||||
switch (object.type) {
|
||||
case 'Follow':
|
||||
rejectFollow(actor, object as IFollow);
|
||||
break;
|
||||
if (isFollow(object)) return await rejectFollow(actor, object);
|
||||
|
||||
default:
|
||||
logger.warn(`Unknown reject type: ${object.type}`);
|
||||
break;
|
||||
}
|
||||
return `skip: Unknown Reject type: ${getApType(object)}`;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -3,14 +3,15 @@ import { IRemoteUser } from '../../../../models/entities/user';
|
|||
import { IAnnounce, getApId } from '../../type';
|
||||
import deleteNote from '../../../../services/note/delete';
|
||||
|
||||
export const undoAnnounce = async (actor: IRemoteUser, activity: IAnnounce): Promise<void> => {
|
||||
export const undoAnnounce = async (actor: IRemoteUser, activity: IAnnounce): Promise<string> => {
|
||||
const uri = getApId(activity);
|
||||
|
||||
const note = await Notes.findOne({
|
||||
uri
|
||||
});
|
||||
|
||||
if (!note) return;
|
||||
if (!note) return 'skip: no such Announce';
|
||||
|
||||
await deleteNote(actor, note);
|
||||
return 'ok: deleted';
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { IRemoteUser } from '../../../../models/entities/user';
|
||||
import { IUndo, IFollow, IBlock, ILike, IAnnounce } from '../../type';
|
||||
import { IUndo, isFollow, isBlock, isLike, isAnnounce, getApType } from '../../type';
|
||||
import unfollow from './follow';
|
||||
import unblock from './block';
|
||||
import undoLike from './like';
|
||||
|
|
@ -9,7 +9,7 @@ import { apLogger } from '../../logger';
|
|||
|
||||
const logger = apLogger;
|
||||
|
||||
export default async (actor: IRemoteUser, activity: IUndo): Promise<void> => {
|
||||
export default async (actor: IRemoteUser, activity: IUndo): Promise<string> => {
|
||||
if ('actor' in activity && actor.uri !== activity.actor) {
|
||||
throw new Error('invalid actor');
|
||||
}
|
||||
|
|
@ -25,20 +25,10 @@ export default async (actor: IRemoteUser, activity: IUndo): Promise<void> => {
|
|||
throw e;
|
||||
});
|
||||
|
||||
switch (object.type) {
|
||||
case 'Follow':
|
||||
unfollow(actor, object as IFollow);
|
||||
break;
|
||||
case 'Block':
|
||||
unblock(actor, object as IBlock);
|
||||
break;
|
||||
case 'Like':
|
||||
case 'EmojiReaction':
|
||||
case 'EmojiReact':
|
||||
undoLike(actor, object as ILike);
|
||||
break;
|
||||
case 'Announce':
|
||||
undoAnnounce(actor, object as IAnnounce);
|
||||
break;
|
||||
}
|
||||
if (isFollow(object)) return await unfollow(actor, object);
|
||||
if (isBlock(object)) return await unblock(actor, object);
|
||||
if (isLike(object)) return await undoLike(actor, object);
|
||||
if (isAnnounce(object)) return await undoAnnounce(actor, object);
|
||||
|
||||
return `skip: unknown object type ${getApType(object)}`;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { IRemoteUser } from '../../../../models/entities/user';
|
||||
import { IUpdate, validActor } from '../../type';
|
||||
import { getApType, IUpdate, isActor } from '../../type';
|
||||
import { apLogger } from '../../logger';
|
||||
import { updateQuestion } from '../../models/question';
|
||||
import Resolver from '../../resolver';
|
||||
|
|
@ -22,13 +22,13 @@ export default async (actor: IRemoteUser, activity: IUpdate): Promise<string> =>
|
|||
throw e;
|
||||
});
|
||||
|
||||
if (validActor.includes(object.type)) {
|
||||
if (isActor(object)) {
|
||||
await updatePerson(actor.uri!, resolver, object);
|
||||
return `ok: Person updated`;
|
||||
} else if (object.type === 'Question') {
|
||||
} else if (getApType(object) === 'Question') {
|
||||
await updateQuestion(object).catch(e => console.log(e));
|
||||
return `ok: Question updated`;
|
||||
} else {
|
||||
return `skip: Unknown type: ${object.type}`;
|
||||
return `skip: Unknown type: ${getApType(object)}`;
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ export async function createImage(actor: IRemoteUser, value: any): Promise<Drive
|
|||
const instance = await fetchMeta();
|
||||
const cache = instance.cacheRemoteFiles;
|
||||
|
||||
let file = await uploadFromUrl(image.url, actor, null, image.url, image.sensitive, false, !cache);
|
||||
let file = await uploadFromUrl(image.url, actor, null, image.url, image.sensitive, false, !cache, image.name);
|
||||
|
||||
if (file.isLink) {
|
||||
// URLが異なっている場合、同じ画像が以前に異なるURLで登録されていたということなので、
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ import { deliverQuestionUpdate } from '../../../services/note/polls/update';
|
|||
import { extractDbHost, toPuny } from '@/misc/convert-host';
|
||||
import { Emojis, Polls, MessagingMessages } from '../../../models';
|
||||
import { Note } from '../../../models/entities/note';
|
||||
import { IObject, getOneApId, getApId, getOneApHrefNullable, validPost, IPost, isEmoji } from '../type';
|
||||
import { IObject, getOneApId, getApId, getOneApHrefNullable, validPost, IPost, isEmoji, getApType } from '../type';
|
||||
import { Emoji } from '../../../models/entities/emoji';
|
||||
import { genId } from '@/misc/gen-id';
|
||||
import { fetchMeta } from '@/misc/fetch-meta';
|
||||
|
|
@ -36,8 +36,8 @@ export function validateNote(object: any, uri: string) {
|
|||
return new Error('invalid Note: object is null');
|
||||
}
|
||||
|
||||
if (!validPost.includes(object.type)) {
|
||||
return new Error(`invalid Note: invalid object type ${object.type}`);
|
||||
if (!validPost.includes(getApType(object))) {
|
||||
return new Error(`invalid Note: invalid object type ${getApType(object)}`);
|
||||
}
|
||||
|
||||
if (object.id && extractDbHost(object.id) !== expectHost) {
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import * as promiseLimit from 'promise-limit';
|
|||
import config from '@/config';
|
||||
import Resolver from '../resolver';
|
||||
import { resolveImage } from './image';
|
||||
import { isCollectionOrOrderedCollection, isCollection, IPerson, getApId, getOneApHrefNullable, IObject, isPropertyValue, IApPropertyValue } from '../type';
|
||||
import { isCollectionOrOrderedCollection, isCollection, IPerson, getApId, getOneApHrefNullable, IObject, isPropertyValue, IApPropertyValue, getApType } from '../type';
|
||||
import { fromHtml } from '../../../mfm/from-html';
|
||||
import { htmlToMfm } from '../misc/html-to-mfm';
|
||||
import { resolveNote, extractEmojis } from './note';
|
||||
|
|
@ -137,7 +137,7 @@ export async function createPerson(uri: string, resolver?: Resolver): Promise<Us
|
|||
|
||||
const tags = extractApHashtags(person.tag).map(tag => normalizeForSearch(tag)).splice(0, 32);
|
||||
|
||||
const isBot = object.type === 'Service';
|
||||
const isBot = getApType(object) === 'Service';
|
||||
|
||||
const bday = person['vcard:bday']?.match(/^\d{4}-\d{2}-\d{2}/);
|
||||
|
||||
|
|
@ -337,7 +337,7 @@ export async function updatePerson(uri: string, resolver?: Resolver | null, hint
|
|||
emojis: emojiNames,
|
||||
name: person.name,
|
||||
tags,
|
||||
isBot: object.type === 'Service',
|
||||
isBot: getApType(object) === 'Service',
|
||||
isCat: (person as any).isCat === true,
|
||||
isLocked: !!person.manuallyApprovesFollowers,
|
||||
isExplorable: !!person.discoverable,
|
||||
|
|
@ -476,7 +476,7 @@ export async function updateFeatured(userId: User['id']) {
|
|||
// Resolve and regist Notes
|
||||
const limit = promiseLimit<Note | null>(2);
|
||||
const featuredNotes = await Promise.all(items
|
||||
.filter(item => item.type === 'Note')
|
||||
.filter(item => getApType(item) === 'Note') // TODO: Noteでなくてもいいかも
|
||||
.slice(0, 5)
|
||||
.map(item => limit(() => resolveNote(item, resolver))));
|
||||
|
||||
|
|
|
|||
|
|
@ -4,5 +4,6 @@ import { DriveFiles } from '../../../models';
|
|||
export default (file: DriveFile) => ({
|
||||
type: 'Document',
|
||||
mediaType: file.type,
|
||||
url: DriveFiles.getPublicUrl(file)
|
||||
url: DriveFiles.getPublicUrl(file),
|
||||
name: file.comment,
|
||||
});
|
||||
|
|
|
|||
|
|
@ -4,5 +4,6 @@ import { DriveFiles } from '../../../models';
|
|||
export default (file: DriveFile) => ({
|
||||
type: 'Image',
|
||||
url: DriveFiles.getPublicUrl(file),
|
||||
sensitive: file.isSensitive
|
||||
sensitive: file.isSensitive,
|
||||
name: file.comment
|
||||
});
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ export type ApObject = IObject | string | (IObject | string)[];
|
|||
|
||||
export interface IObject {
|
||||
'@context': string | obj | obj[];
|
||||
type: string;
|
||||
type: string | string[];
|
||||
id?: string;
|
||||
summary?: string;
|
||||
published?: string;
|
||||
|
|
@ -51,6 +51,15 @@ export function getApId(value: string | IObject): string {
|
|||
throw new Error(`cannot detemine id`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get ActivityStreams Object type
|
||||
*/
|
||||
export function getApType(value: IObject): string {
|
||||
if (typeof value.type === 'string') return value.type;
|
||||
if (Array.isArray(value.type) && typeof value.type[0] === 'string') return value.type[0];
|
||||
throw new Error(`cannot detect type`);
|
||||
}
|
||||
|
||||
export function getOneApHrefNullable(value: ApObject | undefined): string | undefined {
|
||||
const firstOne = Array.isArray(value) ? value[0] : value;
|
||||
return getApHrefNullable(firstOne);
|
||||
|
|
@ -92,6 +101,9 @@ export interface IOrderedCollection extends IObject {
|
|||
|
||||
export const validPost = ['Note', 'Question', 'Article', 'Audio', 'Document', 'Image', 'Page', 'Video', 'Event'];
|
||||
|
||||
export const isPost = (object: IObject): object is IPost =>
|
||||
validPost.includes(getApType(object));
|
||||
|
||||
export interface IPost extends IObject {
|
||||
type: 'Note' | 'Question' | 'Article' | 'Audio' | 'Document' | 'Image' | 'Page' | 'Video' | 'Event';
|
||||
_misskey_content?: string;
|
||||
|
|
@ -112,7 +124,7 @@ export interface IQuestion extends IObject {
|
|||
}
|
||||
|
||||
export const isQuestion = (object: IObject): object is IQuestion =>
|
||||
object.type === 'Note' || object.type === 'Question';
|
||||
getApType(object) === 'Note' || getApType(object) === 'Question';
|
||||
|
||||
interface IQuestionChoice {
|
||||
name?: string;
|
||||
|
|
@ -126,10 +138,13 @@ export interface ITombstone extends IObject {
|
|||
}
|
||||
|
||||
export const isTombstone = (object: IObject): object is ITombstone =>
|
||||
object.type === 'Tombstone';
|
||||
getApType(object) === 'Tombstone';
|
||||
|
||||
export const validActor = ['Person', 'Service', 'Group', 'Organization', 'Application'];
|
||||
|
||||
export const isActor = (object: IObject): object is IPerson =>
|
||||
validActor.includes(getApType(object));
|
||||
|
||||
export interface IPerson extends IObject {
|
||||
type: 'Person' | 'Service' | 'Organization' | 'Group' | 'Application';
|
||||
name?: string;
|
||||
|
|
@ -154,10 +169,10 @@ export interface IPerson extends IObject {
|
|||
}
|
||||
|
||||
export const isCollection = (object: IObject): object is ICollection =>
|
||||
object.type === 'Collection';
|
||||
getApType(object) === 'Collection';
|
||||
|
||||
export const isOrderedCollection = (object: IObject): object is IOrderedCollection =>
|
||||
object.type === 'OrderedCollection';
|
||||
getApType(object) === 'OrderedCollection';
|
||||
|
||||
export const isCollectionOrOrderedCollection = (object: IObject): object is ICollection | IOrderedCollection =>
|
||||
isCollection(object) || isOrderedCollection(object);
|
||||
|
|
@ -171,7 +186,7 @@ export interface IApPropertyValue extends IObject {
|
|||
|
||||
export const isPropertyValue = (object: IObject): object is IApPropertyValue =>
|
||||
object &&
|
||||
object.type === 'PropertyValue' &&
|
||||
getApType(object) === 'PropertyValue' &&
|
||||
typeof object.name === 'string' &&
|
||||
typeof (object as any).value === 'string';
|
||||
|
||||
|
|
@ -181,7 +196,7 @@ export interface IApMention extends IObject {
|
|||
}
|
||||
|
||||
export const isMention = (object: IObject): object is IApMention=>
|
||||
object.type === 'Mention' &&
|
||||
getApType(object) === 'Mention' &&
|
||||
typeof object.href === 'string';
|
||||
|
||||
export interface IApHashtag extends IObject {
|
||||
|
|
@ -190,7 +205,7 @@ export interface IApHashtag extends IObject {
|
|||
}
|
||||
|
||||
export const isHashtag = (object: IObject): object is IApHashtag =>
|
||||
object.type === 'Hashtag' &&
|
||||
getApType(object) === 'Hashtag' &&
|
||||
typeof object.name === 'string';
|
||||
|
||||
export interface IApEmoji extends IObject {
|
||||
|
|
@ -199,7 +214,7 @@ export interface IApEmoji extends IObject {
|
|||
}
|
||||
|
||||
export const isEmoji = (object: IObject): object is IApEmoji =>
|
||||
object.type === 'Emoji' && !Array.isArray(object.icon) && object.icon.url != null;
|
||||
getApType(object) === 'Emoji' && !Array.isArray(object.icon) && object.icon.url != null;
|
||||
|
||||
export interface ICreate extends IActivity {
|
||||
type: 'Create';
|
||||
|
|
@ -258,17 +273,17 @@ export interface IFlag extends IActivity {
|
|||
type: 'Flag';
|
||||
}
|
||||
|
||||
export const isCreate = (object: IObject): object is ICreate => object.type === 'Create';
|
||||
export const isDelete = (object: IObject): object is IDelete => object.type === 'Delete';
|
||||
export const isUpdate = (object: IObject): object is IUpdate => object.type === 'Update';
|
||||
export const isRead = (object: IObject): object is IRead => object.type === 'Read';
|
||||
export const isUndo = (object: IObject): object is IUndo => object.type === 'Undo';
|
||||
export const isFollow = (object: IObject): object is IFollow => object.type === 'Follow';
|
||||
export const isAccept = (object: IObject): object is IAccept => object.type === 'Accept';
|
||||
export const isReject = (object: IObject): object is IReject => object.type === 'Reject';
|
||||
export const isAdd = (object: IObject): object is IAdd => object.type === 'Add';
|
||||
export const isRemove = (object: IObject): object is IRemove => object.type === 'Remove';
|
||||
export const isLike = (object: IObject): object is ILike => object.type === 'Like' || object.type === 'EmojiReaction' || object.type === 'EmojiReact';
|
||||
export const isAnnounce = (object: IObject): object is IAnnounce => object.type === 'Announce';
|
||||
export const isBlock = (object: IObject): object is IBlock => object.type === 'Block';
|
||||
export const isFlag = (object: IObject): object is IFlag => object.type === 'Flag';
|
||||
export const isCreate = (object: IObject): object is ICreate => getApType(object) === 'Create';
|
||||
export const isDelete = (object: IObject): object is IDelete => getApType(object) === 'Delete';
|
||||
export const isUpdate = (object: IObject): object is IUpdate => getApType(object) === 'Update';
|
||||
export const isRead = (object: IObject): object is IRead => getApType(object) === 'Read';
|
||||
export const isUndo = (object: IObject): object is IUndo => getApType(object) === 'Undo';
|
||||
export const isFollow = (object: IObject): object is IFollow => getApType(object) === 'Follow';
|
||||
export const isAccept = (object: IObject): object is IAccept => getApType(object) === 'Accept';
|
||||
export const isReject = (object: IObject): object is IReject => getApType(object) === 'Reject';
|
||||
export const isAdd = (object: IObject): object is IAdd => getApType(object) === 'Add';
|
||||
export const isRemove = (object: IObject): object is IRemove => getApType(object) === 'Remove';
|
||||
export const isLike = (object: IObject): object is ILike => getApType(object) === 'Like' || getApType(object) === 'EmojiReaction' || getApType(object) === 'EmojiReact';
|
||||
export const isAnnounce = (object: IObject): object is IAnnounce => getApType(object) === 'Announce';
|
||||
export const isBlock = (object: IObject): object is IBlock => getApType(object) === 'Block';
|
||||
export const isFlag = (object: IObject): object is IFlag => getApType(object) === 'Flag';
|
||||
|
|
|
|||
|
|
@ -5,6 +5,8 @@ import { ApiError } from './error';
|
|||
import { SchemaType } from '@/misc/schema';
|
||||
import { AccessToken } from '../../models/entities/access-token';
|
||||
|
||||
type NonOptional<T> = T extends undefined ? never : T;
|
||||
|
||||
type SimpleUserInfo = {
|
||||
id: ILocalUser['id'];
|
||||
host: ILocalUser['host'];
|
||||
|
|
@ -17,11 +19,12 @@ type SimpleUserInfo = {
|
|||
isSilenced: ILocalUser['isSilenced'];
|
||||
};
|
||||
|
||||
// TODO: defaultが設定されている場合はその型も考慮する
|
||||
type Params<T extends IEndpointMeta> = {
|
||||
[P in keyof T['params']]: NonNullable<T['params']>[P]['transform'] extends Function
|
||||
? ReturnType<NonNullable<T['params']>[P]['transform']>
|
||||
: ReturnType<NonNullable<T['params']>[P]['validator']['get']>[0];
|
||||
: NonNullable<T['params']>[P]['default'] extends null | number | string
|
||||
? NonOptional<ReturnType<NonNullable<T['params']>[P]['validator']['get']>[0]>
|
||||
: ReturnType<NonNullable<T['params']>[P]['validator']['get']>[0];
|
||||
};
|
||||
|
||||
export type Response = Record<string, any> | void;
|
||||
|
|
|
|||
|
|
@ -8,15 +8,12 @@ export type Param = {
|
|||
transform?: any;
|
||||
default?: any;
|
||||
deprecated?: boolean;
|
||||
desc?: { [key: string]: string };
|
||||
ref?: string;
|
||||
};
|
||||
|
||||
export interface IEndpointMeta {
|
||||
stability?: string; //'deprecated' | 'experimental' | 'stable';
|
||||
|
||||
desc?: { [key: string]: string };
|
||||
|
||||
tags?: string[];
|
||||
|
||||
params?: {
|
||||
|
|
|
|||
|
|
@ -5,11 +5,6 @@ import { AbuseUserReports } from '../../../../models';
|
|||
import { makePaginationQuery } from '../../common/make-pagination-query';
|
||||
|
||||
export const meta = {
|
||||
desc: {
|
||||
'ja-JP': '通報一覧を表示します。',
|
||||
'en-US': 'Show list of abuse user reports.'
|
||||
},
|
||||
|
||||
tags: ['admin'],
|
||||
|
||||
requireCredential: true as const,
|
||||
|
|
@ -64,43 +59,36 @@ export const meta = {
|
|||
type: 'string' as const,
|
||||
nullable: false as const, optional: false as const,
|
||||
format: 'id',
|
||||
description: 'The unique identifier for this User.',
|
||||
example: 'xxxxxxxxxx',
|
||||
},
|
||||
createdAt: {
|
||||
type: 'string' as const,
|
||||
nullable: false as const, optional: false as const,
|
||||
format: 'date-time',
|
||||
description: 'The date that the abuse user report was created on Misskey.'
|
||||
},
|
||||
comment: {
|
||||
type: 'string' as const,
|
||||
nullable: false as const, optional: false as const,
|
||||
description: 'The content of the report.',
|
||||
},
|
||||
resolved: {
|
||||
type: 'boolean' as const,
|
||||
nullable: false as const, optional: false as const,
|
||||
description: 'Returns whether this report has been resolved',
|
||||
example: false
|
||||
},
|
||||
reporterId: {
|
||||
type: 'string' as const,
|
||||
nullable: false as const, optional: false as const,
|
||||
format: 'id',
|
||||
description: 'Reporter\'s user ID.'
|
||||
},
|
||||
targetUserId: {
|
||||
type: 'string' as const,
|
||||
nullable: false as const, optional: false as const,
|
||||
format: 'id',
|
||||
description: 'User ID of the person to be reported.'
|
||||
},
|
||||
assigneeId: {
|
||||
type: 'string' as const,
|
||||
nullable: true as const, optional: false as const,
|
||||
format: 'id',
|
||||
description: 'User ID of the person who responded to the report.'
|
||||
},
|
||||
reporter: {
|
||||
type: 'object' as const,
|
||||
|
|
|
|||
|
|
@ -3,11 +3,6 @@ import { Users } from '../../../../../models';
|
|||
import { signup } from '../../../common/signup';
|
||||
|
||||
export const meta = {
|
||||
desc: {
|
||||
'ja-JP': 'アカウントを作成します。',
|
||||
'en-US': 'Create a account.'
|
||||
},
|
||||
|
||||
tags: ['admin'],
|
||||
|
||||
params: {
|
||||
|
|
@ -28,7 +23,6 @@ export const meta = {
|
|||
token: {
|
||||
type: 'string' as const,
|
||||
optional: false as const, nullable: false as const,
|
||||
description: 'Token to access this user.'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,11 +4,6 @@ import { Announcements } from '../../../../../models';
|
|||
import { genId } from '@/misc/gen-id';
|
||||
|
||||
export const meta = {
|
||||
desc: {
|
||||
'ja-JP': 'アナウンスを作成します。',
|
||||
'en-US': 'Create a announcement.'
|
||||
},
|
||||
|
||||
tags: ['admin'],
|
||||
|
||||
requireCredential: true as const,
|
||||
|
|
@ -34,35 +29,29 @@ export const meta = {
|
|||
type: 'string' as const,
|
||||
optional: false as const, nullable: false as const,
|
||||
format: 'id',
|
||||
description: 'The unique identifier for this Announcement.',
|
||||
example: 'xxxxxxxxxx',
|
||||
},
|
||||
createdAt: {
|
||||
type: 'string' as const,
|
||||
optional: false as const, nullable: false as const,
|
||||
format: 'date-time',
|
||||
description: 'The date that the Announcement was created.'
|
||||
},
|
||||
updatedAt: {
|
||||
type: 'string' as const,
|
||||
optional: false as const, nullable: true as const,
|
||||
format: 'date-time',
|
||||
description: 'The date that the Announcement was updated.'
|
||||
},
|
||||
title: {
|
||||
type: 'string' as const,
|
||||
optional: false as const, nullable: false as const,
|
||||
description: 'Announcement title.'
|
||||
},
|
||||
text: {
|
||||
type: 'string' as const,
|
||||
optional: false as const, nullable: false as const,
|
||||
description: 'Announcement text.'
|
||||
},
|
||||
imageUrl: {
|
||||
type: 'string' as const,
|
||||
optional: false as const, nullable: true as const,
|
||||
description: 'Announcement image.'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,11 +10,6 @@ export const meta = {
|
|||
requireCredential: true as const,
|
||||
requireModerator: true,
|
||||
|
||||
desc: {
|
||||
'ja-JP': 'アナウンスを削除します。',
|
||||
'en-US': 'Delete a announcement.'
|
||||
},
|
||||
|
||||
params: {
|
||||
id: {
|
||||
validator: $.type(ID)
|
||||
|
|
|
|||
|
|
@ -5,11 +5,6 @@ import { Announcements, AnnouncementReads } from '../../../../../models';
|
|||
import { makePaginationQuery } from '../../../common/make-pagination-query';
|
||||
|
||||
export const meta = {
|
||||
desc: {
|
||||
'ja-JP': 'アナウンスのリストを表示します。',
|
||||
'en-US': 'List announcements.'
|
||||
},
|
||||
|
||||
tags: ['admin'],
|
||||
|
||||
requireCredential: true as const,
|
||||
|
|
@ -41,40 +36,33 @@ export const meta = {
|
|||
type: 'string' as const,
|
||||
optional: false as const, nullable: false as const,
|
||||
format: 'id',
|
||||
description: 'The unique identifier for this Announcement.',
|
||||
example: 'xxxxxxxxxx',
|
||||
},
|
||||
createdAt: {
|
||||
type: 'string' as const,
|
||||
optional: false as const, nullable: false as const,
|
||||
format: 'date-time',
|
||||
description: 'The date that the Announcement was created.'
|
||||
},
|
||||
updatedAt: {
|
||||
type: 'string' as const,
|
||||
optional: false as const, nullable: true as const,
|
||||
format: 'date-time',
|
||||
description: 'The date that the Announcement was updated.'
|
||||
},
|
||||
text: {
|
||||
type: 'string' as const,
|
||||
optional: false as const, nullable: false as const,
|
||||
description: 'Announcement text.'
|
||||
},
|
||||
title: {
|
||||
type: 'string' as const,
|
||||
optional: false as const, nullable: false as const,
|
||||
description: 'Announcement title.'
|
||||
},
|
||||
imageUrl: {
|
||||
type: 'string' as const,
|
||||
optional: false as const, nullable: true as const,
|
||||
description: 'Announcement image.'
|
||||
},
|
||||
reads: {
|
||||
type: 'number' as const,
|
||||
optional: false as const, nullable: false as const,
|
||||
description: 'Number of people who read this announcement.'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,11 +5,6 @@ import { Announcements } from '../../../../../models';
|
|||
import { ApiError } from '../../../error';
|
||||
|
||||
export const meta = {
|
||||
desc: {
|
||||
'ja-JP': 'アナウンスの内容を変更します。',
|
||||
'en-US': 'Update a annoucement.'
|
||||
},
|
||||
|
||||
tags: ['admin'],
|
||||
|
||||
requireCredential: true as const,
|
||||
|
|
|
|||
|
|
@ -5,11 +5,6 @@ import { DriveFiles } from '../../../../models';
|
|||
import { ID } from '@/misc/cafy-id';
|
||||
|
||||
export const meta = {
|
||||
desc: {
|
||||
'ja-JP': '対象のユーザーのファイルを全て削除します。',
|
||||
'en-US': 'Delete all files to specified user.'
|
||||
},
|
||||
|
||||
tags: ['admin'],
|
||||
|
||||
requireCredential: true as const,
|
||||
|
|
@ -18,10 +13,6 @@ export const meta = {
|
|||
params: {
|
||||
userId: {
|
||||
validator: $.type(ID),
|
||||
desc: {
|
||||
'ja-JP': '対象のユーザーID',
|
||||
'en-US': 'The user ID which you want to suspend'
|
||||
}
|
||||
},
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -2,11 +2,6 @@ import define from '../../define';
|
|||
import { Logs } from '../../../../models';
|
||||
|
||||
export const meta = {
|
||||
desc: {
|
||||
'ja-JP': 'ログを全て削除します。',
|
||||
'en-US': 'Delete all logs.'
|
||||
},
|
||||
|
||||
tags: ['admin'],
|
||||
|
||||
requireCredential: true as const,
|
||||
|
|
|
|||
|
|
@ -2,11 +2,6 @@ import define from '../../../define';
|
|||
import { createCleanRemoteFilesJob } from '../../../../../queue';
|
||||
|
||||
export const meta = {
|
||||
desc: {
|
||||
'ja-JP': 'キャッシュされたリモートファイルをすべて削除します。',
|
||||
'en-US': 'Deletes all cached remote files.'
|
||||
},
|
||||
|
||||
tags: ['admin'],
|
||||
|
||||
requireCredential: true as const,
|
||||
|
|
|
|||
|
|
@ -4,11 +4,6 @@ import { deleteFile } from '../../../../../services/drive/delete-file';
|
|||
import { DriveFiles } from '../../../../../models';
|
||||
|
||||
export const meta = {
|
||||
desc: {
|
||||
'ja-JP': '使用されていないユーザーのファイルを削除します。',
|
||||
'en-US': 'Delete the unused user\'s files.'
|
||||
},
|
||||
|
||||
tags: ['admin'],
|
||||
|
||||
requireCredential: true as const,
|
||||
|
|
|
|||
|
|
@ -5,11 +5,6 @@ import { makePaginationQuery } from '../../../common/make-pagination-query';
|
|||
import { ID } from '@/misc/cafy-id';
|
||||
|
||||
export const meta = {
|
||||
desc: {
|
||||
'ja-JP': '管理用のドライブの一覧を表示します。',
|
||||
'en-US': 'Displays a list of management drives.'
|
||||
},
|
||||
|
||||
tags: ['admin'],
|
||||
|
||||
requireCredential: false as const,
|
||||
|
|
|
|||
|
|
@ -36,20 +36,17 @@ export const meta = {
|
|||
type: 'string' as const,
|
||||
optional: false as const, nullable: false as const,
|
||||
format: 'id',
|
||||
description: 'The unique identifier for this Drive file.',
|
||||
example: 'xxxxxxxxxx',
|
||||
},
|
||||
createdAt: {
|
||||
type: 'string' as const,
|
||||
optional: false as const, nullable: false as const,
|
||||
format: 'date-time',
|
||||
description: 'The date that the Drive file was created on Misskey.'
|
||||
},
|
||||
userId: {
|
||||
type: 'string' as const,
|
||||
optional: false as const, nullable: true as const,
|
||||
format: 'id',
|
||||
description: 'Owner ID of this Drive file.',
|
||||
example: 'xxxxxxxxxx',
|
||||
},
|
||||
userHost: {
|
||||
|
|
@ -60,25 +57,21 @@ export const meta = {
|
|||
type: 'string' as const,
|
||||
optional: false as const, nullable: false as const,
|
||||
format: 'md5',
|
||||
description: 'The MD5 hash of this Drive file.',
|
||||
example: '15eca7fba0480996e2245f5185bf39f2'
|
||||
},
|
||||
name: {
|
||||
type: 'string' as const,
|
||||
optional: false as const, nullable: false as const,
|
||||
description: 'The file name with extension.',
|
||||
example: 'lenna.jpg'
|
||||
},
|
||||
type: {
|
||||
type: 'string' as const,
|
||||
optional: false as const, nullable: false as const,
|
||||
description: 'The MIME type of this Drive file.',
|
||||
example: 'image/jpeg'
|
||||
},
|
||||
size: {
|
||||
type: 'number' as const,
|
||||
optional: false as const, nullable: false as const,
|
||||
description: 'The size of this Drive file. (bytes)',
|
||||
example: 51469
|
||||
},
|
||||
comment: {
|
||||
|
|
@ -113,41 +106,34 @@ export const meta = {
|
|||
storedInternal: {
|
||||
type: 'boolean' as const,
|
||||
optional: false as const, nullable: true as const,
|
||||
description: 'Indicates whether this file is stored in the same location as Misskey itself',
|
||||
example: true
|
||||
},
|
||||
url: {
|
||||
type: 'string' as const,
|
||||
optional: false as const, nullable: true as const,
|
||||
format: 'url',
|
||||
description: 'The URL of this Drive file.',
|
||||
},
|
||||
thumbnailUrl: {
|
||||
type: 'string' as const,
|
||||
optional: false as const, nullable: true as const,
|
||||
format: 'url',
|
||||
description: 'The thumbnail URL of this Drive file.',
|
||||
},
|
||||
webpublicUrl: {
|
||||
type: 'string' as const,
|
||||
optional: false as const, nullable: true as const,
|
||||
format: 'url',
|
||||
description: 'The public URL of this Drive file.',
|
||||
},
|
||||
accessKey: {
|
||||
type: 'string' as const,
|
||||
optional: false as const, nullable: false as const,
|
||||
description: 'Access key to access this file'
|
||||
},
|
||||
thumbnailAccessKey: {
|
||||
type: 'string' as const,
|
||||
optional: false as const, nullable: false as const,
|
||||
description: 'Access key to access this file for thumbnail'
|
||||
},
|
||||
webpublicAccessKey: {
|
||||
type: 'string' as const,
|
||||
optional: false as const, nullable: false as const,
|
||||
description: 'Access key to access this file for webpublic'
|
||||
},
|
||||
uri: {
|
||||
type: 'string' as const,
|
||||
|
|
@ -161,13 +147,11 @@ export const meta = {
|
|||
type: 'string' as const,
|
||||
optional: false as const, nullable: true as const,
|
||||
format: 'id',
|
||||
description: 'The parent folder ID of this Drive file.',
|
||||
example: 'xxxxxxxxxx',
|
||||
},
|
||||
isSensitive: {
|
||||
type: 'boolean' as const,
|
||||
optional: false as const, nullable: false as const,
|
||||
description: 'Whether this Drive file is sensitive.',
|
||||
},
|
||||
isLink: {
|
||||
type: 'boolean' as const,
|
||||
|
|
|
|||
|
|
@ -10,10 +10,6 @@ import rndstr from 'rndstr';
|
|||
import { publishBroadcastStream } from '../../../../../services/stream';
|
||||
|
||||
export const meta = {
|
||||
desc: {
|
||||
'ja-JP': 'カスタム絵文字を追加します。'
|
||||
},
|
||||
|
||||
tags: ['admin'],
|
||||
|
||||
requireCredential: true as const,
|
||||
|
|
|
|||
|
|
@ -7,13 +7,9 @@ import { ApiError } from '../../../error';
|
|||
import { DriveFile } from '../../../../../models/entities/drive-file';
|
||||
import { ID } from '@/misc/cafy-id';
|
||||
import uploadFromUrl from '../../../../../services/drive/upload-from-url';
|
||||
import { publishBroadcastStream } from '@/services/stream';
|
||||
|
||||
export const meta = {
|
||||
desc: {
|
||||
'ja-JP': '選択したカスタム絵文字をコピーします。',
|
||||
'en-US': 'Copies the selected custom emoji.'
|
||||
},
|
||||
|
||||
tags: ['admin'],
|
||||
|
||||
requireCredential: true as const,
|
||||
|
|
@ -41,7 +37,6 @@ export const meta = {
|
|||
type: 'string' as const,
|
||||
optional: false as const, nullable: false as const,
|
||||
format: 'id',
|
||||
description: 'New copied emoji ID'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -63,7 +58,7 @@ export default define(meta, async (ps, me) => {
|
|||
throw new ApiError();
|
||||
}
|
||||
|
||||
const copied = await Emojis.save({
|
||||
const copied = await Emojis.insert({
|
||||
id: genId(),
|
||||
updatedAt: new Date(),
|
||||
name: emoji.name,
|
||||
|
|
@ -72,10 +67,14 @@ export default define(meta, async (ps, me) => {
|
|||
url: driveFile.url,
|
||||
type: driveFile.type,
|
||||
fileId: driveFile.id,
|
||||
});
|
||||
}).then(x => Emojis.findOneOrFail(x.identifiers[0]));
|
||||
|
||||
await getConnection().queryResultCache!.remove(['meta_emojis']);
|
||||
|
||||
publishBroadcastStream('emojiAdded', {
|
||||
emoji: await Emojis.pack(copied.id)
|
||||
});
|
||||
|
||||
return {
|
||||
id: copied.id
|
||||
};
|
||||
|
|
|
|||
|
|
@ -6,11 +6,6 @@ import { makePaginationQuery } from '../../../common/make-pagination-query';
|
|||
import { ID } from '@/misc/cafy-id';
|
||||
|
||||
export const meta = {
|
||||
desc: {
|
||||
'ja-JP': 'リモートのカスタム絵文字一覧を取得します。',
|
||||
'en-US': 'Gets a list of remote custom emojis.'
|
||||
},
|
||||
|
||||
tags: ['admin'],
|
||||
|
||||
requireCredential: true as const,
|
||||
|
|
@ -19,12 +14,12 @@ export const meta = {
|
|||
params: {
|
||||
query: {
|
||||
validator: $.optional.nullable.str,
|
||||
default: null as any
|
||||
default: null
|
||||
},
|
||||
|
||||
host: {
|
||||
validator: $.optional.nullable.str,
|
||||
default: null as any
|
||||
default: null
|
||||
},
|
||||
|
||||
limit: {
|
||||
|
|
@ -52,12 +47,10 @@ export const meta = {
|
|||
type: 'string' as const,
|
||||
optional: false as const, nullable: false as const,
|
||||
format: 'id',
|
||||
description: 'The unique identifier for this Emoji.'
|
||||
},
|
||||
aliases: {
|
||||
type: 'array' as const,
|
||||
optional: false as const, nullable: false as const,
|
||||
description: 'List to make it easier to be displayed as a candidate when entering emoji.',
|
||||
items: {
|
||||
type: 'string' as const,
|
||||
optional: false as const, nullable: false as const
|
||||
|
|
@ -66,22 +59,18 @@ export const meta = {
|
|||
name: {
|
||||
type: 'string' as const,
|
||||
optional: false as const, nullable: false as const,
|
||||
description: 'Official name of custom emoji.'
|
||||
},
|
||||
category: {
|
||||
type: 'string' as const,
|
||||
optional: false as const, nullable: true as const,
|
||||
description: 'Names categorized in the emoji list.'
|
||||
},
|
||||
host: {
|
||||
type: 'string' as const,
|
||||
optional: false as const, nullable: true as const,
|
||||
description: 'If it is another server, the FQDN will be returned here.'
|
||||
},
|
||||
url: {
|
||||
type: 'string' as const,
|
||||
optional: false as const, nullable: false as const,
|
||||
description: 'Image URL of emoji.'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,11 +6,6 @@ import { ID } from '@/misc/cafy-id';
|
|||
import { Emoji } from '../../../../../models/entities/emoji';
|
||||
|
||||
export const meta = {
|
||||
desc: {
|
||||
'ja-JP': 'カスタム絵文字一覧を取得します。',
|
||||
'en-US': 'List custom emojis.'
|
||||
},
|
||||
|
||||
tags: ['admin'],
|
||||
|
||||
requireCredential: true as const,
|
||||
|
|
@ -19,7 +14,7 @@ export const meta = {
|
|||
params: {
|
||||
query: {
|
||||
validator: $.optional.nullable.str,
|
||||
default: null as any
|
||||
default: null
|
||||
},
|
||||
|
||||
limit: {
|
||||
|
|
@ -47,12 +42,10 @@ export const meta = {
|
|||
type: 'string' as const,
|
||||
optional: false as const, nullable: false as const,
|
||||
format: 'id',
|
||||
description: 'The unique identifier for this Emoji.'
|
||||
},
|
||||
aliases: {
|
||||
type: 'array' as const,
|
||||
optional: false as const, nullable: false as const,
|
||||
description: 'List to make it easier to be displayed as a candidate when entering emoji.',
|
||||
items: {
|
||||
type: 'string' as const,
|
||||
optional: false as const, nullable: false as const
|
||||
|
|
@ -61,22 +54,18 @@ export const meta = {
|
|||
name: {
|
||||
type: 'string' as const,
|
||||
optional: false as const, nullable: false as const,
|
||||
description: 'Official name of custom emoji.'
|
||||
},
|
||||
category: {
|
||||
type: 'string' as const,
|
||||
optional: false as const, nullable: true as const,
|
||||
description: 'Names categorized in the emoji list.'
|
||||
},
|
||||
host: {
|
||||
type: 'string' as const,
|
||||
optional: false as const, nullable: true as const,
|
||||
description: 'If it is another server, the FQDN will be returned here.'
|
||||
},
|
||||
url: {
|
||||
type: 'string' as const,
|
||||
optional: false as const, nullable: false as const,
|
||||
description: 'Image URL of emoji.'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,10 +7,6 @@ import { insertModerationLog } from '../../../../../services/insert-moderation-l
|
|||
import { ApiError } from '../../../error';
|
||||
|
||||
export const meta = {
|
||||
desc: {
|
||||
'ja-JP': 'カスタム絵文字を削除します。'
|
||||
},
|
||||
|
||||
tags: ['admin'],
|
||||
|
||||
requireCredential: true as const,
|
||||
|
|
|
|||
|
|
@ -6,10 +6,6 @@ import { getConnection } from 'typeorm';
|
|||
import { ApiError } from '../../../error';
|
||||
|
||||
export const meta = {
|
||||
desc: {
|
||||
'ja-JP': 'カスタム絵文字を更新します。'
|
||||
},
|
||||
|
||||
tags: ['admin'],
|
||||
|
||||
requireCredential: true as const,
|
||||
|
|
|
|||
|
|
@ -4,11 +4,6 @@ import { deleteFile } from '../../../../../services/drive/delete-file';
|
|||
import { DriveFiles } from '../../../../../models';
|
||||
|
||||
export const meta = {
|
||||
desc: {
|
||||
'ja-JP': '指定したドメインのファイルを全て削除します。',
|
||||
'en-US': 'Deletes all files in the specified domain.'
|
||||
},
|
||||
|
||||
tags: ['admin'],
|
||||
|
||||
requireCredential: true as const,
|
||||
|
|
|
|||
|
|
@ -4,11 +4,6 @@ import deleteFollowing from '../../../../../services/following/delete';
|
|||
import { Followings, Users } from '../../../../../models';
|
||||
|
||||
export const meta = {
|
||||
desc: {
|
||||
'ja-JP': '指定したドメインの全ユーザーのフォローを全て解除します。',
|
||||
'en-US': 'Unfollow all users in the specified domain.'
|
||||
},
|
||||
|
||||
tags: ['admin'],
|
||||
|
||||
requireCredential: true as const,
|
||||
|
|
|
|||
|
|
@ -4,11 +4,6 @@ import { Instances } from '../../../../../models';
|
|||
import { toPuny } from '@/misc/convert-host';
|
||||
|
||||
export const meta = {
|
||||
desc: {
|
||||
'ja-JP': '指定したドメインのアクティビティの配信を停止するかを選択します。',
|
||||
'en-US': 'Select whether to undeliver the activity for the specified domain.'
|
||||
},
|
||||
|
||||
tags: ['admin'],
|
||||
|
||||
requireCredential: true as const,
|
||||
|
|
|
|||
|
|
@ -5,11 +5,6 @@ export const meta = {
|
|||
requireCredential: true as const,
|
||||
requireModerator: true,
|
||||
|
||||
desc: {
|
||||
'ja-JP': 'テーブル情報を取得します。',
|
||||
'en-US': 'Get table stats'
|
||||
},
|
||||
|
||||
tags: ['admin'],
|
||||
|
||||
params: {
|
||||
|
|
|
|||
|
|
@ -4,11 +4,6 @@ import { RegistrationTickets } from '../../../../models';
|
|||
import { genId } from '@/misc/gen-id';
|
||||
|
||||
export const meta = {
|
||||
desc: {
|
||||
'ja-JP': '招待コードを発行します。',
|
||||
'en-US': 'Issue an invitation code.'
|
||||
},
|
||||
|
||||
tags: ['admin'],
|
||||
|
||||
requireCredential: true as const,
|
||||
|
|
@ -23,7 +18,6 @@ export const meta = {
|
|||
code: {
|
||||
type: 'string' as const,
|
||||
optional: false as const, nullable: false as const,
|
||||
description: 'Give this code to the applicant for registration.',
|
||||
example: '2ERUA5VR',
|
||||
maxLength: 8,
|
||||
minLength: 8
|
||||
|
|
|
|||
|
|
@ -4,11 +4,6 @@ import { Logs } from '../../../../models';
|
|||
import { Brackets } from 'typeorm';
|
||||
|
||||
export const meta = {
|
||||
desc: {
|
||||
'ja-JP': 'ログを表示します。',
|
||||
'en-US': 'Show logs.'
|
||||
},
|
||||
|
||||
tags: ['admin'],
|
||||
|
||||
requireCredential: true as const,
|
||||
|
|
@ -22,12 +17,12 @@ export const meta = {
|
|||
|
||||
level: {
|
||||
validator: $.optional.nullable.str,
|
||||
default: null as any
|
||||
default: null
|
||||
},
|
||||
|
||||
domain: {
|
||||
validator: $.optional.nullable.str,
|
||||
default: null as any
|
||||
default: null
|
||||
}
|
||||
},
|
||||
|
||||
|
|
@ -42,14 +37,12 @@ export const meta = {
|
|||
type: 'string' as const,
|
||||
optional: false as const, nullable: false as const,
|
||||
format: 'id',
|
||||
description: 'The unique identifier for this log.',
|
||||
example: 'xxxxxxxxxx',
|
||||
},
|
||||
createdAt: {
|
||||
type: 'string' as const,
|
||||
optional: false as const, nullable: false as const,
|
||||
format: 'date-time',
|
||||
description: 'The date that the Drive file was created on Misskey.'
|
||||
},
|
||||
domain: {
|
||||
type: 'array' as const,
|
||||
|
|
@ -70,12 +63,10 @@ export const meta = {
|
|||
machine: {
|
||||
type: 'string' as const,
|
||||
optional: false as const, nullable: false as const,
|
||||
description: 'The name of the running machine.'
|
||||
},
|
||||
message: {
|
||||
type: 'string' as const,
|
||||
optional: false as const, nullable: false as const,
|
||||
description: 'Log body.'
|
||||
},
|
||||
data: {
|
||||
type: 'object' as const,
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue