wip
This commit is contained in:
		
							parent
							
								
									dc3c80e3ce
								
							
						
					
					
						commit
						2ec25a7729
					
				|  | @ -0,0 +1,97 @@ | |||
| <template> | ||||
| <div class="mkw-hashtags"> | ||||
| 	<mk-widget-container :show-header="!props.compact"> | ||||
| 		<template slot="header">%fa:hashtag%%i18n:@title%</template> | ||||
| 
 | ||||
| 		<div class="mkw-hashtags--body" :data-mobile="platform == 'mobile'"> | ||||
| 			<p class="fetching" v-if="fetching">%fa:spinner .pulse .fw%%i18n:common.loading%<mk-ellipsis/></p> | ||||
| 			<div v-else> | ||||
| 				<router-link v-for="stat in stats" :key="stat.tag" :to="`/tags/${ stat.tag }`">{{ stat.tag }}</router-link> | ||||
| 			</div> | ||||
| 		</div> | ||||
| 	</mk-widget-container> | ||||
| </div> | ||||
| </template> | ||||
| 
 | ||||
| <script lang="ts"> | ||||
| import define from '../../../common/define-widget'; | ||||
| export default define({ | ||||
| 	name: 'hashtags', | ||||
| 	props: () => ({ | ||||
| 		compact: false | ||||
| 	}) | ||||
| }).extend({ | ||||
| 	data() { | ||||
| 		return { | ||||
| 			stats: [], | ||||
| 			fetching: true, | ||||
| 			clock: null | ||||
| 		}; | ||||
| 	}, | ||||
| 	mounted() { | ||||
| 		this.fetch(); | ||||
| 		this.clock = setInterval(this.fetch, 1000 * 60 * 10); | ||||
| 	}, | ||||
| 	beforeDestroy() { | ||||
| 		clearInterval(this.clock); | ||||
| 	}, | ||||
| 	methods: { | ||||
| 		func() { | ||||
| 			this.props.compact = !this.props.compact; | ||||
| 			this.save(); | ||||
| 		}, | ||||
| 		fetch() { | ||||
| 			(this as any).api('hashtags/trend').then(stats => { | ||||
| 				this.stats = stats; | ||||
| 				this.fetching = false; | ||||
| 			}); | ||||
| 		} | ||||
| 	} | ||||
| }); | ||||
| </script> | ||||
| 
 | ||||
| <style lang="stylus" scoped> | ||||
| root(isDark) | ||||
| 	.mkw-rss--body | ||||
| 		.feed | ||||
| 			padding 12px 16px | ||||
| 			font-size 0.9em | ||||
| 
 | ||||
| 			> a | ||||
| 				display block | ||||
| 				padding 4px 0 | ||||
| 				color isDark ? #9aa4b3 : #666 | ||||
| 				border-bottom dashed 1px isDark ? #1c2023 : #eee | ||||
| 
 | ||||
| 				&:last-child | ||||
| 					border-bottom none | ||||
| 
 | ||||
| 		.fetching | ||||
| 			margin 0 | ||||
| 			padding 16px | ||||
| 			text-align center | ||||
| 			color #aaa | ||||
| 
 | ||||
| 			> [data-fa] | ||||
| 				margin-right 4px | ||||
| 
 | ||||
| 		&[data-mobile] | ||||
| 			background isDark ? #21242f : #f3f3f3 | ||||
| 
 | ||||
| 			.feed | ||||
| 				padding 0 | ||||
| 
 | ||||
| 				> a | ||||
| 					padding 8px 16px | ||||
| 					border-bottom none | ||||
| 
 | ||||
| 					&:nth-child(even) | ||||
| 						background isDark ? rgba(#000, 0.05) : rgba(#fff, 0.7) | ||||
| 
 | ||||
| .mkw-rss[data-darkmode] | ||||
| 	root(true) | ||||
| 
 | ||||
| .mkw-rss:not([data-darkmode]) | ||||
| 	root(false) | ||||
| 
 | ||||
| </style> | ||||
|  | @ -13,6 +13,7 @@ import wSlideshow from './slideshow.vue'; | |||
| import wTips from './tips.vue'; | ||||
| import wDonation from './donation.vue'; | ||||
| import wNav from './nav.vue'; | ||||
| import wHashtags from './hashtags.vue'; | ||||
| 
 | ||||
| Vue.component('mkw-analog-clock', wAnalogClock); | ||||
| Vue.component('mkw-nav', wNav); | ||||
|  | @ -27,3 +28,4 @@ Vue.component('mkw-posts-monitor', wPostsMonitor); | |||
| Vue.component('mkw-memo', wMemo); | ||||
| Vue.component('mkw-rss', wRss); | ||||
| Vue.component('mkw-version', wVersion); | ||||
| Vue.component('mkw-hashtags', wHashtags); | ||||
|  |  | |||
|  | @ -23,6 +23,7 @@ | |||
| 					<option value="post-form">%i18n:common.widgets.post-form%</option> | ||||
| 					<option value="messaging">%i18n:common.widgets.messaging%</option> | ||||
| 					<option value="memo">%i18n:common.widgets.memo%</option> | ||||
| 					<option value="hashtags">%i18n:common.widgets.hashtags%</option> | ||||
| 					<option value="posts-monitor">%i18n:common.widgets.posts-monitor%</option> | ||||
| 					<option value="server">%i18n:common.widgets.server%</option> | ||||
| 					<option value="donation">%i18n:common.widgets.donation%</option> | ||||
|  |  | |||
|  | @ -23,6 +23,7 @@ | |||
| 					<option value="post-form">%i18n:common.widgets.post-form%</option> | ||||
| 					<option value="messaging">%i18n:common.widgets.messaging%</option> | ||||
| 					<option value="memo">%i18n:common.widgets.memo%</option> | ||||
| 					<option value="hashtags">%i18n:common.widgets.hashtags%</option> | ||||
| 					<option value="posts-monitor">%i18n:common.widgets.posts-monitor%</option> | ||||
| 					<option value="server">%i18n:common.widgets.server%</option> | ||||
| 					<option value="donation">%i18n:common.widgets.donation%</option> | ||||
|  |  | |||
|  | @ -15,6 +15,7 @@ | |||
| 					<option value="rss">%i18n:common.widgets.rss%</option> | ||||
| 					<option value="photo-stream">%i18n:common.widgets.photo-stream%</option> | ||||
| 					<option value="slideshow">%i18n:common.widgets.slideshow%</option> | ||||
| 					<option value="hashtags">%i18n:common.widgets.hashtags%</option> | ||||
| 					<option value="posts-monitor">%i18n:common.widgets.posts-monitor%</option> | ||||
| 					<option value="version">%i18n:common.widgets.version%</option> | ||||
| 					<option value="server">%i18n:common.widgets.server%</option> | ||||
|  |  | |||
|  | @ -19,7 +19,6 @@ import MachineInfo from './utils/machineInfo'; | |||
| import DependencyInfo from './utils/dependencyInfo'; | ||||
| import serverStats from './daemons/server-stats'; | ||||
| import notesStats from './daemons/notes-stats'; | ||||
| import hashtagsStats from './daemons/hashtags-stats'; | ||||
| 
 | ||||
| import loadConfig from './config/load'; | ||||
| import { Config } from './config/types'; | ||||
|  | @ -53,7 +52,6 @@ function main() { | |||
| 		ev.mount(); | ||||
| 		serverStats(); | ||||
| 		notesStats(); | ||||
| 		hashtagsStats(); | ||||
| 	} else { | ||||
| 		workerMain(opt); | ||||
| 	} | ||||
|  |  | |||
|  | @ -628,6 +628,11 @@ const endpoints: Endpoint[] = [ | |||
| 		withCredential: true | ||||
| 	}, | ||||
| 
 | ||||
| 	{ | ||||
| 		name: 'hashtags/trend', | ||||
| 		withCredential: true | ||||
| 	}, | ||||
| 
 | ||||
| 	{ | ||||
| 		name: 'messaging/history', | ||||
| 		withCredential: true, | ||||
|  |  | |||
|  | @ -0,0 +1,78 @@ | |||
| import Note from '../../../../models/note'; | ||||
| 
 | ||||
| /** | ||||
|  * Get trends of hashtags | ||||
|  */ | ||||
| module.exports = (params, user) => new Promise(async (res, rej) => { | ||||
| 	// 10分
 | ||||
| 	const interval = 1000 * 60 * 10; | ||||
| 
 | ||||
| 	const data = await Note.aggregate([{ | ||||
| 		$match: { | ||||
| 			createdAt: { | ||||
| 				$gt: new Date(Date.now() - interval) | ||||
| 			}, | ||||
| 			tags: { | ||||
| 				$exists: true, | ||||
| 				$ne: [] | ||||
| 			} | ||||
| 		} | ||||
| 	}, { | ||||
| 		$unwind: '$tags' | ||||
| 	}, { | ||||
| 		$group: { | ||||
| 			_id: '$tags', | ||||
| 			count: { | ||||
| 				$sum: 1 | ||||
| 			} | ||||
| 		} | ||||
| 	}, { | ||||
| 		$group: { | ||||
| 			_id: null, | ||||
| 			tags: { | ||||
| 				$push: { | ||||
| 					tag: '$_id', | ||||
| 					count: '$count' | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 	}, { | ||||
| 		$project: { | ||||
| 			_id: false, | ||||
| 			tags: true | ||||
| 		} | ||||
| 	}]) as Array<{ | ||||
| 		tags: Array<{ | ||||
| 			tag: string; | ||||
| 			count: number; | ||||
| 		}> | ||||
| 	}>; | ||||
| 
 | ||||
| 	const hots = data[0].tags | ||||
| 		.sort((a, b) => a.count - b.count) | ||||
| 		.map(tag => tag.tag) | ||||
| 		.slice(0, 10); | ||||
| 
 | ||||
| 	const countPromises: Array<Promise<number[]>> = []; | ||||
| 
 | ||||
| 	for (let i = 0; i < 10; i++) { | ||||
| 		countPromises.push(Promise.all(hots.map(tag => Note.count({ | ||||
| 			tags: tag, | ||||
| 			createdAt: { | ||||
| 				$lt: new Date(Date.now() - (interval * i)), | ||||
| 				$gt: new Date(Date.now() - (interval * (i + 1))) | ||||
| 			} | ||||
| 		})))); | ||||
| 	} | ||||
| 
 | ||||
| 	const countsLog = await Promise.all(countPromises); | ||||
| 
 | ||||
| 	const stats = hots.map((tag, i) => ({ | ||||
| 		tag, | ||||
| 		chart: countsLog.map(counts => counts[i]) | ||||
| 	})); | ||||
| 
 | ||||
| 	console.log(stats); | ||||
| 
 | ||||
| 	res(stats); | ||||
| }); | ||||
|  | @ -1,35 +0,0 @@ | |||
| import * as websocket from 'websocket'; | ||||
| import Xev from 'xev'; | ||||
| 
 | ||||
| const ev = new Xev(); | ||||
| 
 | ||||
| export default function(request: websocket.request, connection: websocket.connection): void { | ||||
| 	const onStats = stats => { | ||||
| 		connection.send(JSON.stringify({ | ||||
| 			type: 'stats', | ||||
| 			body: stats | ||||
| 		})); | ||||
| 	}; | ||||
| 
 | ||||
| 	connection.on('message', async data => { | ||||
| 		const msg = JSON.parse(data.utf8Data); | ||||
| 
 | ||||
| 		switch (msg.type) { | ||||
| 			case 'requestLog': | ||||
| 				ev.once('hashtagsStatsLog:' + msg.id, statsLog => { | ||||
| 					connection.send(JSON.stringify({ | ||||
| 						type: 'statsLog', | ||||
| 						body: statsLog | ||||
| 					})); | ||||
| 				}); | ||||
| 				ev.emit('requestHashtagsStatsLog', msg.id); | ||||
| 				break; | ||||
| 		} | ||||
| 	}); | ||||
| 
 | ||||
| 	ev.addListener('hashtagsStats', onStats); | ||||
| 
 | ||||
| 	connection.on('close', () => { | ||||
| 		ev.removeListener('hashtagsStats', onStats); | ||||
| 	}); | ||||
| } | ||||
|  | @ -14,7 +14,6 @@ import othelloGameStream from './stream/othello-game'; | |||
| import othelloStream from './stream/othello'; | ||||
| import serverStatsStream from './stream/server-stats'; | ||||
| import notesStatsStream from './stream/notes-stats'; | ||||
| import hashtagsStatsStream from './stream/hashtags-stats'; | ||||
| import requestsStream from './stream/requests'; | ||||
| import { ParsedUrlQuery } from 'querystring'; | ||||
| import authenticate from './authenticate'; | ||||
|  | @ -40,11 +39,6 @@ module.exports = (server: http.Server) => { | |||
| 			return; | ||||
| 		} | ||||
| 
 | ||||
| 		if (request.resourceURL.pathname === '/hashtags-stats') { | ||||
| 			hashtagsStatsStream(request, connection); | ||||
| 			return; | ||||
| 		} | ||||
| 
 | ||||
| 		if (request.resourceURL.pathname === '/requests') { | ||||
| 			requestsStream(request, connection); | ||||
| 			return; | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue