Merge branch 'master' of https://github.com/syuilo/misskey
This commit is contained in:
		
						commit
						27183b2142
					
				
					 16 changed files with 325 additions and 1 deletions
				
			
		
							
								
								
									
										1
									
								
								.gitignore
									
										
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								.gitignore
									
										
									
									
										vendored
									
									
								
							| 
						 | 
					@ -1,6 +1,7 @@
 | 
				
			||||||
/.config
 | 
					/.config
 | 
				
			||||||
/.vscode
 | 
					/.vscode
 | 
				
			||||||
/node_modules
 | 
					/node_modules
 | 
				
			||||||
 | 
					/build
 | 
				
			||||||
/built
 | 
					/built
 | 
				
			||||||
/data
 | 
					/data
 | 
				
			||||||
npm-debug.log
 | 
					npm-debug.log
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										9
									
								
								binding.gyp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								binding.gyp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,9 @@
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						'targets': [
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								'target_name': 'crypto_key',
 | 
				
			||||||
 | 
								'sources': ['src/crypto_key.cc'],
 | 
				
			||||||
 | 
								'include_dirs': ['<!(node -e "require(\'nan\')")']
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						]
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -69,6 +69,7 @@ gulp.task('build:ts', () => {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
gulp.task('build:copy', () =>
 | 
					gulp.task('build:copy', () =>
 | 
				
			||||||
	gulp.src([
 | 
						gulp.src([
 | 
				
			||||||
 | 
							'./build/Release/crypto_key.node',
 | 
				
			||||||
		'./src/**/assets/**/*',
 | 
							'./src/**/assets/**/*',
 | 
				
			||||||
		'!./src/web/app/**/assets/**/*'
 | 
							'!./src/web/app/**/assets/**/*'
 | 
				
			||||||
	]).pipe(gulp.dest('./built/'))
 | 
						]).pipe(gulp.dest('./built/'))
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -145,6 +145,7 @@
 | 
				
			||||||
		"morgan": "1.9.0",
 | 
							"morgan": "1.9.0",
 | 
				
			||||||
		"ms": "2.1.1",
 | 
							"ms": "2.1.1",
 | 
				
			||||||
		"multer": "1.3.0",
 | 
							"multer": "1.3.0",
 | 
				
			||||||
 | 
							"nan": "^2.10.0",
 | 
				
			||||||
		"node-sass": "4.7.2",
 | 
							"node-sass": "4.7.2",
 | 
				
			||||||
		"node-sass-json-importer": "3.1.5",
 | 
							"node-sass-json-importer": "3.1.5",
 | 
				
			||||||
		"nprogress": "0.2.0",
 | 
							"nprogress": "0.2.0",
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -59,6 +59,7 @@ export type IUser = {
 | 
				
			||||||
	is_suspended: boolean;
 | 
						is_suspended: boolean;
 | 
				
			||||||
	keywords: string[];
 | 
						keywords: string[];
 | 
				
			||||||
	account: {
 | 
						account: {
 | 
				
			||||||
 | 
							keypair: string;
 | 
				
			||||||
		email: string;
 | 
							email: string;
 | 
				
			||||||
		links: string[];
 | 
							links: string[];
 | 
				
			||||||
		password: string;
 | 
							password: string;
 | 
				
			||||||
| 
						 | 
					@ -160,6 +161,7 @@ export const pack = (
 | 
				
			||||||
	delete _user.latest_post;
 | 
						delete _user.latest_post;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Remove private properties
 | 
						// Remove private properties
 | 
				
			||||||
 | 
						delete _user.account.keypair;
 | 
				
			||||||
	delete _user.account.password;
 | 
						delete _user.account.password;
 | 
				
			||||||
	delete _user.account.token;
 | 
						delete _user.account.token;
 | 
				
			||||||
	delete _user.account.two_factor_temp_secret;
 | 
						delete _user.account.two_factor_temp_secret;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,6 +1,7 @@
 | 
				
			||||||
import * as uuid from 'uuid';
 | 
					import * as uuid from 'uuid';
 | 
				
			||||||
import * as express from 'express';
 | 
					import * as express from 'express';
 | 
				
			||||||
import * as bcrypt from 'bcryptjs';
 | 
					import * as bcrypt from 'bcryptjs';
 | 
				
			||||||
 | 
					import { generate as generateKeypair } from '../../crypto_key';
 | 
				
			||||||
import recaptcha = require('recaptcha-promise');
 | 
					import recaptcha = require('recaptcha-promise');
 | 
				
			||||||
import User, { IUser, validateUsername, validatePassword, pack } from '../models/user';
 | 
					import User, { IUser, validateUsername, validatePassword, pack } from '../models/user';
 | 
				
			||||||
import generateUserToken from '../common/generate-native-user-token';
 | 
					import generateUserToken from '../common/generate-native-user-token';
 | 
				
			||||||
| 
						 | 
					@ -119,6 +120,7 @@ export default async (req: express.Request, res: express.Response) => {
 | 
				
			||||||
		username: username,
 | 
							username: username,
 | 
				
			||||||
		username_lower: username.toLowerCase(),
 | 
							username_lower: username.toLowerCase(),
 | 
				
			||||||
		account: {
 | 
							account: {
 | 
				
			||||||
 | 
								keypair: generateKeypair(),
 | 
				
			||||||
			token: secret,
 | 
								token: secret,
 | 
				
			||||||
			email: null,
 | 
								email: null,
 | 
				
			||||||
			links: null,
 | 
								links: null,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										111
									
								
								src/crypto_key.cc
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										111
									
								
								src/crypto_key.cc
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,111 @@
 | 
				
			||||||
 | 
					#include <nan.h>
 | 
				
			||||||
 | 
					#include <openssl/bio.h>
 | 
				
			||||||
 | 
					#include <openssl/buffer.h>
 | 
				
			||||||
 | 
					#include <openssl/crypto.h>
 | 
				
			||||||
 | 
					#include <openssl/pem.h>
 | 
				
			||||||
 | 
					#include <openssl/rsa.h>
 | 
				
			||||||
 | 
					#include <openssl/x509.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					NAN_METHOD(extractPublic)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						const auto sourceString = info[0]->ToString();
 | 
				
			||||||
 | 
						if (!sourceString->IsOneByte()) {
 | 
				
			||||||
 | 
							Nan::ThrowError("Malformed character found");
 | 
				
			||||||
 | 
							return;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						size_t sourceLength = sourceString->Length();
 | 
				
			||||||
 | 
						const auto sourceBuf = new char[sourceLength];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						Nan::DecodeWrite(sourceBuf, sourceLength, sourceString);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						const auto source = BIO_new_mem_buf(sourceBuf, sourceLength);
 | 
				
			||||||
 | 
						if (source == nullptr) {
 | 
				
			||||||
 | 
							Nan::ThrowError("Memory allocation failed");
 | 
				
			||||||
 | 
							delete sourceBuf;
 | 
				
			||||||
 | 
							return;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						const auto rsa = PEM_read_bio_RSAPrivateKey(source, nullptr, nullptr, nullptr);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						BIO_free(source);
 | 
				
			||||||
 | 
						delete sourceBuf;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (rsa == nullptr) {
 | 
				
			||||||
 | 
							Nan::ThrowError("Decode failed");
 | 
				
			||||||
 | 
							return;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						const auto destination = BIO_new(BIO_s_mem());
 | 
				
			||||||
 | 
						if (destination == nullptr) {
 | 
				
			||||||
 | 
							Nan::ThrowError("Memory allocation failed");
 | 
				
			||||||
 | 
							return;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						const auto result = PEM_write_bio_RSAPublicKey(destination, rsa);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						RSA_free(rsa);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (result != 1) {
 | 
				
			||||||
 | 
							Nan::ThrowError("Public key extraction failed");
 | 
				
			||||||
 | 
							BIO_free(destination);
 | 
				
			||||||
 | 
							return;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						char *pem;
 | 
				
			||||||
 | 
						const auto pemLength = BIO_get_mem_data(destination, &pem);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						info.GetReturnValue().Set(Nan::Encode(pem, pemLength));
 | 
				
			||||||
 | 
						BIO_free(destination);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					NAN_METHOD(generate)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						const auto exponent = BN_new();
 | 
				
			||||||
 | 
						const auto mem = BIO_new(BIO_s_mem());
 | 
				
			||||||
 | 
						const auto rsa = RSA_new();
 | 
				
			||||||
 | 
						char *data;
 | 
				
			||||||
 | 
						long result;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (exponent == nullptr || mem == nullptr || rsa == nullptr) {
 | 
				
			||||||
 | 
							Nan::ThrowError("Memory allocation failed");
 | 
				
			||||||
 | 
							goto done;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						result = BN_set_word(exponent, 65537);
 | 
				
			||||||
 | 
						if (result != 1) {
 | 
				
			||||||
 | 
							Nan::ThrowError("Exponent setting failed");
 | 
				
			||||||
 | 
							goto done;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						result = RSA_generate_key_ex(rsa, 2048, exponent, nullptr);
 | 
				
			||||||
 | 
						if (result != 1) {
 | 
				
			||||||
 | 
							Nan::ThrowError("Key generation failed");
 | 
				
			||||||
 | 
							goto done;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						result = PEM_write_bio_RSAPrivateKey(mem, rsa, NULL, NULL, 0, NULL, NULL);
 | 
				
			||||||
 | 
						if (result != 1) {
 | 
				
			||||||
 | 
							Nan::ThrowError("Key export failed");
 | 
				
			||||||
 | 
							goto done;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						result = BIO_get_mem_data(mem, &data);
 | 
				
			||||||
 | 
						info.GetReturnValue().Set(Nan::Encode(data, result));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					done:
 | 
				
			||||||
 | 
						RSA_free(rsa);
 | 
				
			||||||
 | 
						BIO_free(mem);
 | 
				
			||||||
 | 
						BN_free(exponent);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					NAN_MODULE_INIT(InitAll)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						Nan::Set(target, Nan::New<v8::String>("extractPublic").ToLocalChecked(),
 | 
				
			||||||
 | 
							Nan::GetFunction(Nan::New<v8::FunctionTemplate>(extractPublic)).ToLocalChecked());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						Nan::Set(target, Nan::New<v8::String>("generate").ToLocalChecked(),
 | 
				
			||||||
 | 
							Nan::GetFunction(Nan::New<v8::FunctionTemplate>(generate)).ToLocalChecked());
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					NODE_MODULE(crypto_key, InitAll);
 | 
				
			||||||
							
								
								
									
										1
									
								
								src/crypto_key.d.ts
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								src/crypto_key.d.ts
									
										
									
									
										vendored
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1 @@
 | 
				
			||||||
 | 
					export function generate(): String;
 | 
				
			||||||
| 
						 | 
					@ -1,7 +1,8 @@
 | 
				
			||||||
<template>
 | 
					<template>
 | 
				
			||||||
<div class="mk-media-list" :data-count="mediaList.length">
 | 
					<div class="mk-media-list" :data-count="mediaList.length">
 | 
				
			||||||
	<template v-for="media in mediaList">
 | 
						<template v-for="media in mediaList">
 | 
				
			||||||
		<mk-media-image :image="media" :key="media.id"/>
 | 
							<mk-media-video :video="media" :key="media.id" v-if="media.type.startsWith('video')" :inline-playable="mediaList.length === 1"/>
 | 
				
			||||||
 | 
							<mk-media-image :image="media" :key="media.id" v-else />
 | 
				
			||||||
	</template>
 | 
						</template>
 | 
				
			||||||
</div>
 | 
					</div>
 | 
				
			||||||
</template>
 | 
					</template>
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -13,6 +13,7 @@ import analogClock from './analog-clock.vue';
 | 
				
			||||||
import ellipsisIcon from './ellipsis-icon.vue';
 | 
					import ellipsisIcon from './ellipsis-icon.vue';
 | 
				
			||||||
import mediaImage from './media-image.vue';
 | 
					import mediaImage from './media-image.vue';
 | 
				
			||||||
import mediaImageDialog from './media-image-dialog.vue';
 | 
					import mediaImageDialog from './media-image-dialog.vue';
 | 
				
			||||||
 | 
					import mediaVideo from './media-video.vue';
 | 
				
			||||||
import notifications from './notifications.vue';
 | 
					import notifications from './notifications.vue';
 | 
				
			||||||
import postForm from './post-form.vue';
 | 
					import postForm from './post-form.vue';
 | 
				
			||||||
import repostForm from './repost-form.vue';
 | 
					import repostForm from './repost-form.vue';
 | 
				
			||||||
| 
						 | 
					@ -42,6 +43,7 @@ Vue.component('mk-analog-clock', analogClock);
 | 
				
			||||||
Vue.component('mk-ellipsis-icon', ellipsisIcon);
 | 
					Vue.component('mk-ellipsis-icon', ellipsisIcon);
 | 
				
			||||||
Vue.component('mk-media-image', mediaImage);
 | 
					Vue.component('mk-media-image', mediaImage);
 | 
				
			||||||
Vue.component('mk-media-image-dialog', mediaImageDialog);
 | 
					Vue.component('mk-media-image-dialog', mediaImageDialog);
 | 
				
			||||||
 | 
					Vue.component('mk-media-video', mediaVideo);
 | 
				
			||||||
Vue.component('mk-notifications', notifications);
 | 
					Vue.component('mk-notifications', notifications);
 | 
				
			||||||
Vue.component('mk-post-form', postForm);
 | 
					Vue.component('mk-post-form', postForm);
 | 
				
			||||||
Vue.component('mk-repost-form', repostForm);
 | 
					Vue.component('mk-repost-form', repostForm);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										70
									
								
								src/web/app/desktop/views/components/media-video-dialog.vue
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										70
									
								
								src/web/app/desktop/views/components/media-video-dialog.vue
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,70 @@
 | 
				
			||||||
 | 
					<template>
 | 
				
			||||||
 | 
					<div class="mk-media-video-dialog">
 | 
				
			||||||
 | 
						<div class="bg" @click="close"></div>
 | 
				
			||||||
 | 
						<video :src="video.url" :title="video.name" controls autoplay ref="video"/>
 | 
				
			||||||
 | 
					</div>
 | 
				
			||||||
 | 
					</template>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<script lang="ts">
 | 
				
			||||||
 | 
					import Vue from 'vue';
 | 
				
			||||||
 | 
					import * as anime from 'animejs';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default Vue.extend({
 | 
				
			||||||
 | 
						props: ['video', 'start'],
 | 
				
			||||||
 | 
						mounted() {
 | 
				
			||||||
 | 
							anime({
 | 
				
			||||||
 | 
								targets: this.$el,
 | 
				
			||||||
 | 
								opacity: 1,
 | 
				
			||||||
 | 
								duration: 100,
 | 
				
			||||||
 | 
								easing: 'linear'
 | 
				
			||||||
 | 
							});
 | 
				
			||||||
 | 
							const videoTag = this.$refs.video as HTMLVideoElement
 | 
				
			||||||
 | 
							if (this.start) videoTag.currentTime = this.start
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
						methods: {
 | 
				
			||||||
 | 
							close() {
 | 
				
			||||||
 | 
								anime({
 | 
				
			||||||
 | 
									targets: this.$el,
 | 
				
			||||||
 | 
									opacity: 0,
 | 
				
			||||||
 | 
									duration: 100,
 | 
				
			||||||
 | 
									easing: 'linear',
 | 
				
			||||||
 | 
									complete: () => this.$destroy()
 | 
				
			||||||
 | 
								});
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					</script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<style lang="stylus" scoped>
 | 
				
			||||||
 | 
					.mk-media-video-dialog
 | 
				
			||||||
 | 
						display block
 | 
				
			||||||
 | 
						position fixed
 | 
				
			||||||
 | 
						z-index 2048
 | 
				
			||||||
 | 
						top 0
 | 
				
			||||||
 | 
						left 0
 | 
				
			||||||
 | 
						width 100%
 | 
				
			||||||
 | 
						height 100%
 | 
				
			||||||
 | 
						opacity 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						> .bg
 | 
				
			||||||
 | 
							display block
 | 
				
			||||||
 | 
							position fixed
 | 
				
			||||||
 | 
							z-index 1
 | 
				
			||||||
 | 
							top 0
 | 
				
			||||||
 | 
							left 0
 | 
				
			||||||
 | 
							width 100%
 | 
				
			||||||
 | 
							height 100%
 | 
				
			||||||
 | 
							background rgba(0, 0, 0, 0.7)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						> video
 | 
				
			||||||
 | 
							position fixed
 | 
				
			||||||
 | 
							z-index 2
 | 
				
			||||||
 | 
							top 0
 | 
				
			||||||
 | 
							right 0
 | 
				
			||||||
 | 
							bottom 0
 | 
				
			||||||
 | 
							left 0
 | 
				
			||||||
 | 
							max-width 80vw
 | 
				
			||||||
 | 
							max-height 80vh
 | 
				
			||||||
 | 
							margin auto
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					</style>
 | 
				
			||||||
							
								
								
									
										67
									
								
								src/web/app/desktop/views/components/media-video.vue
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										67
									
								
								src/web/app/desktop/views/components/media-video.vue
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,67 @@
 | 
				
			||||||
 | 
					<template>
 | 
				
			||||||
 | 
						<video class="mk-media-video"
 | 
				
			||||||
 | 
							:src="video.url"
 | 
				
			||||||
 | 
							:title="video.name"
 | 
				
			||||||
 | 
							controls
 | 
				
			||||||
 | 
							@dblclick.prevent="onClick"
 | 
				
			||||||
 | 
							ref="video"
 | 
				
			||||||
 | 
							v-if="inlinePlayable" />
 | 
				
			||||||
 | 
						<a class="mk-media-video-thumbnail"
 | 
				
			||||||
 | 
							:href="video.url"
 | 
				
			||||||
 | 
							:style="imageStyle"
 | 
				
			||||||
 | 
							@click.prevent="onClick"
 | 
				
			||||||
 | 
							:title="video.name"
 | 
				
			||||||
 | 
							v-else>
 | 
				
			||||||
 | 
							%fa:R play-circle%
 | 
				
			||||||
 | 
						</a>
 | 
				
			||||||
 | 
					</template>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<script lang="ts">
 | 
				
			||||||
 | 
					import Vue from 'vue';
 | 
				
			||||||
 | 
					import MkMediaVideoDialog from './media-video-dialog.vue';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default Vue.extend({
 | 
				
			||||||
 | 
						props: ['video', 'inlinePlayable'],
 | 
				
			||||||
 | 
						computed: {
 | 
				
			||||||
 | 
							imageStyle(): any {
 | 
				
			||||||
 | 
								return {
 | 
				
			||||||
 | 
									'background-image': `url(${this.video.url}?thumbnail&size=512)`
 | 
				
			||||||
 | 
								};
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
						methods: {
 | 
				
			||||||
 | 
							onClick() {
 | 
				
			||||||
 | 
								const videoTag = this.$refs.video as (HTMLVideoElement | null)
 | 
				
			||||||
 | 
								var start = 0
 | 
				
			||||||
 | 
								if (videoTag) {
 | 
				
			||||||
 | 
									start = videoTag.currentTime
 | 
				
			||||||
 | 
									videoTag.pause()
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								(this as any).os.new(MkMediaVideoDialog, {
 | 
				
			||||||
 | 
									video: this.video,
 | 
				
			||||||
 | 
									start,
 | 
				
			||||||
 | 
								})
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					})
 | 
				
			||||||
 | 
					</script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<style lang="stylus" scoped>
 | 
				
			||||||
 | 
					.mk-media-video
 | 
				
			||||||
 | 
						display block
 | 
				
			||||||
 | 
						width 100%
 | 
				
			||||||
 | 
						height 100%
 | 
				
			||||||
 | 
						border-radius 4px
 | 
				
			||||||
 | 
					.mk-media-video-thumbnail
 | 
				
			||||||
 | 
						display flex
 | 
				
			||||||
 | 
						justify-content center
 | 
				
			||||||
 | 
						align-items center
 | 
				
			||||||
 | 
						font-size 3.5em
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						cursor zoom-in
 | 
				
			||||||
 | 
						overflow hidden
 | 
				
			||||||
 | 
						background-position center
 | 
				
			||||||
 | 
						background-size cover
 | 
				
			||||||
 | 
						width 100%
 | 
				
			||||||
 | 
						height 100%
 | 
				
			||||||
 | 
					</style>
 | 
				
			||||||
| 
						 | 
					@ -5,6 +5,7 @@ import timeline from './timeline.vue';
 | 
				
			||||||
import post from './post.vue';
 | 
					import post from './post.vue';
 | 
				
			||||||
import posts from './posts.vue';
 | 
					import posts from './posts.vue';
 | 
				
			||||||
import mediaImage from './media-image.vue';
 | 
					import mediaImage from './media-image.vue';
 | 
				
			||||||
 | 
					import mediaVideo from './media-video.vue';
 | 
				
			||||||
import drive from './drive.vue';
 | 
					import drive from './drive.vue';
 | 
				
			||||||
import postPreview from './post-preview.vue';
 | 
					import postPreview from './post-preview.vue';
 | 
				
			||||||
import subPostContent from './sub-post-content.vue';
 | 
					import subPostContent from './sub-post-content.vue';
 | 
				
			||||||
| 
						 | 
					@ -27,6 +28,7 @@ Vue.component('mk-timeline', timeline);
 | 
				
			||||||
Vue.component('mk-post', post);
 | 
					Vue.component('mk-post', post);
 | 
				
			||||||
Vue.component('mk-posts', posts);
 | 
					Vue.component('mk-posts', posts);
 | 
				
			||||||
Vue.component('mk-media-image', mediaImage);
 | 
					Vue.component('mk-media-image', mediaImage);
 | 
				
			||||||
 | 
					Vue.component('mk-media-video', mediaVideo);
 | 
				
			||||||
Vue.component('mk-drive', drive);
 | 
					Vue.component('mk-drive', drive);
 | 
				
			||||||
Vue.component('mk-post-preview', postPreview);
 | 
					Vue.component('mk-post-preview', postPreview);
 | 
				
			||||||
Vue.component('mk-sub-post-content', subPostContent);
 | 
					Vue.component('mk-sub-post-content', subPostContent);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										36
									
								
								src/web/app/mobile/views/components/media-video.vue
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								src/web/app/mobile/views/components/media-video.vue
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,36 @@
 | 
				
			||||||
 | 
					<template>
 | 
				
			||||||
 | 
						<a class="mk-media-video"
 | 
				
			||||||
 | 
							:href="video.url"
 | 
				
			||||||
 | 
							target="_blank"
 | 
				
			||||||
 | 
							:style="imageStyle"
 | 
				
			||||||
 | 
							:title="video.name">
 | 
				
			||||||
 | 
							%fa:R play-circle%
 | 
				
			||||||
 | 
						</a>
 | 
				
			||||||
 | 
					</template>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<script lang="ts">
 | 
				
			||||||
 | 
					import Vue from 'vue'
 | 
				
			||||||
 | 
					export default Vue.extend({
 | 
				
			||||||
 | 
						props: ['video'],
 | 
				
			||||||
 | 
						computed: {
 | 
				
			||||||
 | 
							imageStyle(): any {
 | 
				
			||||||
 | 
								return {
 | 
				
			||||||
 | 
									'background-image': `url(${this.video.url}?thumbnail&size=512)`
 | 
				
			||||||
 | 
								};
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						},})
 | 
				
			||||||
 | 
					</script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<style lang="stylus" scoped>
 | 
				
			||||||
 | 
					.mk-media-video
 | 
				
			||||||
 | 
						display flex
 | 
				
			||||||
 | 
						justify-content center
 | 
				
			||||||
 | 
						align-items center
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						font-size 3.5em
 | 
				
			||||||
 | 
						overflow hidden
 | 
				
			||||||
 | 
						background-position center
 | 
				
			||||||
 | 
						background-size cover
 | 
				
			||||||
 | 
						width 100%
 | 
				
			||||||
 | 
						height 100%
 | 
				
			||||||
 | 
					</style>
 | 
				
			||||||
| 
						 | 
					@ -1161,6 +1161,7 @@ function insertSakurako(opts) {
 | 
				
			||||||
		username: 'sakurako',
 | 
							username: 'sakurako',
 | 
				
			||||||
		username_lower: 'sakurako',
 | 
							username_lower: 'sakurako',
 | 
				
			||||||
		account: {
 | 
							account: {
 | 
				
			||||||
 | 
								keypair: '-----BEGIN RSA PRIVATE KEY-----\nMIIEowIBAAKCAQEAtdTG9rlFWjNqhgbg2V6X5XF1WpQXZS3KNXykEWl2UAiMyfVV\nBvf3zQP0dDEdNtcqdPJgis03bpiHCzQusc/YLyHYB0m+TJXsxJatb8cqUogOFeE4\ngQ4Dc5kAT6gLh/d4yz03EIg9bizX07EiGWnZqWxb+21ypqsPxST64sAtG9f5O/G4\nXe2m3cSbfAAvEUP1Ig1LUNyJB4jhM60w1cQic/qO8++sk/+GoX9g71X+i4NArGv+\n1c11acDIIPGAAQpFeYVeGaKakNDNp8RtJJp8R8FLwJXZ4/gATBnScCiHUSrGfRly\nYyR0w/BNlQ6/NijAdB9pR5csPvyIPkx1gauZewIDAQABAoIBAQCwWf/mhuY2h6uG\n9eDZsZ7Mj2/sO7k9Dl4R5iMSKCDxmnlB3slqitExa+aJUqEs8R5icjkkJcjfYNuJ\nCEFJf3YCsGZfGyyQBtCuEh2ATcBEb2SJ3/f3YuoCEaB1oVwdsOzc4TAovpol4yQo\nUqHp1/mdElVb01jhQQN4h1c02IJnfzvfU1C8szBni+Etfd+MxqGfv006DY3KOEb3\nlCrCS3GmooJW2Fjj7q1kCcaEQbMB1/aQHLXd1qe3KJOzXh3Voxsp/jEH0hvp2TII\nfY9UK+b7mA+xlvXwKuTkHVaZm0ylg0nbembS8MF4GfFMujinSexvLrVKaQhdMFoF\nvBLxHYHRAoGBANfNVYJYeCDPFNLmak5Xg33Rfvc2II8UmrZOVdhOWs8ZK0pis9e+\nPo2MKtTzrzipXI2QXv5w7kO+LJWNDva+xRlW8Wlj9Dde9QdQ7Y8+dk7SJgf24DzM\n023elgX5DvTeLODjStk6SMPRL0FmGovUqAAA8ZeHtJzkIr1HROWnQiwnAoGBANez\nhFwKnVoQu0RpBz/i4W0RKIxOwltN2zmlN8KjJPhSy00A7nBUfKLRbcwiSHE98Yi/\nUrXwMwR5QeD2ngngRppddJnpiRfjNjnsaqeqNtpO8AxB3XjpCC5zmHUMFHKvPpDj\n1zU/F44li0YjKcMBebZy9PbfAjrIgJfxhPo/oXiNAoGAfx6gaTjOAp2ZaaZ7Jozc\nkyft/5et1DrR6+P3I4T8bxQncRj1UXfqhxzzOiAVrm3tbCKIIp/JarRCtRGzp9u2\nZPfXGzra6CcSdW3Rkli7/jBCYNynOIl7XjQI8ZnFmq6phwu80ntH07mMeZy4tHff\nQqlLpvQ0i1rDr/Wkexdsnm8CgYBgxha9ILoF/Xm3MJPjEsxmnYsen/tM8XpIu5pv\nxbhBfQvfKWrQlOcyOVnUexEbVVo3KvdVz0VkXW60GpE/BxNGEGXO49rxD6x1gl87\nh/+CJGZIaYiOxaY5CP2+jcPizEL6yG32Yq8TxD5fIkmLRu8vbxX+aIFclDY1dVNe\n3wt3xQKBgGEL0EjwRch+P2V+YHAhbETPrEqJjHRWT95pIdF9XtC8fasSOVH81cLX\nXXsX1FTvOJNwG9Nk8rQjYJXGTb2O/2unaazlYUwxKwVpwuGzz/vhH/roHZBAkIVT\njvpykpn9QMezEdpzj5BEv01QzSYBPzIh5myrpoJIoSW7py7zFG3h\n-----END RSA PRIVATE KEY-----\n',
 | 
				
			||||||
			token: '!00000000000000000000000000000000',
 | 
								token: '!00000000000000000000000000000000',
 | 
				
			||||||
			password: '$2a$08$FnHXg3tP.M/kINWgQSXNqeoBsiVrkj.ecXX8mW9rfBzMRkibYfjYy', // HimawariDaisuki06160907
 | 
								password: '$2a$08$FnHXg3tP.M/kINWgQSXNqeoBsiVrkj.ecXX8mW9rfBzMRkibYfjYy', // HimawariDaisuki06160907
 | 
				
			||||||
			profile: {},
 | 
								profile: {},
 | 
				
			||||||
| 
						 | 
					@ -1175,6 +1176,7 @@ function insertHimawari(opts) {
 | 
				
			||||||
		username: 'himawari',
 | 
							username: 'himawari',
 | 
				
			||||||
		username_lower: 'himawari',
 | 
							username_lower: 'himawari',
 | 
				
			||||||
		account: {
 | 
							account: {
 | 
				
			||||||
 | 
								keypair: '-----BEGIN RSA PRIVATE KEY-----\nMIIEowIBAAKCAQEAtdTG9rlFWjNqhgbg2V6X5XF1WpQXZS3KNXykEWl2UAiMyfVV\nBvf3zQP0dDEdNtcqdPJgis03bpiHCzQusc/YLyHYB0m+TJXsxJatb8cqUogOFeE4\ngQ4Dc5kAT6gLh/d4yz03EIg9bizX07EiGWnZqWxb+21ypqsPxST64sAtG9f5O/G4\nXe2m3cSbfAAvEUP1Ig1LUNyJB4jhM60w1cQic/qO8++sk/+GoX9g71X+i4NArGv+\n1c11acDIIPGAAQpFeYVeGaKakNDNp8RtJJp8R8FLwJXZ4/gATBnScCiHUSrGfRly\nYyR0w/BNlQ6/NijAdB9pR5csPvyIPkx1gauZewIDAQABAoIBAQCwWf/mhuY2h6uG\n9eDZsZ7Mj2/sO7k9Dl4R5iMSKCDxmnlB3slqitExa+aJUqEs8R5icjkkJcjfYNuJ\nCEFJf3YCsGZfGyyQBtCuEh2ATcBEb2SJ3/f3YuoCEaB1oVwdsOzc4TAovpol4yQo\nUqHp1/mdElVb01jhQQN4h1c02IJnfzvfU1C8szBni+Etfd+MxqGfv006DY3KOEb3\nlCrCS3GmooJW2Fjj7q1kCcaEQbMB1/aQHLXd1qe3KJOzXh3Voxsp/jEH0hvp2TII\nfY9UK+b7mA+xlvXwKuTkHVaZm0ylg0nbembS8MF4GfFMujinSexvLrVKaQhdMFoF\nvBLxHYHRAoGBANfNVYJYeCDPFNLmak5Xg33Rfvc2II8UmrZOVdhOWs8ZK0pis9e+\nPo2MKtTzrzipXI2QXv5w7kO+LJWNDva+xRlW8Wlj9Dde9QdQ7Y8+dk7SJgf24DzM\n023elgX5DvTeLODjStk6SMPRL0FmGovUqAAA8ZeHtJzkIr1HROWnQiwnAoGBANez\nhFwKnVoQu0RpBz/i4W0RKIxOwltN2zmlN8KjJPhSy00A7nBUfKLRbcwiSHE98Yi/\nUrXwMwR5QeD2ngngRppddJnpiRfjNjnsaqeqNtpO8AxB3XjpCC5zmHUMFHKvPpDj\n1zU/F44li0YjKcMBebZy9PbfAjrIgJfxhPo/oXiNAoGAfx6gaTjOAp2ZaaZ7Jozc\nkyft/5et1DrR6+P3I4T8bxQncRj1UXfqhxzzOiAVrm3tbCKIIp/JarRCtRGzp9u2\nZPfXGzra6CcSdW3Rkli7/jBCYNynOIl7XjQI8ZnFmq6phwu80ntH07mMeZy4tHff\nQqlLpvQ0i1rDr/Wkexdsnm8CgYBgxha9ILoF/Xm3MJPjEsxmnYsen/tM8XpIu5pv\nxbhBfQvfKWrQlOcyOVnUexEbVVo3KvdVz0VkXW60GpE/BxNGEGXO49rxD6x1gl87\nh/+CJGZIaYiOxaY5CP2+jcPizEL6yG32Yq8TxD5fIkmLRu8vbxX+aIFclDY1dVNe\n3wt3xQKBgGEL0EjwRch+P2V+YHAhbETPrEqJjHRWT95pIdF9XtC8fasSOVH81cLX\nXXsX1FTvOJNwG9Nk8rQjYJXGTb2O/2unaazlYUwxKwVpwuGzz/vhH/roHZBAkIVT\njvpykpn9QMezEdpzj5BEv01QzSYBPzIh5myrpoJIoSW7py7zFG3h\n-----END RSA PRIVATE KEY-----\n',
 | 
				
			||||||
			token: '!00000000000000000000000000000001',
 | 
								token: '!00000000000000000000000000000001',
 | 
				
			||||||
			password: '$2a$08$OPESxR2RE/ZijjGanNKk6ezSqGFitqsbZqTjWUZPLhORMKxHCbc4O', // ilovesakurako
 | 
								password: '$2a$08$OPESxR2RE/ZijjGanNKk6ezSqGFitqsbZqTjWUZPLhORMKxHCbc4O', // ilovesakurako
 | 
				
			||||||
			profile: {},
 | 
								profile: {},
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										16
									
								
								tools/migration/node.1522066477.user-account-keypair.js
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								tools/migration/node.1522066477.user-account-keypair.js
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,16 @@
 | 
				
			||||||
 | 
					const { default: User } = require('../../built/api/models/user');
 | 
				
			||||||
 | 
					const { generate } = require('../../built/crypto_key');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const updates = [];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					User.find({}).each(function(user) {
 | 
				
			||||||
 | 
						updates.push(User.update({ _id: user._id }, {
 | 
				
			||||||
 | 
							$set: {
 | 
				
			||||||
 | 
								account: {
 | 
				
			||||||
 | 
									keypair: generate(),
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}));
 | 
				
			||||||
 | 
					}).then(function () {
 | 
				
			||||||
 | 
						Promise.all(updates)
 | 
				
			||||||
 | 
					}).then(process.exit);
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue