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