diff --git a/bun.lockb b/bun.lockb
index 7be0543..d32b74e 100644
Binary files a/bun.lockb and b/bun.lockb differ
diff --git a/package.json b/package.json
index 12ba189..b197fb3 100644
--- a/package.json
+++ b/package.json
@@ -16,6 +16,8 @@
},
"dependencies": {
"bcryptjs": "^3.0.2",
+ "consolidate": "^1.0.4",
+ "get-paths": "^0.0.7",
"jsonwebtoken": "^9.0.0",
"knex": "^3.1.0",
"koa": "^3.0.0",
@@ -25,6 +27,7 @@
"module-alias": "^2.2.3",
"node-cron": "^4.1.0",
"path-to-regexp": "^8.2.0",
+ "pug": "^3.0.3",
"sqlite3": "^5.1.7"
},
"_moduleAliases": {
diff --git a/public/index.html b/public/index.html
deleted file mode 100644
index 535e0ac..0000000
--- a/public/index.html
+++ /dev/null
@@ -1,101 +0,0 @@
-
-
-
-
-
- 登录 / 注册
-
-
-
-
-
-
-
diff --git a/public/static/aa.txt b/public/static/aa.txt
new file mode 100644
index 0000000..1050001
--- /dev/null
+++ b/public/static/aa.txt
@@ -0,0 +1 @@
+asd
\ No newline at end of file
diff --git a/src/controllers/Page/HtmxController.js b/src/controllers/Page/HtmxController.js
new file mode 100644
index 0000000..1beabad
--- /dev/null
+++ b/src/controllers/Page/HtmxController.js
@@ -0,0 +1,23 @@
+export const Index = async ctx => {
+ return await ctx.render("index", { name: "bluescurry" })
+}
+
+export const Page = (name, data) => async ctx => {
+ return await ctx.render(name, data)
+}
+
+import Router from "utils/router.js"
+export function createRoutes() {
+ const router = new Router()
+ router.post("/clicked", async ctx => {
+ ctx.cookies.set("token", "sadas", {
+ httpOnly: true,
+ // Setting httpOnly to false allows JavaScript to access the cookie
+ // This enables browsers to automatically include the cookie in requests
+ sameSite: "lax",
+ // maxAge: 86400000, // Optional: cookie expiration in milliseconds (e.g., 24 hours)
+ })
+ return await ctx.render("htmx/fuck", { title: "HTMX Clicked" })
+ })
+ return router
+}
diff --git a/src/controllers/Page/PageController.js b/src/controllers/Page/PageController.js
new file mode 100644
index 0000000..733d9b4
--- /dev/null
+++ b/src/controllers/Page/PageController.js
@@ -0,0 +1,14 @@
+export const Index = async ctx => {
+ return await ctx.render("index", { name: "bluescurry" })
+}
+
+export const Page = (name, data) => async ctx => {
+ return await ctx.render(name, data)
+}
+
+import Router from "utils/router.js"
+export function createRoutes() {
+ const router = new Router()
+ router.get("/", Index)
+ return router
+}
diff --git a/src/controllers/userController.js b/src/controllers/userController.js
index b00549d..913f79a 100644
--- a/src/controllers/userController.js
+++ b/src/controllers/userController.js
@@ -26,6 +26,16 @@ export const login = async (ctx) => {
try {
const { username, email, password } = ctx.request.body
const result = await userService.login({ username, email, password })
+ if (result && result.token) {
+ ctx.cookies.set("token", result.token, {
+ httpOnly: true,
+ // Setting httpOnly to false allows JavaScript to access the cookie
+ // This enables browsers to automatically include the cookie in requests
+ sameSite: "lax",
+ secure: process.env.NODE_ENV === "production", // Use secure cookies in production
+ // maxAge: 86400000, // Optional: cookie expiration in milliseconds (e.g., 24 hours)
+ })
+ }
ctx.body = formatResponse(true, result)
} catch (err) {
ctx.body = formatResponse(false, null, err.message)
diff --git a/src/main.js b/src/main.js
index 65e5d4b..f9bd271 100644
--- a/src/main.js
+++ b/src/main.js
@@ -9,17 +9,12 @@ import log4js from "log4js"
// 应用插件与自动路由
import LoadMiddlewares from "./middlewares/install.js"
-import { autoRegisterControllers } from "utils/autoRegister.js"
-import bodyParser from "koa-bodyparser"
const logger = log4js.getLogger()
const app = new Koa()
-app.use(bodyParser());
// 注册插件
LoadMiddlewares(app)
-// 自动注册所有 controller
-autoRegisterControllers(app)
const PORT = process.env.PORT || 3000
@@ -38,7 +33,10 @@ const server = app.listen(PORT, () => {
return "localhost"
}
const localIP = getLocalIP()
- logger.trace(`服务器运行在: http://${localIP}:${port}`)
+ logger.trace(`===================【服务器地址】====================`)
+ logger.trace(` http://localhost:${port} (本地地址) `)
+ logger.trace(` http://${localIP}:${port} (本地地址) `)
+ logger.trace(`===================【服务器地址】====================`)
})
export default app
diff --git a/src/middlewares/Auth/auth.js b/src/middlewares/Auth/auth.js
index 779d3fd..4cbcae3 100644
--- a/src/middlewares/Auth/auth.js
+++ b/src/middlewares/Auth/auth.js
@@ -17,7 +17,12 @@ function matchList(list, path) {
}
function verifyToken(ctx) {
- const token = ctx.headers["authorization"]?.replace(/^Bearer\s/, "")
+ // 优先从 headers 获取 token
+ let token = ctx.headers["authorization"]?.replace(/^Bearer\s/, "")
+ // 如果 headers 没有,则从 cookies 获取
+ if (!token) {
+ token = ctx.cookies.get("authorization")
+ }
if (!token) return { ok: false }
try {
ctx.state.user = jwt.verify(token, JWT_SECRET)
diff --git a/src/middlewares/Views/index.js b/src/middlewares/Views/index.js
new file mode 100644
index 0000000..72339ec
--- /dev/null
+++ b/src/middlewares/Views/index.js
@@ -0,0 +1,78 @@
+import { resolve } from "path"
+import consolidate from "consolidate"
+import send from "../Send"
+import getPaths from "get-paths"
+// import pretty from "pretty"
+import { logger } from "@/logger"
+
+export default viewsMiddleware
+
+function viewsMiddleware(path, { engineSource = consolidate, extension = "html", options = {}, map } = {}) {
+ return function views(ctx, next) {
+ if (ctx.render) return next()
+
+ ctx.getRender = function (relPath, locals = {}) {
+ return getPaths(path, relPath, extension).then(paths => {
+ const suffix = paths.ext
+ const state = Object.assign(locals, options, ctx.state || {})
+ state.partials = Object.assign({}, options.partials || {})
+
+ if (isHtml(suffix) && !map) {
+ return send.getBody(ctx, paths.rel, { root: path })
+ }
+
+ const engineName = map && map[suffix] ? map[suffix] : suffix
+ const render = engineSource[engineName]
+
+ if (!engineName || !render) {
+ return Promise.reject(new Error(`Engine not found for the ".${suffix}" file extension`))
+ }
+
+ return render(resolve(path, paths.rel), state)
+ })
+ }
+
+ // 将 render 注入到 context 和 response 对象中
+ ctx.response.render = ctx.render = function (relPath, locals = {}) {
+ return getPaths(path, relPath, extension).then(paths => {
+ const suffix = paths.ext
+ const state = Object.assign(locals, options, ctx.state || {})
+ // deep copy partials
+ state.partials = Object.assign({}, options.partials || {})
+ logger.debug("render `%s` with %j", paths.rel, state)
+ ctx.type = "text/html"
+
+ // 如果是 html 文件,不编译直接 send 静态文件
+ if (isHtml(suffix) && !map) {
+ return send(ctx, paths.rel, {
+ root: path,
+ })
+ } else {
+ const engineName = map && map[suffix] ? map[suffix] : suffix
+
+ // 使用 engineSource 配置的渲染引擎 render
+ const render = engineSource[engineName]
+
+ if (!engineName || !render) return Promise.reject(new Error(`Engine not found for the ".${suffix}" file extension`))
+
+ return render(resolve(path, paths.rel), state).then(html => {
+ // since pug has deprecated `pretty` option
+ // we'll use the `pretty` package in the meanwhile
+ // if (locals.pretty) {
+ // debug("using `pretty` package to beautify HTML")
+ // html = pretty(html)
+ // }
+ ctx.body = html
+ })
+ }
+ })
+ }
+
+ // 中间件执行结束
+ return next()
+ }
+}
+
+function isHtml(ext) {
+ return ext === "html"
+}
diff --git a/src/middlewares/errorHandler/index.js b/src/middlewares/errorHandler/index.js
index d643593..e0cda43 100644
--- a/src/middlewares/errorHandler/index.js
+++ b/src/middlewares/errorHandler/index.js
@@ -1,11 +1,14 @@
// src/plugins/errorHandler.js
// 错误处理中间件插件
-function formatError(ctx, status, message) {
+function formatError(ctx, status, message, stack) {
const accept = ctx.accepts('json', 'html', 'text');
+ const isDev = process.env.NODE_ENV === 'development';
if (accept === 'json') {
ctx.type = 'application/json';
- ctx.body = { success: false, error: message };
+ ctx.body = isDev && stack
+ ? { success: false, error: message, stack }
+ : { success: false, error: message };
} else if (accept === 'html') {
ctx.type = 'html';
ctx.body = `
@@ -14,25 +17,38 @@ function formatError(ctx, status, message) {
${status} Error
${message}
+ ${isDev && stack ? `${stack}
` : ''}