23 changed files with 1383 additions and 6141 deletions
File diff suppressed because it is too large
@ -1,136 +0,0 @@ |
|||
@charset "UTF-8"; |
|||
/* 首页样式 */ |
|||
.hero-section { |
|||
position: relative; |
|||
overflow: hidden; |
|||
} |
|||
|
|||
.hero-section::before { |
|||
content: ""; |
|||
position: absolute; |
|||
top: 0; |
|||
left: 0; |
|||
right: 0; |
|||
bottom: 0; |
|||
background: url("/images/hero-bg.svg") no-repeat center center; |
|||
background-size: cover; |
|||
opacity: 0.1; |
|||
z-index: 0; |
|||
} |
|||
|
|||
.hero-content { |
|||
position: relative; |
|||
z-index: 1; |
|||
} |
|||
|
|||
.feature-card { |
|||
transition: all 0.3s ease; |
|||
} |
|||
|
|||
.feature-card:hover { |
|||
transform: translateY(-5px); |
|||
} |
|||
|
|||
.feature-card .material-symbols-light--article, |
|||
.feature-card .material-symbols-light--bookmark, |
|||
.feature-card .material-symbols-light--person { |
|||
transition: all 0.3s ease; |
|||
} |
|||
|
|||
.feature-card:hover .material-symbols-light--article, |
|||
.feature-card:hover .material-symbols-light--bookmark, |
|||
.feature-card:hover .material-symbols-light--person { |
|||
transform: scale(1.1); |
|||
} |
|||
|
|||
.stats-section { |
|||
position: relative; |
|||
} |
|||
|
|||
.stats-section::before { |
|||
content: ""; |
|||
position: absolute; |
|||
top: 0; |
|||
left: 0; |
|||
right: 0; |
|||
bottom: 0; |
|||
background: url("/images/stats-bg.svg") no-repeat center center; |
|||
background-size: cover; |
|||
opacity: 0.05; |
|||
z-index: 0; |
|||
} |
|||
|
|||
.stat-item { |
|||
transition: all 0.3s ease; |
|||
} |
|||
|
|||
.stat-item:hover { |
|||
transform: scale(1.05); |
|||
} |
|||
|
|||
.user-dashboard { |
|||
position: relative; |
|||
} |
|||
|
|||
.user-dashboard::before { |
|||
content: ""; |
|||
position: absolute; |
|||
top: 0; |
|||
left: 0; |
|||
right: 0; |
|||
bottom: 0; |
|||
background: url("/images/dashboard-bg.svg") no-repeat center center; |
|||
background-size: cover; |
|||
opacity: 0.03; |
|||
z-index: 0; |
|||
} |
|||
|
|||
.avatar { |
|||
transition: all 0.3s ease; |
|||
} |
|||
|
|||
.avatar:hover { |
|||
transform: scale(1.05); |
|||
} |
|||
|
|||
/* 响应式设计 */ |
|||
@media (max-width: 768px) { |
|||
.hero-section { |
|||
padding: 4rem 0; |
|||
} |
|||
.hero-content h1 { |
|||
font-size: 2.5rem; |
|||
} |
|||
.features-grid { |
|||
grid-template-columns: 1fr; |
|||
} |
|||
.stats-grid { |
|||
grid-template-columns: 1fr 1fr; |
|||
} |
|||
.user-info { |
|||
text-align: center; |
|||
margin-bottom: 1.5rem; |
|||
} |
|||
.user-actions { |
|||
justify-content: center; |
|||
} |
|||
} |
|||
@media (max-width: 480px) { |
|||
.hero-content h1 { |
|||
font-size: 2rem; |
|||
} |
|||
.hero-content p { |
|||
font-size: 1rem; |
|||
} |
|||
.stats-grid { |
|||
grid-template-columns: 1fr; |
|||
} |
|||
.hero-actions { |
|||
flex-direction: column; |
|||
gap: 1rem; |
|||
} |
|||
.hero-actions a { |
|||
width: 100%; |
|||
text-align: center; |
|||
} |
|||
} |
|||
File diff suppressed because it is too large
File diff suppressed because it is too large
File diff suppressed because it is too large
@ -0,0 +1,25 @@ |
|||
import { R } from "utils/helper.js" |
|||
import Router from "utils/router.js" |
|||
import BaseController from "@/base/BaseController.js" |
|||
|
|||
class AuthController extends BaseController { |
|||
constructor() { |
|||
super() |
|||
} |
|||
|
|||
async loginPost(ctx) { |
|||
return this.success(ctx, null, "登录成功") |
|||
} |
|||
|
|||
/** |
|||
* 路由注册 |
|||
*/ |
|||
static createRoutes() { |
|||
const controller = new AuthController() |
|||
const router = new Router({ prefix: "/api/auth", auth: "try" }) |
|||
router.post("/login", controller.handleRequest(controller.loginPost)) |
|||
return router |
|||
} |
|||
} |
|||
|
|||
export default AuthController |
|||
@ -0,0 +1,35 @@ |
|||
import Router from "utils/router.js" |
|||
import { logger } from "@/logger.js" |
|||
import BaseController from "@/base/BaseController.js" |
|||
|
|||
export default class AuthController extends BaseController { |
|||
constructor() { |
|||
super() |
|||
} |
|||
|
|||
// 首页
|
|||
async loginGet(ctx) { |
|||
return this.render(ctx, "page/login/index", {}) |
|||
} |
|||
|
|||
async loginPost(ctx) { |
|||
console.log(ctx.request.body); |
|||
return this.success(ctx, null, "登录成功") |
|||
} |
|||
|
|||
/** |
|||
* 创建基础页面相关路由 |
|||
* @returns {Router} 路由实例 |
|||
*/ |
|||
static createRoutes() { |
|||
const controller = new AuthController() |
|||
const router = new Router({ auth: false }) |
|||
|
|||
// 首页
|
|||
router.get("", controller.handleRequest(controller.loginGet)) |
|||
router.get("/login", controller.handleRequest(controller.loginGet)) |
|||
router.post("/login", controller.handleRequest(controller.loginPost)) |
|||
|
|||
return router |
|||
} |
|||
} |
|||
@ -0,0 +1,84 @@ |
|||
import UserModel from "../db/models/UserModel.js" |
|||
import CommonError from "utils/error/CommonError.js" |
|||
import { comparePassword } from "utils/bcrypt.js" |
|||
|
|||
/** |
|||
* 认证服务类 |
|||
* 提供认证相关的业务逻辑 |
|||
*/ |
|||
class AuthService { |
|||
// 注册新用户
|
|||
static async register(data) { |
|||
try { |
|||
if (!data.username || !data.password) { |
|||
throw new CommonError("用户名和密码不能为空") |
|||
} |
|||
|
|||
// 检查用户名是否已存在
|
|||
const existUser = await UserModel.findByUsername(data.username) |
|||
if (existUser) { |
|||
throw new CommonError(`用户名${data.username}已存在`) |
|||
} |
|||
|
|||
// 检查邮箱是否已存在
|
|||
if (data.email) { |
|||
const existEmail = await UserModel.findByEmail(data.email) |
|||
if (existEmail) { |
|||
throw new CommonError(`邮箱${data.email}已被使用`) |
|||
} |
|||
} |
|||
|
|||
// 密码加密
|
|||
const hashed = await hashPassword(data.password) |
|||
|
|||
const user = await UserModel.create({ ...data, password: hashed }) |
|||
|
|||
// 返回脱敏信息
|
|||
const { password, ...userInfo } = Array.isArray(user) ? user[0] : user |
|||
return userInfo |
|||
} catch (error) { |
|||
if (error instanceof CommonError) throw error |
|||
throw new CommonError(`注册失败: ${error.message}`) |
|||
} |
|||
} |
|||
|
|||
// 登录
|
|||
static async login({ username, email, password }) { |
|||
try { |
|||
if (!password) { |
|||
throw new CommonError("密码不能为空") |
|||
} |
|||
|
|||
if (!username && !email) { |
|||
throw new CommonError("用户名或邮箱不能为空") |
|||
} |
|||
|
|||
let user |
|||
if (username) { |
|||
user = await UserModel.findByUsername(username) |
|||
} else if (email) { |
|||
user = await UserModel.findByEmail(email) |
|||
} |
|||
|
|||
if (!user) { |
|||
throw new CommonError("用户不存在") |
|||
} |
|||
|
|||
// 校验密码
|
|||
const ok = await comparePassword(password, user.password) |
|||
if (!ok) { |
|||
throw new CommonError("密码错误") |
|||
} |
|||
|
|||
// 生成token
|
|||
const token = jwt.sign({ id: user.id, username: user.username }, JWT_SECRET, { expiresIn: "2h" }) |
|||
|
|||
// 返回token和用户信息
|
|||
const { password: pwd, ...userInfo } = user |
|||
return { token, user: userInfo } |
|||
} catch (error) { |
|||
if (error instanceof CommonError) throw error |
|||
throw new CommonError(`登录失败: ${error.message}`) |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,21 @@ |
|||
@mixin chinese() { |
|||
&.chinese { |
|||
text-indent: 1.5em; |
|||
font-weight: 300; |
|||
h1, |
|||
h2, |
|||
h3, |
|||
h4, |
|||
h5, |
|||
h6, |
|||
ol, |
|||
ul, |
|||
blockquote, |
|||
details, |
|||
summary, |
|||
pre, |
|||
.tabs { |
|||
text-indent: 0; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,51 @@ |
|||
footer.footer.shadow.mt-5 |
|||
.footer-panel(class="bg-white border-t border-gray-200") |
|||
.footer-content.container(class="pt-12 pb-6") |
|||
.footer-main(class="grid grid-cols-1 md:grid-cols-4 gap-8 mb-8") |
|||
.footer-section |
|||
h3.footer-title(class="text-lg font-semibold text-gray-900 mb-4") #{siteConfig.site_title} |
|||
p.footer-desc(class="text-gray-600 text-sm leading-relaxed") 明月照佳人,用真心对待世界。<br>岁月催人老,用真情对待自己。 |
|||
|
|||
.footer-section |
|||
h3.footer-title(class="text-lg font-semibold text-gray-900 mb-4") 快速链接 |
|||
ul.footer-links(class="space-y-3") |
|||
li |
|||
a(href="/" class="text-gray-600 hover:text-blue-600 transition-colors duration-200 text-sm") 首页 |
|||
li |
|||
a(href="/about" class="text-gray-600 hover:text-blue-600 transition-colors duration-200 text-sm") 关于我们 |
|||
li |
|||
a(href="/contact" class="text-gray-600 hover:text-blue-600 transition-colors duration-200 text-sm") 联系我们 |
|||
li |
|||
a(href="/help" class="text-gray-600 hover:text-blue-600 transition-colors duration-200 text-sm") 帮助中心 |
|||
|
|||
.footer-section |
|||
h3.footer-title(class="text-lg font-semibold text-gray-900 mb-4") 服务支持 |
|||
ul.footer-links(class="space-y-3") |
|||
li |
|||
a(href="/terms" class="text-gray-600 hover:text-blue-600 transition-colors duration-200 text-sm") 服务条款 |
|||
li |
|||
a(href="/privacy" class="text-gray-600 hover:text-blue-600 transition-colors duration-200 text-sm") 隐私政策 |
|||
li |
|||
a(href="/faq" class="text-gray-600 hover:text-blue-600 transition-colors duration-200 text-sm") 常见问题 |
|||
li |
|||
a(href="/feedback" class="text-gray-600 hover:text-blue-600 transition-colors duration-200 text-sm") 意见反馈 |
|||
|
|||
.footer-section |
|||
h3.footer-title(class="text-lg font-semibold text-gray-900 mb-4") 关注我 |
|||
.social-links(class="flex space-x-4 flex-wrap") |
|||
a(href="#" class="social-link p-2 rounded-full bg-gray-100 hover:bg-blue-100 transition-colors duration-200" title="微信") |
|||
span.streamline-ultimate-color--wechat-logo |
|||
a(href="#" class="social-link p-2 rounded-full bg-gray-100 hover:bg-blue-100 transition-colors duration-200" title="QQ") |
|||
span.cib--tencent-qq |
|||
a(href="#" class="social-link p-2 rounded-full bg-gray-100 hover:bg-gray-200 transition-colors duration-200" title="GitHub") |
|||
span.ri--github-fill |
|||
a(href="https://blog.xieyaxin.top" target="_blank" class="social-link p-2 rounded-full bg-gray-100 hover:bg-gray-200 transition-colors duration-200" title="GitHub") |
|||
span.icomoon-free--blog |
|||
|
|||
.footer-bottom(class="border-t border-gray-200 pt-6") |
|||
.footer-bottom-content(class="flex flex-col md:flex-row justify-between items-center") |
|||
.copyright(class="text-gray-500 text-sm mb-4 md:mb-0") |
|||
| © 2023-#{new Date().getFullYear()} #{siteConfig.site_author}. 保留所有权利。 |
|||
.footer-actions(class="flex items-center space-x-6") |
|||
a(href="/sitemap" class="text-gray-500 hover:text-blue-600 transition-colors duration-200 text-sm") 网站地图 |
|||
a(href="/rss" class="text-gray-500 hover:text-blue-600 transition-colors duration-200 text-sm") RSS订阅 |
|||
@ -0,0 +1,11 @@ |
|||
:scss |
|||
@import "./style.scss"; |
|||
@import "./markdown-reset.scss"; |
|||
@import "./markdown-green.scss"; |
|||
|
|||
.markdown-body.green |
|||
:markdown-it(linkify langPrefix='highlight-') |
|||
# 关于 |
|||
这是一个关于页面,欢迎访问。 |
|||
|
|||
> 世上本无事,庸人自扰之。 |
|||
@ -1,47 +1,21 @@ |
|||
@mixin chinese() { |
|||
&.chinese { |
|||
text-indent: 1.5em; |
|||
font-weight: 300; |
|||
h1, |
|||
h2, |
|||
h3, |
|||
h4, |
|||
h5, |
|||
h6, |
|||
ol, |
|||
ul, |
|||
blockquote, |
|||
details, |
|||
summary, |
|||
pre, |
|||
.tabs { |
|||
text-indent: 0; |
|||
} |
|||
} |
|||
} |
|||
|
|||
|
|||
// ::selection { |
|||
// background-color: #1abc9c; |
|||
// color: #fff; |
|||
// } |
|||
|
|||
// pre ::selection { |
|||
// color: inherit; |
|||
// } |
|||
.markdown-body.green { |
|||
|
|||
.markdown-body { |
|||
--color-base: #ef4444; |
|||
--markdown-bg: white; |
|||
--color-base-text: #fff; |
|||
--color-bg: #ff47479c; |
|||
--color-light: #ef44441a; |
|||
--color-extra: rgba(239, 68, 68, 0.3); |
|||
--color-more: rgba(239, 68, 68, 0.4); |
|||
} |
|||
|
|||
--prism-background: #eeeeee; |
|||
--prism-font-size: 14px; |
|||
--prism-font-family: "SFMono-Regular", Consolas, "Liberation Mono", Menlo, Courier, monospace; |
|||
|
|||
.markdown-body.green { |
|||
background-color: var(--markdown-bg); |
|||
@apply p-3 lg:p-6; |
|||
::selection { |
|||
background-color: var(--color-base); |
|||
color: var(--color-base-text); |
|||
} |
|||
|
|||
strong { |
|||
&::before { |
|||
@ -0,0 +1,26 @@ |
|||
nav.navbar(class="relative") |
|||
.placeholder.mb-5(class="h-[45px] w-full opacity-0") |
|||
.fixed-container(class="shadow fixed bg-white h-[45px] top-0 left-0 right-0 z-10") |
|||
.container.clearfix(class="h-full") |
|||
.navbar-brand |
|||
a(href="/" class="text-[20px]") #{siteConfig.site_title} |
|||
.left.menu.desktop-only |
|||
a.menu-item( |
|||
href="/articles" |
|||
class=(currentPath === '/articles' || currentPath === '/articles/' |
|||
? 'text-blue-600 font-bold border-b-2 border-blue-600' |
|||
: 'text-gray-700 hover:text-blue-600 hover:border-b-2 hover:border-blue-400' |
|||
) |
|||
) 所有文章 |
|||
if !user |
|||
.right.menu.desktop-only |
|||
a.menu-item(href="/login") 登录 |
|||
a.menu-item(href="/register") 注册 |
|||
else |
|||
.right.menu.desktop-only |
|||
a.menu-item(href="/profile") |
|||
span 欢迎您, |
|||
span.font-semibold #{user.name || user.username} |
|||
a.menu-item(href="/notice") |
|||
.fe--notice-active |
|||
a.menu-item(hx-post="/logout") 退出 |
|||
@ -1,22 +1,6 @@ |
|||
extends /layouts/empty.pug |
|||
|
|||
block pageHead |
|||
:scss |
|||
@use "./_global.scss"; |
|||
@use "./style.scss"; |
|||
@use "./markdown-reset.scss"; |
|||
@use "./markdown-green.scss"; |
|||
|
|||
|
|||
block pageContent |
|||
.markdown-body |
|||
:markdown-it(linkify langPrefix='highlight-') |
|||
# Markdown |
|||
|
|||
Markdown document with http://links.com and |
|||
|
|||
```js |
|||
var codeBlocks; |
|||
``` |
|||
|
|||
> blockquote |
|||
include /htmx/markdown/index.pug |
|||
@ -0,0 +1,9 @@ |
|||
extends /layouts/empty.pug |
|||
|
|||
block pageHead |
|||
|
|||
block pageContent |
|||
form(action="/login" method="post") |
|||
input(type="text" name="username" placeholder="用户名") |
|||
input(type="password" name="password" placeholder="密码") |
|||
button(type="submit") 登录 |
|||
Loading…
Reference in new issue