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) | 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. | 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 | *(optional)* Generating VAPID keys | ||||||
| ---------------------------------------------------------------- | ---------------------------------------------------------------- | ||||||
| If you want to enable ServiceWorker, you need to generate 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)を確認 | 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の依存パッケージをインストール | 5. `npm install` Misskeyの依存パッケージをインストール | ||||||
| 
 | 
 | ||||||
| *(オプション)* reCAPTCHAトークン |  | ||||||
| ---------------------------------------------------------------- |  | ||||||
| reCAPTCHAを有効にする場合、reCAPTCHAトークンを取得する必要があります。 |  | ||||||
| https://www.google.com/recaptcha/intro/ にアクセスしてトークンを取得してください。 |  | ||||||
| 
 |  | ||||||
| *(オプション)* VAPIDキーペアの生成 | *(オプション)* VAPIDキーペアの生成 | ||||||
| ---------------------------------------------------------------- | ---------------------------------------------------------------- | ||||||
| ServiceWorkerを有効にする場合、VAPIDキーペアを生成する必要があります: | ServiceWorkerを有効にする場合、VAPIDキーペアを生成する必要があります: | ||||||
|  |  | ||||||
|  | @ -1085,6 +1085,11 @@ admin/views/instance.vue: | ||||||
|   local-drive-capacity-mb: "ローカルユーザーひとりあたりのドライブ容量" |   local-drive-capacity-mb: "ローカルユーザーひとりあたりのドライブ容量" | ||||||
|   remote-drive-capacity-mb: "リモートユーザーひとりあたりのドライブ容量" |   remote-drive-capacity-mb: "リモートユーザーひとりあたりのドライブ容量" | ||||||
|   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: "投稿の最大文字数" |   max-note-text-length: "投稿の最大文字数" | ||||||
|   disable-registration: "ユーザー登録の受付を停止する" |   disable-registration: "ユーザー登録の受付を停止する" | ||||||
|   disable-local-timeline: "ローカルタイムラインを無効にする" |   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="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> | 			<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> | ||||||
|  | 		<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> | 		<section> | ||||||
| 			<ui-button @click="updateMeta">%i18n:@save%</ui-button> | 			<ui-button @click="updateMeta">%i18n:@save%</ui-button> | ||||||
| 		</section> | 		</section> | ||||||
|  | @ -54,6 +61,9 @@ export default Vue.extend({ | ||||||
| 			localDriveCapacityMb: null, | 			localDriveCapacityMb: null, | ||||||
| 			remoteDriveCapacityMb: null, | 			remoteDriveCapacityMb: null, | ||||||
| 			maxNoteTextLength: null, | 			maxNoteTextLength: null, | ||||||
|  | 			enableRecaptcha: false, | ||||||
|  | 			recaptchaSiteKey: null, | ||||||
|  | 			recaptchaSecretKey: null, | ||||||
| 			inviteCode: null, | 			inviteCode: null, | ||||||
| 		}; | 		}; | ||||||
| 	}, | 	}, | ||||||
|  | @ -67,6 +77,9 @@ export default Vue.extend({ | ||||||
| 			this.localDriveCapacityMb = meta.driveCapacityPerLocalUserMb; | 			this.localDriveCapacityMb = meta.driveCapacityPerLocalUserMb; | ||||||
| 			this.remoteDriveCapacityMb = meta.driveCapacityPerRemoteUserMb; | 			this.remoteDriveCapacityMb = meta.driveCapacityPerRemoteUserMb; | ||||||
| 			this.maxNoteTextLength = meta.maxNoteTextLength; | 			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, | 				cacheRemoteFiles: this.cacheRemoteFiles, | ||||||
| 				localDriveCapacityMb: parseInt(this.localDriveCapacityMb, 10), | 				localDriveCapacityMb: parseInt(this.localDriveCapacityMb, 10), | ||||||
| 				remoteDriveCapacityMb: parseInt(this.remoteDriveCapacityMb, 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(() => { | 			}).then(() => { | ||||||
| 				this.$swal({ | 				this.$swal({ | ||||||
| 					type: 'success', | 					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> | 				<p v-if="passwordRetypeState == 'not-match'" style="color:#FF1161"><fa icon="exclamation-triangle" fixed-width/> %i18n:@password-not-matched%</p> | ||||||
| 			</div> | 			</div> | ||||||
| 		</ui-input> | 		</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> | 		<ui-button type="submit">%i18n:@create%</ui-button> | ||||||
| 	</template> | 	</template> | ||||||
| </form> | </form> | ||||||
|  | @ -130,7 +130,7 @@ export default Vue.extend({ | ||||||
| 				username: this.username, | 				username: this.username, | ||||||
| 				password: this.password, | 				password: this.password, | ||||||
| 				invitationCode: this.invitationCode, | 				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(() => { | 			}, true).then(() => { | ||||||
| 				(this as any).api('signin', { | 				(this as any).api('signin', { | ||||||
| 					username: this.username, | 					username: this.username, | ||||||
|  | @ -141,7 +141,7 @@ export default Vue.extend({ | ||||||
| 			}).catch(() => { | 			}).catch(() => { | ||||||
| 				alert('%i18n:@some-error%'); | 				alert('%i18n:@some-error%'); | ||||||
| 
 | 
 | ||||||
| 				if (this.meta.recaptchaSitekey != null) { | 				if (this.meta.recaptchaSiteKey != null) { | ||||||
| 					(window as any).grecaptcha.reset(); | 					(window as any).grecaptcha.reset(); | ||||||
| 				} | 				} | ||||||
| 			}); | 			}); | ||||||
|  |  | ||||||
|  | @ -40,11 +40,6 @@ export type Source = { | ||||||
| 		port: number; | 		port: number; | ||||||
| 		pass: string; | 		pass: string; | ||||||
| 	}; | 	}; | ||||||
| 	recaptcha?: { |  | ||||||
| 		site_key: string; |  | ||||||
| 		secret_key: string; |  | ||||||
| 	}; |  | ||||||
| 
 |  | ||||||
| 	drive?: { | 	drive?: { | ||||||
| 		storage: string; | 		storage: string; | ||||||
| 		bucket?: 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 = { | export type IMeta = { | ||||||
| 	name?: string; | 	name?: string; | ||||||
|  | @ -79,6 +92,10 @@ export type IMeta = { | ||||||
| 
 | 
 | ||||||
| 	cacheRemoteFiles?: boolean; | 	cacheRemoteFiles?: boolean; | ||||||
| 
 | 
 | ||||||
|  | 	enableRecaptcha?: boolean; | ||||||
|  | 	recaptchaSiteKey?: string; | ||||||
|  | 	recaptchaSecretKey?: string; | ||||||
|  | 
 | ||||||
| 	/** | 	/** | ||||||
| 	 * Drive capacity of a local user (MB) | 	 * Drive capacity of a local user (MB) | ||||||
| 	 */ | 	 */ | ||||||
|  |  | ||||||
|  | @ -88,6 +88,27 @@ export const meta = { | ||||||
| 			desc: { | 			desc: { | ||||||
| 				'ja-JP': 'リモートのファイルをキャッシュするか否か' | 				'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; | 		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({}, { | 	await Meta.update({}, { | ||||||
| 		$set: set | 		$set: set | ||||||
| 	}, { upsert: true }); | 	}, { upsert: true }); | ||||||
|  |  | ||||||
|  | @ -35,7 +35,7 @@ export default define(meta, (ps, me) => new Promise(async (res, rej) => { | ||||||
| 		} | 		} | ||||||
| 	}); | 	}); | ||||||
| 
 | 
 | ||||||
| 	res({ | 	const response: any = { | ||||||
| 		maintainer: config.maintainer, | 		maintainer: config.maintainer, | ||||||
| 
 | 
 | ||||||
| 		version: pkg.version, | 		version: pkg.version, | ||||||
|  | @ -60,24 +60,32 @@ export default define(meta, (ps, me) => new Promise(async (res, rej) => { | ||||||
| 		driveCapacityPerLocalUserMb: instance.localDriveCapacityMb, | 		driveCapacityPerLocalUserMb: instance.localDriveCapacityMb, | ||||||
| 		driveCapacityPerRemoteUserMb: instance.remoteDriveCapacityMb, | 		driveCapacityPerRemoteUserMb: instance.remoteDriveCapacityMb, | ||||||
| 		cacheRemoteFiles: instance.cacheRemoteFiles, | 		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, | 		swPublickey: config.sw ? config.sw.public_key : null, | ||||||
| 		hidedTags: (me && me.isAdmin) ? instance.hidedTags : undefined, |  | ||||||
| 		bannerUrl: instance.bannerUrl, | 		bannerUrl: instance.bannerUrl, | ||||||
| 		maxNoteTextLength: instance.maxNoteTextLength, | 		maxNoteTextLength: instance.maxNoteTextLength, | ||||||
| 
 | 
 | ||||||
| 		emojis: emojis, | 		emojis: emojis, | ||||||
|  | 	}; | ||||||
| 
 | 
 | ||||||
| 		features: ps.detail ? { | 	if (ps.detail) { | ||||||
|  | 		response.features = { | ||||||
| 			registration: !instance.disableRegistration, | 			registration: !instance.disableRegistration, | ||||||
| 			localTimeLine: !instance.disableLocalTimeline, | 			localTimeLine: !instance.disableLocalTimeline, | ||||||
| 			elasticsearch: config.elasticsearch ? true : false, | 			elasticsearch: config.elasticsearch ? true : false, | ||||||
| 			recaptcha: config.recaptcha ? true : false, | 			recaptcha: instance.enableRecaptcha, | ||||||
| 			objectStorage: config.drive && config.drive.storage === 'minio', | 			objectStorage: config.drive && config.drive.storage === 'minio', | ||||||
| 			twitter: config.twitter ? true : false, | 			twitter: config.twitter ? true : false, | ||||||
| 			github: config.github ? true : false, | 			github: config.github ? true : false, | ||||||
| 			serviceWorker: config.sw ? true : false, | 			serviceWorker: config.sw ? true : false, | ||||||
| 			userRecommendation: config.user_recommendation ? config.user_recommendation : {} | 			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 Koa from 'koa'; | ||||||
| import * as bcrypt from 'bcryptjs'; | import * as bcrypt from 'bcryptjs'; | ||||||
| import { generate as generateKeypair } from '../../../crypto_key'; | import { generate as generateKeypair } from '../../../crypto_key'; | ||||||
| const 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'; | ||||||
| import config from '../../../config'; | import config from '../../../config'; | ||||||
|  | @ -10,18 +9,20 @@ import RegistrationTicket from '../../../models/registration-tickets'; | ||||||
| import usersChart from '../../../chart/users'; | import usersChart from '../../../chart/users'; | ||||||
| import fetchMeta from '../../../misc/fetch-meta'; | import fetchMeta from '../../../misc/fetch-meta'; | ||||||
| 
 | 
 | ||||||
| if (config.recaptcha) { |  | ||||||
| 	recaptcha.init({ |  | ||||||
| 		secret_key: config.recaptcha.secret_key |  | ||||||
| 	}); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| export default async (ctx: Koa.Context) => { | export default async (ctx: Koa.Context) => { | ||||||
| 	const body = ctx.request.body as any; | 	const body = ctx.request.body as any; | ||||||
| 
 | 
 | ||||||
|  | 	const instance = await fetchMeta(); | ||||||
|  | 
 | ||||||
|  | 	const recaptcha = require('recaptcha-promise'); | ||||||
|  | 
 | ||||||
| 	// Verify recaptcha
 | 	// 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']); | 		const success = await recaptcha(body['g-recaptcha-response']); | ||||||
| 
 | 
 | ||||||
| 		if (!success) { | 		if (!success) { | ||||||
|  | @ -34,8 +35,6 @@ export default async (ctx: Koa.Context) => { | ||||||
| 	const password = body['password']; | 	const password = body['password']; | ||||||
| 	const invitationCode = body['invitationCode']; | 	const invitationCode = body['invitationCode']; | ||||||
| 
 | 
 | ||||||
| 	const instance = await fetchMeta(); |  | ||||||
| 
 |  | ||||||
| 	if (instance && instance.disableRegistration) { | 	if (instance && instance.disableRegistration) { | ||||||
| 		if (invitationCode == null || typeof invitationCode != 'string') { | 		if (invitationCode == null || typeof invitationCode != 'string') { | ||||||
| 			ctx.status = 400; | 			ctx.status = 400; | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue