diff --git a/Server/src/APIs/BlogComment.ts b/Server/src/APIs/BlogComment.ts index aed0838..50ddbc6 100644 --- a/Server/src/APIs/BlogComment.ts +++ b/Server/src/APIs/BlogComment.ts @@ -1,15 +1,15 @@ import { API } from "../Plugs/API/API"; import ServerStdResponse from "../ServerStdResponse"; -import MySQLConnection from '../Plugs/MySQLConnection' +import Database from '../Plugs/Database' import MountUserAgent from "../Plugs/Middleware/MountUserAgent"; import axios from "axios"; import MountIP from "../Plugs/Middleware/MountIP"; -import CheckCaptchaPassed from "../Plugs/Middleware/CheckCaptchaPassed"; +import { BlogComment as BlogCommentType } from "@/Types/Schema" // 提交博客评论 class BlogComment extends API { constructor() { - super('POST', '/blogComment', CheckCaptchaPassed, MountUserAgent, MountIP); + super('POST', '/blogComment', MountUserAgent, MountIP); } public async onRequset(data: any, res: any) { @@ -26,17 +26,17 @@ class BlogComment extends API { // 获取IPAddress let ip_address = '未知' try { - let ipAddressRes = await axios.get(`https://mesh.if.iqiyi.com/aid/ip/info?version=1.1.1&ip=`+_ip); + let ipAddressRes = await axios.get(`https://mesh.if.iqiyi.com/aid/ip/info?version=1.1.1&ip=` + _ip); if (ipAddressRes.data && ipAddressRes.data.msg == 'success') { - + ip_address = ipAddressRes.data.data.countryCN == '中国' ? ipAddressRes.data.data.provinceCN : ipAddressRes.data.data.countryCN; } } catch (error) { this.logger.warn('获取IP属地失败', error); } - let blogLikeRes = await MySQLConnection.execute('INSERT INTO blog_comment (uuid, content, name, ip, ip_address, user_agent, time) VALUES (?,?,?,?,?,?,?)', [bloguuid, content.trim(), name.trim(), _ip, ip_address, _userAgent, Date.now()]); - if (!blogLikeRes || blogLikeRes.affectedRows != 1) { + let blogLikeRes = await Database.query('INSERT INTO blog_comment (uuid, content, name, ip, ip_address, user_agent, display, created_at) VALUES ($1,$2,$3,$4,$5,$6,true,$7)', [bloguuid, content.trim(), name.trim(), _ip, ip_address, _userAgent, new Date()]); + if (!blogLikeRes) { this.logger.error('发布博客评论时,数据库发生错误'); return res.json(ServerStdResponse.SERVER_ERROR); } diff --git a/Server/src/APIs/BlogLike.ts b/Server/src/APIs/BlogLike.ts index cafba3f..9df219e 100644 --- a/Server/src/APIs/BlogLike.ts +++ b/Server/src/APIs/BlogLike.ts @@ -1,8 +1,9 @@ import { API } from "../Plugs/API/API"; import ServerStdResponse from "../ServerStdResponse"; -import MySQLConnection from '../Plugs/MySQLConnection' +import Database from '../Plugs/Database' import { Buffer } from 'buffer'; import axios from "axios"; +import { Blog } from "@/Types/Schema"; // 点赞 @@ -18,15 +19,11 @@ class BlogLike extends API { return res.json(ServerStdResponse.INVALID_PARAMS); } - let blogLikeRes = await MySQLConnection.execute('UPDATE blog SET like_count = like_count + 1 WHERE access_level > ? AND uuid = ? ', [this.defaultAccessLevel, bloguuid]); + let blogLikeRes = await Database.query('UPDATE blog SET like_count = like_count + 1 WHERE access_level > $1 AND uuid = $2 ', [this.defaultAccessLevel, bloguuid]); if (!blogLikeRes) { this.logger.error('点赞博客时,数据库发生错误'); return res.json(ServerStdResponse.SERVER_ERROR); } - if (blogLikeRes.affectedRows != 1) { - this.logger.warn('查询的博客不存在或不可见', bloguuid); - return res.json(ServerStdResponse.BLOG.NOTFOUND); - } return res.json(ServerStdResponse.OK); } } diff --git a/Server/src/APIs/CheckCaptcha.ts b/Server/src/APIs/CheckCaptcha.ts deleted file mode 100644 index 4f29bf1..0000000 --- a/Server/src/APIs/CheckCaptcha.ts +++ /dev/null @@ -1,41 +0,0 @@ -import { API } from "../Plugs/API/API"; -import ServerStdResponse from "../ServerStdResponse"; -import captchaSession from "../Plugs/Service/captchaSession"; - -// 检查人机验证 -class CheckCaptcha extends API { - constructor() { - super('POST', '/checkCaptcha'); - } - - public async onRequset(data: any, res: any) { - let { session, rotateDeg } = data; - if (!session || !rotateDeg) { - return res.json(ServerStdResponse.PARAMS_MISSING); - } - switch (await captchaSession.check(session, rotateDeg)) { - case 0: - // 验证码已过期或服务器错误 - res.json(ServerStdResponse.CAPTCHA.NOTFOUND); - break; - case 1: - // 验证通过 - res.json(ServerStdResponse.OK); - break; - case -1: - // 超过最大尝试次数 - res.json(ServerStdResponse.CAPTCHA.MAX_TRY_COUNT); - break; - case -2: - // 角度不正确 - res.json(ServerStdResponse.CAPTCHA.NOTRIGHT); - break; - default: - // 未知错误 - res.json(ServerStdResponse.SERVER_ERROR); - break; - } - } -} - -export default CheckCaptcha; \ No newline at end of file diff --git a/Server/src/APIs/Console/DelBlog.ts b/Server/src/APIs/Console/DelBlog.ts index 25c9167..e73d6cf 100644 --- a/Server/src/APIs/Console/DelBlog.ts +++ b/Server/src/APIs/Console/DelBlog.ts @@ -1,6 +1,6 @@ import { API } from "../../Plugs/API/API"; import ServerStdResponse from "../../ServerStdResponse"; -import MySQLConnection from '../../Plugs/MySQLConnection' +import Database from '../../Plugs/Database' import Auth from "../../Plugs/Middleware/Auth"; // 删除博客 @@ -10,13 +10,13 @@ class DelBlog extends API { } public async onRequset(data: any, res: any) { - let { id } = data; - if (!id) { + let { uuid } = data; + if (!uuid) { return res.json(ServerStdResponse.PARAMS_MISSING); } - let execRes = await MySQLConnection.execute('DELETE FROM blog WHERE `id` = ?', [id]); + let execRes = await Database.query('DELETE FROM blog WHERE uuid = $1', [uuid]); - if (!execRes || execRes.affectedRows != 1) { + if (!execRes) { return res.json(ServerStdResponse.SERVER_ERROR); } return res.json({ ...ServerStdResponse.OK }); diff --git a/Server/src/APIs/Console/DelResource.ts b/Server/src/APIs/Console/DelResource.ts index f5c3c5b..3850cf8 100644 --- a/Server/src/APIs/Console/DelResource.ts +++ b/Server/src/APIs/Console/DelResource.ts @@ -1,6 +1,6 @@ import { API } from "../../Plugs/API/API"; import ServerStdResponse from "../../ServerStdResponse"; -import MySQLConnection from '../../Plugs/MySQLConnection' +import Database from '../../Plugs/Database' import Auth from "../../Plugs/Middleware/Auth"; // 删除资源 @@ -10,13 +10,13 @@ class DelResource extends API { } public async onRequset(data: any, res: any) { - let { id } = data; - if (!id) { + let { uuid } = data; + if (!uuid) { return res.json(ServerStdResponse.PARAMS_MISSING); } - let execRes = await MySQLConnection.execute('DELETE FROM resource WHERE `id` = ?', [id]); + let execRes = await Database.query('DELETE FROM resource WHERE uuid = $1', [uuid]); - if (!execRes || execRes.affectedRows != 1) { + if (!execRes) { return res.json(ServerStdResponse.SERVER_ERROR); } return res.json({ ...ServerStdResponse.OK }); diff --git a/Server/src/APIs/Console/GetBlogs.ts b/Server/src/APIs/Console/GetBlogs.ts index 7143415..8f17a59 100644 --- a/Server/src/APIs/Console/GetBlogs.ts +++ b/Server/src/APIs/Console/GetBlogs.ts @@ -1,7 +1,8 @@ import { API } from "../../Plugs/API/API"; import ServerStdResponse from "../../ServerStdResponse"; -import MySQLConnection from '../../Plugs/MySQLConnection' +import Database from '../../Plugs/Database' import Auth from "../../Plugs/Middleware/Auth"; +import { Blog } from "@/Types/Schema"; // 获取博客列表 class GetBlogs extends API { @@ -10,8 +11,7 @@ class GetBlogs extends API { } public async onRequset(data: any, res: any) { - // const { uuid } = data._jwt; - let resourcesRes = await MySQLConnection.execute("SELECT * FROM blog ORDER BY id DESC"); + let resourcesRes = await Database.query("SELECT * FROM blog ORDER BY created_at DESC"); if (!resourcesRes) { return res.json(ServerStdResponse.SERVER_ERROR); } diff --git a/Server/src/APIs/Console/GetLoginStatus.ts b/Server/src/APIs/Console/GetLoginStatus.ts index 1096e8b..5bcaed9 100644 --- a/Server/src/APIs/Console/GetLoginStatus.ts +++ b/Server/src/APIs/Console/GetLoginStatus.ts @@ -1,6 +1,6 @@ import { API, RequestData } from "../../Plugs/API/API"; import ServerStdResponse from "../../ServerStdResponse"; -import MySQLConnection from '../../Plugs/MySQLConnection' +import Database from '../../Plugs/Database' import Auth from "../../Plugs/Middleware/Auth"; import jwt from "jsonwebtoken"; import config from "../../config"; diff --git a/Server/src/APIs/Console/GetResources.ts b/Server/src/APIs/Console/GetResources.ts index 8b5d22c..026cbb9 100644 --- a/Server/src/APIs/Console/GetResources.ts +++ b/Server/src/APIs/Console/GetResources.ts @@ -1,7 +1,8 @@ import { API } from "../../Plugs/API/API"; import ServerStdResponse from "../../ServerStdResponse"; -import MySQLConnection from '../../Plugs/MySQLConnection' +import Database from '../../Plugs/Database' import Auth from "../../Plugs/Middleware/Auth"; +import { Resource } from "@/Types/Schema"; // 获取资源列表 class GetResources extends API { @@ -11,7 +12,7 @@ class GetResources extends API { public async onRequset(data: any, res: any) { // const { uuid } = data._jwt; - let resourcesRes = await MySQLConnection.execute("SELECT * FROM resource"); + let resourcesRes = await Database.query("SELECT * FROM resource ORDER BY type, recommand, created_at DESC"); if (!resourcesRes) { return res.json(ServerStdResponse.SERVER_ERROR); } diff --git a/Server/src/APIs/Console/Login.ts b/Server/src/APIs/Console/Login.ts index 3be2074..60256e5 100644 --- a/Server/src/APIs/Console/Login.ts +++ b/Server/src/APIs/Console/Login.ts @@ -1,17 +1,17 @@ import { API } from "../../Plugs/API/API"; import ServerStdResponse from "../../ServerStdResponse"; -import MySQLConnection from '../../Plugs/MySQLConnection' +import Database from '../../Plugs/Database' import MountUserAgent from "../../Plugs/Middleware/MountUserAgent"; import MountIP from "../../Plugs/Middleware/MountIP"; -import CheckCaptchaPassed from "../../Plugs/Middleware/CheckCaptchaPassed"; import config from "../../config"; import jwt from 'jsonwebtoken' import crypto from 'crypto' +import { User } from "@/Types/Schema"; // 登录 class Login extends API { constructor() { - super('POST', '/console/login', CheckCaptchaPassed, MountUserAgent, MountIP); + super('POST', '/console/login', MountUserAgent, MountIP); } public async onRequset(data: any, res: any) { @@ -21,28 +21,28 @@ class Login extends API { } // 检查用户是否存在 - let userInfoRes = await MySQLConnection.execute('SELECT * FROM user WHERE username = ?', [username]); - if(!userInfoRes){ + let userInfoRes = await Database.query('SELECT * FROM user WHERE username = $1', [username]); + if (!userInfoRes) { return res.json(ServerStdResponse.SERVER_ERROR); } if (userInfoRes.length != 1) { return res.json(ServerStdResponse.USER.NOTFOUND); } - userInfoRes = userInfoRes[0]; + const UserInfo = userInfoRes[0]; // 检查密码是否正确 - if(crypto.createHash('sha256').update(`${userInfoRes.salt}${password}`).digest('hex') != userInfoRes.password){ + if (crypto.createHash('sha256').update(`${UserInfo.salt}${password}`).digest('hex') != UserInfo.password) { return res.json(ServerStdResponse.USER.PASSWORD_ERROR); } // 准备jwtToken const jwtPayload = { - uuid: userInfoRes.uuid, + uuid: UserInfo.uuid, loginTime: Date.now() } let jwtToken = jwt.sign(jwtPayload, config.jwt.secret, { expiresIn: config.jwt.expiresIn }); // 写入登录日志 - MySQLConnection.execute('INSERT INTO user_login_log (user_uuid, ip, user_agent, time) VALUES (?,?,?,?)', [userInfoRes.uuid, _ip, _userAgent, Date.now()]); + Database.query('INSERT INTO user_login_log (user_uuid, ip, user_agent, time) VALUES ($1,$2,$3,$4)', [UserInfo.uuid, _ip, _userAgent, Date.now()]); return res.json({ ...ServerStdResponse.OK, data: { token: jwtToken } }); } } diff --git a/Server/src/APIs/Console/SaveBlog.ts b/Server/src/APIs/Console/SaveBlog.ts index fdf772b..2f69832 100644 --- a/Server/src/APIs/Console/SaveBlog.ts +++ b/Server/src/APIs/Console/SaveBlog.ts @@ -1,8 +1,9 @@ import { API } from "../../Plugs/API/API"; import ServerStdResponse from "../../ServerStdResponse"; -import MySQLConnection from '../../Plugs/MySQLConnection' +import Database from '../../Plugs/Database' import Auth from "../../Plugs/Middleware/Auth"; import crypto from 'crypto' +import { Blog } from "@/Types/Schema"; // 保存博客 class SaveBlog extends API { @@ -11,21 +12,21 @@ class SaveBlog extends API { } public async onRequset(data: any, res: any) { - let { id, uuid, title, description, publish_time, src, access_level } = data; - if (!title || !description || !publish_time || !src || !access_level) { + let { uuid, title, description, created_at, src, access_level } = data; + if (!title || !description || !created_at || !src || !access_level) { return res.json(ServerStdResponse.PARAMS_MISSING); } let execRes: any; - if (id) { + if (uuid) { // 保存 - execRes = await MySQLConnection.execute('UPDATE blog SET title = ?, description = ?, publish_time = ?, src = ?, access_level = ? WHERE `id` = ?', [title, description, publish_time, src, access_level, id]); + execRes = await Database.query('UPDATE blog SET title = $1, description = $2, created_at = $3, src = $4, access_level = $5 WHERE uuid = $6', [title, description, created_at, src, access_level, uuid]); } else { // 新建 const uuid = crypto.createHash('md5').update(`${Math.random()}${Date.now()}`).digest('hex'); - execRes = await MySQLConnection.execute('INSERT INTO blog (uuid, title, description, src, publish_time, access_level, visit_count, like_count) VALUES (?,?,?,?,?,?,?,?)', [uuid, title, description, src, publish_time, access_level, 0, 0]); + execRes = await Database.query('INSERT INTO blog (uuid, title, description, src, created_at, access_level, visit_count, like_count) VALUES ($1,$2,$3,$4,$5,$6,$7,$8)', [uuid, title, description, src, created_at, access_level, 0, 0]); } - if (!execRes || execRes.affectedRows != 1) { + if (!execRes) { return res.json(ServerStdResponse.SERVER_ERROR); } return res.json({ ...ServerStdResponse.OK }); diff --git a/Server/src/APIs/Console/SaveResource.ts b/Server/src/APIs/Console/SaveResource.ts index 1876bc8..12e5c2b 100644 --- a/Server/src/APIs/Console/SaveResource.ts +++ b/Server/src/APIs/Console/SaveResource.ts @@ -1,7 +1,9 @@ import { API } from "../../Plugs/API/API"; import ServerStdResponse from "../../ServerStdResponse"; -import MySQLConnection from '../../Plugs/MySQLConnection' +import Database from '../../Plugs/Database' import Auth from "../../Plugs/Middleware/Auth"; +import { Resource } from "@/Types/Schema"; +import Crypto from 'crypto' // 保存资源 class SaveResource extends API { @@ -10,20 +12,21 @@ class SaveResource extends API { } public async onRequset(data: any, res: any) { - let { id, type, recommand, title, describe, icon_src, addition, src } = data; + let { uuid, type, recommand, title, describe, icon_src, addition, src } = data; if (!type || !recommand || !title || !describe || !icon_src || !addition || !src) { return res.json(ServerStdResponse.PARAMS_MISSING); } let execRes: any; - if (id) { + if (uuid) { // 保存 - execRes = await MySQLConnection.execute('UPDATE resource SET `type` = ?, `recommand` = ?, `title` = ?, `describe` = ?, `addition` = ?, `icon_src` = ?, `src` = ? WHERE `id` = ?', [type, recommand, title, describe, addition, icon_src, src, id]); + execRes = await Database.query('UPDATE resource SET "type" = $1, "recommand" = $2, "title" = $3, "describe" = $4, "addition" = $5, "icon_src" = $6, "src" = $7 WHERE "uuid" = $8', [type, recommand, title, describe, addition, icon_src, src, uuid]); } else { // 新建 - execRes = await MySQLConnection.execute('INSERT INTO resource (`type`, `recommand`, `title`, `describe`, `addition`, `icon_src`, `src`) VALUES (?,?,?,?,?,?,?)', [type, recommand, title, describe, addition, icon_src, src]); + uuid = Crypto.createHash('md5').update(`${Math.random()}${Date.now()}`).digest('hex'); + execRes = await Database.query('INSERT INTO resource ("uuid","type", "recommand", "title", "describe", "addition", "icon_src", "src", "created_at") VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9)', [uuid, type, recommand, title, describe, addition, icon_src, src, new Date()]); } - if (!execRes || execRes.affectedRows != 1) { + if (!execRes) { return res.json(ServerStdResponse.SERVER_ERROR); } return res.json({ ...ServerStdResponse.OK }); diff --git a/Server/src/APIs/Console/SetBlogPasswd.ts b/Server/src/APIs/Console/SetBlogPasswd.ts index 42e7a86..c34e993 100644 --- a/Server/src/APIs/Console/SetBlogPasswd.ts +++ b/Server/src/APIs/Console/SetBlogPasswd.ts @@ -1,8 +1,9 @@ import { API } from "../../Plugs/API/API"; import ServerStdResponse from "../../ServerStdResponse"; -import MySQLConnection from '../../Plugs/MySQLConnection' +import Database from '../../Plugs/Database' import Auth from "../../Plugs/Middleware/Auth"; import crypto from 'crypto' +import { Blog } from "@/Types/Schema"; // 设置博客密码 class SetBlogPasswd extends API { @@ -16,7 +17,7 @@ class SetBlogPasswd extends API { return res.json(ServerStdResponse.PARAMS_MISSING); } const encrypt_p = crypto.createHash('sha256').update(passwd).digest('hex'); - MySQLConnection.execute('UPDATE blog SET encrypt_p = ? WHERE uuid = ?', [encrypt_p, uuid]); + Database.query('UPDATE blog SET encrypt_p = $1 WHERE uuid = $2', [encrypt_p, uuid]); return res.json({ ...ServerStdResponse.OK }); } } diff --git a/Server/src/APIs/GetBlogComment.ts b/Server/src/APIs/GetBlogComment.ts index 043ccca..aee59f8 100644 --- a/Server/src/APIs/GetBlogComment.ts +++ b/Server/src/APIs/GetBlogComment.ts @@ -1,6 +1,7 @@ import { API } from "../Plugs/API/API"; import ServerStdResponse from "../ServerStdResponse"; -import MySQLConnection from '../Plugs/MySQLConnection' +import Database from '../Plugs/Database' +import { BlogComment } from "@/Types/Schema"; // 获取博客评论 class GetBlogComment extends API { @@ -16,7 +17,7 @@ class GetBlogComment extends API { return res.json(ServerStdResponse.INVALID_PARAMS); } - let blogCommentRes = await MySQLConnection.execute('SELECT content, name, ip_address, time FROM blog_comment WHERE uuid = ? AND display = 1 ORDER BY time DESC LIMIT ? OFFSET ?;', [bloguuid, this.pageSize, (page - 1) * this.pageSize]); + let blogCommentRes = await Database.query('SELECT content, name, ip_address, created_at FROM blog_comment WHERE uuid = $1 AND display = true ORDER BY created_at DESC LIMIT $2 OFFSET $3;', [bloguuid, this.pageSize, (page - 1) * this.pageSize]); if (!blogCommentRes) { this.logger.error('获取博客评论时,数据库发生错误'); return res.json(ServerStdResponse.SERVER_ERROR); diff --git a/Server/src/APIs/GetBlogContent.ts b/Server/src/APIs/GetBlogContent.ts index 54705f4..7d35733 100644 --- a/Server/src/APIs/GetBlogContent.ts +++ b/Server/src/APIs/GetBlogContent.ts @@ -1,10 +1,11 @@ import { API } from "../Plugs/API/API"; import ServerStdResponse from "../ServerStdResponse"; -import MySQLConnection from '../Plugs/MySQLConnection' +import Database from '../Plugs/Database' import { Buffer } from 'buffer'; import axios from "axios"; import crypto from 'crypto' import MountIP from "../Plugs/Middleware/MountIP"; +import { Blog } from "@/Types/Schema"; // 获取博客内容 class GetBlogContent extends API { @@ -22,14 +23,14 @@ class GetBlogContent extends API { return res.json(ServerStdResponse.INVALID_PARAMS); } - let blogContentRes = await MySQLConnection.execute(`SELECT * from blog WHERE access_level in (${this.AccessLevelRule.allow.join(',')}) AND uuid = ? `, [bloguuid]); + let blogContentRes = await Database.query(`SELECT * from blog WHERE access_level in (${this.AccessLevelRule.allow.join(',')}) AND uuid = $1 `, [bloguuid]); if (!blogContentRes) { this.logger.error('查询时数据库发生错误'); return res.json(ServerStdResponse.SERVER_ERROR); } if (blogContentRes.length == 0) { // 公开范围不可见,查询允许无连接加密查看的数据 - blogContentRes = await MySQLConnection.execute(`SELECT * from blog WHERE access_level in (${this.AccessLevelRule.encrypt_allow.join(',')}) AND uuid = ? `, [bloguuid]); + blogContentRes = await Database.query(`SELECT * from blog WHERE access_level in (${this.AccessLevelRule.encrypt_allow.join(',')}) AND uuid = $1 `, [bloguuid]); if (!blogContentRes) { this.logger.error('查询时数据库发生错误'); return res.json(ServerStdResponse.SERVER_ERROR); @@ -57,14 +58,14 @@ class GetBlogContent extends API { const base64Content = Buffer.from(response.data, 'utf-8').toString('base64'); // 访问次数+1 - MySQLConnection.execute('UPDATE blog SET visit_count = visit_count + 1 WHERE uuid = ?', [bloguuid]); + Database.query('UPDATE blog SET visit_count = visit_count + 1 WHERE uuid = $1', [bloguuid]); return res.json({ ...ServerStdResponse.OK, data: { data: base64Content, info: { title: blogContentRes[0].title, description: blogContentRes[0].description, - publish_time: blogContentRes[0].publish_time, + publish_time: blogContentRes[0].created_at, visit_count: blogContentRes[0].visit_count, like_count: blogContentRes[0].like_count } diff --git a/Server/src/APIs/GetBlogList.ts b/Server/src/APIs/GetBlogList.ts index 035a249..68c8b9b 100644 --- a/Server/src/APIs/GetBlogList.ts +++ b/Server/src/APIs/GetBlogList.ts @@ -1,6 +1,7 @@ import { API } from "../Plugs/API/API"; import ServerStdResponse from "../ServerStdResponse"; -import MySQLConnection from '../Plugs/MySQLConnection' +import Database from '../Plugs/Database' +import { Blog } from "@/Types/Schema"; // 获取博客列表 class GetBlogList extends API { @@ -10,12 +11,12 @@ class GetBlogList extends API { private defaultAccessLevel = 9; public async onRequset(data: any, res: any) { - let blogListRes = await MySQLConnection.execute('SELECT uuid, title, description, publish_time, access_level, visit_count, like_count from blog WHERE access_level >= ? ORDER BY publish_time DESC',[this.defaultAccessLevel]); - if(!blogListRes){ + let blogListRes = await Database.query('SELECT uuid, title, description, created_at, access_level, visit_count, like_count from blog WHERE access_level >= $1 ORDER BY created_at DESC', [this.defaultAccessLevel]); + if (!blogListRes) { this.logger.error('查询时数据库发生错误'); return res.json(ServerStdResponse.SERVER_ERROR); } - return res.json({...ServerStdResponse.OK, data: blogListRes}); + return res.json({ ...ServerStdResponse.OK, data: blogListRes }); } } diff --git a/Server/src/APIs/GetCaptcha.ts b/Server/src/APIs/GetCaptcha.ts deleted file mode 100644 index 242c27c..0000000 --- a/Server/src/APIs/GetCaptcha.ts +++ /dev/null @@ -1,36 +0,0 @@ -import fs from "fs"; -import { API } from "../Plugs/API/API"; -import ServerStdResponse from "../ServerStdResponse"; -import captchaSession from "../Plugs/Service/captchaSession"; -import path from "path"; -import sharp from "sharp"; -import crypto from 'crypto' - -// 获取人机验证图片及标识符 -class GetCaptcha extends API { - constructor() { - super('GET', '/captcha'); - } - - public async onRequset(data: any, res: any) { - const imgsPath = path.join(__dirname, '../assets/captchaImgs'); - const fileList = fs.readdirSync(imgsPath) - const imgPath = path.join(imgsPath, fileList[Math.floor(Math.random() * fileList.length)]); - const rotateDeg = Math.floor(Math.random() * 240) + 60; - const img = Buffer.from(await sharp(imgPath).rotate(-rotateDeg).toBuffer()).toString('base64') - const session = crypto.createHash('md5').update(`${Math.random()} ${Date.now()}`).digest('hex'); - if (await captchaSession.add(session, rotateDeg)) { - return res.json({ - ...ServerStdResponse.OK, data: { - img: img, - session: session, - imgPreStr: 'data:image/jpeg;base64,' - } - }); - } else { - return res.json(ServerStdResponse.SERVER_ERROR) - } - } -} - -export default GetCaptcha; \ No newline at end of file diff --git a/Server/src/APIs/GetResourceList.ts b/Server/src/APIs/GetResourceList.ts index a51c82f..9ded8de 100644 --- a/Server/src/APIs/GetResourceList.ts +++ b/Server/src/APIs/GetResourceList.ts @@ -1,6 +1,7 @@ import { API } from "../Plugs/API/API"; import ServerStdResponse from "../ServerStdResponse"; -import MySQLConnection from '../Plugs/MySQLConnection' +import Database from '../Plugs/Database' +import { Resource } from "@/Types/Schema"; // 获取资源列表 class GetResourceList extends API { @@ -11,18 +12,18 @@ class GetResourceList extends API { public async onRequset(data: any, res: any) { let { type } = data; - if(!type){ + if (!type) { return res.json(ServerStdResponse.PARAMS_MISSING); } - if(!this.typeList.includes(type)){ + if (!this.typeList.includes(type)) { return res.json(ServerStdResponse.INVALID_PARAMS); } - let resourceListRes = await MySQLConnection.execute('SELECT * from resource WHERE type = ? ORDER BY recommand ASC',[type]); - if(!resourceListRes){ + let resourceListRes = await Database.query('SELECT * from resource WHERE type = $1 ORDER BY recommand ASC', [type]); + if (!resourceListRes) { this.logger.error('查询时数据库发生错误'); return res.json(ServerStdResponse.SERVER_ERROR); } - return res.json({...ServerStdResponse.OK, data: resourceListRes}); + return res.json({ ...ServerStdResponse.OK, data: resourceListRes }); } } diff --git a/Server/src/APIs/GetTest.ts b/Server/src/APIs/GetTest.ts index d8d3c1c..264d55e 100644 --- a/Server/src/APIs/GetTest.ts +++ b/Server/src/APIs/GetTest.ts @@ -1,6 +1,5 @@ import { API } from "../Plugs/API/API"; import ServerStdResponse from "../ServerStdResponse"; -import MySQLConnection from '../Plugs/MySQLConnection' // 测试接口 class GetTest extends API { diff --git a/Server/src/Plugs/Database.ts b/Server/src/Plugs/Database.ts new file mode 100644 index 0000000..6368696 --- /dev/null +++ b/Server/src/Plugs/Database.ts @@ -0,0 +1,78 @@ +/** + * @file Database.ts + * @version 1.0.0 + * @description PostgreSQL数据库连接池 + */ +import pg from 'pg'; +import Logger from "./Logger"; +import config from "../config"; + +class DatabasePool { + private pool: pg.Pool; + private logger = new Logger('Database'); + + constructor() { + this.pool = this.createConnectPool(); + this.logger.info("数据库连接池已创建") + + this.pool.on('error', (err) => { + this.logger.error(`数据库连接池发生错误:${err}`); + }); + + this.testConnection(); + } + + private createConnectPool() { + return new pg.Pool({ + host: config.pg.host, + database: config.pg.database, + user: config.pg.user, + password: config.pg.password, + connectionTimeoutMillis: 5000, // 连接超时时间 + max: 20, // 最大连接数 + idleTimeoutMillis: 30000, // 空闲连接超时时间 + allowExitOnIdle: true // 允许空闲时退出 + }) + } + + private async testConnection() { + let res = await this.query<{ result: number }>("SELECT 1 + 1 As result"); + if (!res) { + this.logger.error("数据库连接测试失败"); + return; + } + if (res[0].result == 2) + return; + this.logger.error("数据库连接测试失败?"); + } + + /** + * 执行SQL查询 + * @param sql SQL语句 + * @param values 可选的查询参数列表 + * @param database 可选的数据库 + * @returns Promise 查询结果 + */ + public async query(sql: string, values?: any[]): Promise { + let connection; + try { + connection = await this.pool.connect(); + const res = await connection.query(sql, values); + return res.rows; + } catch (error) { + if (!connection) { + this.logger.error(`数据库连接失败:${error}`); + } else { + this.logger.error(`数据库查询发生错误:${error}`); + } + return undefined; + } finally { + if (connection) { + connection.release(); + } + } + } +} + +const Database = new DatabasePool(); +export default Database; \ No newline at end of file diff --git a/Server/src/Plugs/Middleware/CheckCaptchaPassed.ts b/Server/src/Plugs/Middleware/CheckCaptchaPassed.ts deleted file mode 100644 index 81c45a1..0000000 --- a/Server/src/Plugs/Middleware/CheckCaptchaPassed.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { Request, Response, NextFunction } from "express"; -import ServerStdResponse from "../../ServerStdResponse"; -import Logger from "../Logger"; -import captchaSession from "../Service/captchaSession"; -const logger = new Logger("CheckCaptcha"); -const CheckCaptchaPassed = async (req: Request, res: Response, next: NextFunction) => { - let session = req.query.session || req.body.session || ''; - if (session) { - if (await captchaSession.isPassed(session)) - return next(); - } - let ip = req.headers['x-forwarded-for'] || req.connection.remoteAddress || req.ip; - logger.info(`API[${req.method}][${req.url.split('?')[0]}] 请求人机验证未通过[${ip}]`); - res.json(ServerStdResponse.AUTH_ERROR); -} - -export default CheckCaptchaPassed; \ No newline at end of file diff --git a/Server/src/Plugs/MySQLConnection.ts b/Server/src/Plugs/MySQLConnection.ts deleted file mode 100644 index baa5631..0000000 --- a/Server/src/Plugs/MySQLConnection.ts +++ /dev/null @@ -1,84 +0,0 @@ -/** - * @file MySQLConnection.ts - * @version 1.0.0 - * @description MySQL数据库连接池 - */ -import mysql from "mysql2/promise"; -import Logger from "./Logger"; -import config from "../config"; - -class MySQLConnectPool { - private pool: any; - private logger = new Logger('MySQLConnection'); - - constructor() { - this.pool = this.createConnectPool(); - this.logger.info("数据库连接池已创建") - setTimeout(async () => { - let res = await this.testConnection(); - if (res) - this.logger.info("数据库测试成功") - else - this.logger.error("数据库测试失败") - }, 10); - } - - private createConnectPool() { - return mysql.createPool({ - host: config.mysql.host, - database: config.mysql.database, - user: config.mysql.user, - password: config.mysql.password, - waitForConnections: true, - connectionLimit: 10, - queueLimit: 0 - }) - } - - private async testConnection() { - try { - let res = await this.execute("SELECT 1 + 1 As result"); - if (res[0].result == 2) - return 1; - else - return 0; - } catch (error) { - this.logger.error(`数据库测试发生了错误:` + error); - return 0; - } - - } - - /** - * 执行SQL查询 - * @param sql SQL语句 - * @param values 可选的查询参数列表 - * @param database 可选的数据库 - * @returns Promise 查询结果 - */ - public async execute(sql: string, values?: any[], database?: string): Promise { - let connection: any; - try { - connection = await this.pool.getConnection(); - - // 如果指定了数据库,则更改当前连接的数据库 - if (database) { - await connection.changeUser({ database }); - } - - let [rows, fields] = await connection.execute(sql, values); - return rows; - } catch (error) { - this.logger.error("数据库发生错误:" + error, '\n##', sql, '\n##', JSON.stringify(values)); - return undefined; - } finally { - if (database) - await connection.changeUser({ database: config.mysql.database });// 恢复默认数据库 - if (connection) - connection.release(); - } - } -} - -const MySQLConnection = new MySQLConnectPool(); -export default MySQLConnection; \ No newline at end of file diff --git a/Server/src/Plugs/RedisConnection.ts b/Server/src/Plugs/RedisConnection.ts deleted file mode 100644 index 403087e..0000000 --- a/Server/src/Plugs/RedisConnection.ts +++ /dev/null @@ -1,43 +0,0 @@ -import Redis from 'ioredis'; -import config from '../config'; -import Logger from './Logger'; - -class RedisConnection { - private pool?: Redis - private logger = new Logger('Redis') - - constructor() { - try { - this.pool = new Redis({ - port: config.redis.port, - host: config.redis.host, - password: config.redis.password, - maxRetriesPerRequest: 10, - }); - this.logger.info('数据库连接池已创建') - } catch (error) { - this.logger.error('数据库连接池创建失败:' + error) - } - setTimeout(async () => { - if(this.pool == undefined) - return; - try { - let res = await this.pool.set('redis_test', '1'); - if (res) - this.logger.info('数据库测试成功') - else - throw new Error('返回值错误') - } catch (error) { - this.logger.error('数据库测试失败:' + error) - } - - }, 10); - } - - public getPool(): Redis { - return this.pool; - } -} - -const redisConnection = new RedisConnection(); -export default redisConnection.getPool(); \ No newline at end of file diff --git a/Server/src/Plugs/Service/captchaSession.ts b/Server/src/Plugs/Service/captchaSession.ts deleted file mode 100644 index 7e4d3c0..0000000 --- a/Server/src/Plugs/Service/captchaSession.ts +++ /dev/null @@ -1,124 +0,0 @@ -/** - * @file captchaSession.ts - * @version 1.0.0 - * @description 旋转图像验证服务 - */ -import Logger from '../Logger'; -import RedisConnection from '../RedisConnection'; -type CaptchaSessionDataJSON = { - rotateDeg: number, - tryCount: number, - isPassed: boolean -} - -class _captchaSession { - private readonly logger = new Logger('Service][captchaSession'); - private readonly AllowMaxTryCount: number = 5; - private readonly AllowMaxAngleDiff: number = 8; - private readonly ExpriedTimeSec: number = 60; - private readonly RedisCommonKey: string = 'Service:captchaSession:'; - - constructor() { - this.logger.info('旋转图像验证服务已启动'); - } - - private async get(session: string): Promise { - try { - const result = await RedisConnection.get(this.RedisCommonKey + session); - if (result === null) - return; - return JSON.parse(result); - } catch (error) { - this.logger.error(`获取session[${session}]时发生错误:${error}`); - return; - } - } - - private async remove(session: string): Promise { - try { - await RedisConnection.del(this.RedisCommonKey + session); - } catch (error) { - this.logger.error(`删除session[${session}]失败`); - } - } - - /** - * - * @param session 验证会话标识符 - * @param rotateDeg 图片旋转角度 - * @returns true存储成功 false存储失败 - */ - public async add(session: string, rotateDeg: number): Promise { - const result: CaptchaSessionDataJSON = { - rotateDeg: rotateDeg, - tryCount: 0, - isPassed: false - } - try { - const res = await RedisConnection.set(this.RedisCommonKey + session, JSON.stringify(result)); - if (res && res === 'OK') { - RedisConnection.expire(this.RedisCommonKey + session, this.ExpriedTimeSec); - this.logger.info(`session[${session}]及角度[${rotateDeg}]已存储`); - return true; - } - this.logger.error(`session[${session}]及角度[${rotateDeg}]存储失败`); - return false; - } catch (error) { - this.logger.error(`session[${session}]及角度[${rotateDeg}]存储失败:${error}`); - return false; - } - } - - /** - * - * @param session 验证会话标识符 - * @returns true已验证过期 false未通过 - */ - public async isPassed(session: string): Promise { - const result = await this.get(session); - if (!result) - return false; - if (result.isPassed) - this.remove(session); - return result.isPassed; - } - - /** - * - * @param session 验证会话标识符 - * @param rotateDeg 图片旋转角度 - * @returns 0验证已过期或服务器错误 1通过验证 -1超过最大允许尝试次数 -2角度差异过大 - */ - public async check(session: string, rotateDeg: number): Promise { - try { - let result = await this.get(session); - if (!result) { - return 0; - } - if (result.isPassed) { - this.logger.info(`session[${session}]已通过验证,无需重复验证`); - return 1; - } - if (Math.abs(result.rotateDeg - rotateDeg) <= this.AllowMaxAngleDiff) { - result.isPassed = true; - await RedisConnection.del(this.RedisCommonKey + session); - await RedisConnection.set(this.RedisCommonKey + session, JSON.stringify(result)); - RedisConnection.expire(this.RedisCommonKey + session, this.ExpriedTimeSec); - return 1; - } - result.tryCount++; - if (result.tryCount >= this.AllowMaxTryCount) { - this.remove(session); - return -1; - } - RedisConnection.set(this.RedisCommonKey + session, JSON.stringify(result)); - return -2; - } catch (error) { - this.logger.error(`检查session[${session}]时发生错误:${error}`); - return 0; - } - } -} - -const captchaSession = new _captchaSession(); -export default captchaSession; \ No newline at end of file diff --git a/Server/src/Server/Server.ts b/Server/src/Server/Server.ts index 4ddb16c..ccec2b2 100644 --- a/Server/src/Server/Server.ts +++ b/Server/src/Server/Server.ts @@ -2,9 +2,6 @@ import Logger from "../Plugs/Logger"; import { APILoader } from "../Plugs/API/APILoader"; import config from "../config"; -// 加载Plugs -import '../Plugs/Service/captchaSession' - // 加载API import GetTest from "../APIs/GetTest"; import GetResourceList from "../APIs/GetResourceList"; @@ -13,8 +10,6 @@ import GetBlogContent from "../APIs/GetBlogContent"; import BlogLike from "../APIs/BlogLike"; import BlogComment from "../APIs/BlogComment"; import GetBlogComment from "../APIs/GetBlogComment"; -import GetCaptcha from "../APIs/GetCaptcha"; -import CheckCaptcha from "../APIs/CheckCaptcha"; import Login from "../APIs/Console/Login"; import GetResources from "../APIs/Console/GetResources"; @@ -45,8 +40,6 @@ class Server { this.apiLoader.add(BlogLike); this.apiLoader.add(BlogComment); this.apiLoader.add(GetBlogComment); - this.apiLoader.add(GetCaptcha); - this.apiLoader.add(CheckCaptcha); this.apiLoader.add(Login); this.apiLoader.add(GetResources); diff --git a/Server/src/Types/Schema.ts b/Server/src/Types/Schema.ts new file mode 100644 index 0000000..89c8b7f --- /dev/null +++ b/Server/src/Types/Schema.ts @@ -0,0 +1,101 @@ +/** 博客文章 */ +export type Blog = { + /** 博客uuid */ + uuid: string; + /** 标题 */ + title: string; + /** 描述 */ + description: string; + /** 文章md链接 */ + src: string; + /** 访问级别 */ + access_level: number; + /** 访问数 */ + visit_count: number; + /** 点赞数 */ + like_count: number; + /** 文章加密密码 */ + encrypt_p?: string; + /** 发布时间 */ + created_at: Date; +}; + +/** 博客评论 */ +export type BlogComment = { + /** 评论id */ + id: number; + /** 文章uuid */ + uuid: string; + /** 评论内容 */ + content: string; + /** 昵称 */ + name: string; + /** ip地址 */ + ip: string; + /** ip属地 */ + ip_address: string; + /** 用户代理 */ + user_agent: string; + /** 是否显示 */ + display: boolean; + /** 评论时间 */ + time: Date; +}; + +/** 资源信息 */ +export type Resource = { + /** 资源ID */ + uuid: string; + /** 资源类型 */ + type: 'download' | 'resource'; + /** 推荐程度,越小越推荐 */ + recommand?: number; + /** 标题 */ + title: string; + /** 描述 */ + describe: string; + /** 附加信息 */ + addition: any; + /** 图片url */ + icon_src: string; + /** 资源src */ + src: string; + /** 创建时间 */ + created_at: Date; +}; + +/** 用户信息 */ +export type User = { + /** 用户uuid */ + uuid: string; + /** 用户名 */ + username: string; + /** 密码salt */ + salt: string; + /** 密码hashed */ + password: string; + /** 手机号 */ + phone: string; + /** 权限列表 */ + permission: any; + /** 邮箱 */ + email?: string; + /** 创建时间 */ + created_at: Date; + /** 更新信息时间 */ + updated_at?: Date; +}; + +/** 用户登录日志 */ +export type UserLoginLog = { + /** 登陆记录id */ + id: number; + /** 用户uuid */ + user_uuid: string; + /** 登陆ip */ + ip: string; + /** 用户代理 */ + user_agent: string; + /** 登陆时间 */ + time: Date; +}; \ No newline at end of file diff --git a/Server/src/assets/captchaImgs/1.jpg b/Server/src/assets/captchaImgs/1.jpg deleted file mode 100644 index ce688c7..0000000 Binary files a/Server/src/assets/captchaImgs/1.jpg and /dev/null differ diff --git a/Server/src/assets/captchaImgs/2.jpg b/Server/src/assets/captchaImgs/2.jpg deleted file mode 100644 index a83748f..0000000 Binary files a/Server/src/assets/captchaImgs/2.jpg and /dev/null differ diff --git a/Server/src/assets/captchaImgs/3.jpg b/Server/src/assets/captchaImgs/3.jpg deleted file mode 100644 index 344c824..0000000 Binary files a/Server/src/assets/captchaImgs/3.jpg and /dev/null differ diff --git a/Server/src/assets/captchaImgs/4.jpg b/Server/src/assets/captchaImgs/4.jpg deleted file mode 100644 index 6c056c6..0000000 Binary files a/Server/src/assets/captchaImgs/4.jpg and /dev/null differ diff --git a/Server/src/assets/captchaImgs/5.jpg b/Server/src/assets/captchaImgs/5.jpg deleted file mode 100644 index 42824de..0000000 Binary files a/Server/src/assets/captchaImgs/5.jpg and /dev/null differ diff --git a/Server/src/assets/captchaImgs/6.jpg b/Server/src/assets/captchaImgs/6.jpg deleted file mode 100644 index 10b1269..0000000 Binary files a/Server/src/assets/captchaImgs/6.jpg and /dev/null differ diff --git a/tonecn/src/components/Blog/BlogComment.vue b/tonecn/src/components/Blog/BlogComment.vue index 43d3b13..2a2d216 100644 --- a/tonecn/src/components/Blog/BlogComment.vue +++ b/tonecn/src/components/Blog/BlogComment.vue @@ -1,6 +1,6 @@ - \ No newline at end of file diff --git a/tonecn/src/components/Console/Blogs.vue b/tonecn/src/components/Console/Blogs.vue index b5b1d9e..f07fc72 100644 --- a/tonecn/src/components/Console/Blogs.vue +++ b/tonecn/src/components/Console/Blogs.vue @@ -6,11 +6,10 @@ import { timestampToString } from '@/lib/timestampToString'; const tableData: Ref = ref([]) const dialogEditFormVisible = ref(false); type BlogContentData = { - id: string, uuid: string, title: string, description: string, - publish_time: Date, + created_at: Date, src: string, access_level: number, visit_count: number, @@ -34,11 +33,10 @@ const loadTableData = async () => { } } const editForm: BlogContentData = reactive({ - id: '', uuid: '', title: '', description: '', - publish_time: new Date(), + created_at: new Date(), src: '', encrypt_p: '', access_level: 0, @@ -46,22 +44,20 @@ const editForm: BlogContentData = reactive({ like_count: 0 }) const editHandle = (data: any) => { - editForm.id = data.id; editForm.uuid = data.uuid; editForm.title = data.title; editForm.description = data.description; - editForm.publish_time = new Date(+data.publish_time); + editForm.created_at = new Date(data.created_at); editForm.src = data.src; editForm.access_level = data.access_level; editForm.visit_count = data.visit_count; dialogEditFormVisible.value = true; } const addHandle = () => { - editForm.id = ''; editForm.uuid = ''; editForm.title = ''; editForm.description = ''; - editForm.publish_time = new Date(); + editForm.created_at = new Date(); editForm.src = ''; editForm.access_level = 10; editForm.visit_count = 0; @@ -70,16 +66,15 @@ const addHandle = () => { } const saveHandle = async () => { // 表单验证 - if (!editForm.title || !editForm.description || !editForm.publish_time || !editForm.src || !editForm.access_level) { + if (!editForm.title || !editForm.description || !editForm.created_at || !editForm.src || !editForm.access_level) { return ElMessage.warning('请先完成表单') } try { let res: BaseResponseData = await request.post('/console/saveBlog', { - id: editForm.id, uuid: editForm.uuid, title: editForm.title, description: editForm.description, - publish_time: editForm.publish_time.getTime(), + created_at: editForm.created_at, src: editForm.src, access_level: editForm.access_level, }) @@ -134,10 +129,10 @@ const saveHandle = async () => { return ElMessage.error(`保存失败 ${error}`); } } -const delHandle = async (data: { id: string, [key: string]: any }) => { - let { id } = data; +const delHandle = async (data: { uuid: string, [key: string]: any }) => { + let { uuid } = data; try { - let res: BaseResponseData = await request.delete('/console/blog?id=' + id); + let res: BaseResponseData = await request.delete('/console/blog?uuid=' + uuid); if (res.code == 0) { ElMessage.success('删除成功'); loadTableData(); @@ -149,7 +144,7 @@ const delHandle = async (data: { id: string, [key: string]: any }) => { } } const formatTime = (row: any, _column: any, _cellValue: any, _index: any) => { - return timestampToString(row.publish_time); + return new Date(row.created_at).toLocaleString(); }