create-pixle-koa-template 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/bin/create-app.js +76 -0
  2. package/package.json +17 -0
  3. package/template/.env +20 -0
  4. package/template/app.js +49 -0
  5. package/template/config/db.js +29 -0
  6. package/template/config/email.js +30 -0
  7. package/template/config/index.js +36 -0
  8. package/template/config/redis.js +19 -0
  9. package/template/controllers/AuthController.js +71 -0
  10. package/template/controllers/DownloadController.js +18 -0
  11. package/template/controllers/UploadController.js +60 -0
  12. package/template/controllers/UserController.js +90 -0
  13. package/template/middleware/auth.js +91 -0
  14. package/template/middleware/errorHandler.js +17 -0
  15. package/template/middleware/logger.js +41 -0
  16. package/template/middleware/notFound.js +84 -0
  17. package/template/middleware/upload.js +165 -0
  18. package/template/models/Auth.js +11 -0
  19. package/template/models/BaseDAO.js +449 -0
  20. package/template/models/User.js +10 -0
  21. package/template/package-lock.json +3427 -0
  22. package/template/package.json +34 -0
  23. package/template/public/404.html +160 -0
  24. package/template/routes/auth.js +21 -0
  25. package/template/routes/download.js +9 -0
  26. package/template/routes/index.js +105 -0
  27. package/template/routes/upload.js +28 -0
  28. package/template/routes/user.js +22 -0
  29. package/template/services/AuthService.js +190 -0
  30. package/template/services/CodeRedisService.js +94 -0
  31. package/template/services/DownloadService.js +54 -0
  32. package/template/services/EmailService.js +245 -0
  33. package/template/services/JwtTokenService.js +50 -0
  34. package/template/services/PasswordService.js +133 -0
  35. package/template/services/TokenRedisService.js +29 -0
  36. package/template/services/UserService.js +128 -0
  37. package/template/utils/crypto.js +9 -0
  38. package/template/utils/passwordValidator.js +81 -0
  39. package/template/utils/prototype/day.js +237 -0
  40. package/template/utils/prototype/deepClone.js +32 -0
  41. package/template/utils/prototype/index.js +61 -0
  42. package/template/utils/response.js +26 -0
  43. package/template//344/275/277/347/224/250/346/225/231/347/250/213.md +881 -0
@@ -0,0 +1,81 @@
1
+ /**
2
+ * 密码校验工具类
3
+ */
4
+ class PasswordValidator {
5
+ /**
6
+ * 密码强度验证
7
+ */
8
+ static validate(password) {
9
+ const rules = {
10
+ minLength: 8,
11
+ maxLength: 128,
12
+ requireUppercase: false,//必须大写
13
+ requireLowercase: false,//必须小写
14
+ requireNumbers: false,//必须有数字
15
+ requireSpecialChars: false,//必须有特殊字符
16
+ weakPassword: ['12345678','87654321', 'password', 'admin123', 'qwertyui'],//禁用弱密码,根据实际扩展
17
+ };
18
+
19
+ const errors = [];
20
+
21
+ // 长度检查
22
+ if (password.length < rules.minLength) {
23
+ errors.push(`密码长度至少 ${rules.minLength} 位`);
24
+ }
25
+ if (password.length > rules.maxLength) {
26
+ errors.push(`密码长度不能超过 ${rules.maxLength} 位`);
27
+ }
28
+
29
+ // 大写字母检查
30
+ if (rules.requireUppercase && !/[A-Z]/.test(password)) {
31
+ errors.push('密码必须包含至少一个大写字母');
32
+ }
33
+
34
+ // 小写字母检查
35
+ if (rules.requireLowercase && !/[a-z]/.test(password)) {
36
+ errors.push('密码必须包含至少一个小写字母');
37
+ }
38
+
39
+ // 数字检查
40
+ if (rules.requireNumbers && !/\d/.test(password)) {
41
+ errors.push('密码必须包含至少一个数字');
42
+ }
43
+
44
+ // 特殊字符检查
45
+ if (rules.requireSpecialChars && !/[!@#$%^&*()_+\-=\[\]{};':"\\|,.<>\/?]/.test(password)) {
46
+ errors.push('密码必须包含至少一个特殊字符');
47
+ }
48
+
49
+ // 常见弱密码检查
50
+ if (rules.weakPassword.includes(password.toLowerCase())) {
51
+ errors.push('密码过于简单,请使用更复杂的密码');
52
+ }
53
+
54
+ return {
55
+ isValid: errors.length === 0,
56
+ errors
57
+ };
58
+ }
59
+
60
+ /**
61
+ * 密码相似度检查(防止与用户名、邮箱等相似)
62
+ */
63
+ static checkSimilarity(password, userInfo) {
64
+ const similarities = [];
65
+
66
+ if (userInfo.username && password.toLowerCase().includes(userInfo.username.toLowerCase())) {
67
+ similarities.push('密码不能包含用户名');
68
+ }
69
+
70
+ if (userInfo.email) {
71
+ const emailLocalPart = userInfo.email.split('@')[0];
72
+ if (password.toLowerCase().includes(emailLocalPart.toLowerCase())) {
73
+ similarities.push('密码不能包含邮箱地址');
74
+ }
75
+ }
76
+
77
+ return similarities;
78
+ }
79
+ }
80
+
81
+ module.exports = PasswordValidator;
@@ -0,0 +1,237 @@
1
+ /**
2
+ * Date日期处理
3
+ *
4
+ */
5
+ const day = () => {
6
+ //Date原型增加格式化日期格式,调用new Date().format('yyyy-MM-dd')
7
+ Object.defineProperty(Date.prototype, 'format', {
8
+ value(type) {
9
+ let _timeStamp = this instanceof Date ? this.getTime() : new Date().getTime();
10
+ function formatTimeStamp(type, timeStamp = _timeStamp) {
11
+ if (typeof timeStamp === 'number' && !isNaN(timeStamp)) {
12
+ let res = '';
13
+ let date = new Date(timeStamp)
14
+ let year = date.getFullYear()
15
+ let month = (date.getMonth() + 1).toString().padStart(2, '0')
16
+ let day = date.getDate().toString().padStart(2, '0')
17
+ let hour = date.getHours().toString().padStart(2, '0')
18
+ let minus = date.getMinutes().toString().padStart(2, '0')
19
+ let second = date.getSeconds().toString().padStart(2, '0')
20
+ switch (type) {
21
+ case 'yyyy':
22
+ res = `${year}`
23
+ break;
24
+ case 'MM':
25
+ res = `${month}`
26
+ break;
27
+ case 'dd':
28
+ res = `${day}`
29
+ break;
30
+ case 'HH':
31
+ res = `${hour}`
32
+ break;
33
+ case 'mm':
34
+ res = `${minus}`
35
+ break;
36
+ case 'ss':
37
+ res = `${second}`
38
+ break;
39
+ case 'yyyy-MM':
40
+ res = `${year}-${month}`
41
+ break;
42
+ case 'yyyy-MM-dd':
43
+ res = `${year}-${month}-${day}`
44
+ break;
45
+ case 'yyyy-MM-dd HH':
46
+ res = `${year}-${month}-${day} ${hour}`
47
+ break;
48
+ case 'yyyy-MM-dd HH:mm':
49
+ res = `${year}-${month}-${day} ${hour}:${minus}`
50
+ break;
51
+ case 'yyyy-MM-dd HH:mm:ss':
52
+ res = `${year}-${month}-${day} ${hour}:${minus}:${second}`
53
+ break;
54
+ case 'yyyy.MM.dd':
55
+ res = `${year}.${month}.${day}`
56
+ break;
57
+ case 'yyyy.MM.dd HH:mm':
58
+ res = `${year}.${month}.${day} ${hour}:${minus}`
59
+ break;
60
+ case 'yyyy.MM.dd HH:mm:ss':
61
+ res = `${year}.${month}.${day} ${hour}:${minus}:${second}`
62
+ break;
63
+ case 'HH:mm:ss':
64
+ res = `${hour}:${minus}:${second}`
65
+ break;
66
+ case 'HH:mm':
67
+ res = `${hour}:${minus}`
68
+ break;
69
+ default:
70
+ res = `${year}.${month}.${day} ${hour}:${minus}:${second}`
71
+ break;
72
+
73
+ }
74
+ return res
75
+ }
76
+ return this
77
+ }
78
+ return formatTimeStamp.call(this, type)
79
+ }
80
+ })
81
+
82
+
83
+ //获取某年某月共多少天
84
+ //new Date().getMonthDays()
85
+ Object.defineProperty(Date.prototype, 'getMonthDays', {
86
+ value() {
87
+ let _timeStamp = this instanceof Date ? this.getTime() : new Date().getTime();
88
+ let date = new Date(_timeStamp).format('yyyy-MM-dd');
89
+ function getSomeMonthDays(year, month) {
90
+ let febDays = year % 4 === 0 ? 29 : 28
91
+ let days = [31, febDays, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
92
+ if (month <= days.length) {
93
+ return days[month - 1]
94
+ }
95
+ return ''
96
+ }
97
+
98
+ return typeof date === 'string' && getSomeMonthDays.call(this, Number(date.split('-')[0]), Number(date.split('-')[1])) || ''
99
+ }
100
+ })
101
+
102
+
103
+ /**Date原型增加last函数,上num个时间last(type,timeFormat,num)
104
+ * @param type :上一个时间刻度类型
105
+ * @param timeFormat : 时间格式
106
+ * @param num : 数量
107
+ */
108
+ //例如:new Date().last('month','yyyy-MM-dd',3)//3个月前日期
109
+ Object.defineProperty(Date.prototype, 'last', {
110
+
111
+ value(type, formatType,num=1) {
112
+ const oneDayStamp = 24 * 60 * 60 * 1000;
113
+ let _timeStamp = this instanceof Date ? this.getTime() : new Date().getTime();
114
+ function last(type, timeStamp = _timeStamp) {
115
+ if (typeof timeStamp === 'number' && !isNaN(timeStamp)) {
116
+ let res = '';
117
+ let dateTime = new Date(timeStamp).format('yyyy-MM-dd HH:mm:ss')
118
+ let arr = dateTime.split(' ')[0].split('-')
119
+ let year = Number(arr[0])
120
+ let month = Number(arr[1])
121
+ let date = Number(arr[2]);
122
+
123
+ switch (type) {
124
+ //上num年
125
+ case 'year':
126
+ if(month===2&&date===29&&new Date(`${year-num}-${month}`).getMonthDays()<29){//2月29号特殊处理
127
+ res = new Date(`${year-num}-${month}-28${dateTime.substring(10)}`).format(formatType)
128
+ }
129
+ else{
130
+ res = new Date(`${year - num}${dateTime.substring(4)}`).format(formatType)
131
+ }
132
+
133
+ break;
134
+ //上num月
135
+ case 'month':
136
+ let minuYear=parseInt(num/12);
137
+ let minuMonth=num%12;
138
+ let minuDate=month-minuMonth > 0 ? `${year-minuYear}-${month-minuMonth}`:`${year-minuYear-1}-${12+month-minuMonth}`
139
+ let total = new Date(minuDate).getMonthDays()
140
+ res = new Date(`${minuDate}-${Math.min(date, total)} ${dateTime.split(' ')[1]}`).format(formatType)
141
+ break;
142
+ //num天前
143
+ case 'day':
144
+ res = new Date(timeStamp - num*oneDayStamp).format(formatType)
145
+ break;
146
+ case 'hour':
147
+ res = new Date(timeStamp -num* 60 * 60 * 1000).format(formatType)
148
+ break;
149
+ case 'minute':
150
+ res = new Date(timeStamp - num*60 * 1000).format(formatType)
151
+ break;
152
+ case 'second':
153
+ res = new Date(timeStamp -num*1000).format(formatType)
154
+ break;
155
+ default:
156
+ res = new Date(timeStamp -num* oneDayStamp).format(formatType)
157
+ break;
158
+
159
+ }
160
+ return res
161
+
162
+ }
163
+ return this
164
+ }
165
+ return last.call(this, type)
166
+
167
+ }
168
+ })
169
+
170
+ /**Date原型增加next函数,下num个时间next(type,timeFormat,num)
171
+ * @param type :上一个时间刻度类型
172
+ * @param timeFormat : 时间格式
173
+ * @param num : 数量
174
+ */
175
+ //例如:new Date().next('month','yyyy-MM-dd',3)//3个月后日期
176
+ Object.defineProperty(Date.prototype, 'next', {
177
+
178
+ value(type, formatType,num=1) {
179
+ const oneDayStamp = 24 * 60 * 60 * 1000;
180
+ let _timeStamp = this instanceof Date ? this.getTime() : new Date().getTime();
181
+ function next(type, timeStamp = _timeStamp) {
182
+ if (typeof timeStamp === 'number' && !isNaN(timeStamp)) {
183
+ let res = '';
184
+ let dateTime = new Date(timeStamp).format('yyyy-MM-dd HH:mm:ss')
185
+ let arr = dateTime.split(' ')[0].split('-')
186
+ let year = Number(arr[0])
187
+ let month = Number(arr[1])
188
+ let date = Number(arr[2]);
189
+ switch (type) {
190
+ //下num年
191
+ case 'year':
192
+ if(month===2&&date===29&&new Date(`${year+num}-${month}`).getMonthDays()<29){//2月29号特殊处理
193
+ res = new Date(`${year+num}-${month}-28${dateTime.substring(10)}`).format(formatType)
194
+ }
195
+ else{
196
+ res = new Date(`${year + num}${dateTime.substring(4)}`).format(formatType)
197
+ }
198
+
199
+ break;
200
+ //下num月
201
+ case 'month':
202
+ let plusYear=parseInt(num/12);
203
+ let plusMonth=num%12;
204
+ let plusDate=month+plusMonth < 13 ? `${year+plusYear}-${month+plusMonth}`:`${year+plusYear+1}-${month+plusMonth-12}`
205
+ let total = new Date(plusDate).getMonthDays()
206
+ res = new Date(`${plusDate}-${Math.min(date, total)} ${dateTime.split(' ')[1]}`).format(formatType)
207
+ break;
208
+ //下num天
209
+ case 'day':
210
+ res = new Date(timeStamp +num* oneDayStamp).format(formatType)
211
+ break;
212
+ case 'hour':
213
+ res = new Date(timeStamp + num*60 * 60 * 1000).format(formatType)
214
+ break;
215
+ case 'minute':
216
+ res = new Date(timeStamp + num*60 * 1000).format(formatType)
217
+ break;
218
+ case 'second':
219
+ res = new Date(timeStamp + num*1000).format(formatType)
220
+ break;
221
+ default:
222
+ res = new Date(timeStamp + num*oneDayStamp).format(formatType)
223
+ break;
224
+
225
+ }
226
+ return res
227
+
228
+ }
229
+ return this
230
+ }
231
+ return next.call(this, type)
232
+
233
+ }
234
+ })
235
+
236
+ }
237
+ module.exports = day
@@ -0,0 +1,32 @@
1
+ /**
2
+ * 深度克隆
3
+ * Object原型增加深度克隆方法,调用object.deepClone()
4
+ */
5
+ const deepClone = () => {
6
+ Object.defineProperty(Object.prototype, 'deepClone', {
7
+ value() {
8
+ function isNullOrUndefined(val) {
9
+ return val === null || val === undefined
10
+ }
11
+ if (Array.isArray(this)) { //数组类型
12
+ return this.map(item => {
13
+ return isNullOrUndefined(item) ? item : item.deepClone()//null和undefined无原型直接返回
14
+ })
15
+
16
+ } else if (Object.prototype.toString.call(this) === '[object Object]') {//对象类型
17
+ let obj = {}
18
+ for (let key in this) {
19
+ if (this.hasOwnProperty(key)) {
20
+ obj[key] = isNullOrUndefined(this[key]) ? this[key] : this[key].deepClone()
21
+ }
22
+ }
23
+ return obj
24
+
25
+ }
26
+ return this.valueOf()//其他类型(基本类型或函数)
27
+ }
28
+ })
29
+
30
+ }
31
+ module.exports =deepClone
32
+
@@ -0,0 +1,61 @@
1
+ // utils/prototype/index.js
2
+ const fs = require("fs");
3
+ const path = require("path");
4
+
5
+ /**
6
+ * 自动执行 prototype 目录下所有 .js 文件的默认导出方法(除了 index.js)
7
+ */
8
+ function loadAndExecutePrototypes() {
9
+ const currentDir = __dirname;
10
+
11
+ try {
12
+ // 读取当前目录下的所有文件
13
+ const files = fs.readdirSync(currentDir);
14
+
15
+ // 过滤出 .js 文件,排除 index.js
16
+ const jsFiles = files.filter((file) => {
17
+ return (
18
+ file.endsWith(".js") && file !== "index.js" && !file.startsWith(".") // 排除隐藏文件
19
+ );
20
+ });
21
+
22
+
23
+
24
+ // 按顺序执行每个文件的默认导出方法
25
+ jsFiles.forEach((file) => {
26
+ const filePath = path.join(currentDir, file);
27
+ const moduleName = path.basename(file, ".js");
28
+
29
+ try {
30
+ // 导入模块
31
+ const module = require(filePath);
32
+
33
+ // 检查是否有默认导出方法
34
+ if (module && typeof module.default === "function") {
35
+ module.default();
36
+ } else if (module && typeof module === "function") {
37
+ // 如果模块本身就是一个函数,也执行
38
+ module();
39
+ } else {
40
+ console.warn(
41
+ `[Prototype Loader] ${moduleName} has no default function to execute`
42
+ );
43
+ }
44
+ } catch (error) {
45
+ console.error(
46
+ `[Prototype Loader] Error executing ${file}:`,
47
+ error.message
48
+ );
49
+ }
50
+ });
51
+
52
+ } catch (error) {
53
+ console.error(
54
+ "[Prototype Loader] Error loading prototype files:",
55
+ error.message
56
+ );
57
+ }
58
+ }
59
+
60
+ // 导出加载函数
61
+ module.exports = { loadAndExecutePrototypes };
@@ -0,0 +1,26 @@
1
+
2
+ /**
3
+ * 响应工具类
4
+ */
5
+
6
+ // 成功
7
+ const success = (ctx, data = null, message = 'success', code = 200) => {
8
+ ctx.body = {
9
+ code,
10
+ message,
11
+ data,
12
+ };
13
+ };
14
+
15
+ // 失败
16
+ const error = (ctx, message = 'error', code = 500) => {
17
+ ctx.body = {
18
+ code,
19
+ message,
20
+ };
21
+ };
22
+
23
+ module.exports = {
24
+ success,
25
+ error
26
+ };