From a53edc96bec3503517daa24837def5d3ecd82624 Mon Sep 17 00:00:00 2001
From: syuilo <syuilotan@yahoo.co.jp>
Date: Thu, 2 Mar 2017 03:16:39 +0900
Subject: [PATCH] wip

---
 src/api/endpoints/posts/create.js | 40 ++++++++++---------------------
 src/api/models/post.ts            |  4 ++++
 src/api/validator.ts              | 26 ++++++++++++++++----
 3 files changed, 39 insertions(+), 31 deletions(-)

diff --git a/src/api/endpoints/posts/create.js b/src/api/endpoints/posts/create.js
index 57e95bd712..2f03ebd8e9 100644
--- a/src/api/endpoints/posts/create.js
+++ b/src/api/endpoints/posts/create.js
@@ -4,8 +4,9 @@
  * Module dependencies
  */
 import * as mongo from 'mongodb';
+import validate from '../../validator';
 import parse from '../../../common/text';
-import Post from '../../models/post';
+import { Post, isValidText } from '../../models/post';
 import User from '../../models/user';
 import Following from '../../models/following';
 import DriveFile from '../../models/drive-file';
@@ -15,16 +16,15 @@ import notify from '../../common/notify';
 import event from '../../event';
 import config from '../../../conf';
 
-/**
- * 最大文字数
- */
-const maxTextLength = 1000;
-
 /**
  * 添付できるファイルの数
  */
 const maxMediaCount = 4;
 
+function hasDuplicates(array) {
+	return (new Set(array)).size !== array.length;
+}
+
 /**
  * Create a post
  *
@@ -37,30 +37,16 @@ module.exports = (params, user, app) =>
 	new Promise(async (res, rej) =>
 {
 	// Get 'text' parameter
-	let text = params.text;
-	if (text !== undefined && text !== null) {
-		if (typeof text != 'string') {
-			return rej('text must be a string');
-		}
-		text = text.trim();
-		if (text.length == 0) {
-			text = null;
-		} else if (text.length > maxTextLength) {
-			return rej('too long text');
-		}
-	} else {
-		text = null;
-	}
+	const [text, textErr] = validate(params.text, 'string', false, isValidText);
+	if (textErr) return rej('invalid text');
 
 	// Get 'media_ids' parameter
-	let medias = params.media_ids;
-	let files = [];
-	if (medias !== undefined && medias !== null) {
-		if (!Array.isArray(medias)) {
-			return rej('media_ids must be an array');
-		}
+	const [mediaIds, mediaIdsErr] = validate(params.media_ids, 'array', false, x => !hasDuplicates(x));
+	if (mediaIdsErr) return rej('invalid media_ids');
 
-		if (medias.length > maxMediaCount) {
+	let files = [];
+	if (mediaIds !== null) {
+		if (mediaIds.length > maxMediaCount) {
 			return rej('too many media');
 		}
 
diff --git a/src/api/models/post.ts b/src/api/models/post.ts
index ab29187251..baab63f991 100644
--- a/src/api/models/post.ts
+++ b/src/api/models/post.ts
@@ -1,3 +1,7 @@
 import db from '../../db/mongodb';
 
 export default db.get('posts') as any; // fuck type definition
+
+export function isValidText(text: string): boolean {
+	return text.length <= 1000 && text.trim() != '';
+}
diff --git a/src/api/validator.ts b/src/api/validator.ts
index 2562535c02..830786a18d 100644
--- a/src/api/validator.ts
+++ b/src/api/validator.ts
@@ -2,7 +2,15 @@ import * as mongo from 'mongodb';
 
 type Type = 'id' | 'string' | 'number' | 'boolean' | 'array' | 'object';
 
-export default <T>(value: any, isRequired: boolean, type: Type, validator?: (any) => boolean): [T, string] => {
+type Validator<T> = ((x: T) => boolean | string) | ((x: T) => boolean | string)[];
+
+function validate(value: any, type: 'id', isRequired?: boolean): [mongo.ObjectID, string];
+function validate(value: any, type: 'string', isRequired?: boolean, validator?: Validator<string>): [string, string];
+function validate(value: any, type: 'number', isRequired?: boolean, validator?: Validator<number>): [number, string];
+function validate(value: any, type: 'boolean', isRequired?: boolean): [boolean, string];
+function validate(value: any, type: 'array', isRequired?: boolean, validator?: Validator<any[]>): [any[], string];
+function validate(value: any, type: 'object', isRequired?: boolean, validator?: Validator<Object>): [Object, string];
+function validate<T>(value: any, type: Type, isRequired?: boolean, validator?: Validator<T>): [T, string] {
 	if (value === undefined || value === null) {
 		if (isRequired) {
 			return [null, 'is-required']
@@ -49,11 +57,21 @@ export default <T>(value: any, isRequired: boolean, type: Type, validator?: (any
 			break;
 	}
 
+	if (type == 'id') value = new mongo.ObjectID(value);
+
 	if (validator) {
-		if (!validator(value)) {
-			return [null, 'invalid-format'];
+		const validators = Array.isArray(validator) ? validator : [validator];
+		for (let i = 0; i < validators.length; i++) {
+			const result = validators[i](value);
+			if (result === false) {
+				return [null, 'invalid-format'];
+			} else if (typeof result == 'string') {
+				return [null, result];
+			}
 		}
 	}
 
 	return [value, null];
-};
+}
+
+export default validate;