forked from FoundKeyGang/FoundKey
インストールしたアプリ見れるようにしたり削除できるようにしたり
This commit is contained in:
parent
b8088dc01a
commit
dcd43a17ba
9 changed files with 153 additions and 40 deletions
|
@ -468,6 +468,10 @@ unableToProcess: "操作を完了できません"
|
||||||
recentUsed: "最近使用"
|
recentUsed: "最近使用"
|
||||||
install: "インストール"
|
install: "インストール"
|
||||||
uninstall: "アンインストール"
|
uninstall: "アンインストール"
|
||||||
|
installedApps: "インストールされたアプリ"
|
||||||
|
nothing: "ありません"
|
||||||
|
installedDate: "インストール日時"
|
||||||
|
lastUsedDate: "最終使用日時"
|
||||||
|
|
||||||
_theme:
|
_theme:
|
||||||
explore: "テーマを探す"
|
explore: "テーマを探す"
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="mk-notes" v-size="[{ max: 500 }]">
|
<div class="mk-notes" v-size="[{ max: 500 }]">
|
||||||
<div class="empty" v-if="empty">
|
<div class="_fullinfo" v-if="empty">
|
||||||
<img src="https://xn--931a.moe/assets/info.jpg" class="_ghost"/>
|
<img src="https://xn--931a.moe/assets/info.jpg" class="_ghost"/>
|
||||||
<div>{{ $t('noNotes') }}</div>
|
<div>{{ $t('noNotes') }}</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -90,18 +90,6 @@ export default Vue.extend({
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.mk-notes {
|
.mk-notes {
|
||||||
> .empty {
|
|
||||||
padding: 32px;
|
|
||||||
text-align: center;
|
|
||||||
|
|
||||||
> img {
|
|
||||||
vertical-align: bottom;
|
|
||||||
height: 128px;
|
|
||||||
margin-bottom: 16px;
|
|
||||||
border-radius: 16px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
> .notes {
|
> .notes {
|
||||||
> ::v-deep *:not(:last-child) {
|
> ::v-deep *:not(:last-child) {
|
||||||
margin-bottom: var(--marginFull);
|
margin-bottom: var(--marginFull);
|
||||||
|
|
98
src/client/pages/apps.vue
Normal file
98
src/client/pages/apps.vue
Normal file
|
@ -0,0 +1,98 @@
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<portal to="icon"><fa :icon="faPlug"/></portal>
|
||||||
|
<portal to="title">{{ $t('installedApps') }}</portal>
|
||||||
|
|
||||||
|
<mk-pagination :pagination="pagination" class="bfomjevm" ref="list">
|
||||||
|
<template #empty>
|
||||||
|
<div class="_fullinfo">
|
||||||
|
<img src="https://xn--931a.moe/assets/info.jpg" class="_ghost"/>
|
||||||
|
<div>{{ $t('nothing') }}</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<template #default="{items}">
|
||||||
|
<div class="token _panel" v-for="token in items" :key="token.id">
|
||||||
|
<img class="icon" :src="token.iconUrl" alt=""/>
|
||||||
|
<div class="body">
|
||||||
|
<div class="name">{{ token.name }}</div>
|
||||||
|
<div class="description">{{ token.description }}</div>
|
||||||
|
<div class="_keyValue">
|
||||||
|
<div>{{ $t('installedDate') }}:</div>
|
||||||
|
<div><mk-time :time="token.createdAt"/></div>
|
||||||
|
</div>
|
||||||
|
<div class="_keyValue">
|
||||||
|
<div>{{ $t('lastUsedDate') }}:</div>
|
||||||
|
<div><mk-time :time="token.lastUsedAt"/></div>
|
||||||
|
</div>
|
||||||
|
<div class="actions">
|
||||||
|
<button class="_button" @click="revoke(token)"><fa :icon="faTrashAlt"/></button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</mk-pagination>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import Vue from 'vue';
|
||||||
|
import { faTrashAlt, faPlug } from '@fortawesome/free-solid-svg-icons';
|
||||||
|
import MkPagination from '../components/ui/pagination.vue';
|
||||||
|
|
||||||
|
export default Vue.extend({
|
||||||
|
metaInfo() {
|
||||||
|
return {
|
||||||
|
title: this.$t('installedApps') as string
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
components: {
|
||||||
|
MkPagination
|
||||||
|
},
|
||||||
|
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
pagination: {
|
||||||
|
endpoint: 'i/apps',
|
||||||
|
limit: 100,
|
||||||
|
},
|
||||||
|
faTrashAlt, faPlug
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
revoke(token) {
|
||||||
|
this.$root.api('i/revoke-token', { tokenId: token.id }).then(() => {
|
||||||
|
this.$refs.list.reload();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.bfomjevm {
|
||||||
|
> .token {
|
||||||
|
display: flex;
|
||||||
|
padding: 16px;
|
||||||
|
|
||||||
|
> .icon {
|
||||||
|
display: block;
|
||||||
|
flex-shrink: 0;
|
||||||
|
margin: 0 12px 0 0;
|
||||||
|
width: 50px;
|
||||||
|
height: 50px;
|
||||||
|
border-radius: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
> .body {
|
||||||
|
width: calc(100% - 62px);
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
> .name {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -5,7 +5,7 @@
|
||||||
|
|
||||||
<mk-pagination :pagination="pagination" class="mk-follow-requests" ref="list">
|
<mk-pagination :pagination="pagination" class="mk-follow-requests" ref="list">
|
||||||
<template #empty>
|
<template #empty>
|
||||||
<div class="tkdrhpxr">
|
<div class="_fullinfo">
|
||||||
<img src="https://xn--931a.moe/assets/info.jpg" class="_ghost"/>
|
<img src="https://xn--931a.moe/assets/info.jpg" class="_ghost"/>
|
||||||
<div>{{ $t('noFollowRequests') }}</div>
|
<div>{{ $t('noFollowRequests') }}</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -75,18 +75,6 @@ export default Vue.extend({
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.mk-follow-requests {
|
.mk-follow-requests {
|
||||||
.tkdrhpxr {
|
|
||||||
padding: 32px;
|
|
||||||
text-align: center;
|
|
||||||
|
|
||||||
> img {
|
|
||||||
vertical-align: bottom;
|
|
||||||
height: 128px;
|
|
||||||
margin-bottom: 16px;
|
|
||||||
border-radius: 16px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
> .user {
|
> .user {
|
||||||
display: flex;
|
display: flex;
|
||||||
padding: 16px;
|
padding: 16px;
|
||||||
|
|
|
@ -31,7 +31,7 @@
|
||||||
</div>
|
</div>
|
||||||
</router-link>
|
</router-link>
|
||||||
</div>
|
</div>
|
||||||
<div class="no-history" v-if="!fetching && messages.length == 0">
|
<div class="_fullinfo" v-if="!fetching && messages.length == 0">
|
||||||
<img src="https://xn--931a.moe/assets/info.jpg" class="_ghost"/>
|
<img src="https://xn--931a.moe/assets/info.jpg" class="_ghost"/>
|
||||||
<div>{{ $t('noHistory') }}</div>
|
<div>{{ $t('noHistory') }}</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -287,18 +287,6 @@ export default Vue.extend({
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
> .no-history {
|
|
||||||
padding: 32px;
|
|
||||||
text-align: center;
|
|
||||||
|
|
||||||
> img {
|
|
||||||
vertical-align: bottom;
|
|
||||||
height: 128px;
|
|
||||||
margin-bottom: 16px;
|
|
||||||
border-radius: 16px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width: 400px) {
|
@media (max-width: 400px) {
|
||||||
> .history {
|
> .history {
|
||||||
> .message {
|
> .message {
|
||||||
|
|
|
@ -32,7 +32,9 @@
|
||||||
<x-integration/>
|
<x-integration/>
|
||||||
<x-api/>
|
<x-api/>
|
||||||
|
|
||||||
<mk-button @click="$root.signout()" primary style="margin: var(--margin) auto;">{{ $t('logout') }}</mk-button>
|
<router-link class="_panel _buttonPrimary" to="/my/apps" style="margin: var(--margin) auto;">{{ $t('installedApps') }}</router-link>
|
||||||
|
|
||||||
|
<button class="_panel _buttonPrimary" @click="$root.signout()" style="margin: var(--margin) auto;">{{ $t('logout') }}</button>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
|
@ -46,6 +46,7 @@ export const router = new VueRouter({
|
||||||
{ path: '/my/groups', component: page('my-groups/index') },
|
{ path: '/my/groups', component: page('my-groups/index') },
|
||||||
{ path: '/my/groups/:group', component: page('my-groups/group') },
|
{ path: '/my/groups/:group', component: page('my-groups/group') },
|
||||||
{ path: '/my/antennas', component: page('my-antennas/index') },
|
{ path: '/my/antennas', component: page('my-antennas/index') },
|
||||||
|
{ path: '/my/apps', component: page('apps') },
|
||||||
{ path: '/preferences', component: page('preferences/index') },
|
{ path: '/preferences', component: page('preferences/index') },
|
||||||
{ path: '/instance', component: page('instance/index') },
|
{ path: '/instance', component: page('instance/index') },
|
||||||
{ path: '/instance/emojis', component: page('instance/emojis') },
|
{ path: '/instance/emojis', component: page('instance/emojis') },
|
||||||
|
|
|
@ -412,6 +412,26 @@ main ._panel {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
._fullinfo {
|
||||||
|
padding: 32px;
|
||||||
|
text-align: center;
|
||||||
|
|
||||||
|
> img {
|
||||||
|
vertical-align: bottom;
|
||||||
|
height: 128px;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
border-radius: 16px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
._keyValue {
|
||||||
|
display: flex;
|
||||||
|
|
||||||
|
> div {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
._link {
|
._link {
|
||||||
color: var(--link);
|
color: var(--link);
|
||||||
}
|
}
|
||||||
|
|
24
src/server/api/endpoints/i/revoke-token.ts
Normal file
24
src/server/api/endpoints/i/revoke-token.ts
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
import $ from 'cafy';
|
||||||
|
import define from '../../define';
|
||||||
|
import { AccessTokens } from '../../../../models';
|
||||||
|
import { ID } from '../../../../misc/cafy-id';
|
||||||
|
|
||||||
|
export const meta = {
|
||||||
|
requireCredential: true as const,
|
||||||
|
|
||||||
|
secure: true,
|
||||||
|
|
||||||
|
params: {
|
||||||
|
tokenId: {
|
||||||
|
validator: $.type(ID)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export default define(meta, async (ps, user) => {
|
||||||
|
const token = await AccessTokens.findOne(ps.tokenId);
|
||||||
|
|
||||||
|
if (token) {
|
||||||
|
AccessTokens.delete(token.id);
|
||||||
|
}
|
||||||
|
});
|
Loading…
Reference in a new issue