Browse Source

feat: Implement authentication and job management controllers

- Added AuthController for user registration, login, and fetching user details.
- Introduced JobController for managing scheduled jobs, including listing, starting, stopping, and updating job frequency.
- Created StatusController to provide a simple health check endpoint.
- Implemented session middleware for managing user sessions.
- Developed login and registration pages with form handling and validation.
route
谢亚昕 1 month ago
parent
commit
d06982da2b
  1. BIN
      database/development.sqlite3-shm
  2. BIN
      database/development.sqlite3-wal
  3. 3047
      package-lock.json
  4. 3
      package.json
  5. 0
      src/controllers/Api/AuthController.js
  6. 0
      src/controllers/Api/JobController.js
  7. 0
      src/controllers/Api/StatusController.js
  8. 24
      src/controllers/Page/PageController.js
  9. 3
      src/middlewares/Auth/auth.js
  10. 14
      src/middlewares/Session/index.js
  11. 2
      src/middlewares/install.js
  12. 2
      src/views/page/index/index.pug
  13. 31
      src/views/page/login/index.pug
  14. 94
      src/views/page/register/index.pug

BIN
database/development.sqlite3-shm

Binary file not shown.

BIN
database/development.sqlite3-wal

Binary file not shown.

3047
package-lock.json

File diff suppressed because it is too large

3
package.json

@ -22,6 +22,7 @@
"knex": "^3.1.0",
"koa": "^3.0.0",
"koa-bodyparser": "^4.4.1",
"koa-session": "^7.0.2",
"log4js": "^6.9.1",
"minimatch": "^9.0.0",
"module-alias": "^2.2.3",
@ -36,4 +37,4 @@
"utils": "./src/utils",
"services": "./src/services"
}
}
}

0
src/controllers/AuthController.js → src/controllers/Api/AuthController.js

0
src/controllers/JobController.js → src/controllers/Api/JobController.js

0
src/controllers/StatusController.js → src/controllers/Api/StatusController.js

24
src/controllers/Page/PageController.js

@ -1,8 +1,26 @@
import Router from "utils/router.js"
import UserService from "services/UserService.js"
class PageController {
constructor() {
this.userService = new UserService()
}
async index(ctx) {
return await ctx.render("page/index/index", { title: "沧源一场" })
const user = ctx.state.user
return await ctx.render("page/index/index", { title: "沧源一场", user: user })
}
async loginPost(ctx) {
const { username, email, password } = ctx.request.body
const result = await this.userService.login({ username, email, password })
ctx.session.user = result.user
ctx.body = { success: true, message: "登录成功" }
}
async registerPost(ctx) {
const { username, email, password } = ctx.request.body
await this.userService.register({ username, email, password })
return ctx.redirect("/login")
}
page(name, data) {
@ -15,6 +33,10 @@ class PageController {
const controller = new PageController()
const router = new Router({ auth: "try" })
router.get("/", controller.index.bind(controller))
router.get("/login", controller.page("page/login/index", { title: "登录" }), { auth: false })
router.post("/login", controller.loginPost.bind(controller), { auth: false })
router.get("/register", controller.page("page/register/index", { title: "注册" }), { auth: false })
router.post("/register", controller.registerPost.bind(controller), { auth: false })
return router
}
}

3
src/middlewares/Auth/auth.js

@ -35,6 +35,9 @@ export default function authMiddleware(options = {
blackList: []
}) {
return async (ctx, next) => {
if(ctx.session.user) {
ctx.state.user = ctx.session.user
}
// 黑名单优先生效
if (matchList(options.blackList, ctx.path).matched) {
ctx.status = 403

14
src/middlewares/Session/index.js

@ -0,0 +1,14 @@
import session from 'koa-session';
export default (app) => {
const CONFIG = {
key: 'koa:sess', // cookie key
maxAge: 86400000, // 1天
httpOnly: true,
signed: true,
rolling: false,
renew: false,
};
app.keys = app.keys || ['koa3-demo-session-secret'];
return session(CONFIG, app);
};

2
src/middlewares/install.js

@ -7,6 +7,7 @@ import ErrorHandler from "./ErrorHandler"
import { auth } from "./Auth"
import bodyParser from "koa-bodyparser"
import Views from "./Views"
import Session from "./Session"
import { autoRegisterControllers } from "@/utils/ForRegister.js"
const __dirname = path.dirname(fileURLToPath(import.meta.url))
@ -15,6 +16,7 @@ const publicPath = resolve(__dirname, "../../public")
export default app => {
app.use(ErrorHandler())
app.use(ResponseTime)
app.use(Session(app));
app.use(
auth({
whiteList: [

2
src/views/page/index/index.pug

@ -6,3 +6,5 @@ extends /layouts/page.pug
block pageContent
div adsd
if user
div user: #{user.username}

31
src/views/page/login/index.pug

@ -0,0 +1,31 @@
extends /layouts/page.pug
block content
.login-container
h2 登录
form#loginForm(action="/login" method="post")
.form-group
label(for="username") 用户名
input#username(type="text" name="username" required placeholder="请输入用户名")
.form-group
label(for="password") 密码
input#password(type="password" name="password" required placeholder="请输入密码")
button(type="submit") 登录
script.
document.getElementById('loginForm').onsubmit = async function(e) {
e.preventDefault();
const form = e.target;
const data = Object.fromEntries(new FormData(form));
const res = await fetch(form.action, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data)
});
const result = await res.json();
if(result.success) {
alert('登录成功');
window.location.href = '/';
} else {
alert(result.message || '登录失败');
}
}

94
src/views/page/register/index.pug

@ -0,0 +1,94 @@
doctype html
html
head
meta(charset="UTF-8")
meta(name="viewport", content="width=device-width, initial-scale=1.0")
title 注册
style.
body {
background: #f5f7fa;
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
}
.register-container {
max-width: 400px;
margin: 60px auto;
background: #fff;
border-radius: 10px;
box-shadow: 0 2px 16px rgba(0,0,0,0.08);
padding: 32px 28px 24px 28px;
}
.register-title {
text-align: center;
font-size: 2rem;
margin-bottom: 24px;
color: #333;
font-weight: 600;
}
.form-group {
margin-bottom: 18px;
}
label {
display: block;
margin-bottom: 6px;
color: #555;
font-size: 1rem;
}
input[type="text"],
input[type="email"],
input[type="password"] {
width: 100%;
padding: 10px 12px;
border: 1px solid #d1d5db;
border-radius: 6px;
font-size: 1rem;
background: #f9fafb;
transition: border 0.2s;
}
input:focus {
border-color: #409eff;
outline: none;
}
.register-btn {
width: 100%;
padding: 12px 0;
background: linear-gradient(90deg, #409eff 0%, #66b1ff 100%);
color: #fff;
border: none;
border-radius: 6px;
font-size: 1.1rem;
font-weight: 600;
cursor: pointer;
margin-top: 10px;
transition: background 0.2s;
}
.register-btn:hover {
background: linear-gradient(90deg, #66b1ff 0%, #409eff 100%);
}
.login-link {
display: block;
text-align: right;
margin-top: 14px;
color: #409eff;
text-decoration: none;
font-size: 0.95rem;
}
.login-link:hover {
text-decoration: underline;
body
.register-container
.register-title 注册账号
form(action="/register" method="post")
.form-group
label(for="username") 用户名
input(type="text" id="username" name="username" required placeholder="请输入用户名")
.form-group
label(for="email") 邮箱
input(type="email" id="email" name="email" required placeholder="请输入邮箱")
.form-group
label(for="password") 密码
input(type="password" id="password" name="password" required placeholder="请输入密码")
.form-group
label(for="confirm_password") 确认密码
input(type="password" id="confirm_password" name="confirm_password" required placeholder="请再次输入密码")
button.register-btn(type="submit") 注册
a.login-link(href="/login") 已有账号?去登录
Loading…
Cancel
Save