Merge branch 'master' of github.com:syuilo/misskey into swagger
This commit is contained in:
		
						commit
						0420fee5d2
					
				
					 26 changed files with 147 additions and 193 deletions
				
			
		|  | @ -1,6 +1,9 @@ | ||||||
| language: node_js | language: node_js | ||||||
| node_js: | node_js: | ||||||
|   - "7.3.0" |   - "7.3.0" | ||||||
|  | services: | ||||||
|  |   - mongodb | ||||||
|  |   - redis-server | ||||||
| before_script: | before_script: | ||||||
|   - "mkdir -p ./.config && cp ./.ci-files/config.yml ./.config" |   - "mkdir -p ./.config && cp ./.ci-files/config.yml ./.config" | ||||||
| env: | env: | ||||||
|  |  | ||||||
							
								
								
									
										11
									
								
								CHANGELOG.md
									
										
									
									
									
								
							
							
						
						
									
										11
									
								
								CHANGELOG.md
									
										
									
									
									
								
							|  | @ -1,2 +1,11 @@ | ||||||
| # 1 | # Server | ||||||
|  | ## 1 | ||||||
|  | First version | ||||||
|  | 
 | ||||||
|  | # API | ||||||
|  | ## 2 | ||||||
|  | * パラメータ: _userkey --> i | ||||||
|  | * トークンは、アクセストークン + アプリのシークレットキーをsha512したものに | ||||||
|  | 
 | ||||||
|  | ## 1 | ||||||
| First version | First version | ||||||
|  |  | ||||||
|  | @ -70,5 +70,7 @@ block content | ||||||
| 				| 次に、<code>#{api_url}/auth/session/userkey</code>へ<code>app_secret</code>としてApp Secretを、<code>token</code>としてセッションのトークンをパラメータとして付与したリクエストを送信してください。 | 				| 次に、<code>#{api_url}/auth/session/userkey</code>へ<code>app_secret</code>としてApp Secretを、<code>token</code>としてセッションのトークンをパラメータとして付与したリクエストを送信してください。 | ||||||
| 				br | 				br | ||||||
| 				| 上手くいけば、認証したユーザーのアクセストークンがレスポンスとして取得できます。おめでとうございます! | 				| 上手くいけば、認証したユーザーのアクセストークンがレスポンスとして取得できます。おめでとうございます! | ||||||
|  | 			p | ||||||
|  | 				| 以降アクセストークンは、<strong>ユーザーのアクセストークン+アプリのシークレットキーをsha512したもの</strong>として扱います。 | ||||||
| 
 | 
 | ||||||
| 	p アクセストークンを取得できたら、あとは簡単です。REST APIなら、リクエストにアクセストークンを<code>_userkey</code>(「自分のアクセストークンを取得したい場合」の方法で取得したアクセストークンの場合は<code>i</code>)としてパラメータに含めるだけです。 | 	p アクセストークンを取得できたら、あとは簡単です。REST APIなら、リクエストにアクセストークンを<code>i</code>としてパラメータに含めるだけです。 | ||||||
|  |  | ||||||
|  | @ -149,6 +149,7 @@ const aliasifyConfig = { | ||||||
| 		'chart.js': './node_modules/chart.js/src/chart.js', | 		'chart.js': './node_modules/chart.js/src/chart.js', | ||||||
| 		'textarea-caret-position': './node_modules/textarea-caret/index.js', | 		'textarea-caret-position': './node_modules/textarea-caret/index.js', | ||||||
| 		'misskey-text': './src/common/text/index.js', | 		'misskey-text': './src/common/text/index.js', | ||||||
|  | 		'nyaize': './node_modules/nyaize/built/index.js', | ||||||
| 		'strength.js': './node_modules/syuilo-password-strength/strength.js', | 		'strength.js': './node_modules/syuilo-password-strength/strength.js', | ||||||
| 		'cropper': './node_modules/cropperjs/dist/cropper.js', | 		'cropper': './node_modules/cropperjs/dist/cropper.js', | ||||||
| 		'Sortable': './node_modules/sortablejs/Sortable.js', | 		'Sortable': './node_modules/sortablejs/Sortable.js', | ||||||
|  |  | ||||||
|  | @ -25,7 +25,7 @@ | ||||||
|     "@types/browserify": "12.0.30", |     "@types/browserify": "12.0.30", | ||||||
|     "@types/chalk": "0.4.31", |     "@types/chalk": "0.4.31", | ||||||
|     "@types/compression": "0.0.33", |     "@types/compression": "0.0.33", | ||||||
|     "@types/cors": "0.0.33", |     "@types/cors": "2.8.0", | ||||||
|     "@types/elasticsearch": "5.0.9", |     "@types/elasticsearch": "5.0.9", | ||||||
|     "@types/escape-html": "0.0.19", |     "@types/escape-html": "0.0.19", | ||||||
|     "@types/event-stream": "3.3.30", |     "@types/event-stream": "3.3.30", | ||||||
|  | @ -63,13 +63,14 @@ | ||||||
|     "babel-preset-stage-3": "6.17.0", |     "babel-preset-stage-3": "6.17.0", | ||||||
|     "bcrypt": "1.0.2", |     "bcrypt": "1.0.2", | ||||||
|     "body-parser": "1.15.2", |     "body-parser": "1.15.2", | ||||||
|     "browserify": "13.1.1", |     "browserify": "13.3.0", | ||||||
|     "browserify-livescript": "0.2.3", |     "browserify-livescript": "0.2.3", | ||||||
|     "chalk": "1.1.3", |     "chalk": "1.1.3", | ||||||
|     "chart.js": "2.4.0", |     "chart.js": "2.4.0", | ||||||
|     "compression": "1.6.2", |     "compression": "1.6.2", | ||||||
|     "cors": "2.8.1", |     "cors": "2.8.1", | ||||||
|     "cropperjs": "1.0.0-beta", |     "cropperjs": "1.0.0-beta", | ||||||
|  |     "crypto": "0.0.3", | ||||||
|     "deepcopy": "0.6.3", |     "deepcopy": "0.6.3", | ||||||
|     "del": "2.2.2", |     "del": "2.2.2", | ||||||
|     "elasticsearch": "12.1.3", |     "elasticsearch": "12.1.3", | ||||||
|  | @ -99,10 +100,11 @@ | ||||||
|     "livescript": "1.5.0", |     "livescript": "1.5.0", | ||||||
|     "mime-types": "2.1.13", |     "mime-types": "2.1.13", | ||||||
|     "mocha": "3.2.0", |     "mocha": "3.2.0", | ||||||
|     "mongodb": "2.2.16", |     "mongodb": "2.2.19", | ||||||
|     "ms": "0.7.2", |     "ms": "0.7.2", | ||||||
|     "multer": "1.2.1", |     "multer": "1.2.1", | ||||||
|     "nprogress": "0.2.0", |     "nprogress": "0.2.0", | ||||||
|  |     "nyaize": "0.0.2", | ||||||
|     "page": "1.7.1", |     "page": "1.7.1", | ||||||
|     "prominence": "0.2.0", |     "prominence": "0.2.0", | ||||||
|     "pug": "2.0.0-beta6", |     "pug": "2.0.0-beta6", | ||||||
|  |  | ||||||
|  | @ -1,7 +1,8 @@ | ||||||
| import * as express from 'express'; | import * as express from 'express'; | ||||||
| import App from './models/app'; | import App from './models/app'; | ||||||
| import User from './models/user'; | import User from './models/user'; | ||||||
| import Userkey from './models/userkey'; | import AccessToken from './models/access-token'; | ||||||
|  | import isNativeToken from './common/is-native-token'; | ||||||
| 
 | 
 | ||||||
| export interface IAuthContext { | export interface IAuthContext { | ||||||
| 	/** | 	/** | ||||||
|  | @ -20,10 +21,14 @@ export interface IAuthContext { | ||||||
| 	isSecure: boolean; | 	isSecure: boolean; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| export default (req: express.Request) => | export default (req: express.Request) => new Promise<IAuthContext>(async (resolve, reject) => { | ||||||
| 	new Promise<IAuthContext>(async (resolve, reject) => { |  | ||||||
| 	const token = req.body['i']; | 	const token = req.body['i']; | ||||||
| 	if (token) { | 
 | ||||||
|  | 	if (token == null) { | ||||||
|  | 		return resolve({ app: null, user: null, isSecure: false }); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if (isNativeToken(token)) { | ||||||
| 		const user = await User | 		const user = await User | ||||||
| 			.findOne({ token: token }); | 			.findOne({ token: token }); | ||||||
| 
 | 
 | ||||||
|  | @ -36,26 +41,21 @@ export default (req: express.Request) => | ||||||
| 			user: user, | 			user: user, | ||||||
| 			isSecure: true | 			isSecure: true | ||||||
| 		}); | 		}); | ||||||
| 	} | 	} else { | ||||||
| 
 | 		const accessToken = await AccessToken.findOne({ | ||||||
| 	const userkey = req.headers['userkey'] || req.body['_userkey']; | 			hash: token | ||||||
| 	if (userkey) { |  | ||||||
| 		const userkeyDoc = await Userkey.findOne({ |  | ||||||
| 			key: userkey |  | ||||||
| 		}); | 		}); | ||||||
| 
 | 
 | ||||||
| 		if (userkeyDoc === null) { | 		if (accessToken === null) { | ||||||
| 			return reject('invalid userkey'); | 			return reject('invalid signature'); | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		const app = await App | 		const app = await App | ||||||
| 			.findOne({ _id: userkeyDoc.app_id }); | 			.findOne({ _id: accessToken.app_id }); | ||||||
| 
 | 
 | ||||||
| 		const user = await User | 		const user = await User | ||||||
| 			.findOne({ _id: userkeyDoc.user_id }); | 			.findOne({ _id: accessToken.user_id }); | ||||||
| 
 | 
 | ||||||
| 		return resolve({ app: app, user: user, isSecure: false }); | 		return resolve({ app: app, user: user, isSecure: false }); | ||||||
| 	} | 	} | ||||||
| 
 |  | ||||||
| 	return resolve({ app: null, user: null, isSecure: false }); |  | ||||||
| }); | }); | ||||||
|  |  | ||||||
							
								
								
									
										1
									
								
								src/api/common/is-native-token.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								src/api/common/is-native-token.ts
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1 @@ | ||||||
|  | export default (token: string) => token[0] == '!'; | ||||||
|  | @ -4,8 +4,10 @@ | ||||||
|  * Module dependencies |  * Module dependencies | ||||||
|  */ |  */ | ||||||
| import rndstr from 'rndstr'; | import rndstr from 'rndstr'; | ||||||
|  | const crypto = require('crypto'); | ||||||
|  | import App from '../../models/app'; | ||||||
| import AuthSess from '../../models/auth-session'; | import AuthSess from '../../models/auth-session'; | ||||||
| import Userkey from '../../models/userkey'; | import AccessToken from '../../models/access-token'; | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  * @swagger |  * @swagger | ||||||
|  | @ -41,35 +43,46 @@ module.exports = (params, user) => | ||||||
| 	new Promise(async (res, rej) => | 	new Promise(async (res, rej) => | ||||||
| { | { | ||||||
| 	// Get 'token' parameter
 | 	// Get 'token' parameter
 | ||||||
| 	const token = params.token; | 	const sesstoken = params.token; | ||||||
| 	if (token == null) { | 	if (sesstoken == null) { | ||||||
| 		return rej('token is required'); | 		return rej('token is required'); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	// Fetch token
 | 	// Fetch token
 | ||||||
| 	const session = await AuthSess | 	const session = await AuthSess | ||||||
| 		.findOne({ token: token }); | 		.findOne({ token: sesstoken }); | ||||||
| 
 | 
 | ||||||
| 	if (session === null) { | 	if (session === null) { | ||||||
| 		return rej('session not found'); | 		return rej('session not found'); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	// Generate userkey
 | 	// Generate access token
 | ||||||
| 	const key = rndstr('a-zA-Z0-9', 32); | 	const token = rndstr('a-zA-Z0-9', 32); | ||||||
| 
 | 
 | ||||||
| 	// Fetch exist userkey
 | 	// Fetch exist access token
 | ||||||
| 	const exist = await Userkey.findOne({ | 	const exist = await AccessToken.findOne({ | ||||||
| 		app_id: session.app_id, | 		app_id: session.app_id, | ||||||
| 		user_id: user._id, | 		user_id: user._id, | ||||||
| 	}); | 	}); | ||||||
| 
 | 
 | ||||||
| 	if (exist === null) { | 	if (exist === null) { | ||||||
| 		// Insert userkey doc
 | 		// Lookup app
 | ||||||
| 		await Userkey.insert({ | 		const app = await App.findOne({ | ||||||
|  | 			app_id: session.app_id | ||||||
|  | 		}); | ||||||
|  | 
 | ||||||
|  | 		// Generate Hash
 | ||||||
|  | 		const sha512 = crypto.createHash('sha512'); | ||||||
|  | 		sha512.update(token + app.secret); | ||||||
|  | 		const hash = sha512.digest('hex'); | ||||||
|  | 
 | ||||||
|  | 		// Insert access token doc
 | ||||||
|  | 		await AccessToken.insert({ | ||||||
| 			created_at: new Date(), | 			created_at: new Date(), | ||||||
| 			app_id: session.app_id, | 			app_id: session.app_id, | ||||||
| 			user_id: user._id, | 			user_id: user._id, | ||||||
| 			key: key | 			token: token, | ||||||
|  | 			hash: hash | ||||||
| 		}); | 		}); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -5,7 +5,7 @@ | ||||||
|  */ |  */ | ||||||
| import App from '../../../models/app'; | import App from '../../../models/app'; | ||||||
| import AuthSess from '../../../models/auth-session'; | import AuthSess from '../../../models/auth-session'; | ||||||
| import Userkey from '../../../models/userkey'; | import AccessToken from '../../../models/access-token'; | ||||||
| import serialize from '../../../serializers/user'; | import serialize from '../../../serializers/user'; | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  | @ -89,8 +89,8 @@ module.exports = (params) => | ||||||
| 		return rej('this session is not allowed yet'); | 		return rej('this session is not allowed yet'); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	// Lookup userkey
 | 	// Lookup access token
 | ||||||
| 	const userkey = await Userkey.findOne({ | 	const accessToken = await AccessToken.findOne({ | ||||||
| 		app_id: app._id, | 		app_id: app._id, | ||||||
| 		user_id: session.user_id | 		user_id: session.user_id | ||||||
| 	}); | 	}); | ||||||
|  | @ -102,7 +102,7 @@ module.exports = (params) => | ||||||
| 
 | 
 | ||||||
| 	// Response
 | 	// Response
 | ||||||
| 	res({ | 	res({ | ||||||
| 		userkey: userkey.key, | 		access_token: accessToken.token, | ||||||
| 		user: await serialize(session.user_id, null, { | 		user: await serialize(session.user_id, null, { | ||||||
| 			detail: true | 			detail: true | ||||||
| 		}) | 		}) | ||||||
|  |  | ||||||
|  | @ -19,9 +19,19 @@ module.exports = (params, me) => | ||||||
| 	new Promise(async (res, rej) => | 	new Promise(async (res, rej) => | ||||||
| { | { | ||||||
| 	// Get 'user_id' parameter
 | 	// Get 'user_id' parameter
 | ||||||
| 	const userId = params.user_id; | 	let userId = params.user_id; | ||||||
| 	if (userId === undefined || userId === null) { | 	if (userId === undefined || userId === null || userId === '') { | ||||||
| 		return rej('user_id is required'); | 		userId = null; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	// Get 'username' parameter
 | ||||||
|  | 	let username = params.username; | ||||||
|  | 	if (username === undefined || username === null || username === '') { | ||||||
|  | 		username = null; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if (userId === null && username === null) { | ||||||
|  | 		return rej('user_id or username is required'); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	// Get 'with_replies' parameter
 | 	// Get 'with_replies' parameter
 | ||||||
|  | @ -62,9 +72,9 @@ module.exports = (params, me) => | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	// Lookup user
 | 	// Lookup user
 | ||||||
| 	const user = await User.findOne({ | 	const user = userId !== null | ||||||
| 		_id: new mongo.ObjectID(userId) | 		? await User.findOne({ _id: new mongo.ObjectID(userId) }) | ||||||
| 	}); | 		: await User.findOne({ username_lower: username.toLowerCase() }); | ||||||
| 
 | 
 | ||||||
| 	if (user === null) { | 	if (user === null) { | ||||||
| 		return rej('user not found'); | 		return rej('user not found'); | ||||||
|  |  | ||||||
							
								
								
									
										6
									
								
								src/api/models/access-token.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								src/api/models/access-token.ts
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,6 @@ | ||||||
|  | const collection = global.db.collection('access_tokens'); | ||||||
|  | 
 | ||||||
|  | collection.createIndex('token'); | ||||||
|  | collection.createIndex('hash'); | ||||||
|  | 
 | ||||||
|  | export default collection; | ||||||
|  | @ -1,5 +0,0 @@ | ||||||
| const collection = global.db.collection('userkeys'); |  | ||||||
| 
 |  | ||||||
| collection.createIndex('key'); |  | ||||||
| 
 |  | ||||||
| export default collection; |  | ||||||
|  | @ -48,7 +48,7 @@ export default async (req: express.Request, res: express.Response) => { | ||||||
| 	const hash = bcrypt.hashSync(password, salt); | 	const hash = bcrypt.hashSync(password, salt); | ||||||
| 
 | 
 | ||||||
| 	// Generate secret
 | 	// Generate secret
 | ||||||
| 	const secret = rndstr('a-zA-Z0-9', 32); | 	const secret = '!' + rndstr('a-zA-Z0-9', 32); | ||||||
| 
 | 
 | ||||||
| 	// Create account
 | 	// Create account
 | ||||||
| 	const inserted = await User.insert({ | 	const inserted = await User.insert({ | ||||||
|  |  | ||||||
|  | @ -7,7 +7,7 @@ import * as mongo from 'mongodb'; | ||||||
| import deepcopy = require('deepcopy'); | import deepcopy = require('deepcopy'); | ||||||
| import App from '../models/app'; | import App from '../models/app'; | ||||||
| import User from '../models/user'; | import User from '../models/user'; | ||||||
| import Userkey from '../models/userkey'; | import AccessToken from '../models/access-token'; | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  * Serialize an app |  * Serialize an app | ||||||
|  | @ -71,7 +71,7 @@ export default ( | ||||||
| 
 | 
 | ||||||
| 	if (me) { | 	if (me) { | ||||||
| 		// 既に連携しているか
 | 		// 既に連携しているか
 | ||||||
| 		const exist = await Userkey.count({ | 		const exist = await AccessToken.count({ | ||||||
| 			app_id: _app.id, | 			app_id: _app.id, | ||||||
| 			user_id: me, | 			user_id: me, | ||||||
| 		}, { | 		}, { | ||||||
|  |  | ||||||
|  | @ -2,6 +2,8 @@ import * as http from 'http'; | ||||||
| import * as websocket from 'websocket'; | import * as websocket from 'websocket'; | ||||||
| import * as redis from 'redis'; | import * as redis from 'redis'; | ||||||
| import User from './models/user'; | import User from './models/user'; | ||||||
|  | import AccessToken from './models/access-token'; | ||||||
|  | import isNativeToken from './common/is-native-token'; | ||||||
| 
 | 
 | ||||||
| import homeStream from './stream/home'; | import homeStream from './stream/home'; | ||||||
| import messagingStream from './stream/messaging'; | import messagingStream from './stream/messaging'; | ||||||
|  | @ -17,7 +19,13 @@ module.exports = (server: http.Server) => { | ||||||
| 	ws.on('request', async (request) => { | 	ws.on('request', async (request) => { | ||||||
| 		const connection = request.accept(); | 		const connection = request.accept(); | ||||||
| 
 | 
 | ||||||
| 		const user = await authenticate(connection); | 		const user = await authenticate(connection, request.resourceURL.query.i); | ||||||
|  | 
 | ||||||
|  | 		if (user == null) { | ||||||
|  | 			connection.send('authentication-failed'); | ||||||
|  | 			connection.close(); | ||||||
|  | 			return; | ||||||
|  | 		} | ||||||
| 
 | 
 | ||||||
| 		// Connect to Redis
 | 		// Connect to Redis
 | ||||||
| 		const subscriber = redis.createClient( | 		const subscriber = redis.createClient( | ||||||
|  | @ -41,29 +49,36 @@ module.exports = (server: http.Server) => { | ||||||
| 	}); | 	}); | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| function authenticate(connection: websocket.connection): Promise<any> { | function authenticate(connection: websocket.connection, token: string): Promise<any> { | ||||||
| 	return new Promise((resolve, reject) => { | 	return new Promise(async (resolve, reject) => { | ||||||
| 		// Listen first message
 | 		if (isNativeToken(token)) { | ||||||
| 		connection.once('message', async (data) => { |  | ||||||
| 			const msg = JSON.parse(data.utf8Data); |  | ||||||
| 
 |  | ||||||
| 			// Fetch user
 | 			// Fetch user
 | ||||||
| 			// SELECT _id
 | 			// SELECT _id
 | ||||||
| 			const user = await User | 			const user = await User | ||||||
| 				.findOne({ | 				.findOne({ | ||||||
| 					token: msg.i | 					token: token | ||||||
| 				}, { | 				}, { | ||||||
| 					_id: true | 					_id: true | ||||||
| 				}); | 				}); | ||||||
| 
 | 
 | ||||||
| 			if (user === null) { | 			resolve(user); | ||||||
| 				connection.close(); | 		} else { | ||||||
| 				return; | 			const accessToken = await AccessToken.findOne({ | ||||||
|  | 				hash: token | ||||||
|  | 			}); | ||||||
|  | 
 | ||||||
|  | 			if (accessToken == null) { | ||||||
|  | 				return reject('invalid signature'); | ||||||
| 			} | 			} | ||||||
| 
 | 
 | ||||||
| 			connection.send('authenticated'); | 			// Fetch user
 | ||||||
|  | 			// SELECT _id
 | ||||||
|  | 			const user = await User | ||||||
|  | 				.findOne({ _id: accessToken.user_id }, { | ||||||
|  | 					_id: true | ||||||
|  | 				}); | ||||||
| 
 | 
 | ||||||
| 			resolve(user); | 			resolve(user); | ||||||
| 		}); | 		} | ||||||
| 	}); | 	}); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -39,7 +39,7 @@ try { | ||||||
| checkForUpdate(); | checkForUpdate(); | ||||||
| 
 | 
 | ||||||
| // Get token from cookie
 | // Get token from cookie
 | ||||||
| const i = (document.cookie.match(/i=(\w+)/) || [null, null])[1]; | const i = (document.cookie.match(/i=(!\w+)/) || [null, null])[1]; | ||||||
| 
 | 
 | ||||||
| // ユーザーをフェッチしてコールバックする
 | // ユーザーをフェッチしてコールバックする
 | ||||||
| module.exports = callback => { | module.exports = callback => { | ||||||
|  |  | ||||||
|  | @ -3,7 +3,7 @@ module.exports = (date) -> | ||||||
| 
 | 
 | ||||||
| 	text = | 	text = | ||||||
| 		date.get-full-year! + \年 + | 		date.get-full-year! + \年 + | ||||||
| 		date.get-month!     + \月 + | 		date.get-month! + 1 + \月 + | ||||||
| 		date.get-date!      + \日 + | 		date.get-date!      + \日 + | ||||||
| 		' ' + | 		' ' + | ||||||
| 		date.get-hours!     + \時 + | 		date.get-hours!     + \時 + | ||||||
|  |  | ||||||
|  | @ -9,7 +9,7 @@ class Connection | ||||||
| 		@event = riot.observable! | 		@event = riot.observable! | ||||||
| 		@me = me | 		@me = me | ||||||
| 		host = CONFIG.api.url.replace \http \ws | 		host = CONFIG.api.url.replace \http \ws | ||||||
| 		@socket = new ReconnectingWebSocket "#{host}/messaging?otherparty=#{otherparty}" | 		@socket = new ReconnectingWebSocket "#{host}/messaging?i=#{me.token}&otherparty=#{otherparty}" | ||||||
| 
 | 
 | ||||||
| 		@socket.add-event-listener \open @on-open | 		@socket.add-event-listener \open @on-open | ||||||
| 		@socket.add-event-listener \message @on-message | 		@socket.add-event-listener \message @on-message | ||||||
|  |  | ||||||
|  | @ -9,13 +9,12 @@ module.exports = (me) ~> | ||||||
| 	state-ev = riot.observable! | 	state-ev = riot.observable! | ||||||
| 	event = riot.observable! | 	event = riot.observable! | ||||||
| 
 | 
 | ||||||
| 	socket = new ReconnectingWebSocket CONFIG.api.url.replace \http \ws | 	host = CONFIG.api.url.replace \http \ws | ||||||
|  | 	socket = new ReconnectingWebSocket "#{host}?i=#{me.token}" | ||||||
| 
 | 
 | ||||||
| 	socket.onopen = ~> | 	socket.onopen = ~> | ||||||
| 		state := \connected | 		state := \connected | ||||||
| 		state-ev.trigger \connected | 		state-ev.trigger \connected | ||||||
| 		socket.send JSON.stringify do |  | ||||||
| 			i: me.token |  | ||||||
| 
 | 
 | ||||||
| 	socket.onclose = ~> | 	socket.onclose = ~> | ||||||
| 		state := \reconnecting | 		state := \reconnecting | ||||||
|  |  | ||||||
|  | @ -1,4 +1,5 @@ | ||||||
| const riot = require('riot'); | const riot = require('riot'); | ||||||
|  | const nyaize = require('nyaize').default; | ||||||
| 
 | 
 | ||||||
| module.exports = function(tokens, shouldBreak, escape) { | module.exports = function(tokens, shouldBreak, escape) { | ||||||
| 	if (shouldBreak == null) { | 	if (shouldBreak == null) { | ||||||
|  | @ -34,10 +35,7 @@ module.exports = function(tokens, shouldBreak, escape) { | ||||||
| 	}).join(''); | 	}).join(''); | ||||||
| 
 | 
 | ||||||
| 	if (me && me.data && me.data.nya) { | 	if (me && me.data && me.data.nya) { | ||||||
| 		text = text.replace(/な/g, 'にゃ') | 		text = nyaize(text); | ||||||
| 			.replace(/ニャ/g, 'にゃ') |  | ||||||
| 			.replace(/にゃでにゃで/g, 'なでなで') |  | ||||||
| 			.replace(/ニャデニャデ/g, 'ナデナデ'); |  | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	return text; | 	return text; | ||||||
|  |  | ||||||
|  | @ -11,7 +11,7 @@ script. | ||||||
| 
 | 
 | ||||||
| 	@absolute = | 	@absolute = | ||||||
| 		@time.get-full-year! + \年 + | 		@time.get-full-year! + \年 + | ||||||
| 		@time.get-month!     + \月 + | 		@time.get-month! + 1 + \月 + | ||||||
| 		@time.get-date!      + \日 + | 		@time.get-date!      + \日 + | ||||||
| 		' ' + | 		' ' + | ||||||
| 		@time.get-hours!     + \時 + | 		@time.get-hours!     + \時 + | ||||||
|  |  | ||||||
|  | @ -32,11 +32,6 @@ boot(me => { | ||||||
| 	// Register mixins
 | 	// Register mixins
 | ||||||
| 	mixins(me); | 	mixins(me); | ||||||
| 
 | 
 | ||||||
| 	// Debug
 |  | ||||||
| 	if (me != null && me.data.debug) { |  | ||||||
| 		riot.mount(document.body.appendChild(document.createElement('mk-log-window'))); |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	// Start routing
 | 	// Start routing
 | ||||||
| 	route(me); | 	route(me); | ||||||
| }); | }); | ||||||
|  |  | ||||||
|  | @ -99,5 +99,3 @@ require './tags/user-followers-window.tag' | ||||||
| require './tags/list-user.tag' | require './tags/list-user.tag' | ||||||
| require './tags/ui-notification.tag' | require './tags/ui-notification.tag' | ||||||
| require './tags/signin-history.tag' | require './tags/signin-history.tag' | ||||||
| require './tags/log.tag' |  | ||||||
| require './tags/log-window.tag' |  | ||||||
|  |  | ||||||
|  | @ -1,20 +0,0 @@ | ||||||
| mk-log-window |  | ||||||
| 	mk-window@window(width={ '600px' }, height={ '400px' }) |  | ||||||
| 		<yield to="header"> |  | ||||||
| 		i.fa.fa-terminal |  | ||||||
| 		| Log |  | ||||||
| 		</yield> |  | ||||||
| 		<yield to="content"> |  | ||||||
| 		mk-log |  | ||||||
| 		</yield> |  | ||||||
| 
 |  | ||||||
| style. |  | ||||||
| 	> mk-window |  | ||||||
| 		[data-yield='header'] |  | ||||||
| 			> i |  | ||||||
| 				margin-right 4px |  | ||||||
| 
 |  | ||||||
| script. |  | ||||||
| 	@on \mount ~> |  | ||||||
| 		@refs.window.on \closed ~> |  | ||||||
| 			@unmount! |  | ||||||
|  | @ -1,62 +0,0 @@ | ||||||
| mk-log |  | ||||||
| 	header |  | ||||||
| 		button.follow(class={ following: following }, onclick={ follow }) Follow |  | ||||||
| 	div.logs@logs |  | ||||||
| 		code(each={ logs }) |  | ||||||
| 			span.date { date.getHours() + ':' + date.getMinutes() + ':' + date.getSeconds() } |  | ||||||
| 			span.message { message } |  | ||||||
| 
 |  | ||||||
| style. |  | ||||||
| 	display block |  | ||||||
| 	height 100% |  | ||||||
| 	color #fff |  | ||||||
| 	background #000 |  | ||||||
| 
 |  | ||||||
| 	> header |  | ||||||
| 		height 32px |  | ||||||
| 		background #343a42 |  | ||||||
| 
 |  | ||||||
| 		> button |  | ||||||
| 			line-height 32px |  | ||||||
| 
 |  | ||||||
| 		> .follow |  | ||||||
| 			position absolute |  | ||||||
| 			top 0 |  | ||||||
| 			right 0 |  | ||||||
| 
 |  | ||||||
| 			&.following |  | ||||||
| 				color #ff0 |  | ||||||
| 
 |  | ||||||
| 	> .logs |  | ||||||
| 		height calc(100% - 32px) |  | ||||||
| 		overflow auto |  | ||||||
| 
 |  | ||||||
| 		> code |  | ||||||
| 			display block |  | ||||||
| 			padding 4px 8px |  | ||||||
| 
 |  | ||||||
| 			&:hover |  | ||||||
| 				background rgba(#fff, 0.15) |  | ||||||
| 
 |  | ||||||
| 			> .date |  | ||||||
| 				margin-right 8px |  | ||||||
| 				opacity 0.5 |  | ||||||
| 
 |  | ||||||
| script. |  | ||||||
| 	@mixin \log |  | ||||||
| 
 |  | ||||||
| 	@following = true |  | ||||||
| 
 |  | ||||||
| 	@on \mount ~> |  | ||||||
| 		@log-event.on \log @on-log |  | ||||||
| 
 |  | ||||||
| 	@on \unmount ~> |  | ||||||
| 		@log-event.off \log @on-log |  | ||||||
| 
 |  | ||||||
| 	@follow = ~> |  | ||||||
| 		@following = true |  | ||||||
| 
 |  | ||||||
| 	@on-log = ~> |  | ||||||
| 		@update! |  | ||||||
| 		if @following |  | ||||||
| 			@refs.logs.scroll-top = @refs.logs.scroll-height |  | ||||||
|  | @ -3,11 +3,10 @@ mk-user-preview | ||||||
| 		img.avatar(src={ user.avatar_url + '?thumbnail&size=64' }, alt='avatar') | 		img.avatar(src={ user.avatar_url + '?thumbnail&size=64' }, alt='avatar') | ||||||
| 	div.main | 	div.main | ||||||
| 		header | 		header | ||||||
| 			div.left | 			a.name(href={ CONFIG.url + '/' + user.username }) | ||||||
| 				a.name(href={ CONFIG.url + '/' + user.username }) | 				| { user.name } | ||||||
| 					| { user.name } | 			span.username | ||||||
| 				span.username | 				| @{ user.username } | ||||||
| 					| @{ user.username } |  | ||||||
| 		div.body | 		div.body | ||||||
| 			div.bio { user.bio } | 			div.bio { user.bio } | ||||||
| 
 | 
 | ||||||
|  | @ -57,36 +56,26 @@ style. | ||||||
| 			width calc(100% - 74px) | 			width calc(100% - 74px) | ||||||
| 
 | 
 | ||||||
| 		> header | 		> header | ||||||
| 			white-space nowrap |  | ||||||
| 
 |  | ||||||
| 			@media (min-width 500px) | 			@media (min-width 500px) | ||||||
| 				margin-bottom 2px | 				margin-bottom 2px | ||||||
| 
 | 
 | ||||||
| 			&:after | 			> .name | ||||||
| 				content "" | 				display inline | ||||||
| 				display block | 				margin 0 | ||||||
| 				clear both | 				padding 0 | ||||||
|  | 				color #777 | ||||||
|  | 				font-size 1em | ||||||
|  | 				font-weight 700 | ||||||
|  | 				text-align left | ||||||
|  | 				text-decoration none | ||||||
| 
 | 
 | ||||||
| 			> .left | 				&:hover | ||||||
| 				float left | 					text-decoration underline | ||||||
| 
 | 
 | ||||||
| 				> .name | 			> .username | ||||||
| 					display inline | 				text-align left | ||||||
| 					margin 0 | 				margin 0 0 0 8px | ||||||
| 					padding 0 | 				color #ccc | ||||||
| 					color #777 |  | ||||||
| 					font-size 1em |  | ||||||
| 					font-weight 700 |  | ||||||
| 					text-align left |  | ||||||
| 					text-decoration none |  | ||||||
| 
 |  | ||||||
| 					&:hover |  | ||||||
| 						text-decoration underline |  | ||||||
| 
 |  | ||||||
| 				> .username |  | ||||||
| 					text-align left |  | ||||||
| 					margin 0 0 0 8px |  | ||||||
| 					color #ccc |  | ||||||
| 
 | 
 | ||||||
| 		> .body | 		> .body | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue