連合しているインスタンスを一覧できるように
This commit is contained in:
		
							parent
							
								
									7275bc6d3b
								
							
						
					
					
						commit
						c3140f57b9
					
				
					 7 changed files with 277 additions and 55 deletions
				
			
		|  | @ -1371,6 +1371,30 @@ admin/views/announcements.vue: | |||
| admin/views/hashtags.vue: | ||||
|   hided-tags: "Hidden Tags" | ||||
| 
 | ||||
| admin/views/federation.vue: | ||||
|   federation: "連合" | ||||
|   host: "ホスト" | ||||
|   notes: "投稿" | ||||
|   users: "ユーザー" | ||||
|   following: "フォロー中" | ||||
|   followers: "フォロワー" | ||||
|   status: "ステータス" | ||||
|   lookup: "照会" | ||||
|   instances: "インスタンス" | ||||
|   instance-not-registered: "そのインスタンスは登録されていません" | ||||
|   sort: "ソート" | ||||
|   sorts: | ||||
|     caughtAtAsc: "登録日時が古い順" | ||||
|     caughtAtDesc: "登録日時が新しい順" | ||||
|     notesAsc: "投稿が少ない順" | ||||
|     notesDesc: "投稿が多い順" | ||||
|     usersAsc: "ユーザーが少ない順" | ||||
|     usersDesc: "ユーザーが多い順" | ||||
|     followingAsc: "フォローが少ない順" | ||||
|     followingDesc: "フォローが多い順" | ||||
|     followersAsc: "フォロワーが少ない順" | ||||
|     followersDesc: "フォロワーが多い順" | ||||
| 
 | ||||
| desktop/views/pages/welcome.vue: | ||||
|   about: "詳しく..." | ||||
|   gotit: "わかった" | ||||
|  |  | |||
|  | @ -124,7 +124,7 @@ export default Vue.extend({ | |||
| 			this.meta = meta; | ||||
| 		}); | ||||
| 
 | ||||
| 		this.$root.api('instances', { | ||||
| 		this.$root.api('federation/instances', { | ||||
| 			sort: '+notes' | ||||
| 		}).then(instances => { | ||||
| 			for (const i of instances) { | ||||
|  |  | |||
							
								
								
									
										141
									
								
								src/client/app/admin/views/federation.vue
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										141
									
								
								src/client/app/admin/views/federation.vue
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,141 @@ | |||
| <template> | ||||
| <div> | ||||
| 	<ui-card> | ||||
| 		<div slot="title"><fa :icon="faTerminal"/> {{ $t('federation') }}</div> | ||||
| 		<section class="fit-top"> | ||||
| 			<ui-input class="target" v-model="target" type="text" @enter="showInstance"> | ||||
| 				<span>{{ $t('host') }}</span> | ||||
| 			</ui-input> | ||||
| 			<ui-button @click="showInstance"><fa :icon="faSearch"/> {{ $t('lookup') }}</ui-button> | ||||
| 
 | ||||
| 			<div class="instance" v-if="instance"> | ||||
| 				{{ instance.host }} | ||||
| 			</div> | ||||
| 		</section> | ||||
| 	</ui-card> | ||||
| 
 | ||||
| 	<ui-card> | ||||
| 		<div slot="title"><fa :icon="faUsers"/> {{ $t('instances') }}</div> | ||||
| 		<section class="fit-top"> | ||||
| 			<ui-horizon-group inputs> | ||||
| 				<ui-select v-model="sort"> | ||||
| 					<span slot="label">{{ $t('sort') }}</span> | ||||
| 					<option value="-caughtAt">{{ $t('sorts.caughtAtAsc') }}</option> | ||||
| 					<option value="+caughtAt">{{ $t('sorts.caughtAtDesc') }}</option> | ||||
| 					<option value="-notes">{{ $t('sorts.notesAsc') }}</option> | ||||
| 					<option value="+notes">{{ $t('sorts.notesDesc') }}</option> | ||||
| 					<option value="-users">{{ $t('sorts.usersAsc') }}</option> | ||||
| 					<option value="+users">{{ $t('sorts.usersDesc') }}</option> | ||||
| 					<option value="-following">{{ $t('sorts.followingAsc') }}</option> | ||||
| 					<option value="+following">{{ $t('sorts.followingDesc') }}</option> | ||||
| 					<option value="-followers">{{ $t('sorts.followersAsc') }}</option> | ||||
| 					<option value="+followers">{{ $t('sorts.followersDesc') }}</option> | ||||
| 				</ui-select> | ||||
| 			</ui-horizon-group> | ||||
| 
 | ||||
| 			<div class="instances"> | ||||
| 				<header> | ||||
| 					<span>{{ $t('host') }}</span> | ||||
| 					<span>{{ $t('notes') }}</span> | ||||
| 					<span>{{ $t('users') }}</span> | ||||
| 					<span>{{ $t('following') }}</span> | ||||
| 					<span>{{ $t('followers') }}</span> | ||||
| 					<span>{{ $t('status') }}</span> | ||||
| 				</header> | ||||
| 				<div v-for="instance in instances"> | ||||
| 					<span>{{ instance.host }}</span> | ||||
| 					<span>{{ instance.notesCount | number }}</span> | ||||
| 					<span>{{ instance.usersCount | number }}</span> | ||||
| 					<span>{{ instance.followingCount | number }}</span> | ||||
| 					<span>{{ instance.followersCount | number }}</span> | ||||
| 					<span>{{ instance.latestStatus }}</span> | ||||
| 				</div> | ||||
| 			</div> | ||||
| 		</section> | ||||
| 	</ui-card> | ||||
| </div> | ||||
| </template> | ||||
| 
 | ||||
| <script lang="ts"> | ||||
| import Vue from 'vue'; | ||||
| import i18n from '../../i18n'; | ||||
| import { faGlobe, faTerminal, faSearch } from '@fortawesome/free-solid-svg-icons'; | ||||
| 
 | ||||
| export default Vue.extend({ | ||||
| 	i18n: i18n('admin/views/federation.vue'), | ||||
| 
 | ||||
| 	data() { | ||||
| 		return { | ||||
| 			instance: null, | ||||
| 			target: null, | ||||
| 			sort: '+caughtAt', | ||||
| 			limit: 50, | ||||
| 			instances: [], | ||||
| 			faGlobe, faTerminal, faSearch | ||||
| 		}; | ||||
| 	}, | ||||
| 
 | ||||
| 	watch: { | ||||
| 		sort() { | ||||
| 			this.instances = []; | ||||
| 			this.fetchInstances(); | ||||
| 		}, | ||||
| 	}, | ||||
| 
 | ||||
| 	mounted() { | ||||
| 		this.fetchInstances(); | ||||
| 	}, | ||||
| 
 | ||||
| 	methods: { | ||||
| 		showInstance() { | ||||
| 			this.$root.api('federation/show-instance', { | ||||
| 				host: this.target | ||||
| 			}).then(instance => { | ||||
| 				if (instance == null) { | ||||
| 					this.$root.dialog({ | ||||
| 						type: 'error', | ||||
| 						text: this.$t('instance-not-registered') | ||||
| 					}); | ||||
| 				} else { | ||||
| 					this.instance = instance; | ||||
| 					this.target = ''; | ||||
| 				} | ||||
| 			}); | ||||
| 		}, | ||||
| 
 | ||||
| 		fetchInstances() { | ||||
| 			this.$root.api('federation/instances', { | ||||
| 				sort: this.sort | ||||
| 			}).then(instances => { | ||||
| 				this.instances = instances; | ||||
| 			}); | ||||
| 		} | ||||
| 	} | ||||
| }); | ||||
| </script> | ||||
| 
 | ||||
| <style lang="stylus" scoped> | ||||
| .target | ||||
| 	margin-bottom 16px !important | ||||
| 
 | ||||
| .instances | ||||
| 	width 100% | ||||
| 
 | ||||
| 	> header | ||||
| 		display flex | ||||
| 
 | ||||
| 		> * | ||||
| 			color var(--text) | ||||
| 			font-weight bold | ||||
| 
 | ||||
| 	> div | ||||
| 		display flex | ||||
| 
 | ||||
| 	> * > * | ||||
| 		flex 1 | ||||
| 		overflow auto | ||||
| 
 | ||||
| 		&:first-child | ||||
| 			min-width 200px | ||||
| 
 | ||||
| </style> | ||||
|  | @ -24,7 +24,7 @@ | |||
| 			<li @click="nav('moderators')" :class="{ active: page == 'moderators' }"><fa :icon="faHeadset" fixed-width/>{{ $t('moderators') }}</li> | ||||
| 			<li @click="nav('users')" :class="{ active: page == 'users' }"><fa icon="users" fixed-width/>{{ $t('users') }}</li> | ||||
| 			<li @click="nav('drive')" :class="{ active: page == 'drive' }"><fa icon="cloud" fixed-width/>{{ $t('@.drive') }}</li> | ||||
| 			<!-- <li @click="nav('federation')" :class="{ active: page == 'federation' }"><fa :icon="faShareAlt" fixed-width/>{{ $t('federation') }}</li> --> | ||||
| 			<li @click="nav('federation')" :class="{ active: page == 'federation' }"><fa :icon="faGlobe" fixed-width/>{{ $t('federation') }}</li> | ||||
| 			<li @click="nav('emoji')" :class="{ active: page == 'emoji' }"><fa :icon="faGrin" fixed-width/>{{ $t('emoji') }}</li> | ||||
| 			<li @click="nav('announcements')" :class="{ active: page == 'announcements' }"><fa icon="broadcast-tower" fixed-width/>{{ $t('announcements') }}</li> | ||||
| 			<li @click="nav('hashtags')" :class="{ active: page == 'hashtags' }"><fa icon="hashtag" fixed-width/>{{ $t('hashtags') }}</li> | ||||
|  | @ -48,6 +48,7 @@ | |||
| 			<div v-if="page == 'announcements'"><x-announcements/></div> | ||||
| 			<div v-if="page == 'hashtags'"><x-hashtags/></div> | ||||
| 			<div v-if="page == 'drive'"><x-drive/></div> | ||||
| 			<div v-if="page == 'federation'"><x-federation/></div> | ||||
| 			<div v-if="page == 'abuse'"><x-abuse/></div> | ||||
| 		</div> | ||||
| 	</main> | ||||
|  | @ -68,7 +69,9 @@ import XHashtags from "./hashtags.vue"; | |||
| import XUsers from "./users.vue"; | ||||
| import XDrive from "./drive.vue"; | ||||
| import XAbuse from "./abuse.vue"; | ||||
| import { faHeadset, faArrowLeft, faShareAlt, faExclamationCircle, faTasks } from '@fortawesome/free-solid-svg-icons'; | ||||
| import XFederation from "./federation.vue"; | ||||
| 
 | ||||
| import { faHeadset, faArrowLeft, faGlobe, faExclamationCircle, faTasks } from '@fortawesome/free-solid-svg-icons'; | ||||
| import { faGrin } from '@fortawesome/free-regular-svg-icons'; | ||||
| 
 | ||||
| // Detect the user agent | ||||
|  | @ -88,6 +91,7 @@ export default Vue.extend({ | |||
| 		XUsers, | ||||
| 		XDrive, | ||||
| 		XAbuse, | ||||
| 		XFederation, | ||||
| 	}, | ||||
| 	provide: { | ||||
| 		isMobile | ||||
|  | @ -101,7 +105,7 @@ export default Vue.extend({ | |||
| 			faGrin, | ||||
| 			faArrowLeft, | ||||
| 			faHeadset, | ||||
| 			faShareAlt, | ||||
| 			faGlobe, | ||||
| 			faExclamationCircle, | ||||
| 			faTasks | ||||
| 		}; | ||||
|  |  | |||
							
								
								
									
										84
									
								
								src/server/api/endpoints/federation/instances.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										84
									
								
								src/server/api/endpoints/federation/instances.ts
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,84 @@ | |||
| import $ from 'cafy'; | ||||
| import define from '../../define'; | ||||
| import Instance from '../../../../models/instance'; | ||||
| 
 | ||||
| export const meta = { | ||||
| 	requireCredential: false, | ||||
| 
 | ||||
| 	params: { | ||||
| 		limit: { | ||||
| 			validator: $.num.optional.range(1, 100), | ||||
| 			default: 30 | ||||
| 		}, | ||||
| 
 | ||||
| 		offset: { | ||||
| 			validator: $.num.optional.min(0), | ||||
| 			default: 0 | ||||
| 		}, | ||||
| 
 | ||||
| 		sort: { | ||||
| 			validator: $.str.optional, | ||||
| 		} | ||||
| 	} | ||||
| }; | ||||
| 
 | ||||
| export default define(meta, (ps, me) => new Promise(async (res, rej) => { | ||||
| 	let sort; | ||||
| 
 | ||||
| 	if (ps.sort) { | ||||
| 		if (ps.sort == '+notes') { | ||||
| 			sort = { | ||||
| 				notesCount: -1 | ||||
| 			}; | ||||
| 		} else if (ps.sort == '-notes') { | ||||
| 			sort = { | ||||
| 				notesCount: 1 | ||||
| 			}; | ||||
| 		} else if (ps.sort == '+users') { | ||||
| 			sort = { | ||||
| 				usersCount: -1 | ||||
| 			}; | ||||
| 		} else if (ps.sort == '-users') { | ||||
| 			sort = { | ||||
| 				usersCount: 1 | ||||
| 			}; | ||||
| 		} else if (ps.sort == '+following') { | ||||
| 			sort = { | ||||
| 				followingCount: -1 | ||||
| 			}; | ||||
| 		} else if (ps.sort == '-following') { | ||||
| 			sort = { | ||||
| 				followingCount: 1 | ||||
| 			}; | ||||
| 		} else if (ps.sort == '+followers') { | ||||
| 			sort = { | ||||
| 				followersCount: -1 | ||||
| 			}; | ||||
| 		} else if (ps.sort == '-followers') { | ||||
| 			sort = { | ||||
| 				followersCount: 1 | ||||
| 			}; | ||||
| 		} else if (ps.sort == '+caughtAt') { | ||||
| 			sort = { | ||||
| 				caughtAt: -1 | ||||
| 			}; | ||||
| 		} else if (ps.sort == '-caughtAt') { | ||||
| 			sort = { | ||||
| 				caughtAt: 1 | ||||
| 			}; | ||||
| 		} | ||||
| 	} else { | ||||
| 		sort = { | ||||
| 			_id: -1 | ||||
| 		}; | ||||
| 	} | ||||
| 
 | ||||
| 	const instances = await Instance | ||||
| 		.find({}, { | ||||
| 			limit: ps.limit, | ||||
| 			sort: sort, | ||||
| 			skip: ps.offset | ||||
| 		}); | ||||
| 
 | ||||
| 	res(instances); | ||||
| })); | ||||
							
								
								
									
										20
									
								
								src/server/api/endpoints/federation/show-instance.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								src/server/api/endpoints/federation/show-instance.ts
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,20 @@ | |||
| import $ from 'cafy'; | ||||
| import define from '../../define'; | ||||
| import Instance from '../../../../models/instance'; | ||||
| 
 | ||||
| export const meta = { | ||||
| 	requireCredential: false, | ||||
| 
 | ||||
| 	params: { | ||||
| 		host: { | ||||
| 			validator: $.str | ||||
| 		} | ||||
| 	} | ||||
| }; | ||||
| 
 | ||||
| export default define(meta, (ps, me) => new Promise(async (res, rej) => { | ||||
| 	const instance = await Instance | ||||
| 		.findOne({ host: ps.host }); | ||||
| 
 | ||||
| 	res(instance); | ||||
| })); | ||||
|  | @ -1,51 +0,0 @@ | |||
| import $ from 'cafy'; | ||||
| import define from '../define'; | ||||
| import Instance from '../../../models/instance'; | ||||
| 
 | ||||
| export const meta = { | ||||
| 	requireCredential: false, | ||||
| 
 | ||||
| 	params: { | ||||
| 		limit: { | ||||
| 			validator: $.num.optional.range(1, 100), | ||||
| 			default: 30 | ||||
| 		}, | ||||
| 
 | ||||
| 		offset: { | ||||
| 			validator: $.num.optional.min(0), | ||||
| 			default: 0 | ||||
| 		}, | ||||
| 
 | ||||
| 		sort: { | ||||
| 			validator: $.str.optional.or('+notes|-notes'), | ||||
| 		} | ||||
| 	} | ||||
| }; | ||||
| 
 | ||||
| export default define(meta, (ps, me) => new Promise(async (res, rej) => { | ||||
| 	let _sort; | ||||
| 	if (ps.sort) { | ||||
| 		if (ps.sort == '+notes') { | ||||
| 			_sort = { | ||||
| 				notesCount: -1 | ||||
| 			}; | ||||
| 		} else if (ps.sort == '-notes') { | ||||
| 			_sort = { | ||||
| 				notesCount: 1 | ||||
| 			}; | ||||
| 		} | ||||
| 	} else { | ||||
| 		_sort = { | ||||
| 			_id: -1 | ||||
| 		}; | ||||
| 	} | ||||
| 
 | ||||
| 	const instances = await Instance | ||||
| 		.find({}, { | ||||
| 			limit: ps.limit, | ||||
| 			sort: _sort, | ||||
| 			skip: ps.offset | ||||
| 		}); | ||||
| 
 | ||||
| 	res(instances); | ||||
| })); | ||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue