feat(base): 优化项目基础开发环境
This commit is contained in:
parent
004044a085
commit
68248c1d24
@ -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
7
.env
Normal 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
4
.gitignore
vendored
@ -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
1
.husky/commit-msg
Normal file
@ -0,0 +1 @@
|
|||||||
|
npx --no -- commitlint --edit $1
|
1
.husky/pre-commit
Normal file
1
.husky/pre-commit
Normal file
@ -0,0 +1 @@
|
|||||||
|
lint-staged
|
3
.vscode/settings.json
vendored
Normal file
3
.vscode/settings.json
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"cSpell.words": ["bunx", "oxlint"]
|
||||||
|
}
|
2
bunfig.toml
Normal file
2
bunfig.toml
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
[install]
|
||||||
|
registry = "https://registry.npmjs.org"
|
1
commitlint.config.js
Normal file
1
commitlint.config.js
Normal file
@ -0,0 +1 @@
|
|||||||
|
export default { extends: ["@commitlint/config-conventional"] }
|
@ -2,6 +2,6 @@ const templateFunc = () => {}
|
|||||||
|
|
||||||
const template = {
|
const template = {
|
||||||
templateFunc,
|
templateFunc,
|
||||||
};
|
}
|
||||||
|
|
||||||
export default template;
|
export default template
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import user from "./user";
|
import user from "./user"
|
||||||
|
|
||||||
const db = {
|
const db = {
|
||||||
user,
|
user,
|
||||||
};
|
}
|
||||||
|
|
||||||
export default db;
|
export default db
|
||||||
|
@ -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
19
db/prisma/user/index.ts
Normal 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()
|
||||||
|
})
|
@ -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
|
||||||
|
16
docker/mysql/dev.docker-compose.yml
Normal file
16
docker/mysql/dev.docker-compose.yml
Normal 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
1
docker/mysql/run.sh
Normal file
@ -0,0 +1 @@
|
|||||||
|
docker compose -f dev.docker-compose.yml up -d
|
1
docker/mysql/stop.sh
Normal file
1
docker/mysql/stop.sh
Normal file
@ -0,0 +1 @@
|
|||||||
|
docker compose -f dev.docker-compose.yml down
|
@ -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",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
};
|
}
|
||||||
|
12
index.ts
12
index.ts
@ -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,
|
||||||
});
|
})
|
||||||
|
29
package.json
29
package.json
@ -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
6
prettier.config.js
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
export default {
|
||||||
|
trailingComma: "es5",
|
||||||
|
tabWidth: 2,
|
||||||
|
semi: false,
|
||||||
|
singleQuote: false,
|
||||||
|
}
|
9
prisma/migrations/20241116101639_init/migration.sql
Normal file
9
prisma/migrations/20241116101639_init/migration.sql
Normal 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;
|
3
prisma/migrations/migration_lock.toml
Normal file
3
prisma/migrations/migration_lock.toml
Normal 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
20
prisma/schema.prisma
Normal 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
|
||||||
|
}
|
@ -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()
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
@ -0,0 +1,4 @@
|
|||||||
|
export interface FakeType {
|
||||||
|
name: string
|
||||||
|
age: number
|
||||||
|
}
|
@ -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
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user