Compare commits

...

2 Commits

Author SHA1 Message Date
谢亚昕 2c3d6c86b7 feat: 添加 Vite 配置文件,设置构建和插件选项 1 month ago
谢亚昕 dcfa188b85 feat: add background image and enhance styles across the application 1 month ago
  1. BIN
      bun.lockb
  2. BIN
      database/development.sqlite3-shm
  3. BIN
      database/development.sqlite3-wal
  4. 18
      entrypoint.sh
  5. 3047
      package-lock.json
  6. 7
      package.json
  7. 12
      public/css/page/index.css
  8. BIN
      public/static/bg.jpg
  9. 68
      public/styles.css
  10. 4
      src/db/seeds/20250621013324_site_config_seed.mjs
  11. 1
      src/main.js
  12. 8
      src/utils/ForRegister.js
  13. 13
      src/views/htmx/footer.pug
  14. 4
      src/views/layouts/base.pug
  15. 3
      src/views/layouts/page.pug
  16. 20
      src/views/page/about/index.pug
  17. 102
      src/views/page/index/index.pug
  18. 83
      vite.config.ts

BIN
bun.lockb

Binary file not shown.

BIN
database/development.sqlite3-shm

Binary file not shown.

BIN
database/development.sqlite3-wal

Binary file not shown.

18
entrypoint.sh

@ -3,16 +3,26 @@ set -e
# 数据库文件路径(可根据实际环境调整)
DB_FILE=./database/db.sqlite3
ENV=${NODE_ENV:-production}
# 检查 bun 是否存在
if command -v bun >/dev/null 2>&1; then
RUNNER="bun run"
START="exec bun src/main.js"
else
RUNNER="npx"
START="exec npm run start"
fi
# 如果数据库文件不存在,先 migrate 再 seed
if [ ! -f "$DB_FILE" ]; then
echo "Database not found, running migration and seed..."
bun run npx knex migrate:latest
bun run npx knex seed:run
$RUNNER npx knex migrate:latest --env $ENV
$RUNNER npx knex seed:run --env $ENV
else
echo "Database exists, running migration only..."
bun run npx knex migrate:latest
$RUNNER npx knex migrate:latest
fi
# 启动主服务
exec bun src/main.js
$START

3047
package-lock.json

File diff suppressed because it is too large

7
package.json

@ -5,6 +5,7 @@
"scripts": {
"dev": "bun --hot src/main.js",
"start": "bun run src/main.js",
"build": "vite build",
"migrate:make": "npx knex migrate:make ",
"migrate": "npx knex migrate:latest",
"seed:make": "npx knex seed:make ",
@ -13,7 +14,8 @@
},
"devDependencies": {
"@types/bun": "latest",
"@types/node": "^24.0.1"
"@types/node": "^24.0.1",
"vite": "^7.0.0"
},
"dependencies": {
"bcryptjs": "^3.0.2",
@ -31,7 +33,8 @@
"node-cron": "^4.1.0",
"path-to-regexp": "^8.2.0",
"pug": "^3.0.3",
"sqlite3": "^5.1.7"
"sqlite3": "^5.1.7",
"vite-plugin-static-copy": "^3.1.0"
},
"_moduleAliases": {
"@": "./src",

12
public/css/page/index.css

@ -0,0 +1,12 @@
.home-hero {
margin: 20px 20px 40px;
background: rgba(255, 255, 255, 0.1);
backdrop-filter: blur(12px);
}
@media screen and (max-width: 768px) {
.home-hero {
margin: 0;
margin-top: 20px;
}
}

BIN
public/static/bg.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 280 KiB

68
public/styles.css

@ -3,7 +3,6 @@ body {
margin: 0;
padding: 0;
height: 100%;
background-color: #f9f9f9;
font-family: Arial, sans-serif;
color: #333;
}
@ -14,6 +13,22 @@ body {
flex-direction: column;
}
body::after {
content: "";
position: fixed;
pointer-events: none;
left: 0;
top: 0;
right: 0;
bottom: 0;
background-image: var(--bg);
background-size: 100% 100%;
background-repeat: no-repeat;
background-color: #f9f9f9;
filter: brightness(.55);
z-index: -1;
}
.navbar {
height: 49px;
display: flex;
@ -35,7 +50,7 @@ body {
}
.page {
width: 100%;
width: 100%;
display: flex;
flex-direction: row;
flex: 1;
@ -63,11 +78,13 @@ body {
width: 80px;
padding: 40px 0;
border-radius: 80px;
background: linear-gradient(135deg, #4fd1ff 0%, #ff6a6a 100%);
/* background: linear-gradient(135deg, #4fd1ff 0%, #ff6a6a 100%); */
/* 更明亮的渐变色 */
box-shadow: 0 8px 32px 0 rgba(80, 80, 200, 0.18), 0 2px 16px 0 rgba(255, 106, 106, 0.12);
/* box-shadow: 0 8px 32px 0 rgba(80, 80, 200, 0.18), 0 2px 16px 0 rgba(255, 106, 106, 0.12); */
/* 更明显的阴影 */
transition: background 0.3s, box-shadow 0.3s;
/* transition: background 0.3s, box-shadow 0.3s; */
background: rgba(255, 255, 255, 0.1);
backdrop-filter: blur(12px);
}
.flota-nav .item {
@ -98,4 +115,45 @@ body {
background: #fff200;
color: #ff6a6a;
box-shadow: 0 4px 20px 0 rgba(255, 242, 0, 0.22);
}
.card {
padding: 56px 40px 44px 40px;
border-radius: 28px;
background: rgba(255, 255, 255, 0.25);
backdrop-filter: blur(18px);
color: #fff;
}
@media screen and (max-width: 768px) {
.nav {
width: 0;
height: 0;
overflow: hidden;
padding: 0;
margin: 0;
}
.flota-nav {
position: fixed;
top: 0;
left: 0;
right: 0;
width: 100%;
display: flex;
z-index: 9999;
padding: 0 10px;
height: 40px;
border-radius: 0;
}
.flota-nav .item{
margin-bottom: 0;
padding: 0 10px;
}
.content {
padding: 0 10px;
padding-top: 40px;
}
}

4
src/db/seeds/20250621013324_site_config_seed.mjs

@ -4,9 +4,11 @@ export const seed = async (knex) => {
// 插入常用站点配置项
await knex('site_config').insert([
{ key: 'site_title', value: '🥔未野明的小屋' },
{ key: 'site_title', value: '罗非鱼的秘密' },
{ key: 'site_author', value: '罗非鱼' },
{ key: 'site_description', value: '一屋很小,却也很大' },
{ key: 'site_logo', value: '/static/logo.png' },
{ key: 'site_bg', value: '/static/bg.jpg' },
{ key: 'keywords', value: 'blog' },
{ key: 'base', value: '/' }
]);

1
src/main.js

@ -5,7 +5,6 @@ import "./jobs/index.js"
// 第三方依赖
import Koa from "koa"
import os from "os"
import log4js from "log4js"
// 应用插件与自动路由
import LoadMiddlewares from "./middlewares/install.js"

8
src/utils/ForRegister.js

@ -3,6 +3,14 @@
import fs from "fs"
import path from "path"
// 保证不会被摇树(tree-shaking),即使在生产环境也会被打包
if (import.meta.env.PROD) {
// 通过引用返回值,防止被摇树优化
let controllers = import.meta.glob("../controllers/**/*Controller.js", { eager: true })
controllers = null
console.log(controllers);
}
/**
* 自动扫描 controllers 目录注册所有导出的路由
* 自动检测 routes 目录下已手动注册的 controller避免重复注册

13
src/views/htmx/footer.pug

@ -1,16 +1,17 @@
.footer-panel
.footer-content
p © 2023-2025 My Website. 保留所有权利。
p © 2023-#{new Date().getFullYear()} #{$site.site_title}. 保留所有权利。
ul.footer-links
li
a(href="/about") 关于我们
li
a(href="/contact") 联系方式
li
a(href="/privacy") 隐私
//- li
//- a(href="/contact") 联系方式
//- li
//- a(href="/privacy") 隐私
style.
.footer-panel {
background: #222;
background: rgba(34,34,34,.3);
backdrop-filter: blur(12px);
color: #eee;
padding: 40px 0 24px 0;
font-size: 15px;

4
src/views/layouts/base.pug

@ -4,7 +4,7 @@ mixin include()
mixin css(url, extranl = false)
if extranl || url.startsWith('http') || url.startsWith('//')
style(type="text/css" href=url)
link(rel="stylesheet" type="text/css" href=url)
else
link(rel="stylesheet", href=($config && $config.base || "") + url)
@ -34,7 +34,7 @@ html(lang="zh-CN")
//- +css('https://unpkg.com/simplebar@latest/dist/simplebar.css', true)
//- +css('simplebar-shim.css')
//- +js('https://unpkg.com/simplebar@latest/dist/simplebar.min.js', true)
body
body(style="--bg:url("+($site && $site.site_bg || '#fff')+")")
noscript
style.
.simplebar-content-wrapper {

3
src/views/layouts/page.pug

@ -5,13 +5,12 @@ block head
block pageHead
block content
// 页面整体flex布局,footer吸底
.page-layout
.page
- const navs = [];
- navs.push({ href: '/', label: '首页' });
- navs.push({ href: '/articles', label: '文章' });
- navs.push({ href: '/article', label: '留言板' });
- navs.push({ href: '/article', label: '收藏' });
- navs.push({ href: '/about', label: '关于' });
nav.nav
ul.flota-nav

20
src/views/page/about/index.pug

@ -23,28 +23,28 @@ block pageContent
.about-container {
margin: 32px auto 0 auto;
padding: 56px 40px 44px 40px;
background: linear-gradient(120deg, #e3f3ff 0%, #fafdff 100%);
border-radius: 28px;
box-shadow: 0 8px 36px rgba(126,198,247,0.13), 0 2px 12px rgba(255,140,168,0.08);
border: 1.5px solid #b2ebf2;
background: rgba(255, 255, 255, 0.25);
backdrop-filter: blur(18px);
//- max-width: 900px;
//- min-width: 340px;
}
.about-container h1 {
font-size: 2.7em;
color: #2196f3;
color: rgb(80, 168, 255);
margin-bottom: 28px;
text-align: center;
font-weight: 900;
letter-spacing: 2.5px;
text-shadow: 0 2px 16px rgba(33,150,243,0.10);
background: linear-gradient(90deg, #2196f3 30%, #7ec6f7 100%);
background: linear-gradient(90deg,rgb(80, 168, 255) 30%, #7ec6f7 100%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
}
.about-container p {
font-size: 1.18em;
color: #333;
color: #fff;
margin-bottom: 26px;
line-height: 1.85;
}
@ -58,11 +58,11 @@ block pageContent
}
.about-section h2 {
font-size: 1.22em;
color: #1976d2;
color:rgb(80, 168, 255);
margin-bottom: 10px;
font-weight: 700;
letter-spacing: 1.2px;
background: linear-gradient(90deg, #1976d2 60%, #7ec6f7 100%);
background: linear-gradient(90deg,rgb(80, 168, 255) 60%, #7ec6f7 100%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
}
@ -72,12 +72,12 @@ block pageContent
}
.about-section li {
font-size: 1.08em;
color: #444;
color: #fff;
margin-bottom: 8px;
line-height: 1.7;
}
.about-container a {
color: #2196f3;
color:rgb(80, 168, 255);
text-decoration: underline;
font-weight: 600;
transition: color 0.2s, background 0.2s;

102
src/views/page/index/index.pug

@ -1,100 +1,10 @@
extends /layouts/page.pug
block pageHead
+css("css/page/index.css")
block pageContent
.home-hero
.card.home-hero
h1 #{$site.site_title}
//- p.subtitle #{$site.site_description}
.actions
a.btn-primary(href="/about") 了解更多
a.btn-secondary(href="/contact") 联系我们
.features
.feature
h2 🚀 极速开发
p 使用 Koa3 和现代前端技术,快速搭建高效网站。
.feature
h2 🔒 安全可靠
p 内置多项安全机制,保障数据与用户安全。
.feature
h2 🌈 易于扩展
p 结构清晰,方便二次开发和功能拓展。
style.
.home-hero {
text-align: center;
padding: 60px 0 40px 0;
margin: 20px 20px;
background: linear-gradient(90deg, #4fc3f7 0%, #1976d2 100%);
color: #fff;
border-radius: 12px;
margin-bottom: 40px;
}
.home-hero h1 {
font-size: 2.8em;
margin-bottom: 42px;
letter-spacing: 2px;
}
.home-hero .subtitle {
font-size: 1.3em;
margin-bottom: 28px;
color: #e3f2fd;
}
.home-hero .actions {
margin-top: 18px;
}
.btn-primary, .btn-secondary {
display: inline-block;
padding: 10px 28px;
border-radius: 24px;
font-size: 1em;
margin: 0 10px;
text-decoration: none;
transition: background 0.2s, color 0.2s;
}
.btn-primary {
background: #fff;
color: #1976d2;
font-weight: bold;
border: none;
}
.btn-primary:hover {
background: #e3f2fd;
color: #1565c0;
}
.btn-secondary {
background: transparent;
color: #fff;
border: 1px solid #fff;
}
.btn-secondary:hover {
background: #1976d2;
color: #fff;
border-color: #e3f2fd;
}
.features {
display: flex;
justify-content: space-around;
margin-top: 40px;
gap: 24px;
flex-wrap: wrap;
}
.feature {
background: #fafbfc;
border-radius: 10px;
box-shadow: 0 2px 8px rgba(30, 136, 229, 0.08);
padding: 28px 24px;
flex: 1 1 220px;
min-width: 220px;
max-width: 320px;
text-align: center;
margin: 0 8px;
}
.feature h2 {
font-size: 1.3em;
margin-bottom: 10px;
color: #1976d2;
}
.feature p {
color: #333;
font-size: 1em;
margin: 0;
p.subtitle #{$site.site_description}

83
vite.config.ts

@ -0,0 +1,83 @@
import { dirname, resolve } from "node:path"
import { fileURLToPath } from "node:url"
import module from "node:module"
import { defineConfig } from "vite"
import pkg from "./package.json"
import { viteStaticCopy } from "vite-plugin-static-copy"
const __dirname = dirname(fileURLToPath(import.meta.url))
function getExternal(): string[] {
return [...Object.keys(pkg.dependencies || {}), ...module.builtinModules]
}
export default defineConfig({
publicDir: false,
resolve: {
alias: {
"@": resolve(__dirname, "src"),
db: resolve(__dirname, "src/db"),
config: resolve(__dirname, "src/config"),
utils: resolve(__dirname, "src/utils"),
services: resolve(__dirname, "src/services"),
},
},
build: {
lib: {
entry: resolve(__dirname, "src/main.js"),
formats: ["es"],
fileName: () => `[name].js`,
},
outDir: resolve(__dirname, "dist"),
rollupOptions: {
external: getExternal(),
// watch: {
// include: "src/**",
// exclude: "node_modules/**",
// },
output: {
preserveModules: true,
preserveModulesRoot: "src",
inlineDynamicImports: false,
},
},
},
plugins: [
viteStaticCopy({
targets: [
{
src: "public",
dest: "",
},
{
src: "src/views",
dest: "",
},
{
src: "src/db/migrations",
dest: "db",
},
{
src: "src/db/seeds",
dest: "db",
},
{
src: "entrypoint.sh",
dest: "",
},
{
src: "package.json",
dest: "",
},
{
src: "knexfile.mjs",
dest: "",
},
{
src: "bun.lockb",
dest: "",
},
],
}),
],
})
Loading…
Cancel
Save