commit
b35897d60b
19 changed files with 506 additions and 0 deletions
@ -0,0 +1,175 @@ |
|||||
|
# Based on https://raw.githubusercontent.com/github/gitignore/main/Node.gitignore |
||||
|
|
||||
|
# Logs |
||||
|
|
||||
|
logs |
||||
|
_.log |
||||
|
npm-debug.log_ |
||||
|
yarn-debug.log* |
||||
|
yarn-error.log* |
||||
|
lerna-debug.log* |
||||
|
.pnpm-debug.log* |
||||
|
|
||||
|
# Caches |
||||
|
|
||||
|
.cache |
||||
|
|
||||
|
# Diagnostic reports (https://nodejs.org/api/report.html) |
||||
|
|
||||
|
report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json |
||||
|
|
||||
|
# Runtime data |
||||
|
|
||||
|
pids |
||||
|
_.pid |
||||
|
_.seed |
||||
|
*.pid.lock |
||||
|
|
||||
|
# Directory for instrumented libs generated by jscoverage/JSCover |
||||
|
|
||||
|
lib-cov |
||||
|
|
||||
|
# Coverage directory used by tools like istanbul |
||||
|
|
||||
|
coverage |
||||
|
*.lcov |
||||
|
|
||||
|
# nyc test coverage |
||||
|
|
||||
|
.nyc_output |
||||
|
|
||||
|
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) |
||||
|
|
||||
|
.grunt |
||||
|
|
||||
|
# Bower dependency directory (https://bower.io/) |
||||
|
|
||||
|
bower_components |
||||
|
|
||||
|
# node-waf configuration |
||||
|
|
||||
|
.lock-wscript |
||||
|
|
||||
|
# Compiled binary addons (https://nodejs.org/api/addons.html) |
||||
|
|
||||
|
build/Release |
||||
|
|
||||
|
# Dependency directories |
||||
|
|
||||
|
node_modules/ |
||||
|
jspm_packages/ |
||||
|
|
||||
|
# Snowpack dependency directory (https://snowpack.dev/) |
||||
|
|
||||
|
web_modules/ |
||||
|
|
||||
|
# TypeScript cache |
||||
|
|
||||
|
*.tsbuildinfo |
||||
|
|
||||
|
# Optional npm cache directory |
||||
|
|
||||
|
.npm |
||||
|
|
||||
|
# Optional eslint cache |
||||
|
|
||||
|
.eslintcache |
||||
|
|
||||
|
# Optional stylelint cache |
||||
|
|
||||
|
.stylelintcache |
||||
|
|
||||
|
# Microbundle cache |
||||
|
|
||||
|
.rpt2_cache/ |
||||
|
.rts2_cache_cjs/ |
||||
|
.rts2_cache_es/ |
||||
|
.rts2_cache_umd/ |
||||
|
|
||||
|
# Optional REPL history |
||||
|
|
||||
|
.node_repl_history |
||||
|
|
||||
|
# Output of 'npm pack' |
||||
|
|
||||
|
*.tgz |
||||
|
|
||||
|
# Yarn Integrity file |
||||
|
|
||||
|
.yarn-integrity |
||||
|
|
||||
|
# dotenv environment variable files |
||||
|
|
||||
|
.env |
||||
|
.env.development.local |
||||
|
.env.test.local |
||||
|
.env.production.local |
||||
|
.env.local |
||||
|
|
||||
|
# parcel-bundler cache (https://parceljs.org/) |
||||
|
|
||||
|
.parcel-cache |
||||
|
|
||||
|
# Next.js build output |
||||
|
|
||||
|
.next |
||||
|
out |
||||
|
|
||||
|
# Nuxt.js build / generate output |
||||
|
|
||||
|
.nuxt |
||||
|
dist |
||||
|
|
||||
|
# Gatsby files |
||||
|
|
||||
|
# Comment in the public line in if your project uses Gatsby and not Next.js |
||||
|
|
||||
|
# https://nextjs.org/blog/next-9-1#public-directory-support |
||||
|
|
||||
|
# public |
||||
|
|
||||
|
# vuepress build output |
||||
|
|
||||
|
.vuepress/dist |
||||
|
|
||||
|
# vuepress v2.x temp and cache directory |
||||
|
|
||||
|
.temp |
||||
|
|
||||
|
# Docusaurus cache and generated files |
||||
|
|
||||
|
.docusaurus |
||||
|
|
||||
|
# Serverless directories |
||||
|
|
||||
|
.serverless/ |
||||
|
|
||||
|
# FuseBox cache |
||||
|
|
||||
|
.fusebox/ |
||||
|
|
||||
|
# DynamoDB Local files |
||||
|
|
||||
|
.dynamodb/ |
||||
|
|
||||
|
# TernJS port file |
||||
|
|
||||
|
.tern-port |
||||
|
|
||||
|
# Stores VSCode versions used for testing VSCode extensions |
||||
|
|
||||
|
.vscode-test |
||||
|
|
||||
|
# yarn v2 |
||||
|
|
||||
|
.yarn/cache |
||||
|
.yarn/unplugged |
||||
|
.yarn/build-state.yml |
||||
|
.yarn/install-state.gz |
||||
|
.pnp.* |
||||
|
|
||||
|
# IntelliJ based IDEs |
||||
|
.idea |
||||
|
|
||||
|
# Finder (MacOS) folder config |
||||
|
.DS_Store |
@ -0,0 +1 @@ |
|||||
|
msvs_version=2017 |
@ -0,0 +1,11 @@ |
|||||
|
{ |
||||
|
"tabWidth": 4, |
||||
|
"useTabs": false, |
||||
|
"semi": false, |
||||
|
"singleQuote": false, |
||||
|
"TrailingCooma": "all", |
||||
|
"bracketSpacing": true, |
||||
|
"jsxBracketSameLine": false, |
||||
|
"arrowParens": "avoid", |
||||
|
"printWidth": 140 |
||||
|
} |
@ -0,0 +1,12 @@ |
|||||
|
|
||||
|
## koa3-demo |
||||
|
|
||||
|
当前支持功能: |
||||
|
|
||||
|
- [ ] 路由 |
||||
|
- [x] 日志 |
||||
|
- [ ] 权限 |
||||
|
- [x] 数据库 |
||||
|
- [ ] 缓存 |
||||
|
- [ ] 界面 |
||||
|
- [ ] 定时任务 |
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -0,0 +1,59 @@ |
|||||
|
// knexfile.mjs (ESM格式)
|
||||
|
export default { |
||||
|
development: { |
||||
|
client: "sqlite3", |
||||
|
connection: { |
||||
|
filename: "./database/development.sqlite3", |
||||
|
}, |
||||
|
migrations: { |
||||
|
directory: "./src/db/migrations", // 迁移文件目录
|
||||
|
// 启用ES模块支持
|
||||
|
extension: "mjs", |
||||
|
loadExtensions: [".mjs", ".js"], |
||||
|
}, |
||||
|
seeds: { |
||||
|
directory: "./src/db/seeds", // 种子数据目录,
|
||||
|
// 启用ES模块支持
|
||||
|
extension: "mjs", |
||||
|
loadExtensions: [".mjs", ".js"], |
||||
|
timestampFilenamePrefix: true, |
||||
|
}, |
||||
|
useNullAsDefault: true, // SQLite需要这一选项
|
||||
|
pool: { |
||||
|
min: 1, |
||||
|
max: 1, // SQLite 建议设为 1,避免并发问题
|
||||
|
afterCreate: (conn, done) => { |
||||
|
conn.run("PRAGMA journal_mode = WAL", done) // 启用 WAL 模式提高并发
|
||||
|
}, |
||||
|
}, |
||||
|
}, |
||||
|
|
||||
|
// 生产环境、测试环境配置可按需添加
|
||||
|
production: { |
||||
|
client: "sqlite3", |
||||
|
connection: { |
||||
|
filename: "./database/db.sqlite3", |
||||
|
}, |
||||
|
migrations: { |
||||
|
directory: "./src/db/migrations", // 迁移文件目录
|
||||
|
// 启用ES模块支持
|
||||
|
extension: "mjs", |
||||
|
loadExtensions: [".mjs", ".js"], |
||||
|
}, |
||||
|
seeds: { |
||||
|
directory: "./src/db/seeds", // 种子数据目录,
|
||||
|
// 启用ES模块支持
|
||||
|
extension: "mjs", |
||||
|
loadExtensions: [".mjs", ".js"], |
||||
|
timestampFilenamePrefix: true, |
||||
|
}, |
||||
|
useNullAsDefault: true, // SQLite需要这一选项
|
||||
|
pool: { |
||||
|
min: 1, |
||||
|
max: 1, // SQLite 建议设为 1,避免并发问题
|
||||
|
afterCreate: (conn, done) => { |
||||
|
conn.run("PRAGMA journal_mode = WAL", done) // 启用 WAL 模式提高并发
|
||||
|
}, |
||||
|
}, |
||||
|
}, |
||||
|
} |
@ -0,0 +1,28 @@ |
|||||
|
{ |
||||
|
"name": "koa3-demo", |
||||
|
"module": "index.js", |
||||
|
"type": "module", |
||||
|
"scripts": { |
||||
|
"dev": "bun --hot src/main.js", |
||||
|
"start": "bun run src/main.js", |
||||
|
"migrate:make": "npx knex migrate:make ", |
||||
|
"migrate": "npx knex migrate:latest", |
||||
|
"seed:make": "npx knex seed:make ", |
||||
|
"seed": "npx knex seed:run " |
||||
|
}, |
||||
|
"devDependencies": { |
||||
|
"@types/bun": "latest", |
||||
|
"@types/node": "^24.0.1", |
||||
|
"knex": "^3.1.0" |
||||
|
}, |
||||
|
"dependencies": { |
||||
|
"koa": "^3.0.0", |
||||
|
"log4js": "^6.9.1", |
||||
|
"module-alias": "^2.2.3", |
||||
|
"sqlite3": "^5.1.7" |
||||
|
}, |
||||
|
"_moduleAliases": { |
||||
|
"@": "./src", |
||||
|
"db": "./src/db" |
||||
|
} |
||||
|
} |
@ -0,0 +1,23 @@ |
|||||
|
import db from "../db/index.js" |
||||
|
|
||||
|
// 创建用户
|
||||
|
export async function createUser(userData) { |
||||
|
const [id] = await db("users").insert(userData) |
||||
|
return id |
||||
|
} |
||||
|
|
||||
|
// 查询所有用户
|
||||
|
export async function getUsers() { |
||||
|
return db("users").select("*") |
||||
|
} |
||||
|
|
||||
|
// 更新用户
|
||||
|
export async function updateUser(id, updates) { |
||||
|
updates.updated_at = new Date() |
||||
|
return db("users").where("id", id).update(updates) |
||||
|
} |
||||
|
|
||||
|
// 删除用户
|
||||
|
export async function deleteUser(id) { |
||||
|
return db("users").where("id", id).del() |
||||
|
} |
@ -0,0 +1,35 @@ |
|||||
|
import buildKnex from "knex" |
||||
|
import knexConfig from "../../knexfile.mjs" |
||||
|
|
||||
|
const environment = process.env.NODE_ENV || 'development'; |
||||
|
const db = buildKnex(knexConfig[environment]); |
||||
|
|
||||
|
export default db; |
||||
|
|
||||
|
// async function createDatabase() {
|
||||
|
// try {
|
||||
|
// // SQLite会自动创建数据库文件,只需验证连接
|
||||
|
// await db.raw("SELECT 1")
|
||||
|
// console.log("SQLite数据库连接成功")
|
||||
|
|
||||
|
// // 检查users表是否存在(示例)
|
||||
|
// const [tableExists] = await db.raw(`
|
||||
|
// SELECT name
|
||||
|
// FROM sqlite_master
|
||||
|
// WHERE type='table' AND name='users'
|
||||
|
// `)
|
||||
|
|
||||
|
// if (tableExists) {
|
||||
|
// console.log("表 users 已存在")
|
||||
|
// } else {
|
||||
|
// console.log("表 users 不存在,需要创建(通过迁移)")
|
||||
|
// }
|
||||
|
|
||||
|
// await db.destroy()
|
||||
|
// } catch (error) {
|
||||
|
// console.error("数据库操作失败:", error)
|
||||
|
// process.exit(1)
|
||||
|
// }
|
||||
|
// }
|
||||
|
|
||||
|
// createDatabase()
|
@ -0,0 +1,22 @@ |
|||||
|
/** |
||||
|
* @param { import("knex").Knex } knex |
||||
|
* @returns { Promise<void> } |
||||
|
*/ |
||||
|
export const up = async knex => { |
||||
|
return knex.schema.createTable("users", function (table) { |
||||
|
table.increments("id").primary() // 自增主键
|
||||
|
table.string("name", 100).notNullable() // 字符串字段(最大长度100)
|
||||
|
table.string("email", 100).unique().notNullable() // 唯一邮箱
|
||||
|
table.integer("age").unsigned() // 无符号整数
|
||||
|
table.timestamp("created_at").defaultTo(knex.fn.now()) // 创建时间
|
||||
|
table.timestamp("updated_at").defaultTo(knex.fn.now()) // 更新时间
|
||||
|
}) |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* @param { import("knex").Knex } knex |
||||
|
* @returns { Promise<void> } |
||||
|
*/ |
||||
|
export const down = async knex => { |
||||
|
return knex.schema.dropTable("users") // 回滚时删除表
|
||||
|
} |
@ -0,0 +1,26 @@ |
|||||
|
import db from "../index.js" |
||||
|
|
||||
|
class UserModel { |
||||
|
static async findAll() { |
||||
|
return db("users").select("*") |
||||
|
} |
||||
|
|
||||
|
static async findById(id) { |
||||
|
return db("users").where("id", id).first() |
||||
|
} |
||||
|
|
||||
|
static async create(data) { |
||||
|
return db("users").insert(data).returning("*") |
||||
|
} |
||||
|
|
||||
|
static async update(id, data) { |
||||
|
return db("users").where("id", id).update(data).returning("*") |
||||
|
} |
||||
|
|
||||
|
static async delete(id) { |
||||
|
return db("users").where("id", id).del() |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
export default UserModel |
||||
|
export { UserModel } |
@ -0,0 +1,19 @@ |
|||||
|
export const seed = async knex => { |
||||
|
// 检查表是否存在
|
||||
|
const tables = await knex.raw(` |
||||
|
SELECT name FROM sqlite_master WHERE type='table' AND name='users' |
||||
|
`)
|
||||
|
|
||||
|
if (tables.length === 0) { |
||||
|
console.error("表 users 不存在,请先执行迁移") |
||||
|
return |
||||
|
} |
||||
|
// Deletes ALL existing entries
|
||||
|
await knex("users").del() |
||||
|
|
||||
|
// Inserts seed entries
|
||||
|
await knex("users").insert([ |
||||
|
{ name: "Alice", email: "alice@example.com" }, |
||||
|
{ name: "Bob", email: "bob@example.com" }, |
||||
|
]) |
||||
|
} |
@ -0,0 +1,39 @@ |
|||||
|
import log4js from "log4js" |
||||
|
|
||||
|
log4js.configure({ |
||||
|
appenders: { |
||||
|
debug: { |
||||
|
type: "file", |
||||
|
filename: "logs/debug.log", |
||||
|
maxLogSize: 102400, |
||||
|
pattern: "-yyyy-MM-dd.log", |
||||
|
alwaysIncludePattern: true, |
||||
|
backups: 3, |
||||
|
}, |
||||
|
all: { |
||||
|
type: "file", |
||||
|
filename: "logs/all.log", |
||||
|
maxLogSize: 102400, |
||||
|
pattern: "-yyyy-MM-dd.log", |
||||
|
alwaysIncludePattern: true, |
||||
|
backups: 3, |
||||
|
}, |
||||
|
error: { |
||||
|
type: "file", |
||||
|
filename: "logs/error.log", |
||||
|
maxLogSize: 102400, |
||||
|
pattern: "-yyyy-MM-dd.log", |
||||
|
alwaysIncludePattern: true, |
||||
|
backups: 3, |
||||
|
}, |
||||
|
console: { |
||||
|
type: "console", |
||||
|
layout: { type: "colored" }, |
||||
|
}, |
||||
|
}, |
||||
|
categories: { |
||||
|
error: { appenders: ["console", "error"], level: "error" }, |
||||
|
default: { appenders: ["console", "all"], level: "ALL" }, |
||||
|
debug: { appenders: ["debug"], level: "debug" }, |
||||
|
}, |
||||
|
}) |
@ -0,0 +1,38 @@ |
|||||
|
import "./logger" |
||||
|
import "module-alias/register" |
||||
|
import Koa from "koa" |
||||
|
import os from "os" |
||||
|
import LoadPlugins from "./plugins/install" |
||||
|
import UserModel from "./db/models/UserModel" |
||||
|
import log4js from "log4js" |
||||
|
|
||||
|
const logger = log4js.getLogger() |
||||
|
|
||||
|
const app = new Koa() |
||||
|
|
||||
|
LoadPlugins(app) |
||||
|
|
||||
|
app.use(async ctx => { |
||||
|
ctx.body = await UserModel.findAll() |
||||
|
}) |
||||
|
|
||||
|
app.on("error", err => { |
||||
|
logger.error("server error", err) |
||||
|
}) |
||||
|
|
||||
|
const server = app.listen(3000, () => { |
||||
|
const port = server.address().port |
||||
|
const getLocalIP = () => { |
||||
|
const interfaces = os.networkInterfaces() |
||||
|
for (const name of Object.keys(interfaces)) { |
||||
|
for (const iface of interfaces[name]) { |
||||
|
if (iface.family === "IPv4" && !iface.internal) { |
||||
|
return iface.address |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
return "localhost" |
||||
|
} |
||||
|
const localIP = getLocalIP() |
||||
|
logger.trace(`服务器运行在: http://${localIP}:${port}`) |
||||
|
}) |
@ -0,0 +1,13 @@ |
|||||
|
import log4js from "log4js" |
||||
|
|
||||
|
const logger = log4js.getLogger() |
||||
|
|
||||
|
export default async (ctx, next) => { |
||||
|
logger.debug("::in:: %s %s", ctx.method, ctx.path) |
||||
|
const start = Date.now() |
||||
|
await next() |
||||
|
const ms = Date.now() - start |
||||
|
ctx.set("X-Response-Time", `${ms}ms`) |
||||
|
const rt = ctx.response.get("X-Response-Time") |
||||
|
logger.debug(`::out:: takes ${rt} for ${ctx.method} ${ctx.url}`) |
||||
|
} |
@ -0,0 +1,5 @@ |
|||||
|
import ResponseTime from "./ResponseTime"; |
||||
|
|
||||
|
export default (app)=>{ |
||||
|
app.use(ResponseTime) |
||||
|
} |
Loading…
Reference in new issue