summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--locales/ja-JP.yml33
-rw-r--r--src/client/app/dev/views/apps.vue2
-rw-r--r--src/client/app/dev/views/new-app.vue44
-rw-r--r--src/prelude/array.ts5
-rw-r--r--src/server/api/endpoints/i/favorites.ts2
-rw-r--r--src/server/api/endpoints/messaging/history.ts2
-rw-r--r--src/server/api/endpoints/messaging/messages.ts2
-rw-r--r--src/server/api/endpoints/messaging/messages/create.ts2
-rw-r--r--src/server/api/endpoints/messaging/messages/delete.ts2
-rw-r--r--src/server/api/endpoints/messaging/messages/read.ts2
-rw-r--r--src/server/api/endpoints/notes/favorites/create.ts2
-rw-r--r--src/server/api/endpoints/notes/favorites/delete.ts2
-rw-r--r--src/server/api/endpoints/notes/polls/vote.ts2
-rw-r--r--src/server/api/endpoints/permissions.ts29
-rw-r--r--src/server/api/kinds.ts58
-rw-r--r--src/server/api/openapi/description.ts20
-rw-r--r--src/server/api/openapi/gen-spec.ts9
-rw-r--r--test/api.ts12
-rw-r--r--test/mfm.ts2
19 files changed, 183 insertions, 49 deletions
diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml
index cc6fe2b086..82115a7224 100644
--- a/locales/ja-JP.yml
+++ b/locales/ja-JP.yml
@@ -74,10 +74,26 @@ common:
favorites: "お気に入り"
permissions:
- 'read:account': "アカウントの情報を見る"
- 'write:account': "アカウントの情報を変更する"
- 'read:drive': "ドライブを見る"
- 'write:drive': "ドライブを操作する"
+ "read:account": "アカウントの情報を見る"
+ "write:account": "アカウントの情報を変更する"
+ "read:blocks": "ブロックを見る"
+ "write:blocks": "ブロックを操作する"
+ "read:drive": "ドライブを見る"
+ "write:drive": "ドライブを操作する"
+ "read:favorites": "お気に入りを見る"
+ "write:favorites": "お気に入りを操作する"
+ "read:following": "フォローの情報を見る"
+ "write:following": "フォロー・フォロー解除する"
+ "read:messaging": "トークを見る"
+ "write:messaging": "トークを操作する"
+ "read:mutes": "ミュートを見る"
+ "write:mutes": "ミュートを操作する"
+ "write:notes": "投稿を作成・削除する"
+ "read:notifications": "通知を見る"
+ "write:notifications": "通知を操作する"
+ "read:reactions": "リアクションを見る"
+ "write:reactions": "リアクションを操作する"
+ "write:votes": "投票する"
empty-timeline-info:
follow-users-to-make-your-timeline: "ユーザーをフォローすると投稿がタイムラインに表示されます。"
@@ -1804,14 +1820,17 @@ dev/views/apps.vue:
app-missing: "アプリなし"
dev/views/new-app.vue:
+ new-app: "新しいアプリケーション"
+ new-app-info: "アプリケーションはAPIからでも作成できます。 (app/create)"
create-app: "アプリケーションの作成"
app-name: "アプリケーション名"
+ app-name-placeholder: "ex) Misskey for iOS"
app-name-desc: "あなたのアプリの名称。"
- app-name-ex: "ex) Misskey for iOS"
app-overview: "アプリの概要"
- app-desc: "あなたのアプリの簡単な説明や紹介。"
- app-desc-ex: "ex) Misskey iOSクライアント。"
+ app-overview-placeholder: " ex) Misskey iOSクライアント。"
+ app-overview-desc: "あなたのアプリの簡単な説明や紹介。"
callback-url: "コールバックURL (オプション)"
+ callback-url-placeholder: "ex) https://your.app.example.com/callback.php"
callback-url-desc: "ユーザーが認証フォームで認証した際にリダイレクトするURLを設定できます。"
authority: "権限"
authority-desc: "ここで要求した機能だけがAPIからアクセスできます。"
diff --git a/src/client/app/dev/views/apps.vue b/src/client/app/dev/views/apps.vue
index 78a7cede98..b99ccdf576 100644
--- a/src/client/app/dev/views/apps.vue
+++ b/src/client/app/dev/views/apps.vue
@@ -1,6 +1,6 @@
<template>
<mk-ui>
- <b-card :header="$t('header')">
+ <b-card :header="$t('manage-apps')">
<b-button to="/app/new" variant="primary">{{ $t('create-app') }}</b-button>
<hr>
<div class="apps">
diff --git a/src/client/app/dev/views/new-app.vue b/src/client/app/dev/views/new-app.vue
index 00f2ed60d9..6b67d220a7 100644
--- a/src/client/app/dev/views/new-app.vue
+++ b/src/client/app/dev/views/new-app.vue
@@ -1,35 +1,22 @@
<template>
<mk-ui>
- <b-card :header="$t('header')">
+ <b-card :header="$t('new-app')">
+ <b-alert show variant="info"><fa icon="info-circle"/> {{ $t('new-app-info') }}</b-alert>
<b-form @submit.prevent="onSubmit" autocomplete="off">
- <b-form-group :label="$t('app-name')" :description="$t('description')">
- <b-form-input v-model="name" type="text" :placeholder="$t('placeholder')" autocomplete="off" required/>
+ <b-form-group :label="$t('app-name')" :description="$t('app-name-desc')">
+ <b-form-input v-model="name" type="text" :placeholder="$t('app-name-placeholder')" autocomplete="off" required/>
</b-form-group>
- <b-form-group :label="$t('app-overview')" :description="$t('description')">
- <b-textarea v-model="description" :placeholder="$t('placeholder')" autocomplete="off" required></b-textarea>
+ <b-form-group :label="$t('app-overview')" :description="$t('app-overview-desc')">
+ <b-textarea v-model="description" :placeholder="$t('app-overview-placeholder')" autocomplete="off" required></b-textarea>
</b-form-group>
- <b-form-group :label="$t('callback-url')" :description="$t('description')">
- <b-input v-model="cb" type="url" placeholder="ex) https://your.app.example.com/callback.php" autocomplete="off"/>
+ <b-form-group :label="$t('callback-url')" :description="$t('callback-url-desc')">
+ <b-input v-model="cb" type="url" :placeholder="$t('callback-url-placeholder')" autocomplete="off"/>
</b-form-group>
- <b-card :header="$t('header')">
- <b-form-group :description="$t('description')">
+ <b-card :header="$t('authority')">
+ <b-form-group :description="$t('authority-desc')">
<b-alert show variant="warning"><fa icon="exclamation-triangle"/> {{ $t('authority-warning') }}</b-alert>
<b-form-checkbox-group v-model="permission" stacked>
- <b-form-checkbox value="read:account">{{ $t('read:account') }}</b-form-checkbox>
- <b-form-checkbox value="write:account">{{ $t('write:account') }}</b-form-checkbox>
- <b-form-checkbox value="write:notes">{{ $t('write:notes') }}</b-form-checkbox>
- <b-form-checkbox value="read:reactions">{{ $t('read:reactions') }}</b-form-checkbox>
- <b-form-checkbox value="write:reactions">{{ $t('write:reactions') }}</b-form-checkbox>
- <b-form-checkbox value="read:following">{{ $t('read:following') }}</b-form-checkbox>
- <b-form-checkbox value="write:following">{{ $t('write:following') }}</b-form-checkbox>
- <b-form-checkbox value="read:mutes">{{ $t('read:mutes') }}</b-form-checkbox>
- <b-form-checkbox value="write:mutes">{{ $t('write:mutes') }}</b-form-checkbox>
- <b-form-checkbox value="read:blocks">{{ $t('read:blocks') }}</b-form-checkbox>
- <b-form-checkbox value="write:blocks">{{ $t('write:blocks') }}</b-form-checkbox>
- <b-form-checkbox value="read:drive">{{ $t('read:drive') }}</b-form-checkbox>
- <b-form-checkbox value="write:drive">{{ $t('write:drive') }}</b-form-checkbox>
- <b-form-checkbox value="read:notifications">{{ $t('read:notifications') }}</b-form-checkbox>
- <b-form-checkbox value="write:notifications">{{ $t('write:notifications') }}</b-form-checkbox>
+ <b-form-checkbox v-for="v in permissionsList" :value="v" :key="v">{{ $t(`@.permissions.${v}`) }} ({{ v }})</b-form-checkbox>
</b-form-checkbox-group>
</b-form-group>
</b-card>
@@ -43,6 +30,7 @@
<script lang="ts">
import Vue from 'vue';
import i18n from '../../i18n';
+
export default Vue.extend({
i18n: i18n('dev/views/new-app.vue'),
data() {
@@ -51,9 +39,15 @@ export default Vue.extend({
description: '',
cb: '',
nidState: null,
- permission: []
+ permission: [],
+ permissionsList: []
};
},
+ created() {
+ this.$root.api('permissions').then(permissions => {
+ this.permissionsList = permissions
+ });
+ },
methods: {
onSubmit() {
this.$root.api('app/create', {
diff --git a/src/prelude/array.ts b/src/prelude/array.ts
index 560dfa080d..44482c57cd 100644
--- a/src/prelude/array.ts
+++ b/src/prelude/array.ts
@@ -115,3 +115,8 @@ export function cumulativeSum(xs: number[]): number[] {
for (let i = 1; i < ys.length; i++) ys[i] += ys[i - 1];
return ys;
}
+
+// Object.fromEntries()
+export function fromEntries(xs: [string, any][]): { [x: string]: any; } {
+ return xs.reduce((obj, [k, v]) => Object.assign(obj, { [k]: v }), {} as { [x: string]: any; });
+}
diff --git a/src/server/api/endpoints/i/favorites.ts b/src/server/api/endpoints/i/favorites.ts
index 2c25250bea..aad706545a 100644
--- a/src/server/api/endpoints/i/favorites.ts
+++ b/src/server/api/endpoints/i/favorites.ts
@@ -14,7 +14,7 @@ export const meta = {
requireCredential: true,
- kind: 'favorites-read',
+ kind: 'read:favorites',
params: {
limit: {
diff --git a/src/server/api/endpoints/messaging/history.ts b/src/server/api/endpoints/messaging/history.ts
index c12378eb7e..c2d746e481 100644
--- a/src/server/api/endpoints/messaging/history.ts
+++ b/src/server/api/endpoints/messaging/history.ts
@@ -14,7 +14,7 @@ export const meta = {
requireCredential: true,
- kind: 'messaging-read',
+ kind: 'read:messaging',
params: {
limit: {
diff --git a/src/server/api/endpoints/messaging/messages.ts b/src/server/api/endpoints/messaging/messages.ts
index 02c57b8d03..add21e5f19 100644
--- a/src/server/api/endpoints/messaging/messages.ts
+++ b/src/server/api/endpoints/messaging/messages.ts
@@ -17,7 +17,7 @@ export const meta = {
requireCredential: true,
- kind: 'messaging-read',
+ kind: 'read:messaging',
params: {
userId: {
diff --git a/src/server/api/endpoints/messaging/messages/create.ts b/src/server/api/endpoints/messaging/messages/create.ts
index 2c7e5ad2d9..30ac0849a3 100644
--- a/src/server/api/endpoints/messaging/messages/create.ts
+++ b/src/server/api/endpoints/messaging/messages/create.ts
@@ -20,7 +20,7 @@ export const meta = {
requireCredential: true,
- kind: 'messaging-write',
+ kind: 'write:messaging',
params: {
userId: {
diff --git a/src/server/api/endpoints/messaging/messages/delete.ts b/src/server/api/endpoints/messaging/messages/delete.ts
index 9f55caba62..6a896cd8d1 100644
--- a/src/server/api/endpoints/messaging/messages/delete.ts
+++ b/src/server/api/endpoints/messaging/messages/delete.ts
@@ -18,7 +18,7 @@ export const meta = {
requireCredential: true,
- kind: 'messaging-write',
+ kind: 'write:messaging',
limit: {
duration: ms('1hour'),
diff --git a/src/server/api/endpoints/messaging/messages/read.ts b/src/server/api/endpoints/messaging/messages/read.ts
index 24a28285bf..50b7f39870 100644
--- a/src/server/api/endpoints/messaging/messages/read.ts
+++ b/src/server/api/endpoints/messaging/messages/read.ts
@@ -15,7 +15,7 @@ export const meta = {
requireCredential: true,
- kind: 'messaging-write',
+ kind: 'write:messaging',
params: {
messageId: {
diff --git a/src/server/api/endpoints/notes/favorites/create.ts b/src/server/api/endpoints/notes/favorites/create.ts
index 7e04637758..bb0c9594bb 100644
--- a/src/server/api/endpoints/notes/favorites/create.ts
+++ b/src/server/api/endpoints/notes/favorites/create.ts
@@ -18,7 +18,7 @@ export const meta = {
requireCredential: true,
- kind: 'favorite-write',
+ kind: 'write:favorites',
params: {
noteId: {
diff --git a/src/server/api/endpoints/notes/favorites/delete.ts b/src/server/api/endpoints/notes/favorites/delete.ts
index a889c84d4d..49f7631773 100644
--- a/src/server/api/endpoints/notes/favorites/delete.ts
+++ b/src/server/api/endpoints/notes/favorites/delete.ts
@@ -17,7 +17,7 @@ export const meta = {
requireCredential: true,
- kind: 'favorite-write',
+ kind: 'write:favorites',
params: {
noteId: {
diff --git a/src/server/api/endpoints/notes/polls/vote.ts b/src/server/api/endpoints/notes/polls/vote.ts
index e8b8b66da5..d13405597d 100644
--- a/src/server/api/endpoints/notes/polls/vote.ts
+++ b/src/server/api/endpoints/notes/polls/vote.ts
@@ -26,7 +26,7 @@ export const meta = {
requireCredential: true,
- kind: 'vote-write',
+ kind: 'write:votes',
params: {
noteId: {
diff --git a/src/server/api/endpoints/permissions.ts b/src/server/api/endpoints/permissions.ts
new file mode 100644
index 0000000000..347e1e3f2e
--- /dev/null
+++ b/src/server/api/endpoints/permissions.ts
@@ -0,0 +1,29 @@
+import define from '../define';
+import { kindsList } from '../kinds';
+
+export const meta = {
+ stability: 'stable',
+
+ desc: {
+ 'ja-JP': 'パーミッションの一覧を返します。',
+ 'en-US': 'Get the list of permissons.'
+ },
+
+ tags: ['meta'],
+
+ requireCredential: false,
+
+ params: {
+ },
+
+ res: {
+ type: 'array',
+ items: {
+ type: 'string',
+ }
+ },
+};
+
+export default define(meta, async () => {
+ return kindsList;
+});
diff --git a/src/server/api/kinds.ts b/src/server/api/kinds.ts
new file mode 100644
index 0000000000..d496fa6919
--- /dev/null
+++ b/src/server/api/kinds.ts
@@ -0,0 +1,58 @@
+import endpoints from './endpoints';
+import * as locale from '../../../locales/';
+import { fromEntries } from '../../prelude/array';
+
+export const kindsList = [
+ 'read:account',
+ 'write:account',
+ 'read:blocks',
+ 'write:blocks',
+ 'read:drive',
+ 'write:drive',
+ 'read:favorites',
+ 'write:favorites',
+ 'read:following',
+ 'write:following',
+ 'read:messaging',
+ 'write:messaging',
+ 'read:mutes',
+ 'write:mutes',
+ 'write:notes',
+ 'read:notifications',
+ 'write:notifications',
+ 'read:reactions',
+ 'write:reactions',
+ 'write:votes'
+];
+
+export interface IKindInfo {
+ endpoints: string[];
+ descs: { [x: string]: string; };
+}
+
+export function kinds() {
+ const kinds = fromEntries(
+ kindsList
+ .map(k => [k, {
+ endpoints: [],
+ descs: fromEntries(
+ Object.keys(locale)
+ .map(l => [l, locale[l].common.permissions[k] as string] as [string, string])
+ ) as { [x: string]: string; }
+ }] as [ string, IKindInfo ])
+ ) as { [x: string]: IKindInfo; };
+
+ const errors = [] as string[][];
+
+ for (const endpoint of endpoints.filter(ep => !ep.meta.secure)) {
+ if (endpoint.meta.kind) {
+ const kind = endpoint.meta.kind;
+ if (kind in kinds) kinds[kind].endpoints.push(endpoint.name);
+ else errors.push([kind, endpoint.name]);
+ }
+ }
+
+ if (errors.length > 0) throw Error('\n ' + errors.map((e) => `Unknown kind (permission) "${e[0]}" found at ${e[1]}.`).join('\n '));
+
+ return kinds;
+}
diff --git a/src/server/api/openapi/description.ts b/src/server/api/openapi/description.ts
index 04a0b4c719..b801c86387 100644
--- a/src/server/api/openapi/description.ts
+++ b/src/server/api/openapi/description.ts
@@ -1,6 +1,14 @@
import config from '../../../config';
+import { IKindInfo, kinds } from '../kinds';
+
+export function getDescription(lang = 'ja-JP'): string {
+ const permissionTable = (Object.entries(kinds()) as [string, IKindInfo][])
+ .map(e => `|${e[0]}|${e[1].descs[lang]}|${e[1].endpoints.map(f => `[${f}](#operation/${f})`).join(', ')}|`)
+ .join('\n');
+
+ const descriptions = {
+ 'ja-JP': `**Misskey is a decentralized microblogging platform.**
-export const description = `
## Usage
**APIはすべてPOSTでリクエスト/レスポンスともにJSON形式です。**
一部のAPIはリクエストに認証情報(APIキー)が必要です。リクエストの際に\`i\`というパラメータでAPIキーを添付してください。
@@ -44,4 +52,12 @@ APIキーの生成方法を擬似コードで表すと次のようになりま
\`\`\` js
const i = sha256(userToken + secretKey);
\`\`\`
-`;
+
+## Permissions
+|Permisson (kind)|Description|Endpoints|
+|:--|:--|:--|
+${permissionTable}
+`
+ } as { [x: string]: string };
+ return lang in descriptions ? descriptions[lang] : descriptions['ja-JP'];
+}
diff --git a/src/server/api/openapi/gen-spec.ts b/src/server/api/openapi/gen-spec.ts
index 915fb5a6a3..d194c6c8a8 100644
--- a/src/server/api/openapi/gen-spec.ts
+++ b/src/server/api/openapi/gen-spec.ts
@@ -3,7 +3,7 @@ import { Context } from 'cafy';
import config from '../../../config';
import { errors as basicErrors } from './errors';
import { schemas } from './schemas';
-import { description } from './description';
+import { getDescription } from './description';
import { convertOpenApiSchema } from '../../../misc/schema';
export function genOpenapiSpec(lang = 'ja-JP') {
@@ -13,7 +13,7 @@ export function genOpenapiSpec(lang = 'ja-JP') {
info: {
version: 'v1',
title: 'Misskey API',
- description: '**Misskey is a decentralized microblogging platform.**\n\n' + description,
+ description: getDescription(lang),
'x-logo': { url: '/assets/api-doc.png' }
},
@@ -110,7 +110,10 @@ export function genOpenapiSpec(lang = 'ja-JP') {
let desc = (endpoint.meta.desc ? endpoint.meta.desc[lang] : 'No description provided.') + '\n\n';
desc += `**Credential required**: *${endpoint.meta.requireCredential ? 'Yes' : 'No'}*`;
- if (endpoint.meta.kind) desc += ` / **Permission**: *${endpoint.meta.kind}*`;
+ if (endpoint.meta.kind) {
+ const kind = endpoint.meta.kind;
+ desc += ` / **Permission**: *${kind}*`;
+ }
const info = {
operationId: endpoint.name,
diff --git a/test/api.ts b/test/api.ts
index d14b286482..318aa84248 100644
--- a/test/api.ts
+++ b/test/api.ts
@@ -18,6 +18,8 @@ import * as assert from 'assert';
import * as childProcess from 'child_process';
import { async, signup, request, post, react, uploadFile } from './utils';
+import { kinds } from '../src/server/api/kinds';
+
describe('API', () => {
let p: childProcess.ChildProcess;
@@ -792,7 +794,7 @@ describe('API', () => {
parentId: folderA.id
}, arisugawa);
- expect(res).have.status(400);
+ assert.strictEqual(res.status, 400);
}));
it('存在しない親フォルダを設定できない', async(async () => {
@@ -965,5 +967,13 @@ describe('API', () => {
assert.strictEqual(res.body[0].id, alicePost.id);
}));
});
+
+ describe('kinds', () => {
+ it('登録されていないパーミッションを利用しているAPIがない', () => {
+ const res = kinds();
+
+ assert.strictEqual(typeof res === 'object', true);
+ });
+ });
});
*/
diff --git a/test/mfm.ts b/test/mfm.ts
index 69260a5415..8098102e92 100644
--- a/test/mfm.ts
+++ b/test/mfm.ts
@@ -1141,7 +1141,7 @@ describe('MFM', () => {
it('exlude emotes', () => {
const tokens = parse('*.*');
assert.deepStrictEqual(tokens, [
- text("*.*"),
+ text('*.*'),
]);
});