連合しているインスタンスを一覧できるように
This commit is contained in:
		
							parent
							
								
									7275bc6d3b
								
							
						
					
					
						commit
						c3140f57b9
					
				
					 7 changed files with 277 additions and 55 deletions
				
			
		| 
						 | 
				
			
			@ -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