feat(base): 优化项目基础开发环境

This commit is contained in:
zhaoyingbo 2024-11-16 11:14:55 +00:00
parent 004044a085
commit 68248c1d24
28 changed files with 201 additions and 79 deletions

View File

@ -1,8 +1,12 @@
{ {
"name": "replace_me", "name": "replace_me",
"image": "micr.cloud.mioffice.cn/zhaoyingbo/dev:bun", "image": "mcr.microsoft.com/devcontainers/typescript-node:20",
"remoteUser": "bun", "features": {
"containerUser": "bun", "ghcr.io/devcontainers/features/docker-in-docker:2": {
"version": "latest",
"moby": true
}
},
"customizations": { "customizations": {
"vscode": { "vscode": {
"settings": { "settings": {
@ -10,20 +14,25 @@
"editor.guides.bracketPairs": true, "editor.guides.bracketPairs": true,
"editor.defaultFormatter": "esbenp.prettier-vscode", "editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.formatOnSave": true, "editor.formatOnSave": true,
"github.copilot.chat.localeOverride": "zh-CN" "github.copilot.chat.localeOverride": "zh-CN",
"editor.codeActionsOnSave": {
"source.fixAll.eslint": "always"
}
}, },
"extensions": [ "extensions": [
"dbaeumer.vscode-eslint", "jock.svg",
"esbenp.prettier-vscode", "GitHub.copilot",
"eamodio.gitlens", "eamodio.gitlens",
"unifiedjs.vscode-mdx", "unifiedjs.vscode-mdx",
"oderwat.indent-rainbow", "dbaeumer.vscode-eslint",
"jock.svg", "esbenp.prettier-vscode",
"ChakrounAnas.turbo-console-log",
"Gruntfuggly.todo-tree", "Gruntfuggly.todo-tree",
"ChakrounAnas.turbo-console-log",
"streetsidesoftware.code-spell-checker",
"MS-CEINTL.vscode-language-pack-zh-hans", "MS-CEINTL.vscode-language-pack-zh-hans",
"GitHub.copilot" "Prisma.prisma"
] ]
} }
} },
"onCreateCommand": "curl -fsSL https://bun.sh/install | bash"
} }

7
.env Normal file
View File

@ -0,0 +1,7 @@
# Environment variables declared in this file are automatically made available to Prisma.
# See the documentation for more detail: https://pris.ly/d/prisma-schema#accessing-environment-variables-from-the-schema
# Prisma supports the native connection string format for PostgreSQL, MySQL, SQLite, SQL Server, MongoDB and CockroachDB.
# See the documentation for all the connection string options: https://pris.ly/d/connection-strings
DATABASE_URL="mysql://root:rootpassword@localhost:3306/testdb"

4
.gitignore vendored
View File

@ -49,8 +49,8 @@ profile-*
.idea .idea
# vscode # vscode
.vscode # .vscode
*code-workspace # *code-workspace
# clinic # clinic
profile* profile*

1
.husky/commit-msg Normal file
View File

@ -0,0 +1 @@
npx --no -- commitlint --edit $1

1
.husky/pre-commit Normal file
View File

@ -0,0 +1 @@
lint-staged

3
.vscode/settings.json vendored Normal file
View File

@ -0,0 +1,3 @@
{
"cSpell.words": ["bunx", "oxlint"]
}

BIN
bun.lockb

Binary file not shown.

2
bunfig.toml Normal file
View File

@ -0,0 +1,2 @@
[install]
registry = "https://registry.npmjs.org"

1
commitlint.config.js Normal file
View File

@ -0,0 +1 @@
export default { extends: ["@commitlint/config-conventional"] }

View File

@ -2,6 +2,6 @@ const templateFunc = () => {}
const template = { const template = {
templateFunc, templateFunc,
}; }
export default template; export default template

View File

@ -1,7 +1,7 @@
import user from "./user"; import user from "./user"
const db = { const db = {
user, user,
}; }
export default db; export default db

View File

@ -1,7 +1,7 @@
import PocketBase from "pocketbase"; import PocketBase from "pocketbase"
const pbClient = new PocketBase("https://ci-pb.xiaomiwh.cn"); const pbClient = new PocketBase("https://ci-pb.xiaomiwh.cn")
pbClient.autoCancellation(false); pbClient.autoCancellation(false)
export default pbClient; export default pbClient

19
db/prisma/user/index.ts Normal file
View File

@ -0,0 +1,19 @@
import { PrismaClient } from "@prisma/client"
const prisma = new PrismaClient()
const main = async () => {
const user = await prisma.user.create({
data: {
name: "Alice",
email: "alice@example.com",
},
})
console.log(user)
const users = await prisma.user.findMany()
console.log(users)
}
main().then(() => {
prisma.$disconnect()
})

View File

@ -1,19 +1,19 @@
import { RecordModel } from "pocketbase"; import { RecordModel } from "pocketbase"
import { managePb404 } from "../../utils/pbTools"; import { managePb404 } from "../../utils/pbTools"
import pbClient from "../pbClient"; import pbClient from "../pbClient"
export interface UserRecordModel extends RecordModel { export interface UserRecordModel extends RecordModel {
user_id: number; user_id: number
username: string; username: string
name: string; name: string
avatar_url: string; avatar_url: string
web_url: string; web_url: string
} }
const getOne = (id: string) => const getOne = (id: string) =>
managePb404<UserRecordModel>( managePb404<UserRecordModel>(
async () => await pbClient.collection("user").getOne(id) async () => await pbClient.collection("user").getOne(id)
); )
const getOneByUserId = (user_id: number) => { const getOneByUserId = (user_id: number) => {
return managePb404<UserRecordModel>( return managePb404<UserRecordModel>(
@ -23,24 +23,24 @@ const getOneByUserId = (user_id: number) => {
.getFirstListItem(`user_id="${user_id}"`, { .getFirstListItem(`user_id="${user_id}"`, {
sort: "-created", sort: "-created",
}) })
); )
}; }
const create = async (data: Partial<UserRecordModel>) => const create = async (data: Partial<UserRecordModel>) =>
await pbClient.collection("user").create<UserRecordModel>(data); await pbClient.collection("user").create<UserRecordModel>(data)
const upsert = async (data: Partial<UserRecordModel>) => { const upsert = async (data: Partial<UserRecordModel>) => {
if (!data.user_id) return null; if (!data.user_id) return null
const userInfo = await getOneByUserId(data.user_id); const userInfo = await getOneByUserId(data.user_id)
if (userInfo) return userInfo; if (userInfo) return userInfo
return await create(data); return await create(data)
}; }
const user = { const user = {
create, create,
upsert, upsert,
getOne, getOne,
getOneByUserId, getOneByUserId,
}; }
export default user; export default user

View File

@ -0,0 +1,16 @@
version: "3.8"
services:
mysql:
image: micr.cloud.mioffice.cn/zhaoyingbo/mysql:8.0
container_name: mysql
environment:
MYSQL_ROOT_PASSWORD: rootpassword
MYSQL_DATABASE: testdb
ports:
- "3306:3306"
volumes:
- mysql-data:/var/lib/mysql
volumes:
mysql-data:

1
docker/mysql/run.sh Normal file
View File

@ -0,0 +1 @@
docker compose -f dev.docker-compose.yml up -d

1
docker/mysql/stop.sh Normal file
View File

@ -0,0 +1 @@
docker compose -f dev.docker-compose.yml down

View File

@ -1,26 +1,26 @@
export default { export default {
languageOptions: { languageOptions: {
ecmaVersion: 2021, ecmaVersion: 2021,
sourceType: 'module', sourceType: "module",
}, },
ignores: ['*.js', '*.cjs', '*.mjs', '/src/Backup/*'], ignores: ["*.js", "*.cjs", "*.mjs", "/src/Backup/*"],
plugins: ['@typescript-eslint', 'simple-import-sort'], plugins: ["@typescript-eslint", "simple-import-sort"],
extends: [ extends: [
'eslint:recommended', "eslint:recommended",
'plugin:@typescript-eslint/recommended', "plugin:@typescript-eslint/recommended",
'prettier', "prettier",
], ],
rules: { rules: {
'@typescript-eslint/no-explicit-any': 'off', "@typescript-eslint/no-explicit-any": "off",
'simple-import-sort/imports': 'error', "simple-import-sort/imports": "error",
'simple-import-sort/exports': 'error', "simple-import-sort/exports": "error",
}, },
overrides: [ overrides: [
{ {
files: ['**/*.ts', '**/*.tsx'], files: ["**/*.ts", "**/*.tsx"],
languageOptions: { languageOptions: {
parser: '@typescript-eslint/parser', parser: "@typescript-eslint/parser",
}, },
}, },
], ],
}; }

View File

@ -1,14 +1,14 @@
import { initSchedule } from "./schedule"; import { initSchedule } from "./schedule"
initSchedule() initSchedule()
Bun.serve({ Bun.serve({
async fetch(req) { async fetch(req) {
const url = new URL(req.url); const url = new URL(req.url)
// 根路由 // 根路由
if (url.pathname === "/") return new Response("hello, glade to see you!"); if (url.pathname === "/") return new Response("hello, glade to see you!")
if (url.pathname === '/ci') return new Response("OK") if (url.pathname === "/ci") return new Response("OK")
return new Response("OK"); return new Response("OK")
}, },
port: 3000, port: 3000,
}); })

View File

@ -3,21 +3,40 @@
"module": "index.ts", "module": "index.ts",
"type": "module", "type": "module",
"scripts": { "scripts": {
"start": "bun run index.ts" "start": "NODE_ENV=production bun run index.ts",
"dev": "NODE_ENV=dev bun run index.ts --watch",
"lint": "oxlint --fix .",
"prepare": "husky",
"prettier": "prettier --write .",
"init:prisma": "bunx prisma migrate dev --name init"
},
"lint-staged": {
"*.{js,jsx,ts,tsx}": [
"oxlint --fix",
"eslint --fix",
"prettier --write"
]
}, },
"devDependencies": { "devDependencies": {
"eslint": "^9.2.0", "@commitlint/cli": "^19.5.0",
"bun-types": "latest", "@commitlint/config-conventional": "^19.5.0",
"@types/lodash": "^4.14.202", "@types/lodash": "^4.14.202",
"@types/node-schedule": "^2.1.6", "@types/node-schedule": "^2.1.6",
"eslint-plugin-simple-import-sort": "^12.1.0",
"@typescript-eslint/eslint-plugin": "^7.8.0", "@typescript-eslint/eslint-plugin": "^7.8.0",
"@typescript-eslint/parser": "^7.8.0" "@typescript-eslint/parser": "^7.8.0",
"bun-types": "latest",
"eslint": "^9.2.0",
"eslint-plugin-simple-import-sort": "^12.1.0",
"husky": "^9.1.6",
"lint-staged": "^15.2.10",
"oxlint": "^0.11.1",
"prettier": "^3.3.3"
}, },
"peerDependencies": { "peerDependencies": {
"typescript": "^5.0.0" "typescript": "^5.0.0"
}, },
"dependencies": { "dependencies": {
"@prisma/client": "^5.22.0",
"lodash": "^4.17.21", "lodash": "^4.17.21",
"node-schedule": "^2.1.1", "node-schedule": "^2.1.1",
"pocketbase": "^0.21.1" "pocketbase": "^0.21.1"

6
prettier.config.js Normal file
View File

@ -0,0 +1,6 @@
export default {
trailingComma: "es5",
tabWidth: 2,
semi: false,
singleQuote: false,
}

View File

@ -0,0 +1,9 @@
-- CreateTable
CREATE TABLE `User` (
`id` INTEGER NOT NULL AUTO_INCREMENT,
`name` VARCHAR(191) NOT NULL,
`email` VARCHAR(191) NOT NULL,
UNIQUE INDEX `User_email_key`(`email`),
PRIMARY KEY (`id`)
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;

View File

@ -0,0 +1,3 @@
# Please do not edit this file manually
# It should be added in your version-control system (i.e. Git)
provider = "mysql"

20
prisma/schema.prisma Normal file
View File

@ -0,0 +1,20 @@
// This is your Prisma schema file,
// learn more about it in the docs: https://pris.ly/d/prisma-schema
// Looking for ways to speed up your queries, or scale easily with your serverless or edge functions?
// Try Prisma Accelerate: https://pris.ly/cli/accelerate-init
generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "mysql"
url = env("DATABASE_URL")
}
model User {
id Int @id @default(autoincrement())
name String
email String @unique
}

View File

@ -1,10 +1,10 @@
import schedule from 'node-schedule' import schedule from "node-schedule"
const func = () => {} const func = () => {}
export const initSchedule = async () => { export const initSchedule = async () => {
// 定时任务每15分钟刷新一次token // 定时任务每15分钟刷新一次token
schedule.scheduleJob('*/15 * * * *', func); schedule.scheduleJob("*/15 * * * *", func)
// 立即执行一次 // 立即执行一次
func() func()
} }

View File

@ -1,7 +1,7 @@
const fetchGetParams = { const fetchGetParams = {
method: "GET", method: "GET",
headers: { "PRIVATE-TOKEN": "Zd1UASPcMwVox5tNS6ep" }, headers: { "PRIVATE-TOKEN": "Zd1UASPcMwVox5tNS6ep" },
}; }
/** /**
* *
@ -12,17 +12,17 @@ const fetchTemplate = async (id: number) => {
const response = await fetch( const response = await fetch(
`https://git.n.xiaomi.com/api/v4/projects/${id}`, `https://git.n.xiaomi.com/api/v4/projects/${id}`,
fetchGetParams fetchGetParams
); )
const body = (await response.json()) as any; const body = (await response.json()) as any
if (body.message === "404 Project Not Found") return null; if (body.message === "404 Project Not Found") return null
return body; return body
} catch { } catch {
return null; return null
} }
}; }
const service = { const service = {
fetchTemplate, fetchTemplate,
}; }
export default service; export default service

View File

@ -0,0 +1,4 @@
export interface FakeType {
name: string
age: number
}

View File

@ -1,9 +1,9 @@
export const managePb404 = async <T>(dbFunc: Function): Promise<T | null> => { export const managePb404 = async <T>(dbFunc: Function): Promise<T | null> => {
try { try {
return await dbFunc(); return await dbFunc()
} catch (err: any) { } catch (err: any) {
if (err?.message === "The requested resource wasn't found.") { if (err?.message === "The requested resource wasn't found.") {
return null; return null
} else throw err; } else throw err
} }
}; }