使用pg数据库重构
This commit is contained in:
@@ -1,15 +1,15 @@
|
|||||||
import { API } from "../Plugs/API/API";
|
import { API } from "../Plugs/API/API";
|
||||||
import ServerStdResponse from "../ServerStdResponse";
|
import ServerStdResponse from "../ServerStdResponse";
|
||||||
import MySQLConnection from '../Plugs/MySQLConnection'
|
import Database from '../Plugs/Database'
|
||||||
import MountUserAgent from "../Plugs/Middleware/MountUserAgent";
|
import MountUserAgent from "../Plugs/Middleware/MountUserAgent";
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
import MountIP from "../Plugs/Middleware/MountIP";
|
import MountIP from "../Plugs/Middleware/MountIP";
|
||||||
import CheckCaptchaPassed from "../Plugs/Middleware/CheckCaptchaPassed";
|
import { BlogComment as BlogCommentType } from "@/Types/Schema"
|
||||||
|
|
||||||
// 提交博客评论
|
// 提交博客评论
|
||||||
class BlogComment extends API {
|
class BlogComment extends API {
|
||||||
constructor() {
|
constructor() {
|
||||||
super('POST', '/blogComment', CheckCaptchaPassed, MountUserAgent, MountIP);
|
super('POST', '/blogComment', MountUserAgent, MountIP);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async onRequset(data: any, res: any) {
|
public async onRequset(data: any, res: any) {
|
||||||
@@ -26,17 +26,17 @@ class BlogComment extends API {
|
|||||||
// 获取IPAddress
|
// 获取IPAddress
|
||||||
let ip_address = '未知'
|
let ip_address = '未知'
|
||||||
try {
|
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') {
|
if (ipAddressRes.data && ipAddressRes.data.msg == 'success') {
|
||||||
|
|
||||||
ip_address = ipAddressRes.data.data.countryCN == '中国' ? ipAddressRes.data.data.provinceCN : ipAddressRes.data.data.countryCN;
|
ip_address = ipAddressRes.data.data.countryCN == '中国' ? ipAddressRes.data.data.provinceCN : ipAddressRes.data.data.countryCN;
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.logger.warn('获取IP属地失败', 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()]);
|
let blogLikeRes = await Database.query<BlogCommentType>('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 || blogLikeRes.affectedRows != 1) {
|
if (!blogLikeRes) {
|
||||||
this.logger.error('发布博客评论时,数据库发生错误');
|
this.logger.error('发布博客评论时,数据库发生错误');
|
||||||
return res.json(ServerStdResponse.SERVER_ERROR);
|
return res.json(ServerStdResponse.SERVER_ERROR);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
import { API } from "../Plugs/API/API";
|
import { API } from "../Plugs/API/API";
|
||||||
import ServerStdResponse from "../ServerStdResponse";
|
import ServerStdResponse from "../ServerStdResponse";
|
||||||
import MySQLConnection from '../Plugs/MySQLConnection'
|
import Database from '../Plugs/Database'
|
||||||
import { Buffer } from 'buffer';
|
import { Buffer } from 'buffer';
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
|
import { Blog } from "@/Types/Schema";
|
||||||
|
|
||||||
|
|
||||||
// 点赞
|
// 点赞
|
||||||
@@ -18,15 +19,11 @@ class BlogLike extends API {
|
|||||||
return res.json(ServerStdResponse.INVALID_PARAMS);
|
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<Blog>('UPDATE blog SET like_count = like_count + 1 WHERE access_level > $1 AND uuid = $2 ', [this.defaultAccessLevel, bloguuid]);
|
||||||
if (!blogLikeRes) {
|
if (!blogLikeRes) {
|
||||||
this.logger.error('点赞博客时,数据库发生错误');
|
this.logger.error('点赞博客时,数据库发生错误');
|
||||||
return res.json(ServerStdResponse.SERVER_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);
|
return res.json(ServerStdResponse.OK);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
import { API } from "../../Plugs/API/API";
|
import { API } from "../../Plugs/API/API";
|
||||||
import ServerStdResponse from "../../ServerStdResponse";
|
import ServerStdResponse from "../../ServerStdResponse";
|
||||||
import MySQLConnection from '../../Plugs/MySQLConnection'
|
import Database from '../../Plugs/Database'
|
||||||
import Auth from "../../Plugs/Middleware/Auth";
|
import Auth from "../../Plugs/Middleware/Auth";
|
||||||
|
|
||||||
// 删除博客
|
// 删除博客
|
||||||
@@ -10,13 +10,13 @@ class DelBlog extends API {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public async onRequset(data: any, res: any) {
|
public async onRequset(data: any, res: any) {
|
||||||
let { id } = data;
|
let { uuid } = data;
|
||||||
if (!id) {
|
if (!uuid) {
|
||||||
return res.json(ServerStdResponse.PARAMS_MISSING);
|
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.SERVER_ERROR);
|
||||||
}
|
}
|
||||||
return res.json({ ...ServerStdResponse.OK });
|
return res.json({ ...ServerStdResponse.OK });
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { API } from "../../Plugs/API/API";
|
import { API } from "../../Plugs/API/API";
|
||||||
import ServerStdResponse from "../../ServerStdResponse";
|
import ServerStdResponse from "../../ServerStdResponse";
|
||||||
import MySQLConnection from '../../Plugs/MySQLConnection'
|
import Database from '../../Plugs/Database'
|
||||||
import Auth from "../../Plugs/Middleware/Auth";
|
import Auth from "../../Plugs/Middleware/Auth";
|
||||||
|
|
||||||
// 删除资源
|
// 删除资源
|
||||||
@@ -10,13 +10,13 @@ class DelResource extends API {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public async onRequset(data: any, res: any) {
|
public async onRequset(data: any, res: any) {
|
||||||
let { id } = data;
|
let { uuid } = data;
|
||||||
if (!id) {
|
if (!uuid) {
|
||||||
return res.json(ServerStdResponse.PARAMS_MISSING);
|
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.SERVER_ERROR);
|
||||||
}
|
}
|
||||||
return res.json({ ...ServerStdResponse.OK });
|
return res.json({ ...ServerStdResponse.OK });
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
import { API } from "../../Plugs/API/API";
|
import { API } from "../../Plugs/API/API";
|
||||||
import ServerStdResponse from "../../ServerStdResponse";
|
import ServerStdResponse from "../../ServerStdResponse";
|
||||||
import MySQLConnection from '../../Plugs/MySQLConnection'
|
import Database from '../../Plugs/Database'
|
||||||
import Auth from "../../Plugs/Middleware/Auth";
|
import Auth from "../../Plugs/Middleware/Auth";
|
||||||
|
import { Blog } from "@/Types/Schema";
|
||||||
|
|
||||||
// 获取博客列表
|
// 获取博客列表
|
||||||
class GetBlogs extends API {
|
class GetBlogs extends API {
|
||||||
@@ -10,8 +11,7 @@ class GetBlogs extends API {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public async onRequset(data: any, res: any) {
|
public async onRequset(data: any, res: any) {
|
||||||
// const { uuid } = data._jwt;
|
let resourcesRes = await Database.query<Blog>("SELECT * FROM blog ORDER BY created_at DESC");
|
||||||
let resourcesRes = await MySQLConnection.execute("SELECT * FROM blog ORDER BY id DESC");
|
|
||||||
if (!resourcesRes) {
|
if (!resourcesRes) {
|
||||||
return res.json(ServerStdResponse.SERVER_ERROR);
|
return res.json(ServerStdResponse.SERVER_ERROR);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { API, RequestData } from "../../Plugs/API/API";
|
import { API, RequestData } from "../../Plugs/API/API";
|
||||||
import ServerStdResponse from "../../ServerStdResponse";
|
import ServerStdResponse from "../../ServerStdResponse";
|
||||||
import MySQLConnection from '../../Plugs/MySQLConnection'
|
import Database from '../../Plugs/Database'
|
||||||
import Auth from "../../Plugs/Middleware/Auth";
|
import Auth from "../../Plugs/Middleware/Auth";
|
||||||
import jwt from "jsonwebtoken";
|
import jwt from "jsonwebtoken";
|
||||||
import config from "../../config";
|
import config from "../../config";
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
import { API } from "../../Plugs/API/API";
|
import { API } from "../../Plugs/API/API";
|
||||||
import ServerStdResponse from "../../ServerStdResponse";
|
import ServerStdResponse from "../../ServerStdResponse";
|
||||||
import MySQLConnection from '../../Plugs/MySQLConnection'
|
import Database from '../../Plugs/Database'
|
||||||
import Auth from "../../Plugs/Middleware/Auth";
|
import Auth from "../../Plugs/Middleware/Auth";
|
||||||
|
import { Resource } from "@/Types/Schema";
|
||||||
|
|
||||||
// 获取资源列表
|
// 获取资源列表
|
||||||
class GetResources extends API {
|
class GetResources extends API {
|
||||||
@@ -11,7 +12,7 @@ class GetResources extends API {
|
|||||||
|
|
||||||
public async onRequset(data: any, res: any) {
|
public async onRequset(data: any, res: any) {
|
||||||
// const { uuid } = data._jwt;
|
// const { uuid } = data._jwt;
|
||||||
let resourcesRes = await MySQLConnection.execute("SELECT * FROM resource");
|
let resourcesRes = await Database.query<Resource>("SELECT * FROM resource ORDER BY type, recommand, created_at DESC");
|
||||||
if (!resourcesRes) {
|
if (!resourcesRes) {
|
||||||
return res.json(ServerStdResponse.SERVER_ERROR);
|
return res.json(ServerStdResponse.SERVER_ERROR);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,17 +1,17 @@
|
|||||||
import { API } from "../../Plugs/API/API";
|
import { API } from "../../Plugs/API/API";
|
||||||
import ServerStdResponse from "../../ServerStdResponse";
|
import ServerStdResponse from "../../ServerStdResponse";
|
||||||
import MySQLConnection from '../../Plugs/MySQLConnection'
|
import Database from '../../Plugs/Database'
|
||||||
import MountUserAgent from "../../Plugs/Middleware/MountUserAgent";
|
import MountUserAgent from "../../Plugs/Middleware/MountUserAgent";
|
||||||
import MountIP from "../../Plugs/Middleware/MountIP";
|
import MountIP from "../../Plugs/Middleware/MountIP";
|
||||||
import CheckCaptchaPassed from "../../Plugs/Middleware/CheckCaptchaPassed";
|
|
||||||
import config from "../../config";
|
import config from "../../config";
|
||||||
import jwt from 'jsonwebtoken'
|
import jwt from 'jsonwebtoken'
|
||||||
import crypto from 'crypto'
|
import crypto from 'crypto'
|
||||||
|
import { User } from "@/Types/Schema";
|
||||||
|
|
||||||
// 登录
|
// 登录
|
||||||
class Login extends API {
|
class Login extends API {
|
||||||
constructor() {
|
constructor() {
|
||||||
super('POST', '/console/login', CheckCaptchaPassed, MountUserAgent, MountIP);
|
super('POST', '/console/login', MountUserAgent, MountIP);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async onRequset(data: any, res: any) {
|
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]);
|
let userInfoRes = await Database.query<User>('SELECT * FROM user WHERE username = $1', [username]);
|
||||||
if(!userInfoRes){
|
if (!userInfoRes) {
|
||||||
return res.json(ServerStdResponse.SERVER_ERROR);
|
return res.json(ServerStdResponse.SERVER_ERROR);
|
||||||
}
|
}
|
||||||
if (userInfoRes.length != 1) {
|
if (userInfoRes.length != 1) {
|
||||||
return res.json(ServerStdResponse.USER.NOTFOUND);
|
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);
|
return res.json(ServerStdResponse.USER.PASSWORD_ERROR);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 准备jwtToken
|
// 准备jwtToken
|
||||||
const jwtPayload = {
|
const jwtPayload = {
|
||||||
uuid: userInfoRes.uuid,
|
uuid: UserInfo.uuid,
|
||||||
loginTime: Date.now()
|
loginTime: Date.now()
|
||||||
}
|
}
|
||||||
let jwtToken = jwt.sign(jwtPayload, config.jwt.secret, { expiresIn: config.jwt.expiresIn });
|
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 } });
|
return res.json({ ...ServerStdResponse.OK, data: { token: jwtToken } });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
import { API } from "../../Plugs/API/API";
|
import { API } from "../../Plugs/API/API";
|
||||||
import ServerStdResponse from "../../ServerStdResponse";
|
import ServerStdResponse from "../../ServerStdResponse";
|
||||||
import MySQLConnection from '../../Plugs/MySQLConnection'
|
import Database from '../../Plugs/Database'
|
||||||
import Auth from "../../Plugs/Middleware/Auth";
|
import Auth from "../../Plugs/Middleware/Auth";
|
||||||
import crypto from 'crypto'
|
import crypto from 'crypto'
|
||||||
|
import { Blog } from "@/Types/Schema";
|
||||||
|
|
||||||
// 保存博客
|
// 保存博客
|
||||||
class SaveBlog extends API {
|
class SaveBlog extends API {
|
||||||
@@ -11,21 +12,21 @@ class SaveBlog extends API {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public async onRequset(data: any, res: any) {
|
public async onRequset(data: any, res: any) {
|
||||||
let { id, uuid, title, description, publish_time, src, access_level } = data;
|
let { uuid, title, description, created_at, src, access_level } = data;
|
||||||
if (!title || !description || !publish_time || !src || !access_level) {
|
if (!title || !description || !created_at || !src || !access_level) {
|
||||||
return res.json(ServerStdResponse.PARAMS_MISSING);
|
return res.json(ServerStdResponse.PARAMS_MISSING);
|
||||||
}
|
}
|
||||||
let execRes: any;
|
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<Blog>('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 {
|
} else {
|
||||||
// 新建
|
// 新建
|
||||||
const uuid = crypto.createHash('md5').update(`${Math.random()}${Date.now()}`).digest('hex');
|
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<Blog>('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.SERVER_ERROR);
|
||||||
}
|
}
|
||||||
return res.json({ ...ServerStdResponse.OK });
|
return res.json({ ...ServerStdResponse.OK });
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
import { API } from "../../Plugs/API/API";
|
import { API } from "../../Plugs/API/API";
|
||||||
import ServerStdResponse from "../../ServerStdResponse";
|
import ServerStdResponse from "../../ServerStdResponse";
|
||||||
import MySQLConnection from '../../Plugs/MySQLConnection'
|
import Database from '../../Plugs/Database'
|
||||||
import Auth from "../../Plugs/Middleware/Auth";
|
import Auth from "../../Plugs/Middleware/Auth";
|
||||||
|
import { Resource } from "@/Types/Schema";
|
||||||
|
import Crypto from 'crypto'
|
||||||
|
|
||||||
// 保存资源
|
// 保存资源
|
||||||
class SaveResource extends API {
|
class SaveResource extends API {
|
||||||
@@ -10,20 +12,21 @@ class SaveResource extends API {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public async onRequset(data: any, res: any) {
|
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) {
|
if (!type || !recommand || !title || !describe || !icon_src || !addition || !src) {
|
||||||
return res.json(ServerStdResponse.PARAMS_MISSING);
|
return res.json(ServerStdResponse.PARAMS_MISSING);
|
||||||
}
|
}
|
||||||
let execRes: any;
|
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<Resource>('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 {
|
} 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<Resource>('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.SERVER_ERROR);
|
||||||
}
|
}
|
||||||
return res.json({ ...ServerStdResponse.OK });
|
return res.json({ ...ServerStdResponse.OK });
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
import { API } from "../../Plugs/API/API";
|
import { API } from "../../Plugs/API/API";
|
||||||
import ServerStdResponse from "../../ServerStdResponse";
|
import ServerStdResponse from "../../ServerStdResponse";
|
||||||
import MySQLConnection from '../../Plugs/MySQLConnection'
|
import Database from '../../Plugs/Database'
|
||||||
import Auth from "../../Plugs/Middleware/Auth";
|
import Auth from "../../Plugs/Middleware/Auth";
|
||||||
import crypto from 'crypto'
|
import crypto from 'crypto'
|
||||||
|
import { Blog } from "@/Types/Schema";
|
||||||
|
|
||||||
// 设置博客密码
|
// 设置博客密码
|
||||||
class SetBlogPasswd extends API {
|
class SetBlogPasswd extends API {
|
||||||
@@ -16,7 +17,7 @@ class SetBlogPasswd extends API {
|
|||||||
return res.json(ServerStdResponse.PARAMS_MISSING);
|
return res.json(ServerStdResponse.PARAMS_MISSING);
|
||||||
}
|
}
|
||||||
const encrypt_p = crypto.createHash('sha256').update(passwd).digest('hex');
|
const encrypt_p = crypto.createHash('sha256').update(passwd).digest('hex');
|
||||||
MySQLConnection.execute('UPDATE blog SET encrypt_p = ? WHERE uuid = ?', [encrypt_p, uuid]);
|
Database.query<Blog>('UPDATE blog SET encrypt_p = $1 WHERE uuid = $2', [encrypt_p, uuid]);
|
||||||
return res.json({ ...ServerStdResponse.OK });
|
return res.json({ ...ServerStdResponse.OK });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { API } from "../Plugs/API/API";
|
import { API } from "../Plugs/API/API";
|
||||||
import ServerStdResponse from "../ServerStdResponse";
|
import ServerStdResponse from "../ServerStdResponse";
|
||||||
import MySQLConnection from '../Plugs/MySQLConnection'
|
import Database from '../Plugs/Database'
|
||||||
|
import { BlogComment } from "@/Types/Schema";
|
||||||
|
|
||||||
// 获取博客评论
|
// 获取博客评论
|
||||||
class GetBlogComment extends API {
|
class GetBlogComment extends API {
|
||||||
@@ -16,7 +17,7 @@ class GetBlogComment extends API {
|
|||||||
return res.json(ServerStdResponse.INVALID_PARAMS);
|
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<BlogComment>('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) {
|
if (!blogCommentRes) {
|
||||||
this.logger.error('获取博客评论时,数据库发生错误');
|
this.logger.error('获取博客评论时,数据库发生错误');
|
||||||
return res.json(ServerStdResponse.SERVER_ERROR);
|
return res.json(ServerStdResponse.SERVER_ERROR);
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
import { API } from "../Plugs/API/API";
|
import { API } from "../Plugs/API/API";
|
||||||
import ServerStdResponse from "../ServerStdResponse";
|
import ServerStdResponse from "../ServerStdResponse";
|
||||||
import MySQLConnection from '../Plugs/MySQLConnection'
|
import Database from '../Plugs/Database'
|
||||||
import { Buffer } from 'buffer';
|
import { Buffer } from 'buffer';
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
import crypto from 'crypto'
|
import crypto from 'crypto'
|
||||||
import MountIP from "../Plugs/Middleware/MountIP";
|
import MountIP from "../Plugs/Middleware/MountIP";
|
||||||
|
import { Blog } from "@/Types/Schema";
|
||||||
|
|
||||||
// 获取博客内容
|
// 获取博客内容
|
||||||
class GetBlogContent extends API {
|
class GetBlogContent extends API {
|
||||||
@@ -22,14 +23,14 @@ class GetBlogContent extends API {
|
|||||||
return res.json(ServerStdResponse.INVALID_PARAMS);
|
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<Blog>(`SELECT * from blog WHERE access_level in (${this.AccessLevelRule.allow.join(',')}) AND uuid = $1 `, [bloguuid]);
|
||||||
if (!blogContentRes) {
|
if (!blogContentRes) {
|
||||||
this.logger.error('查询时数据库发生错误');
|
this.logger.error('查询时数据库发生错误');
|
||||||
return res.json(ServerStdResponse.SERVER_ERROR);
|
return res.json(ServerStdResponse.SERVER_ERROR);
|
||||||
}
|
}
|
||||||
if (blogContentRes.length == 0) {
|
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<Blog>(`SELECT * from blog WHERE access_level in (${this.AccessLevelRule.encrypt_allow.join(',')}) AND uuid = $1 `, [bloguuid]);
|
||||||
if (!blogContentRes) {
|
if (!blogContentRes) {
|
||||||
this.logger.error('查询时数据库发生错误');
|
this.logger.error('查询时数据库发生错误');
|
||||||
return res.json(ServerStdResponse.SERVER_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');
|
const base64Content = Buffer.from(response.data, 'utf-8').toString('base64');
|
||||||
|
|
||||||
// 访问次数+1
|
// 访问次数+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({
|
return res.json({
|
||||||
...ServerStdResponse.OK, data: {
|
...ServerStdResponse.OK, data: {
|
||||||
data: base64Content,
|
data: base64Content,
|
||||||
info: {
|
info: {
|
||||||
title: blogContentRes[0].title,
|
title: blogContentRes[0].title,
|
||||||
description: blogContentRes[0].description,
|
description: blogContentRes[0].description,
|
||||||
publish_time: blogContentRes[0].publish_time,
|
publish_time: blogContentRes[0].created_at,
|
||||||
visit_count: blogContentRes[0].visit_count,
|
visit_count: blogContentRes[0].visit_count,
|
||||||
like_count: blogContentRes[0].like_count
|
like_count: blogContentRes[0].like_count
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { API } from "../Plugs/API/API";
|
import { API } from "../Plugs/API/API";
|
||||||
import ServerStdResponse from "../ServerStdResponse";
|
import ServerStdResponse from "../ServerStdResponse";
|
||||||
import MySQLConnection from '../Plugs/MySQLConnection'
|
import Database from '../Plugs/Database'
|
||||||
|
import { Blog } from "@/Types/Schema";
|
||||||
|
|
||||||
// 获取博客列表
|
// 获取博客列表
|
||||||
class GetBlogList extends API {
|
class GetBlogList extends API {
|
||||||
@@ -10,12 +11,12 @@ class GetBlogList extends API {
|
|||||||
private defaultAccessLevel = 9;
|
private defaultAccessLevel = 9;
|
||||||
|
|
||||||
public async onRequset(data: any, res: any) {
|
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]);
|
let blogListRes = await Database.query<Blog>('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){
|
if (!blogListRes) {
|
||||||
this.logger.error('查询时数据库发生错误');
|
this.logger.error('查询时数据库发生错误');
|
||||||
return res.json(ServerStdResponse.SERVER_ERROR);
|
return res.json(ServerStdResponse.SERVER_ERROR);
|
||||||
}
|
}
|
||||||
return res.json({...ServerStdResponse.OK, data: blogListRes});
|
return res.json({ ...ServerStdResponse.OK, data: blogListRes });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
import { API } from "../Plugs/API/API";
|
import { API } from "../Plugs/API/API";
|
||||||
import ServerStdResponse from "../ServerStdResponse";
|
import ServerStdResponse from "../ServerStdResponse";
|
||||||
import MySQLConnection from '../Plugs/MySQLConnection'
|
import Database from '../Plugs/Database'
|
||||||
|
import { Resource } from "@/Types/Schema";
|
||||||
|
|
||||||
// 获取资源列表
|
// 获取资源列表
|
||||||
class GetResourceList extends API {
|
class GetResourceList extends API {
|
||||||
@@ -11,18 +12,18 @@ class GetResourceList extends API {
|
|||||||
|
|
||||||
public async onRequset(data: any, res: any) {
|
public async onRequset(data: any, res: any) {
|
||||||
let { type } = data;
|
let { type } = data;
|
||||||
if(!type){
|
if (!type) {
|
||||||
return res.json(ServerStdResponse.PARAMS_MISSING);
|
return res.json(ServerStdResponse.PARAMS_MISSING);
|
||||||
}
|
}
|
||||||
if(!this.typeList.includes(type)){
|
if (!this.typeList.includes(type)) {
|
||||||
return res.json(ServerStdResponse.INVALID_PARAMS);
|
return res.json(ServerStdResponse.INVALID_PARAMS);
|
||||||
}
|
}
|
||||||
let resourceListRes = await MySQLConnection.execute('SELECT * from resource WHERE type = ? ORDER BY recommand ASC',[type]);
|
let resourceListRes = await Database.query<Resource>('SELECT * from resource WHERE type = $1 ORDER BY recommand ASC', [type]);
|
||||||
if(!resourceListRes){
|
if (!resourceListRes) {
|
||||||
this.logger.error('查询时数据库发生错误');
|
this.logger.error('查询时数据库发生错误');
|
||||||
return res.json(ServerStdResponse.SERVER_ERROR);
|
return res.json(ServerStdResponse.SERVER_ERROR);
|
||||||
}
|
}
|
||||||
return res.json({...ServerStdResponse.OK, data: resourceListRes});
|
return res.json({ ...ServerStdResponse.OK, data: resourceListRes });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import { API } from "../Plugs/API/API";
|
import { API } from "../Plugs/API/API";
|
||||||
import ServerStdResponse from "../ServerStdResponse";
|
import ServerStdResponse from "../ServerStdResponse";
|
||||||
import MySQLConnection from '../Plugs/MySQLConnection'
|
|
||||||
|
|
||||||
// 测试接口
|
// 测试接口
|
||||||
class GetTest extends API {
|
class GetTest extends API {
|
||||||
|
|||||||
78
Server/src/Plugs/Database.ts
Normal file
78
Server/src/Plugs/Database.ts
Normal file
@@ -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<any | undefined> 查询结果
|
||||||
|
*/
|
||||||
|
public async query<T>(sql: string, values?: any[]): Promise<T[] | undefined> {
|
||||||
|
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;
|
||||||
@@ -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;
|
|
||||||
@@ -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<any | undefined> 查询结果
|
|
||||||
*/
|
|
||||||
public async execute(sql: string, values?: any[], database?: string): Promise<any | undefined> {
|
|
||||||
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;
|
|
||||||
@@ -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 <Redis>this.pool;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const redisConnection = new RedisConnection();
|
|
||||||
export default redisConnection.getPool();
|
|
||||||
@@ -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<CaptchaSessionDataJSON | undefined> {
|
|
||||||
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<void> {
|
|
||||||
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<boolean> {
|
|
||||||
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<boolean> {
|
|
||||||
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<number> {
|
|
||||||
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;
|
|
||||||
@@ -2,9 +2,6 @@ import Logger from "../Plugs/Logger";
|
|||||||
import { APILoader } from "../Plugs/API/APILoader";
|
import { APILoader } from "../Plugs/API/APILoader";
|
||||||
import config from "../config";
|
import config from "../config";
|
||||||
|
|
||||||
// 加载Plugs
|
|
||||||
import '../Plugs/Service/captchaSession'
|
|
||||||
|
|
||||||
// 加载API
|
// 加载API
|
||||||
import GetTest from "../APIs/GetTest";
|
import GetTest from "../APIs/GetTest";
|
||||||
import GetResourceList from "../APIs/GetResourceList";
|
import GetResourceList from "../APIs/GetResourceList";
|
||||||
@@ -13,8 +10,6 @@ import GetBlogContent from "../APIs/GetBlogContent";
|
|||||||
import BlogLike from "../APIs/BlogLike";
|
import BlogLike from "../APIs/BlogLike";
|
||||||
import BlogComment from "../APIs/BlogComment";
|
import BlogComment from "../APIs/BlogComment";
|
||||||
import GetBlogComment from "../APIs/GetBlogComment";
|
import GetBlogComment from "../APIs/GetBlogComment";
|
||||||
import GetCaptcha from "../APIs/GetCaptcha";
|
|
||||||
import CheckCaptcha from "../APIs/CheckCaptcha";
|
|
||||||
|
|
||||||
import Login from "../APIs/Console/Login";
|
import Login from "../APIs/Console/Login";
|
||||||
import GetResources from "../APIs/Console/GetResources";
|
import GetResources from "../APIs/Console/GetResources";
|
||||||
@@ -45,8 +40,6 @@ class Server {
|
|||||||
this.apiLoader.add(BlogLike);
|
this.apiLoader.add(BlogLike);
|
||||||
this.apiLoader.add(BlogComment);
|
this.apiLoader.add(BlogComment);
|
||||||
this.apiLoader.add(GetBlogComment);
|
this.apiLoader.add(GetBlogComment);
|
||||||
this.apiLoader.add(GetCaptcha);
|
|
||||||
this.apiLoader.add(CheckCaptcha);
|
|
||||||
|
|
||||||
this.apiLoader.add(Login);
|
this.apiLoader.add(Login);
|
||||||
this.apiLoader.add(GetResources);
|
this.apiLoader.add(GetResources);
|
||||||
|
|||||||
101
Server/src/Types/Schema.ts
Normal file
101
Server/src/Types/Schema.ts
Normal file
@@ -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;
|
||||||
|
};
|
||||||
Binary file not shown.
|
Before Width: | Height: | Size: 141 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 116 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 120 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 134 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 142 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 110 KiB |
@@ -1,30 +1,24 @@
|
|||||||
const config = {
|
const config = {
|
||||||
mysql: {
|
pg: {
|
||||||
host: 'localhost',
|
host: 'localhost',
|
||||||
// host:'server.tonesc.cn',
|
// host:'server.tonesc.cn',
|
||||||
database: 'tonecn',
|
database: 'tonecn',
|
||||||
user: 'root',
|
user: 'tone',
|
||||||
password: 'Shi15023847146'// localhost
|
password: 'Shi15023847146'// localhost
|
||||||
// password: '245565' // server
|
// password: '245565' // server
|
||||||
},
|
},
|
||||||
redis: {
|
|
||||||
port: 6379,
|
|
||||||
host: 'localhost',
|
|
||||||
password: ''// localhost
|
|
||||||
// password: '2Pj4Ss9al3mS1'// server
|
|
||||||
},
|
|
||||||
jwt: {
|
jwt: {
|
||||||
secret: '17e50223f4a545ec9e36ebf08e2f71bb',
|
secret: '17e50223f4a545ec9e36ebf08e2f71bb',
|
||||||
expiresIn: '1d',
|
expiresIn: '1d',
|
||||||
},
|
},
|
||||||
oss: {
|
oss: {
|
||||||
accessKeyId: '',
|
accessKeyId: 'LTAI5t6ceNJqmMzPkjETgTzg',
|
||||||
accessKeySecret: '',
|
accessKeySecret: 'Eak6jFN0koDQ0yOg5KdxubjMbQ00Tk',
|
||||||
roleArn: '',
|
roleArn: 'acs:ram::1283668906130262:role/ctbu-co-oss-role',
|
||||||
bucket: '',
|
bucket: 'tone-personal',
|
||||||
region: '',
|
region: 'oss-cn-chengdu',
|
||||||
callbackUrl: '',
|
callbackUrl: '',
|
||||||
dir: '',
|
dir: 'personal-web',
|
||||||
stsExpirationSec: 3600
|
stsExpirationSec: 3600
|
||||||
},
|
},
|
||||||
apiPort: 23500,
|
apiPort: 23500,
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<script setup lang='ts'>
|
<script setup lang='ts'>
|
||||||
import { request, type BaseResponseData } from '@/lib/request';
|
import { request, type BaseResponseData } from '@/lib/request';
|
||||||
import { computed, onMounted, reactive, ref, watch } from 'vue';
|
import { computed, onMounted, reactive, ref, watch, watchEffect } from 'vue';
|
||||||
import { useRoute } from 'vue-router';
|
import { useRoute } from 'vue-router';
|
||||||
import { timestampToString } from '@/lib/timestampToString';
|
import { timestampToString } from '@/lib/timestampToString';
|
||||||
const model = defineModel();
|
const model = defineModel();
|
||||||
@@ -39,8 +39,9 @@ const loadComment = async () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
watch(model, (newValue, oldValue) => {
|
watchEffect(() => {
|
||||||
if (newValue) {
|
if (model.value) {
|
||||||
|
model.value = false;
|
||||||
loadComment();
|
loadComment();
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -54,8 +55,10 @@ watch(model, (newValue, oldValue) => {
|
|||||||
<div class="my-[10px]" v-for="blogcomment of blogCommentList">
|
<div class="my-[10px]" v-for="blogcomment of blogCommentList">
|
||||||
<div class=" text-[#555] dark:text-[#fff]">{{ blogcomment.name }}</div>
|
<div class=" text-[#555] dark:text-[#fff]">{{ blogcomment.name }}</div>
|
||||||
<div class="text-[12px] text-[#888] dark:text-[#aaa]">IP属地:{{ blogcomment.ip_address }}</div>
|
<div class="text-[12px] text-[#888] dark:text-[#aaa]">IP属地:{{ blogcomment.ip_address }}</div>
|
||||||
<div class="text-[12px] text-[#888] dark:text-[#aaa]">{{ timestampToString(blogcomment.time) }}</div>
|
<div class="text-[12px] text-[#888] dark:text-[#aaa]">{{ new Date(blogcomment.created_at).toLocaleString()
|
||||||
<div class="py-[10px] border-b border-b-[#ddd] text-[#333] dark:text-[#fff]">「{{ blogcomment.content }}」</div>
|
}}</div>
|
||||||
|
<div class="py-[10px] border-b border-b-[#ddd] text-[#333] dark:text-[#fff] whitespace-pre-wrap">{{
|
||||||
|
blogcomment.content }}</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="text-[14px] text-[#666] dark:text-[#fff] my-[15px]">—— {{ getStatusText }} ——</div>
|
<div class="text-[14px] text-[#666] dark:text-[#fff] my-[15px]">—— {{ getStatusText }} ——</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import { ElMessage, ElMessageBox } from 'element-plus';
|
|||||||
import { ref, onMounted, onUnmounted } from 'vue';
|
import { ref, onMounted, onUnmounted } from 'vue';
|
||||||
import { useRoute } from 'vue-router';
|
import { useRoute } from 'vue-router';
|
||||||
import { request, type BaseResponseData } from '@/lib/request';
|
import { request, type BaseResponseData } from '@/lib/request';
|
||||||
import RotationVerification from '../Common/RotationVerification.vue';
|
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
const bloguuid = route.params.uuid;
|
const bloguuid = route.params.uuid;
|
||||||
const emit = defineEmits(['comment-success'])
|
const emit = defineEmits(['comment-success'])
|
||||||
@@ -56,7 +55,7 @@ const commentHandle = () => {
|
|||||||
cancelButtonText: '取消',
|
cancelButtonText: '取消',
|
||||||
}).then(({ value }) => {
|
}).then(({ value }) => {
|
||||||
inputCommentName = value ? value : '';
|
inputCommentName = value ? value : '';
|
||||||
isCaptchaViewShow.value = true;
|
submitComment();
|
||||||
}).catch(() => {
|
}).catch(() => {
|
||||||
ElMessage.info('已取消')
|
ElMessage.info('已取消')
|
||||||
})
|
})
|
||||||
@@ -73,6 +72,7 @@ const submitComment = async () => {
|
|||||||
})
|
})
|
||||||
if (commentRes.code == 0) {
|
if (commentRes.code == 0) {
|
||||||
emit('comment-success');
|
emit('comment-success');
|
||||||
|
inputComment.value = '';
|
||||||
return ElMessage.success('评论成功~');
|
return ElMessage.success('评论成功~');
|
||||||
} else {
|
} else {
|
||||||
throw new Error(commentRes.message);
|
throw new Error(commentRes.message);
|
||||||
@@ -96,6 +96,4 @@ const submitComment = async () => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</transition>
|
</transition>
|
||||||
<RotationVerification v-if="isCaptchaViewShow"
|
|
||||||
@fail="() => { isCaptchaViewShow = false; ElMessage.warning('验证失败') }" @success="submitComment" />
|
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ let copyTextwithMsg = (text : string) => {
|
|||||||
<a href="https://beian.miit.gov.cn/">
|
<a href="https://beian.miit.gov.cn/">
|
||||||
<div class="mt-[5px] cursor-pointer dark:text-[#ccc]">备案号:渝ICP备2023009516号-1</div>
|
<div class="mt-[5px] cursor-pointer dark:text-[#ccc]">备案号:渝ICP备2023009516号-1</div>
|
||||||
</a>
|
</a>
|
||||||
<div class="mt-[6px] sm:mt-0 dark:text-[#ccc]">Copyright ©2020-2024 TONE All Rights Reserved.</div>
|
<div class="mt-[6px] sm:mt-0 dark:text-[#ccc]">Copyright ©2020-{{ new Date().getFullYear() }} TONE All Rights Reserved.</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="mr-0 sm:mr-[25px] flex sm:pb-0 pb-[20px]">
|
<div class="mr-0 sm:mr-[25px] flex sm:pb-0 pb-[20px]">
|
||||||
<el-popover trigger="click" placement="top" :width="160">
|
<el-popover trigger="click" placement="top" :width="160">
|
||||||
|
|||||||
@@ -1,199 +0,0 @@
|
|||||||
<script setup lang='ts'>
|
|
||||||
/**
|
|
||||||
* 旋转图像验证码组件
|
|
||||||
* @event success 验证成功
|
|
||||||
* @event fail 验证失败
|
|
||||||
* @requires ServerSDK
|
|
||||||
* @since 1.0.2
|
|
||||||
*/
|
|
||||||
import { request, type BaseResponseData } from '@/lib/request';
|
|
||||||
import { onBeforeMount, onMounted, ref } from 'vue';
|
|
||||||
let imageBase64 = ref('');
|
|
||||||
const emit = defineEmits(['fail', 'success'])
|
|
||||||
let onVerifying = ref(false);
|
|
||||||
let onVerifyFail = ref(false);
|
|
||||||
onBeforeMount(async () => {
|
|
||||||
try {
|
|
||||||
let res: BaseResponseData = await request.get('captcha')
|
|
||||||
localStorage.setItem('captcha-session', res.data.session)
|
|
||||||
imageBase64.value = res.data.imgPreStr + res.data.img;
|
|
||||||
} catch (error) {
|
|
||||||
console.log("获取图像验证码失败:" + error);
|
|
||||||
emit('fail');
|
|
||||||
}
|
|
||||||
})
|
|
||||||
onMounted(() => {
|
|
||||||
let slider = document.getElementById('RV-slider');
|
|
||||||
let imageEle = document.getElementById('RV-image');
|
|
||||||
let isDragging = false;// 正在移动标志位
|
|
||||||
let silderMoveable = true;// 是否可以开始移动标识位
|
|
||||||
let startX: number;
|
|
||||||
let deltaX: number;// 拖动距离
|
|
||||||
if(!slider || !imageEle){
|
|
||||||
throw new Error('element is null')
|
|
||||||
}
|
|
||||||
slider.addEventListener('mousedown', function (e) {
|
|
||||||
if (silderMoveable) {
|
|
||||||
isDragging = true;
|
|
||||||
startX = e.clientX;
|
|
||||||
onVerifyFail.value = false;
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
slider.addEventListener('touchstart', function (e) {
|
|
||||||
if (silderMoveable) {
|
|
||||||
e.preventDefault();
|
|
||||||
isDragging = true;
|
|
||||||
startX = e.touches[0].clientX;
|
|
||||||
onVerifyFail.value = false;
|
|
||||||
}
|
|
||||||
}, { passive: false });
|
|
||||||
|
|
||||||
document.addEventListener('mousemove', function (e) {
|
|
||||||
if (isDragging) {
|
|
||||||
silderMoveable = false;// 产生了位移,则需等待验证后才可再次移动
|
|
||||||
deltaX = e.clientX - startX;
|
|
||||||
// 对位移距离限幅
|
|
||||||
if (deltaX < 0)
|
|
||||||
deltaX = 0;
|
|
||||||
if (deltaX > 200)
|
|
||||||
deltaX = 200;
|
|
||||||
// 调整滑块条位置样式
|
|
||||||
slider.style.transform = ` translateX(${deltaX}px)`;
|
|
||||||
// 调整图片旋转样式 (200 -> 360)映射 y = 9/5x
|
|
||||||
imageEle.style.transform = `rotate(${deltaX * 9 / 5}deg)`
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
document.addEventListener('touchmove', function (e) {
|
|
||||||
if (isDragging) {
|
|
||||||
e.preventDefault();
|
|
||||||
silderMoveable = false;
|
|
||||||
deltaX = e.touches[0].clientX - startX;
|
|
||||||
if (deltaX < 0) deltaX = 0;
|
|
||||||
if (deltaX > 200) deltaX = 200;
|
|
||||||
slider.style.transform = `translateX(${deltaX}px)`;
|
|
||||||
imageEle.style.transform = `rotate(${deltaX * 9 / 5}deg)`;
|
|
||||||
}
|
|
||||||
}, { passive: false });
|
|
||||||
|
|
||||||
|
|
||||||
document.addEventListener('mouseup', async () => {
|
|
||||||
if (isDragging) {
|
|
||||||
isDragging = false;
|
|
||||||
if (!deltaX) {
|
|
||||||
silderMoveable = true;// 位移距离为0,无需验证可以继续移动
|
|
||||||
} else {
|
|
||||||
try {
|
|
||||||
onVerifying.value = true;
|
|
||||||
let res: BaseResponseData = await request.post('checkCaptcha', {
|
|
||||||
session: localStorage.getItem('captcha-session'),
|
|
||||||
rotateDeg: deltaX * 9 / 5
|
|
||||||
})
|
|
||||||
switch (res.code) {
|
|
||||||
case 0:
|
|
||||||
// 验证成功
|
|
||||||
emit('success');
|
|
||||||
break;
|
|
||||||
case -5002:
|
|
||||||
// 可以再试一次
|
|
||||||
onVerifyFail.value = true;
|
|
||||||
slider.style.transition = "transform 0.6s";
|
|
||||||
slider.style.transform = "translateX(0px)";
|
|
||||||
imageEle.style.transition = "transform 0.6s";
|
|
||||||
imageEle.style.transform = "rotate(0deg)";
|
|
||||||
setTimeout(() => {
|
|
||||||
isDragging = false;
|
|
||||||
silderMoveable = true;
|
|
||||||
slider.style.transition = "transform 0s";
|
|
||||||
imageEle.style.transition = "transform 0s";
|
|
||||||
}, 600);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
console.log('验证session过期、不存在、服务器错误')
|
|
||||||
emit('fail');
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.log("图像验证码错误:" + error);
|
|
||||||
emit('fail');
|
|
||||||
} finally {
|
|
||||||
onVerifying.value = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
document.addEventListener('touchend', async () => {
|
|
||||||
if (isDragging) {
|
|
||||||
isDragging = false;
|
|
||||||
if (!deltaX) {
|
|
||||||
silderMoveable = true;// 位移距离为0,无需验证可以继续移动
|
|
||||||
} else {
|
|
||||||
try {
|
|
||||||
onVerifying.value = true;
|
|
||||||
let res: BaseResponseData = await request.post('checkCaptcha', {
|
|
||||||
session: localStorage.getItem('captcha-session'),
|
|
||||||
rotateDeg: deltaX * 9 / 5
|
|
||||||
})
|
|
||||||
switch (res.code) {
|
|
||||||
case 0:
|
|
||||||
// 验证成功
|
|
||||||
emit('success');
|
|
||||||
break;
|
|
||||||
case -5002:
|
|
||||||
// 可以再试一次
|
|
||||||
onVerifyFail.value = true;
|
|
||||||
slider.style.transition = "transform 0.6s";
|
|
||||||
slider.style.transform = "translateX(0px)";
|
|
||||||
imageEle.style.transition = "transform 0.6s";
|
|
||||||
imageEle.style.transform = "rotate(0deg)";
|
|
||||||
setTimeout(() => {
|
|
||||||
isDragging = false;
|
|
||||||
silderMoveable = true;
|
|
||||||
slider.style.transition = "transform 0s";
|
|
||||||
imageEle.style.transition = "transform 0s";
|
|
||||||
}, 600);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
console.log('验证session过期、不存在、服务器错误')
|
|
||||||
emit('fail');
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.log("图像验证码错误:" + error);
|
|
||||||
emit('fail');
|
|
||||||
} finally {
|
|
||||||
onVerifying.value = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
})
|
|
||||||
|
|
||||||
</script>
|
|
||||||
<template>
|
|
||||||
<transition name="el-fade-in-linear">
|
|
||||||
<div class="fixed w-full h-full inset-0 bg-[#00000022] z-[2000] flex justify-center items-center" @click="$emit('fail')">
|
|
||||||
<div class="w-[300px] h-[400px] bg-white dark:bg-[#222] dark:border-[1px] flex flex-col items-center rounded-[15px]" onclick="event.stopPropagation()">
|
|
||||||
<div class="mt-[25px] text-[#888] dark:text-white">安全验证</div>
|
|
||||||
<div class="mt-[8px] dark:text-[#ccc]">{{ onVerifying ? "正在验证,请稍后..." : (onVerifyFail ? "验证失败,请再试一次" : "拖动滑块,使图片角度为水平")
|
|
||||||
}}
|
|
||||||
</div>
|
|
||||||
<div class="mt-[28px] w-[160px] h-[160px] rounded-full flex justify-center items-center overflow-hidden">
|
|
||||||
<img class="w-[226px] h-[226px] bg-gray-400 object-cover" :src="imageBase64" alt="" id="RV-image">
|
|
||||||
</div>
|
|
||||||
<div class="w-[240px] h-[40px] rounded-[20px] bg-slate-200 dark:bg-[#444] mt-[25px] flex items-center">
|
|
||||||
<div class="w-[45px] h-[45px] leading-[45px] bg-white dark:bg-[#999] rounded-full shadow-md text-center cursor-pointer relative translate-x-0" id="RV-slider">
|
|
||||||
<svg t="1706696449802" class="absolute left-[14px] top-[14px] dark:fill-white" viewBox="0 0 1024 1024" fill="#666" version="1.1"
|
|
||||||
xmlns="http://www.w3.org/2000/svg" p-id="1924" width="18" height="18">
|
|
||||||
<path
|
|
||||||
d="M567.32505 547.18536c20.970614-21.479197 20.970614-56.307424 0-77.790714L185.251168 77.115332c-20.971637-21.47715-54.975079-21.47715-75.948763 0-20.973684 21.484314-20.973684 56.30947 0 77.793784l344.188016 353.383446-344.188016 353.384469c-20.973684 21.484314-20.973684 56.311517 0 77.79276 20.971637 21.482267 54.975079 21.482267 75.948763 0l382.072858-392.280337 0.001024-0.004094zM440.60802 154.908092l344.18597 353.383446-344.18597 353.385493c-20.973684 21.484314-20.973684 56.311517 0 77.79276 20.972661 21.482267 54.975079 21.482267 75.949786 0l382.074905-392.281361c20.966521-21.478174 20.966521-56.307424 0-77.790714L516.555759 77.115332c-20.972661-21.47715-54.975079-21.47715-75.949786 0-20.971637 21.48329-20.971637 56.30947 0.002047 77.79276z"
|
|
||||||
p-id="1925"></path>
|
|
||||||
</svg>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</transition>
|
|
||||||
</template>
|
|
||||||
@@ -6,11 +6,10 @@ import { timestampToString } from '@/lib/timestampToString';
|
|||||||
const tableData: Ref<any[]> = ref([])
|
const tableData: Ref<any[]> = ref([])
|
||||||
const dialogEditFormVisible = ref(false);
|
const dialogEditFormVisible = ref(false);
|
||||||
type BlogContentData = {
|
type BlogContentData = {
|
||||||
id: string,
|
|
||||||
uuid: string,
|
uuid: string,
|
||||||
title: string,
|
title: string,
|
||||||
description: string,
|
description: string,
|
||||||
publish_time: Date,
|
created_at: Date,
|
||||||
src: string,
|
src: string,
|
||||||
access_level: number,
|
access_level: number,
|
||||||
visit_count: number,
|
visit_count: number,
|
||||||
@@ -34,11 +33,10 @@ const loadTableData = async () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
const editForm: BlogContentData = reactive({
|
const editForm: BlogContentData = reactive({
|
||||||
id: '',
|
|
||||||
uuid: '',
|
uuid: '',
|
||||||
title: '',
|
title: '',
|
||||||
description: '',
|
description: '',
|
||||||
publish_time: new Date(),
|
created_at: new Date(),
|
||||||
src: '',
|
src: '',
|
||||||
encrypt_p: '',
|
encrypt_p: '',
|
||||||
access_level: 0,
|
access_level: 0,
|
||||||
@@ -46,22 +44,20 @@ const editForm: BlogContentData = reactive({
|
|||||||
like_count: 0
|
like_count: 0
|
||||||
})
|
})
|
||||||
const editHandle = (data: any) => {
|
const editHandle = (data: any) => {
|
||||||
editForm.id = data.id;
|
|
||||||
editForm.uuid = data.uuid;
|
editForm.uuid = data.uuid;
|
||||||
editForm.title = data.title;
|
editForm.title = data.title;
|
||||||
editForm.description = data.description;
|
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.src = data.src;
|
||||||
editForm.access_level = data.access_level;
|
editForm.access_level = data.access_level;
|
||||||
editForm.visit_count = data.visit_count;
|
editForm.visit_count = data.visit_count;
|
||||||
dialogEditFormVisible.value = true;
|
dialogEditFormVisible.value = true;
|
||||||
}
|
}
|
||||||
const addHandle = () => {
|
const addHandle = () => {
|
||||||
editForm.id = '';
|
|
||||||
editForm.uuid = '';
|
editForm.uuid = '';
|
||||||
editForm.title = '';
|
editForm.title = '';
|
||||||
editForm.description = '';
|
editForm.description = '';
|
||||||
editForm.publish_time = new Date();
|
editForm.created_at = new Date();
|
||||||
editForm.src = '';
|
editForm.src = '';
|
||||||
editForm.access_level = 10;
|
editForm.access_level = 10;
|
||||||
editForm.visit_count = 0;
|
editForm.visit_count = 0;
|
||||||
@@ -70,16 +66,15 @@ const addHandle = () => {
|
|||||||
}
|
}
|
||||||
const saveHandle = async () => {
|
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('请先完成表单')
|
return ElMessage.warning('请先完成表单')
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
let res: BaseResponseData = await request.post('/console/saveBlog', {
|
let res: BaseResponseData = await request.post('/console/saveBlog', {
|
||||||
id: editForm.id,
|
|
||||||
uuid: editForm.uuid,
|
uuid: editForm.uuid,
|
||||||
title: editForm.title,
|
title: editForm.title,
|
||||||
description: editForm.description,
|
description: editForm.description,
|
||||||
publish_time: editForm.publish_time.getTime(),
|
created_at: editForm.created_at,
|
||||||
src: editForm.src,
|
src: editForm.src,
|
||||||
access_level: editForm.access_level,
|
access_level: editForm.access_level,
|
||||||
})
|
})
|
||||||
@@ -134,10 +129,10 @@ const saveHandle = async () => {
|
|||||||
return ElMessage.error(`保存失败 ${error}`);
|
return ElMessage.error(`保存失败 ${error}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const delHandle = async (data: { id: string, [key: string]: any }) => {
|
const delHandle = async (data: { uuid: string, [key: string]: any }) => {
|
||||||
let { id } = data;
|
let { uuid } = data;
|
||||||
try {
|
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) {
|
if (res.code == 0) {
|
||||||
ElMessage.success('删除成功');
|
ElMessage.success('删除成功');
|
||||||
loadTableData();
|
loadTableData();
|
||||||
@@ -149,7 +144,7 @@ const delHandle = async (data: { id: string, [key: string]: any }) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
const formatTime = (row: any, _column: any, _cellValue: any, _index: any) => {
|
const formatTime = (row: any, _column: any, _cellValue: any, _index: any) => {
|
||||||
return timestampToString(row.publish_time);
|
return new Date(row.created_at).toLocaleString();
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
@@ -159,11 +154,10 @@ const formatTime = (row: any, _column: any, _cellValue: any, _index: any) => {
|
|||||||
</div>
|
</div>
|
||||||
<!-- 数据列表 -->
|
<!-- 数据列表 -->
|
||||||
<el-table :data="tableData" border class="w-full">
|
<el-table :data="tableData" border class="w-full">
|
||||||
<el-table-column prop="id" label="id" width="50" />
|
|
||||||
<el-table-column prop="uuid" label="uuid" width="120" show-overflow-tooltip />
|
<el-table-column prop="uuid" label="uuid" width="120" show-overflow-tooltip />
|
||||||
<el-table-column prop="title" label="标题" width="240" />
|
<el-table-column prop="title" label="标题" width="250" />
|
||||||
<el-table-column prop="description" label="描述" width="200" show-overflow-tooltip />
|
<el-table-column prop="description" label="描述" width="300" show-overflow-tooltip />
|
||||||
<el-table-column prop="publish_time" label="发布时间" width="160" :formatter="formatTime" />
|
<el-table-column prop="created_at" label="发布时间" width="160" :formatter="formatTime" />
|
||||||
<el-table-column prop="access_level" label="可访问级别" width="100" />
|
<el-table-column prop="access_level" label="可访问级别" width="100" />
|
||||||
<el-table-column prop="visit_count" label="访问量" width="80" />
|
<el-table-column prop="visit_count" label="访问量" width="80" />
|
||||||
<el-table-column prop="like_count" label="点赞量" width="80" />
|
<el-table-column prop="like_count" label="点赞量" width="80" />
|
||||||
@@ -171,7 +165,7 @@ const formatTime = (row: any, _column: any, _cellValue: any, _index: any) => {
|
|||||||
<template #default="scope">
|
<template #default="scope">
|
||||||
<el-text>{{ scope.encrypt_p ? "是" : "否" }}</el-text>
|
<el-text>{{ scope.encrypt_p ? "是" : "否" }}</el-text>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column> -->
|
</el-table-column> -->
|
||||||
<el-table-column fixed="right" label="操作" min-width="110">
|
<el-table-column fixed="right" label="操作" min-width="110">
|
||||||
<template #default="scope">
|
<template #default="scope">
|
||||||
<el-button link type="primary" size="small" @click="editHandle(scope.row)">编辑</el-button>
|
<el-button link type="primary" size="small" @click="editHandle(scope.row)">编辑</el-button>
|
||||||
@@ -182,9 +176,6 @@ const formatTime = (row: any, _column: any, _cellValue: any, _index: any) => {
|
|||||||
<!-- 编辑、添加博客对话框 -->
|
<!-- 编辑、添加博客对话框 -->
|
||||||
<el-dialog v-model="dialogEditFormVisible" title="编辑" width="800">
|
<el-dialog v-model="dialogEditFormVisible" title="编辑" width="800">
|
||||||
<el-form :model="editForm" label-width="auto" style="margin: 0 30px;">
|
<el-form :model="editForm" label-width="auto" style="margin: 0 30px;">
|
||||||
<el-form-item label="id">
|
|
||||||
<el-input v-model="editForm.id" disabled />
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item label="uuid">
|
<el-form-item label="uuid">
|
||||||
<el-input v-model="editForm.uuid" disabled />
|
<el-input v-model="editForm.uuid" disabled />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
@@ -195,7 +186,7 @@ const formatTime = (row: any, _column: any, _cellValue: any, _index: any) => {
|
|||||||
<el-input v-model="editForm.description" type="textarea" autosize />
|
<el-input v-model="editForm.description" type="textarea" autosize />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="发布时间">
|
<el-form-item label="发布时间">
|
||||||
<el-date-picker v-model="editForm.publish_time" type="datetime" placeholder="选择发布时间" />
|
<el-date-picker v-model="editForm.created_at" type="datetime" placeholder="选择发布时间" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="文章链接">
|
<el-form-item label="文章链接">
|
||||||
<el-input v-model="editForm.src" type="textarea" autosize />
|
<el-input v-model="editForm.src" type="textarea" autosize />
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { onMounted, reactive, ref, type Ref } from 'vue';
|
|||||||
import { request, type BaseResponseData } from '../../lib/request'
|
import { request, type BaseResponseData } from '../../lib/request'
|
||||||
import { ElMessage } from 'element-plus';
|
import { ElMessage } from 'element-plus';
|
||||||
type ResourceData = {
|
type ResourceData = {
|
||||||
id: string,
|
uuid: string,
|
||||||
type: string,
|
type: string,
|
||||||
recommand: number,
|
recommand: number,
|
||||||
title: string,
|
title: string,
|
||||||
@@ -31,7 +31,7 @@ const loadTableData = async () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
const editForm: ResourceData = reactive({
|
const editForm: ResourceData = reactive({
|
||||||
id: '',
|
uuid: '',
|
||||||
type: '',
|
type: '',
|
||||||
recommand: 1,
|
recommand: 1,
|
||||||
title: '',
|
title: '',
|
||||||
@@ -42,7 +42,7 @@ const editForm: ResourceData = reactive({
|
|||||||
})
|
})
|
||||||
const openEditFormSrc = () => { window.open(editForm.src); }
|
const openEditFormSrc = () => { window.open(editForm.src); }
|
||||||
const editHandle = (data: ResourceData) => {
|
const editHandle = (data: ResourceData) => {
|
||||||
editForm.id = data.id;
|
editForm.uuid = data.uuid;
|
||||||
editForm.type = data.type;
|
editForm.type = data.type;
|
||||||
editForm.recommand = +data.recommand;
|
editForm.recommand = +data.recommand;
|
||||||
editForm.title = data.title;
|
editForm.title = data.title;
|
||||||
@@ -53,7 +53,7 @@ const editHandle = (data: ResourceData) => {
|
|||||||
dialogEditFormVisible.value = true;
|
dialogEditFormVisible.value = true;
|
||||||
}
|
}
|
||||||
const addHandle = () => {
|
const addHandle = () => {
|
||||||
editForm.id = '';
|
editForm.uuid = '';
|
||||||
editForm.type = '';
|
editForm.type = '';
|
||||||
editForm.recommand = 1;
|
editForm.recommand = 1;
|
||||||
editForm.title = '';
|
editForm.title = '';
|
||||||
@@ -70,7 +70,7 @@ const saveHandle = async () => {
|
|||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
let res: BaseResponseData = await request.post('/console/saveResource', {
|
let res: BaseResponseData = await request.post('/console/saveResource', {
|
||||||
id: editForm.id,
|
uuid: editForm.uuid,
|
||||||
type: editForm.type,
|
type: editForm.type,
|
||||||
recommand: editForm.recommand,
|
recommand: editForm.recommand,
|
||||||
title: editForm.title,
|
title: editForm.title,
|
||||||
@@ -91,9 +91,9 @@ const saveHandle = async () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
const delHandle = async (data: { id: string, [key: string]: any }) => {
|
const delHandle = async (data: { id: string, [key: string]: any }) => {
|
||||||
let { id } = data;
|
let { uuid } = data;
|
||||||
try {
|
try {
|
||||||
let res: BaseResponseData = await request.delete('/console/resource?id=' + id);
|
let res: BaseResponseData = await request.delete('/console/resource?uuid=' + uuid);
|
||||||
if (res.code == 0) {
|
if (res.code == 0) {
|
||||||
ElMessage.success('删除成功');
|
ElMessage.success('删除成功');
|
||||||
loadTableData();
|
loadTableData();
|
||||||
@@ -104,6 +104,9 @@ const delHandle = async (data: { id: string, [key: string]: any }) => {
|
|||||||
return ElMessage.error(`删除失败 ${error}`);
|
return ElMessage.error(`删除失败 ${error}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
const formatTime = (row: any, _column: any, _cellValue: any, _index: any) => {
|
||||||
|
return new Date(row.created_at).toLocaleString();
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<div class="py-[15px] px-[20px]">
|
<div class="py-[15px] px-[20px]">
|
||||||
@@ -112,7 +115,7 @@ const delHandle = async (data: { id: string, [key: string]: any }) => {
|
|||||||
</div>
|
</div>
|
||||||
<!-- 数据列表 -->
|
<!-- 数据列表 -->
|
||||||
<el-table :data="tableData" border class="w-full">
|
<el-table :data="tableData" border class="w-full">
|
||||||
<el-table-column prop="id" label="id" width="50" />
|
<el-table-column prop="uuid" label="uuid" width="60" show-overflow-tooltip />
|
||||||
<el-table-column prop="type" label="类型" width="80" sortable>
|
<el-table-column prop="type" label="类型" width="80" sortable>
|
||||||
<template #default="scope">
|
<template #default="scope">
|
||||||
{{ scope.row.type == 'resource' ? '资源' : scope.row.type == 'download' ? '下载' : '未知' }}
|
{{ scope.row.type == 'resource' ? '资源' : scope.row.type == 'download' ? '下载' : '未知' }}
|
||||||
@@ -140,12 +143,13 @@ const delHandle = async (data: { id: string, [key: string]: any }) => {
|
|||||||
<el-button link type="primary" size="small" @click="delHandle(scope.row)">删除</el-button>
|
<el-button link type="primary" size="small" @click="delHandle(scope.row)">删除</el-button>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
|
<el-table-column prop="created_at" label="创建时间" width="180" :formatter="formatTime" />
|
||||||
</el-table>
|
</el-table>
|
||||||
<!-- 编辑、添加资源对话框 -->
|
<!-- 编辑、添加资源对话框 -->
|
||||||
<el-dialog v-model="dialogEditFormVisible" title="编辑" width="800">
|
<el-dialog v-model="dialogEditFormVisible" title="编辑" width="800">
|
||||||
<el-form :model="editForm" label-width="auto" class="mx-[30px]">
|
<el-form :model="editForm" label-width="auto" class="mx-[30px]">
|
||||||
<el-form-item label="id">
|
<el-form-item label="uuid">
|
||||||
<el-input v-model="editForm.id" disabled />
|
<el-input v-model="editForm.uuid" disabled />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="类型">
|
<el-form-item label="类型">
|
||||||
<el-select v-model="editForm.type" placeholder="请选择类型">
|
<el-select v-model="editForm.type" placeholder="请选择类型">
|
||||||
|
|||||||
@@ -4,8 +4,8 @@ type BaseResponseData = {
|
|||||||
message: string,
|
message: string,
|
||||||
data: any
|
data: any
|
||||||
};
|
};
|
||||||
// axios.defaults.baseURL = "http://localhost:23500";
|
axios.defaults.baseURL = "http://localhost:23500";
|
||||||
axios.defaults.baseURL = "https://tonesc.cn/apis";
|
// axios.defaults.baseURL = "https://tonesc.cn/apis";
|
||||||
|
|
||||||
axios.interceptors.response.use((response) => {
|
axios.interceptors.response.use((response) => {
|
||||||
if (response.data && response.data.code == -5) {
|
if (response.data && response.data.code == -5) {
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
<script setup lang='ts'>
|
<script setup lang='ts'>
|
||||||
import { ElMessage } from 'element-plus';
|
import { ElMessage } from 'element-plus';
|
||||||
import { onMounted, reactive, ref } from 'vue';
|
import { onMounted, reactive, ref } from 'vue';
|
||||||
import RotationVerification from '@/components/Common/RotationVerification.vue';
|
|
||||||
import { request, type BaseResponseData } from '@/lib/request';
|
import { request, type BaseResponseData } from '@/lib/request';
|
||||||
|
|
||||||
const containerHeight = ref('800px');
|
const containerHeight = ref('800px');
|
||||||
@@ -11,21 +10,17 @@ const formData = reactive({
|
|||||||
username: '',
|
username: '',
|
||||||
password: ''
|
password: ''
|
||||||
})
|
})
|
||||||
const loginHandle = () => {
|
const login = async () => {
|
||||||
if (!formData.username || !formData.password) {
|
if (!formData.username || !formData.password) {
|
||||||
return ElMessage.warning('请填写账户名和密码')
|
return ElMessage.warning('请填写账户名和密码')
|
||||||
}
|
}
|
||||||
isCaptchaShow.value = true;
|
|
||||||
}
|
|
||||||
const login = async () => {
|
|
||||||
loginStatus.value = true;
|
|
||||||
try {
|
try {
|
||||||
|
loginStatus.value = true;
|
||||||
let loginRes: BaseResponseData = await request.post('/console/login', {
|
let loginRes: BaseResponseData = await request.post('/console/login', {
|
||||||
username: formData.username,
|
username: formData.username,
|
||||||
password: formData.password,
|
password: formData.password,
|
||||||
session: localStorage.getItem('captcha-session')
|
session: localStorage.getItem('captcha-session')
|
||||||
})
|
})
|
||||||
loginStatus.value = false;
|
|
||||||
switch (loginRes.code) {
|
switch (loginRes.code) {
|
||||||
case 0:
|
case 0:
|
||||||
// 成功
|
// 成功
|
||||||
@@ -50,8 +45,9 @@ const login = async () => {
|
|||||||
return ElMessage.error(`未知错误 ${loginRes.message}`)
|
return ElMessage.error(`未知错误 ${loginRes.message}`)
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
loginStatus.value = false;
|
|
||||||
return ElMessage.error(`未知错误 ${error}`)
|
return ElMessage.error(`未知错误 ${error}`)
|
||||||
|
} finally {
|
||||||
|
loginStatus.value = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
@@ -75,12 +71,11 @@ onMounted(async () => {
|
|||||||
clearable>
|
clearable>
|
||||||
</el-input>
|
</el-input>
|
||||||
<el-input v-model="formData.password" show-password class="w-full h-[35px] text-[16px] mt-[10px]"
|
<el-input v-model="formData.password" show-password class="w-full h-[35px] text-[16px] mt-[10px]"
|
||||||
placeholder="密码" @keyup.enter="loginHandle">
|
placeholder="密码" @keyup.enter="login">
|
||||||
</el-input>
|
</el-input>
|
||||||
<el-button class="mt-[12px] mb-[120px] w-full h-[35px] font-bold login-button hover:!bg-white hover:!border-gray-300 hover:!text-gray-800" @click="loginHandle"
|
<el-button
|
||||||
:loading="loginStatus">登录</el-button>
|
class="mt-[12px] mb-[120px] w-full h-[35px] font-bold login-button hover:!bg-white hover:!border-gray-300 hover:!text-gray-800"
|
||||||
|
@click="login" :loading="loginStatus">登录</el-button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<RotationVerification v-if="isCaptchaShow" @fail="() => { isCaptchaShow = false; ElMessage.warning('验证失败') }"
|
|
||||||
@success="() => { isCaptchaShow = false; login() }" />
|
|
||||||
</template>
|
</template>
|
||||||
Reference in New Issue
Block a user