reCAPTCHAの設定をDBに保存するように
This commit is contained in:
		
							parent
							
								
									d5ab6b41c9
								
							
						
					
					
						commit
						a6f8327aa2
					
				
					 10 changed files with 99 additions and 36 deletions
				
			
		|  | @ -47,11 +47,6 @@ In root : | |||
| 4. `git checkout $(git tag -l | grep -v 'rc[0-9]*$' | sort -V | tail -n 1)` Checkout to the [latest release](https://github.com/syuilo/misskey/releases/latest) | ||||
| 5. `npm install` Install misskey dependencies. | ||||
| 
 | ||||
| *(optional)* reCAPTCHA tokens | ||||
| ---------------------------------------------------------------- | ||||
| If you want to enable reCAPTCHA, you need to generate reCAPTCHA tokens: | ||||
| Please visit https://www.google.com/recaptcha/intro/ and generate keys. | ||||
| 
 | ||||
| *(optional)* Generating VAPID keys | ||||
| ---------------------------------------------------------------- | ||||
| If you want to enable ServiceWorker, you need to generate VAPID keys: | ||||
|  |  | |||
|  | @ -53,11 +53,6 @@ adduser --disabled-password --disabled-login misskey | |||
| 4. `git checkout $(git tag -l | grep -v 'rc[0-9]*$' | sort -V | tail -n 1)` [最新のリリース](https://github.com/syuilo/misskey/releases/latest)を確認 | ||||
| 5. `npm install` Misskeyの依存パッケージをインストール | ||||
| 
 | ||||
| *(オプション)* reCAPTCHAトークン | ||||
| ---------------------------------------------------------------- | ||||
| reCAPTCHAを有効にする場合、reCAPTCHAトークンを取得する必要があります。 | ||||
| https://www.google.com/recaptcha/intro/ にアクセスしてトークンを取得してください。 | ||||
| 
 | ||||
| *(オプション)* VAPIDキーペアの生成 | ||||
| ---------------------------------------------------------------- | ||||
| ServiceWorkerを有効にする場合、VAPIDキーペアを生成する必要があります: | ||||
|  |  | |||
|  | @ -1085,6 +1085,11 @@ admin/views/instance.vue: | |||
|   local-drive-capacity-mb: "ローカルユーザーひとりあたりのドライブ容量" | ||||
|   remote-drive-capacity-mb: "リモートユーザーひとりあたりのドライブ容量" | ||||
|   mb: "メガバイト単位" | ||||
|   recaptcha-config: "reCAPTCHAの設定" | ||||
|   recaptcha-info: "reCAPTCHAを有効にする場合、reCAPTCHAトークンを取得する必要があります。https://www.google.com/recaptcha/intro/ にアクセスしてトークンを取得してください。" | ||||
|   enable-recaptcha: "reCAPTCHAを有効にする" | ||||
|   recaptcha-site-key: "reCAPTCHA site key" | ||||
|   recaptcha-secret-key: "reCAPTCHA secret key" | ||||
|   max-note-text-length: "投稿の最大文字数" | ||||
|   disable-registration: "ユーザー登録の受付を停止する" | ||||
|   disable-local-timeline: "ローカルタイムラインを無効にする" | ||||
|  |  | |||
|  | @ -16,6 +16,13 @@ | |||
| 			<ui-input v-model="localDriveCapacityMb">%i18n:@local-drive-capacity-mb%<span slot="desc">%i18n:@mb%</span><span slot="suffix">MB</span></ui-input> | ||||
| 			<ui-input v-model="remoteDriveCapacityMb" :disabled="!cacheRemoteFiles">%i18n:@remote-drive-capacity-mb%<span slot="desc">%i18n:@mb%</span><span slot="suffix">MB</span></ui-input> | ||||
| 		</section> | ||||
| 		<section class="fit-bottom"> | ||||
| 			<header><fa icon="shield-alt"/> %i18n:@recaptcha-config%</header> | ||||
| 			<ui-switch v-model="enableRecaptcha">%i18n:@enable-recaptcha%</ui-switch> | ||||
| 			<ui-info>%i18n:@recaptcha-info%</ui-info> | ||||
| 			<ui-input v-model="recaptchaSiteKey" :disabled="!enableRecaptcha"><i slot="icon"><fa icon="key"/></i>%i18n:@recaptcha-site-key%</ui-input> | ||||
| 			<ui-input v-model="recaptchaSecretKey" :disabled="!enableRecaptcha"><i slot="icon"><fa icon="key"/></i>%i18n:@recaptcha-secret-key%</ui-input> | ||||
| 		</section> | ||||
| 		<section> | ||||
| 			<ui-button @click="updateMeta">%i18n:@save%</ui-button> | ||||
| 		</section> | ||||
|  | @ -54,6 +61,9 @@ export default Vue.extend({ | |||
| 			localDriveCapacityMb: null, | ||||
| 			remoteDriveCapacityMb: null, | ||||
| 			maxNoteTextLength: null, | ||||
| 			enableRecaptcha: false, | ||||
| 			recaptchaSiteKey: null, | ||||
| 			recaptchaSecretKey: null, | ||||
| 			inviteCode: null, | ||||
| 		}; | ||||
| 	}, | ||||
|  | @ -67,6 +77,9 @@ export default Vue.extend({ | |||
| 			this.localDriveCapacityMb = meta.driveCapacityPerLocalUserMb; | ||||
| 			this.remoteDriveCapacityMb = meta.driveCapacityPerRemoteUserMb; | ||||
| 			this.maxNoteTextLength = meta.maxNoteTextLength; | ||||
| 			this.enableRecaptcha = meta.enableRecaptcha; | ||||
| 			this.recaptchaSiteKey = meta.recaptchaSiteKey; | ||||
| 			this.recaptchaSecretKey = meta.recaptchaSecretKey; | ||||
| 		}); | ||||
| 	}, | ||||
| 
 | ||||
|  | @ -92,7 +105,10 @@ export default Vue.extend({ | |||
| 				cacheRemoteFiles: this.cacheRemoteFiles, | ||||
| 				localDriveCapacityMb: parseInt(this.localDriveCapacityMb, 10), | ||||
| 				remoteDriveCapacityMb: parseInt(this.remoteDriveCapacityMb, 10), | ||||
| 				maxNoteTextLength: parseInt(this.maxNoteTextLength, 10) | ||||
| 				maxNoteTextLength: parseInt(this.maxNoteTextLength, 10), | ||||
| 				enableRecaptcha: this.enableRecaptcha, | ||||
| 				recaptchaSiteKey: this.recaptchaSiteKey, | ||||
| 				recaptchaSecretKey: this.recaptchaSecretKey | ||||
| 			}).then(() => { | ||||
| 				this.$swal({ | ||||
| 					type: 'success', | ||||
|  |  | |||
|  | @ -35,7 +35,7 @@ | |||
| 				<p v-if="passwordRetypeState == 'not-match'" style="color:#FF1161"><fa icon="exclamation-triangle" fixed-width/> %i18n:@password-not-matched%</p> | ||||
| 			</div> | ||||
| 		</ui-input> | ||||
| 		<div v-if="meta.recaptchaSitekey != null" class="g-recaptcha" :data-sitekey="meta.recaptchaSitekey" style="margin: 16px 0;"></div> | ||||
| 		<div v-if="meta.recaptchaSiteKey != null" class="g-recaptcha" :data-sitekey="meta.recaptchaSiteKey" style="margin: 16px 0;"></div> | ||||
| 		<ui-button type="submit">%i18n:@create%</ui-button> | ||||
| 	</template> | ||||
| </form> | ||||
|  | @ -130,7 +130,7 @@ export default Vue.extend({ | |||
| 				username: this.username, | ||||
| 				password: this.password, | ||||
| 				invitationCode: this.invitationCode, | ||||
| 				'g-recaptcha-response': this.meta.recaptchaSitekey != null ? (window as any).grecaptcha.getResponse() : null | ||||
| 				'g-recaptcha-response': this.meta.recaptchaSiteKey != null ? (window as any).grecaptcha.getResponse() : null | ||||
| 			}, true).then(() => { | ||||
| 				(this as any).api('signin', { | ||||
| 					username: this.username, | ||||
|  | @ -141,7 +141,7 @@ export default Vue.extend({ | |||
| 			}).catch(() => { | ||||
| 				alert('%i18n:@some-error%'); | ||||
| 
 | ||||
| 				if (this.meta.recaptchaSitekey != null) { | ||||
| 				if (this.meta.recaptchaSiteKey != null) { | ||||
| 					(window as any).grecaptcha.reset(); | ||||
| 				} | ||||
| 			}); | ||||
|  |  | |||
|  | @ -40,11 +40,6 @@ export type Source = { | |||
| 		port: number; | ||||
| 		pass: string; | ||||
| 	}; | ||||
| 	recaptcha?: { | ||||
| 		site_key: string; | ||||
| 		secret_key: string; | ||||
| 	}; | ||||
| 
 | ||||
| 	drive?: { | ||||
| 		storage: string; | ||||
| 		bucket?: string; | ||||
|  |  | |||
|  | @ -61,6 +61,19 @@ if ((config as any).preventCacheRemoteFiles) { | |||
| 		} | ||||
| 	}); | ||||
| } | ||||
| if ((config as any).recaptcha) { | ||||
| 	Meta.findOne({}).then(m => { | ||||
| 		if (m != null && m.enableRecaptcha == null) { | ||||
| 			Meta.update({}, { | ||||
| 				$set: { | ||||
| 					enableRecaptcha: (config as any).recaptcha != null, | ||||
| 					recaptchaSiteKey: (config as any).recaptcha.site_key, | ||||
| 					recaptchaSecretKey: (config as any).recaptcha.secret_key, | ||||
| 				} | ||||
| 			}); | ||||
| 		} | ||||
| 	}); | ||||
| } | ||||
| 
 | ||||
| export type IMeta = { | ||||
| 	name?: string; | ||||
|  | @ -79,6 +92,10 @@ export type IMeta = { | |||
| 
 | ||||
| 	cacheRemoteFiles?: boolean; | ||||
| 
 | ||||
| 	enableRecaptcha?: boolean; | ||||
| 	recaptchaSiteKey?: string; | ||||
| 	recaptchaSecretKey?: string; | ||||
| 
 | ||||
| 	/** | ||||
| 	 * Drive capacity of a local user (MB) | ||||
| 	 */ | ||||
|  |  | |||
|  | @ -88,6 +88,27 @@ export const meta = { | |||
| 			desc: { | ||||
| 				'ja-JP': 'リモートのファイルをキャッシュするか否か' | ||||
| 			} | ||||
| 		}, | ||||
| 
 | ||||
| 		enableRecaptcha: { | ||||
| 			validator: $.bool.optional, | ||||
| 			desc: { | ||||
| 				'ja-JP': 'reCAPTCHAを使用するか否か' | ||||
| 			} | ||||
| 		}, | ||||
| 
 | ||||
| 		recaptchaSiteKey: { | ||||
| 			validator: $.str.optional, | ||||
| 			desc: { | ||||
| 				'ja-JP': 'reCAPTCHA site key' | ||||
| 			} | ||||
| 		}, | ||||
| 
 | ||||
| 		recaptchaSecretKey: { | ||||
| 			validator: $.str.optional, | ||||
| 			desc: { | ||||
| 				'ja-JP': 'reCAPTCHA secret key' | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| }; | ||||
|  | @ -139,6 +160,18 @@ export default define(meta, (ps) => new Promise(async (res, rej) => { | |||
| 		set.cacheRemoteFiles = ps.cacheRemoteFiles; | ||||
| 	} | ||||
| 
 | ||||
| 	if (ps.enableRecaptcha !== undefined) { | ||||
| 		set.enableRecaptcha = ps.enableRecaptcha; | ||||
| 	} | ||||
| 
 | ||||
| 	if (ps.recaptchaSiteKey !== undefined) { | ||||
| 		set.recaptchaSiteKey = ps.recaptchaSiteKey; | ||||
| 	} | ||||
| 
 | ||||
| 	if (ps.recaptchaSecretKey !== undefined) { | ||||
| 		set.recaptchaSecretKey = ps.recaptchaSecretKey; | ||||
| 	} | ||||
| 
 | ||||
| 	await Meta.update({}, { | ||||
| 		$set: set | ||||
| 	}, { upsert: true }); | ||||
|  |  | |||
|  | @ -35,7 +35,7 @@ export default define(meta, (ps, me) => new Promise(async (res, rej) => { | |||
| 		} | ||||
| 	}); | ||||
| 
 | ||||
| 	res({ | ||||
| 	const response: any = { | ||||
| 		maintainer: config.maintainer, | ||||
| 
 | ||||
| 		version: pkg.version, | ||||
|  | @ -60,24 +60,32 @@ export default define(meta, (ps, me) => new Promise(async (res, rej) => { | |||
| 		driveCapacityPerLocalUserMb: instance.localDriveCapacityMb, | ||||
| 		driveCapacityPerRemoteUserMb: instance.remoteDriveCapacityMb, | ||||
| 		cacheRemoteFiles: instance.cacheRemoteFiles, | ||||
| 		recaptchaSitekey: config.recaptcha ? config.recaptcha.site_key : null, | ||||
| 		recaptchaSiteKey: instance.enableRecaptcha ? instance.recaptchaSiteKey : null, | ||||
| 		swPublickey: config.sw ? config.sw.public_key : null, | ||||
| 		hidedTags: (me && me.isAdmin) ? instance.hidedTags : undefined, | ||||
| 		bannerUrl: instance.bannerUrl, | ||||
| 		maxNoteTextLength: instance.maxNoteTextLength, | ||||
| 
 | ||||
| 		emojis: emojis, | ||||
| 	}; | ||||
| 
 | ||||
| 		features: ps.detail ? { | ||||
| 	if (ps.detail) { | ||||
| 		response.features = { | ||||
| 			registration: !instance.disableRegistration, | ||||
| 			localTimeLine: !instance.disableLocalTimeline, | ||||
| 			elasticsearch: config.elasticsearch ? true : false, | ||||
| 			recaptcha: config.recaptcha ? true : false, | ||||
| 			recaptcha: instance.enableRecaptcha, | ||||
| 			objectStorage: config.drive && config.drive.storage === 'minio', | ||||
| 			twitter: config.twitter ? true : false, | ||||
| 			github: config.github ? true : false, | ||||
| 			serviceWorker: config.sw ? true : false, | ||||
| 			userRecommendation: config.user_recommendation ? config.user_recommendation : {} | ||||
| 		} : undefined | ||||
| 	}); | ||||
| 		}; | ||||
| 	} | ||||
| 
 | ||||
| 	if (me && me.isAdmin) { | ||||
| 		response.hidedTags = instance.hidedTags; | ||||
| 		response.recaptchaSecretKey = instance.recaptchaSecretKey; | ||||
| 	} | ||||
| 
 | ||||
| 	res(response); | ||||
| })); | ||||
|  |  | |||
|  | @ -1,7 +1,6 @@ | |||
| import * as Koa from 'koa'; | ||||
| import * as bcrypt from 'bcryptjs'; | ||||
| import { generate as generateKeypair } from '../../../crypto_key'; | ||||
| const recaptcha = require('recaptcha-promise'); | ||||
| import User, { IUser, validateUsername, validatePassword, pack } from '../../../models/user'; | ||||
| import generateUserToken from '../common/generate-native-user-token'; | ||||
| import config from '../../../config'; | ||||
|  | @ -10,18 +9,20 @@ import RegistrationTicket from '../../../models/registration-tickets'; | |||
| import usersChart from '../../../chart/users'; | ||||
| import fetchMeta from '../../../misc/fetch-meta'; | ||||
| 
 | ||||
| if (config.recaptcha) { | ||||
| 	recaptcha.init({ | ||||
| 		secret_key: config.recaptcha.secret_key | ||||
| 	}); | ||||
| } | ||||
| 
 | ||||
| export default async (ctx: Koa.Context) => { | ||||
| 	const body = ctx.request.body as any; | ||||
| 
 | ||||
| 	const instance = await fetchMeta(); | ||||
| 
 | ||||
| 	const recaptcha = require('recaptcha-promise'); | ||||
| 
 | ||||
| 	// Verify recaptcha
 | ||||
| 	// ただしテスト時はこの機構は障害となるため無効にする
 | ||||
| 	if (process.env.NODE_ENV !== 'test' && config.recaptcha != null) { | ||||
| 	if (process.env.NODE_ENV !== 'test' && instance.enableRecaptcha) { | ||||
| 		recaptcha.init({ | ||||
| 			secret_key: instance.recaptchaSecretKey | ||||
| 		}); | ||||
| 
 | ||||
| 		const success = await recaptcha(body['g-recaptcha-response']); | ||||
| 
 | ||||
| 		if (!success) { | ||||
|  | @ -34,8 +35,6 @@ export default async (ctx: Koa.Context) => { | |||
| 	const password = body['password']; | ||||
| 	const invitationCode = body['invitationCode']; | ||||
| 
 | ||||
| 	const instance = await fetchMeta(); | ||||
| 
 | ||||
| 	if (instance && instance.disableRegistration) { | ||||
| 		if (invitationCode == null || typeof invitationCode != 'string') { | ||||
| 			ctx.status = 400; | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue