✌️
This commit is contained in:
parent
ea2ddf3a4d
commit
dbc421cb57
15 changed files with 272 additions and 377 deletions
25
src/web/app/auth/script.ts
Normal file
25
src/web/app/auth/script.ts
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
/**
|
||||||
|
* Authorize Form
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Style
|
||||||
|
import './style.styl';
|
||||||
|
|
||||||
|
import init from '../init';
|
||||||
|
|
||||||
|
import Index from './views/index.vue';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* init
|
||||||
|
*/
|
||||||
|
init(async (launch) => {
|
||||||
|
document.title = 'Misskey | アプリの連携';
|
||||||
|
|
||||||
|
// Launch the app
|
||||||
|
const [app] = launch();
|
||||||
|
|
||||||
|
// Routing
|
||||||
|
app.$router.addRoutes([
|
||||||
|
{ path: '/:token', component: Index },
|
||||||
|
]);
|
||||||
|
});
|
|
@ -42,10 +42,14 @@ export default Vue.extend({
|
||||||
return {
|
return {
|
||||||
state: null,
|
state: null,
|
||||||
session: null,
|
session: null,
|
||||||
fetching: true,
|
fetching: true
|
||||||
token: window.location.href.split('/').pop()
|
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
computed: {
|
||||||
|
token(): string {
|
||||||
|
return this.$route.params.token;
|
||||||
|
}
|
||||||
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
if (!this.$root.$data.os.isSignedIn) return;
|
if (!this.$root.$data.os.isSignedIn) return;
|
||||||
|
|
||||||
|
|
|
@ -5,11 +5,25 @@
|
||||||
// Style
|
// Style
|
||||||
import './style.styl';
|
import './style.styl';
|
||||||
|
|
||||||
require('./tags');
|
|
||||||
import init from '../init';
|
import init from '../init';
|
||||||
|
|
||||||
|
import Index from './views/index.vue';
|
||||||
|
import Apps from './views/apps.vue';
|
||||||
|
import AppNew from './views/new-app.vue';
|
||||||
|
import App from './views/app.vue';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* init
|
* init
|
||||||
*/
|
*/
|
||||||
init(() => {
|
init(launch => {
|
||||||
|
// Launch the app
|
||||||
|
const [app] = launch();
|
||||||
|
|
||||||
|
// Routing
|
||||||
|
app.$router.addRoutes([
|
||||||
|
{ path: '/', component: Index },
|
||||||
|
{ path: '/app', component: Apps },
|
||||||
|
{ path: '/app/new', component: AppNew },
|
||||||
|
{ path: '/app/:id', component: App },
|
||||||
|
]);
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,5 +0,0 @@
|
||||||
require('./pages/index.tag');
|
|
||||||
require('./pages/apps.tag');
|
|
||||||
require('./pages/app.tag');
|
|
||||||
require('./pages/new-app.tag');
|
|
||||||
require('./new-app-form.tag');
|
|
|
@ -1,252 +0,0 @@
|
||||||
<mk-new-app-form>
|
|
||||||
<form onsubmit={ onsubmit } autocomplete="off">
|
|
||||||
<section class="name">
|
|
||||||
<label>
|
|
||||||
<p class="caption">アプリケーション名</p>
|
|
||||||
<input ref="name" type="text" placeholder="ex) Misskey for iOS" autocomplete="off" required="required"/>
|
|
||||||
</label>
|
|
||||||
</section>
|
|
||||||
<section class="nid">
|
|
||||||
<label>
|
|
||||||
<p class="caption">Named ID</p>
|
|
||||||
<input ref="nid" type="text" pattern="^[a-zA-Z0-9-]{3,30}$" placeholder="ex) misskey-for-ios" autocomplete="off" required="required" onkeyup={ onChangeNid }/>
|
|
||||||
<p class="info" v-if="nidState == 'wait'" style="color:#999">%fa:spinner .pulse .fw%確認しています...</p>
|
|
||||||
<p class="info" v-if="nidState == 'ok'" style="color:#3CB7B5">%fa:fw check%利用できます</p>
|
|
||||||
<p class="info" v-if="nidState == 'unavailable'" style="color:#FF1161">%fa:fw exclamation-triangle%既に利用されています</p>
|
|
||||||
<p class="info" v-if="nidState == 'error'" style="color:#FF1161">%fa:fw exclamation-triangle%通信エラー</p>
|
|
||||||
<p class="info" v-if="nidState == 'invalid-format'" style="color:#FF1161">%fa:fw exclamation-triangle%a~z、A~Z、0~9、-(ハイフン)が使えます</p>
|
|
||||||
<p class="info" v-if="nidState == 'min-range'" style="color:#FF1161">%fa:fw exclamation-triangle%3文字以上でお願いします!</p>
|
|
||||||
<p class="info" v-if="nidState == 'max-range'" style="color:#FF1161">%fa:fw exclamation-triangle%30文字以内でお願いします</p>
|
|
||||||
</label>
|
|
||||||
</section>
|
|
||||||
<section class="description">
|
|
||||||
<label>
|
|
||||||
<p class="caption">アプリの概要</p>
|
|
||||||
<textarea ref="description" placeholder="ex) Misskey iOSクライアント。" autocomplete="off" required="required"></textarea>
|
|
||||||
</label>
|
|
||||||
</section>
|
|
||||||
<section class="callback">
|
|
||||||
<label>
|
|
||||||
<p class="caption">コールバックURL (オプション)</p>
|
|
||||||
<input ref="cb" type="url" placeholder="ex) https://your.app.example.com/callback.php" autocomplete="off"/>
|
|
||||||
</label>
|
|
||||||
</section>
|
|
||||||
<section class="permission">
|
|
||||||
<p class="caption">権限</p>
|
|
||||||
<div ref="permission">
|
|
||||||
<label>
|
|
||||||
<input type="checkbox" value="account-read"/>
|
|
||||||
<p>アカウントの情報を見る。</p>
|
|
||||||
</label>
|
|
||||||
<label>
|
|
||||||
<input type="checkbox" value="account-write"/>
|
|
||||||
<p>アカウントの情報を操作する。</p>
|
|
||||||
</label>
|
|
||||||
<label>
|
|
||||||
<input type="checkbox" value="post-write"/>
|
|
||||||
<p>投稿する。</p>
|
|
||||||
</label>
|
|
||||||
<label>
|
|
||||||
<input type="checkbox" value="reaction-write"/>
|
|
||||||
<p>リアクションしたりリアクションをキャンセルする。</p>
|
|
||||||
</label>
|
|
||||||
<label>
|
|
||||||
<input type="checkbox" value="following-write"/>
|
|
||||||
<p>フォローしたりフォロー解除する。</p>
|
|
||||||
</label>
|
|
||||||
<label>
|
|
||||||
<input type="checkbox" value="drive-read"/>
|
|
||||||
<p>ドライブを見る。</p>
|
|
||||||
</label>
|
|
||||||
<label>
|
|
||||||
<input type="checkbox" value="drive-write"/>
|
|
||||||
<p>ドライブを操作する。</p>
|
|
||||||
</label>
|
|
||||||
<label>
|
|
||||||
<input type="checkbox" value="notification-read"/>
|
|
||||||
<p>通知を見る。</p>
|
|
||||||
</label>
|
|
||||||
<label>
|
|
||||||
<input type="checkbox" value="notification-write"/>
|
|
||||||
<p>通知を操作する。</p>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
<p>%fa:exclamation-triangle%アプリ作成後も変更できますが、新たな権限を付与する場合、その時点で関連付けられているユーザーキーはすべて無効になります。</p>
|
|
||||||
</section>
|
|
||||||
<button @click="onsubmit">アプリ作成</button>
|
|
||||||
</form>
|
|
||||||
<style lang="stylus" scoped>
|
|
||||||
:scope
|
|
||||||
display block
|
|
||||||
overflow hidden
|
|
||||||
|
|
||||||
> form
|
|
||||||
|
|
||||||
section
|
|
||||||
display block
|
|
||||||
margin 16px 0
|
|
||||||
|
|
||||||
.caption
|
|
||||||
margin 0 0 4px 0
|
|
||||||
color #616161
|
|
||||||
font-size 0.95em
|
|
||||||
|
|
||||||
> [data-fa]
|
|
||||||
margin-right 0.25em
|
|
||||||
color #96adac
|
|
||||||
|
|
||||||
.info
|
|
||||||
display block
|
|
||||||
margin 4px 0
|
|
||||||
font-size 0.8em
|
|
||||||
|
|
||||||
> [data-fa]
|
|
||||||
margin-right 0.3em
|
|
||||||
|
|
||||||
section.permission
|
|
||||||
div
|
|
||||||
padding 8px 0
|
|
||||||
max-height 160px
|
|
||||||
overflow auto
|
|
||||||
background #fff
|
|
||||||
border solid 1px #cecece
|
|
||||||
border-radius 4px
|
|
||||||
|
|
||||||
label
|
|
||||||
display block
|
|
||||||
padding 0 12px
|
|
||||||
line-height 32px
|
|
||||||
cursor pointer
|
|
||||||
|
|
||||||
&:hover
|
|
||||||
> p
|
|
||||||
color #999
|
|
||||||
|
|
||||||
[type='checkbox']:checked + p
|
|
||||||
color #000
|
|
||||||
|
|
||||||
[type='checkbox']
|
|
||||||
margin-right 4px
|
|
||||||
|
|
||||||
[type='checkbox']:checked + p
|
|
||||||
color #111
|
|
||||||
|
|
||||||
> p
|
|
||||||
display inline
|
|
||||||
color #aaa
|
|
||||||
user-select none
|
|
||||||
|
|
||||||
> p:last-child
|
|
||||||
margin 6px
|
|
||||||
font-size 0.8em
|
|
||||||
color #999
|
|
||||||
|
|
||||||
> [data-fa]
|
|
||||||
margin-right 4px
|
|
||||||
|
|
||||||
[type=text]
|
|
||||||
[type=url]
|
|
||||||
textarea
|
|
||||||
user-select text
|
|
||||||
display inline-block
|
|
||||||
cursor auto
|
|
||||||
padding 8px 12px
|
|
||||||
margin 0
|
|
||||||
width 100%
|
|
||||||
font-size 1em
|
|
||||||
color #333
|
|
||||||
background #fff
|
|
||||||
outline none
|
|
||||||
border solid 1px #cecece
|
|
||||||
border-radius 4px
|
|
||||||
|
|
||||||
&:hover
|
|
||||||
border-color #bbb
|
|
||||||
|
|
||||||
&:focus
|
|
||||||
border-color $theme-color
|
|
||||||
|
|
||||||
&:disabled
|
|
||||||
opacity 0.5
|
|
||||||
|
|
||||||
> button
|
|
||||||
margin 20px 0 32px 0
|
|
||||||
width 100%
|
|
||||||
font-size 1em
|
|
||||||
color #111
|
|
||||||
border-radius 3px
|
|
||||||
|
|
||||||
</style>
|
|
||||||
<script lang="typescript">
|
|
||||||
this.mixin('api');
|
|
||||||
|
|
||||||
this.nidState = null;
|
|
||||||
|
|
||||||
this.onChangeNid = () => {
|
|
||||||
const nid = this.$refs.nid.value;
|
|
||||||
|
|
||||||
if (nid == '') {
|
|
||||||
this.update({
|
|
||||||
nidState: null
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const err =
|
|
||||||
!nid.match(/^[a-zA-Z0-9\-]+$/) ? 'invalid-format' :
|
|
||||||
nid.length < 3 ? 'min-range' :
|
|
||||||
nid.length > 30 ? 'max-range' :
|
|
||||||
null;
|
|
||||||
|
|
||||||
if (err) {
|
|
||||||
this.update({
|
|
||||||
nidState: err
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.update({
|
|
||||||
nidState: 'wait'
|
|
||||||
});
|
|
||||||
|
|
||||||
this.$root.$data.os.api('app/name_id/available', {
|
|
||||||
name_id: nid
|
|
||||||
}).then(result => {
|
|
||||||
this.update({
|
|
||||||
nidState: result.available ? 'ok' : 'unavailable'
|
|
||||||
});
|
|
||||||
}).catch(err => {
|
|
||||||
this.update({
|
|
||||||
nidState: 'error'
|
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
this.onsubmit = () => {
|
|
||||||
const name = this.$refs.name.value;
|
|
||||||
const nid = this.$refs.nid.value;
|
|
||||||
const description = this.$refs.description.value;
|
|
||||||
const cb = this.$refs.cb.value;
|
|
||||||
const permission = [];
|
|
||||||
|
|
||||||
this.$refs.permission.querySelectorAll('input').forEach(el => {
|
|
||||||
if (el.checked) permission.push(el.value);
|
|
||||||
});
|
|
||||||
|
|
||||||
const locker = document.body.appendChild(document.createElement('mk-locker'));
|
|
||||||
|
|
||||||
this.$root.$data.os.api('app/create', {
|
|
||||||
name: name,
|
|
||||||
name_id: nid,
|
|
||||||
description: description,
|
|
||||||
callback_url: cb,
|
|
||||||
permission: permission
|
|
||||||
}).then(() => {
|
|
||||||
location.href = '/apps';
|
|
||||||
}).catch(() => {
|
|
||||||
alert('アプリの作成に失敗しました。再度お試しください。');
|
|
||||||
locker.parentNode.removeChild(locker);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
</mk-new-app-form>
|
|
|
@ -1,32 +0,0 @@
|
||||||
<mk-app-page>
|
|
||||||
<p v-if="fetching">読み込み中</p>
|
|
||||||
<main v-if="!fetching">
|
|
||||||
<header>
|
|
||||||
<h1>{ app.name }</h1>
|
|
||||||
</header>
|
|
||||||
<div class="body">
|
|
||||||
<p>App Secret</p>
|
|
||||||
<input value={ app.secret } readonly="readonly"/>
|
|
||||||
</div>
|
|
||||||
</main>
|
|
||||||
<style lang="stylus" scoped>
|
|
||||||
:scope
|
|
||||||
display block
|
|
||||||
</style>
|
|
||||||
<script lang="typescript">
|
|
||||||
this.mixin('api');
|
|
||||||
|
|
||||||
this.fetching = true;
|
|
||||||
|
|
||||||
this.on('mount', () => {
|
|
||||||
this.$root.$data.os.api('app/show', {
|
|
||||||
app_id: this.opts.app
|
|
||||||
}).then(app => {
|
|
||||||
this.update({
|
|
||||||
fetching: false,
|
|
||||||
app: app
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
</mk-app-page>
|
|
|
@ -1,33 +0,0 @@
|
||||||
<mk-apps-page>
|
|
||||||
<h1>アプリを管理</h1><a href="/app/new">アプリ作成</a>
|
|
||||||
<div class="apps">
|
|
||||||
<p v-if="fetching">読み込み中</p>
|
|
||||||
<template v-if="!fetching">
|
|
||||||
<p v-if="apps.length == 0">アプリなし</p>
|
|
||||||
<ul v-if="apps.length > 0">
|
|
||||||
<li each={ app in apps }><a href={ '/app/' + app.id }>
|
|
||||||
<p class="name">{ app.name }</p></a></li>
|
|
||||||
</ul>
|
|
||||||
</template>
|
|
||||||
</div>
|
|
||||||
<style lang="stylus" scoped>
|
|
||||||
:scope
|
|
||||||
display block
|
|
||||||
</style>
|
|
||||||
<script lang="typescript">
|
|
||||||
this.mixin('api');
|
|
||||||
|
|
||||||
this.fetching = true;
|
|
||||||
|
|
||||||
this.on('mount', () => {
|
|
||||||
this.$root.$data.os.api('my/apps').then(apps => {
|
|
||||||
this.fetching = false
|
|
||||||
this.apps = apps
|
|
||||||
this.update({
|
|
||||||
fetching: false,
|
|
||||||
apps: apps
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
</mk-apps-page>
|
|
|
@ -1,6 +0,0 @@
|
||||||
<mk-index><a href="/apps">アプリ</a>
|
|
||||||
<style lang="stylus" scoped>
|
|
||||||
:scope
|
|
||||||
display block
|
|
||||||
</style>
|
|
||||||
</mk-index>
|
|
|
@ -1,42 +0,0 @@
|
||||||
<mk-new-app-page>
|
|
||||||
<main>
|
|
||||||
<header>
|
|
||||||
<h1>新しいアプリを作成</h1>
|
|
||||||
<p>MisskeyのAPIを利用したアプリケーションを作成できます。</p>
|
|
||||||
</header>
|
|
||||||
<mk-new-app-form/>
|
|
||||||
</main>
|
|
||||||
<style lang="stylus" scoped>
|
|
||||||
:scope
|
|
||||||
display block
|
|
||||||
padding 64px 0
|
|
||||||
|
|
||||||
> main
|
|
||||||
width 100%
|
|
||||||
max-width 700px
|
|
||||||
margin 0 auto
|
|
||||||
|
|
||||||
> header
|
|
||||||
margin 0 0 16px 0
|
|
||||||
padding 0 0 16px 0
|
|
||||||
border-bottom solid 1px #282827
|
|
||||||
|
|
||||||
> h1
|
|
||||||
margin 0 0 12px 0
|
|
||||||
padding 0
|
|
||||||
line-height 32px
|
|
||||||
font-size 32px
|
|
||||||
font-weight normal
|
|
||||||
color #000
|
|
||||||
|
|
||||||
> p
|
|
||||||
margin 0
|
|
||||||
line-height 16px
|
|
||||||
color #9a9894
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
</style>
|
|
||||||
</mk-new-app-page>
|
|
43
src/web/app/dev/views/app.vue
Normal file
43
src/web/app/dev/views/app.vue
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<p v-if="fetching">読み込み中</p>
|
||||||
|
<main v-if="!fetching">
|
||||||
|
<header>
|
||||||
|
<h1>{{ app.name }}</h1>
|
||||||
|
</header>
|
||||||
|
<div class="body">
|
||||||
|
<p>App Secret</p>
|
||||||
|
<input :value="app.secret" readonly/>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import Vue from 'vue';
|
||||||
|
export default Vue.extend({
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
fetching: true,
|
||||||
|
app: null
|
||||||
|
};
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
$route: 'fetch'
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.fetch();
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
fetch() {
|
||||||
|
this.fetching = true;
|
||||||
|
(this as any).api('app/show', {
|
||||||
|
app_id: this.$route.params.id
|
||||||
|
}).then(app => {
|
||||||
|
this.app = app;
|
||||||
|
this.fetching = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
24
src/web/app/dev/views/apps.vue
Normal file
24
src/web/app/dev/views/apps.vue
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<h1>アプリを管理</h1>
|
||||||
|
<router-link to="/app/new">アプリ作成</router-link>
|
||||||
|
<div class="apps">
|
||||||
|
<p v-if="fetching">読み込み中</p>
|
||||||
|
<template v-if="!fetching">
|
||||||
|
<p v-if="apps.length == 0">アプリなし</p>
|
||||||
|
<ul v-else>
|
||||||
|
<li v-for="app in apps" :key="app.id">
|
||||||
|
<router-link :to="`/app/${app.id}`">
|
||||||
|
<p class="name">{{ app.name }}</p>
|
||||||
|
</router-link>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import Vue from 'vue';
|
||||||
|
export default Vue.extend();
|
||||||
|
</script>
|
10
src/web/app/dev/views/index.vue
Normal file
10
src/web/app/dev/views/index.vue
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<router-link to="/app">アプリ</router-link>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import Vue from 'vue';
|
||||||
|
export default Vue.extend();
|
||||||
|
</script>
|
145
src/web/app/dev/views/new-app.vue
Normal file
145
src/web/app/dev/views/new-app.vue
Normal file
|
@ -0,0 +1,145 @@
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<form @submit="onSubmit" autocomplete="off">
|
||||||
|
<section class="name">
|
||||||
|
<label>
|
||||||
|
<p class="caption">アプリケーション名</p>
|
||||||
|
<input v-model="name" type="text" placeholder="ex) Misskey for iOS" autocomplete="off" required/>
|
||||||
|
</label>
|
||||||
|
</section>
|
||||||
|
<section class="nid">
|
||||||
|
<label>
|
||||||
|
<p class="caption">Named ID</p>
|
||||||
|
<input v-model="nid" type="text" pattern="^[a-zA-Z0-9-]{3,30}$" placeholder="ex) misskey-for-ios" autocomplete="off" required/>
|
||||||
|
<p class="info" v-if="nidState == 'wait'" style="color:#999">%fa:spinner .pulse .fw%確認しています...</p>
|
||||||
|
<p class="info" v-if="nidState == 'ok'" style="color:#3CB7B5">%fa:fw check%利用できます</p>
|
||||||
|
<p class="info" v-if="nidState == 'unavailable'" style="color:#FF1161">%fa:fw exclamation-triangle%既に利用されています</p>
|
||||||
|
<p class="info" v-if="nidState == 'error'" style="color:#FF1161">%fa:fw exclamation-triangle%通信エラー</p>
|
||||||
|
<p class="info" v-if="nidState == 'invalid-format'" style="color:#FF1161">%fa:fw exclamation-triangle%a~z、A~Z、0~9、-(ハイフン)が使えます</p>
|
||||||
|
<p class="info" v-if="nidState == 'min-range'" style="color:#FF1161">%fa:fw exclamation-triangle%3文字以上でお願いします!</p>
|
||||||
|
<p class="info" v-if="nidState == 'max-range'" style="color:#FF1161">%fa:fw exclamation-triangle%30文字以内でお願いします</p>
|
||||||
|
</label>
|
||||||
|
</section>
|
||||||
|
<section class="description">
|
||||||
|
<label>
|
||||||
|
<p class="caption">アプリの概要</p>
|
||||||
|
<textarea v-model="description" placeholder="ex) Misskey iOSクライアント。" autocomplete="off" required></textarea>
|
||||||
|
</label>
|
||||||
|
</section>
|
||||||
|
<section class="callback">
|
||||||
|
<label>
|
||||||
|
<p class="caption">コールバックURL (オプション)</p>
|
||||||
|
<input v-model="cb" type="url" placeholder="ex) https://your.app.example.com/callback.php" autocomplete="off"/>
|
||||||
|
</label>
|
||||||
|
</section>
|
||||||
|
<section class="permission">
|
||||||
|
<p class="caption">権限</p>
|
||||||
|
<div ref="permission">
|
||||||
|
<label>
|
||||||
|
<input type="checkbox" value="account-read"/>
|
||||||
|
<p>アカウントの情報を見る。</p>
|
||||||
|
</label>
|
||||||
|
<label>
|
||||||
|
<input type="checkbox" value="account-write"/>
|
||||||
|
<p>アカウントの情報を操作する。</p>
|
||||||
|
</label>
|
||||||
|
<label>
|
||||||
|
<input type="checkbox" value="post-write"/>
|
||||||
|
<p>投稿する。</p>
|
||||||
|
</label>
|
||||||
|
<label>
|
||||||
|
<input type="checkbox" value="reaction-write"/>
|
||||||
|
<p>リアクションしたりリアクションをキャンセルする。</p>
|
||||||
|
</label>
|
||||||
|
<label>
|
||||||
|
<input type="checkbox" value="following-write"/>
|
||||||
|
<p>フォローしたりフォロー解除する。</p>
|
||||||
|
</label>
|
||||||
|
<label>
|
||||||
|
<input type="checkbox" value="drive-read"/>
|
||||||
|
<p>ドライブを見る。</p>
|
||||||
|
</label>
|
||||||
|
<label>
|
||||||
|
<input type="checkbox" value="drive-write"/>
|
||||||
|
<p>ドライブを操作する。</p>
|
||||||
|
</label>
|
||||||
|
<label>
|
||||||
|
<input type="checkbox" value="notification-read"/>
|
||||||
|
<p>通知を見る。</p>
|
||||||
|
</label>
|
||||||
|
<label>
|
||||||
|
<input type="checkbox" value="notification-write"/>
|
||||||
|
<p>通知を操作する。</p>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<p>%fa:exclamation-triangle%アプリ作成後も変更できますが、新たな権限を付与する場合、その時点で関連付けられているユーザーキーはすべて無効になります。</p>
|
||||||
|
</section>
|
||||||
|
<button type="submit">アプリ作成</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import Vue from 'vue';
|
||||||
|
export default Vue.extend({
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
name: '',
|
||||||
|
nid: '',
|
||||||
|
description: '',
|
||||||
|
cb: '',
|
||||||
|
nidState: null
|
||||||
|
};
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
nid() {
|
||||||
|
if (this.nid == null || this.nid == '') {
|
||||||
|
this.nidState = null;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const err =
|
||||||
|
!this.nid.match(/^[a-zA-Z0-9\-]+$/) ? 'invalid-format' :
|
||||||
|
this.nid.length < 3 ? 'min-range' :
|
||||||
|
this.nid.length > 30 ? 'max-range' :
|
||||||
|
null;
|
||||||
|
|
||||||
|
if (err) {
|
||||||
|
this.nidState = err;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.nidState = 'wait';
|
||||||
|
|
||||||
|
(this as any).api('app/name_id/available', {
|
||||||
|
name_id: this.nid
|
||||||
|
}).then(result => {
|
||||||
|
this.nidState = result.available ? 'ok' : 'unavailable';
|
||||||
|
}).catch(err => {
|
||||||
|
this.nidState = 'error';
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
onSubmit() {
|
||||||
|
const permission = [];
|
||||||
|
|
||||||
|
(this.$refs.permission as any).querySelectorAll('input').forEach(el => {
|
||||||
|
if (el.checked) permission.push(el.value);
|
||||||
|
});
|
||||||
|
|
||||||
|
(this as any).api('app/create', {
|
||||||
|
name: this.name,
|
||||||
|
name_id: this.nid,
|
||||||
|
description: this.description,
|
||||||
|
callback_url: this.cb,
|
||||||
|
permission: permission
|
||||||
|
}).then(() => {
|
||||||
|
location.href = '/apps';
|
||||||
|
}).catch(() => {
|
||||||
|
alert('アプリの作成に失敗しました。再度お試しください。');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
|
@ -77,7 +77,7 @@ if (localStorage.getItem('should-refresh') == 'true') {
|
||||||
}
|
}
|
||||||
|
|
||||||
// MiOSを初期化してコールバックする
|
// MiOSを初期化してコールバックする
|
||||||
export default (callback: (launch: (api: (os: MiOS) => API) => [Vue, MiOS]) => void, sw = false) => {
|
export default (callback: (launch: (api?: (os: MiOS) => API) => [Vue, MiOS]) => void, sw = false) => {
|
||||||
const os = new MiOS(sw);
|
const os = new MiOS(sw);
|
||||||
|
|
||||||
os.init(() => {
|
os.init(() => {
|
||||||
|
|
|
@ -38,8 +38,8 @@ module.exports = Object.keys(langs).map(lang => {
|
||||||
//ch: './src/web/app/ch/script.ts',
|
//ch: './src/web/app/ch/script.ts',
|
||||||
//stats: './src/web/app/stats/script.ts',
|
//stats: './src/web/app/stats/script.ts',
|
||||||
//status: './src/web/app/status/script.ts',
|
//status: './src/web/app/status/script.ts',
|
||||||
//dev: './src/web/app/dev/script.ts',
|
dev: './src/web/app/dev/script.ts',
|
||||||
//auth: './src/web/app/auth/script.ts',
|
auth: './src/web/app/auth/script.ts',
|
||||||
sw: './src/web/app/sw.js'
|
sw: './src/web/app/sw.js'
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue