mirror of
https://gitee.com/bitdance-team/chrome-extension
synced 2025-01-10 21:58:14 +08:00
Merge branch 'feat-sst' into develop
This commit is contained in:
commit
3686ddb6c9
@ -2,8 +2,11 @@
|
|||||||
"root": true,
|
"root": true,
|
||||||
"ignorePatterns": ["**/*"],
|
"ignorePatterns": ["**/*"],
|
||||||
"plugins": ["@nrwl/nx"],
|
"plugins": ["@nrwl/nx"],
|
||||||
"overrides": [
|
"rules": {
|
||||||
{
|
"@typescript-eslint/no-explicit-any": "off",
|
||||||
|
"@typescript-eslint/no-unused-vars": "off"
|
||||||
|
},
|
||||||
|
"overrides": [{
|
||||||
"files": ["*.ts", "*.tsx", "*.js", "*.jsx"],
|
"files": ["*.ts", "*.tsx", "*.js", "*.jsx"],
|
||||||
"rules": {
|
"rules": {
|
||||||
"@nrwl/nx/enforce-module-boundaries": [
|
"@nrwl/nx/enforce-module-boundaries": [
|
||||||
@ -11,12 +14,10 @@
|
|||||||
{
|
{
|
||||||
"enforceBuildableLibDependency": true,
|
"enforceBuildableLibDependency": true,
|
||||||
"allow": [],
|
"allow": [],
|
||||||
"depConstraints": [
|
"depConstraints": [{
|
||||||
{
|
"sourceTag": "*",
|
||||||
"sourceTag": "*",
|
"onlyDependOnLibsWithTags": ["*"]
|
||||||
"onlyDependOnLibsWithTags": ["*"]
|
}]
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
10
.gitignore
vendored
10
.gitignore
vendored
@ -1,12 +1,12 @@
|
|||||||
# See http://help.github.com/ignore-files/ for more about ignoring files.
|
# See http://help.github.com/ignore-files/ for more about ignoring files.
|
||||||
|
|
||||||
# compiled output
|
# compiled output
|
||||||
/dist
|
dist
|
||||||
/tmp
|
tmp
|
||||||
/out-tsc
|
out-tsc
|
||||||
|
|
||||||
# dependencies
|
# dependencies
|
||||||
/node_modules
|
node_modules
|
||||||
|
|
||||||
# IDEs and editors
|
# IDEs and editors
|
||||||
/.idea
|
/.idea
|
||||||
@ -37,3 +37,5 @@ testem.log
|
|||||||
# System Files
|
# System Files
|
||||||
.DS_Store
|
.DS_Store
|
||||||
Thumbs.db
|
Thumbs.db
|
||||||
|
|
||||||
|
.inspirecloud.service.conf
|
||||||
|
3
.vscode/settings.json
vendored
Normal file
3
.vscode/settings.json
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"typescript.tsdk": "node_modules\\typescript\\lib"
|
||||||
|
}
|
13
jsconfig.json
Normal file
13
jsconfig.json
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"module": "commonjs",
|
||||||
|
"target": "es2020",
|
||||||
|
"jsx": "preserve",
|
||||||
|
"strictFunctionTypes": true,
|
||||||
|
"experimentalDecorators": true
|
||||||
|
},
|
||||||
|
"exclude": [
|
||||||
|
"node_modules",
|
||||||
|
"**/node_modules/*"
|
||||||
|
]
|
||||||
|
}
|
3871
package-lock.json
generated
3871
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -10,6 +10,8 @@
|
|||||||
},
|
},
|
||||||
"private": true,
|
"private": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@byteinspire/api": "^1.0.12",
|
||||||
|
"lodash": "^4.17.21",
|
||||||
"tslib": "^2.0.0",
|
"tslib": "^2.0.0",
|
||||||
"vue": "^3.0.5"
|
"vue": "^3.0.5"
|
||||||
},
|
},
|
||||||
@ -24,6 +26,9 @@
|
|||||||
"@nrwl/workspace": "13.5.3",
|
"@nrwl/workspace": "13.5.3",
|
||||||
"@nx-plus/vite": "^12.2.0",
|
"@nx-plus/vite": "^12.2.0",
|
||||||
"@types/jest": "27.0.2",
|
"@types/jest": "27.0.2",
|
||||||
|
"@types/koa": "^2.13.4",
|
||||||
|
"@types/koa__router": "^8.0.11",
|
||||||
|
"@types/lodash": "^4.14.178",
|
||||||
"@types/node": "16.11.7",
|
"@types/node": "16.11.7",
|
||||||
"@typescript-eslint/eslint-plugin": "~5.3.0",
|
"@typescript-eslint/eslint-plugin": "~5.3.0",
|
||||||
"@typescript-eslint/parser": "~5.3.0",
|
"@typescript-eslint/parser": "~5.3.0",
|
||||||
|
18
packages/services-api/.eslintrc.json
Normal file
18
packages/services-api/.eslintrc.json
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
{
|
||||||
|
"extends": ["../../.eslintrc.json"],
|
||||||
|
"ignorePatterns": ["!**/*"],
|
||||||
|
"overrides": [
|
||||||
|
{
|
||||||
|
"files": ["*.ts", "*.tsx", "*.js", "*.jsx"],
|
||||||
|
"rules": {}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"files": ["*.ts", "*.tsx"],
|
||||||
|
"rules": {}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"files": ["*.js", "*.jsx"],
|
||||||
|
"rules": {}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
11
packages/services-api/README.md
Normal file
11
packages/services-api/README.md
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
# services-api
|
||||||
|
|
||||||
|
This library was generated with [Nx](https://nx.dev).
|
||||||
|
|
||||||
|
## Building
|
||||||
|
|
||||||
|
Run `nx build services-api` to build the library.
|
||||||
|
|
||||||
|
## Running unit tests
|
||||||
|
|
||||||
|
Run `nx test services-api` to execute the unit tests via [Jest](https://jestjs.io).
|
14
packages/services-api/jest.config.js
Normal file
14
packages/services-api/jest.config.js
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
module.exports = {
|
||||||
|
displayName: 'services-api',
|
||||||
|
preset: '../../jest.preset.js',
|
||||||
|
globals: {
|
||||||
|
'ts-jest': {
|
||||||
|
tsconfig: '<rootDir>/tsconfig.spec.json',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
transform: {
|
||||||
|
'^.+\\.[tj]s$': 'ts-jest',
|
||||||
|
},
|
||||||
|
moduleFileExtensions: ['ts', 'js', 'html'],
|
||||||
|
coverageDirectory: '../../coverage/packages/services-api',
|
||||||
|
};
|
5
packages/services-api/package.json
Normal file
5
packages/services-api/package.json
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"name": "@bitdance/services-api",
|
||||||
|
"version": "0.0.1",
|
||||||
|
"type": "commonjs"
|
||||||
|
}
|
33
packages/services-api/project.json
Normal file
33
packages/services-api/project.json
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
{
|
||||||
|
"root": "packages/services-api",
|
||||||
|
"sourceRoot": "packages/services-api/src",
|
||||||
|
"projectType": "library",
|
||||||
|
"targets": {
|
||||||
|
"build": {
|
||||||
|
"executor": "@nrwl/js:tsc",
|
||||||
|
"outputs": ["{options.outputPath}"],
|
||||||
|
"options": {
|
||||||
|
"outputPath": "dist/packages/services-api",
|
||||||
|
"main": "packages/services-api/src/index.ts",
|
||||||
|
"tsConfig": "packages/services-api/tsconfig.lib.json",
|
||||||
|
"assets": ["packages/services-api/*.md"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"lint": {
|
||||||
|
"executor": "@nrwl/linter:eslint",
|
||||||
|
"outputs": ["{options.outputFile}"],
|
||||||
|
"options": {
|
||||||
|
"lintFilePatterns": ["packages/services-api/**/*.ts"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"test": {
|
||||||
|
"executor": "@nrwl/jest:jest",
|
||||||
|
"outputs": ["coverage/packages/services-api"],
|
||||||
|
"options": {
|
||||||
|
"jestConfig": "packages/services-api/jest.config.js",
|
||||||
|
"passWithNoTests": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"tags": []
|
||||||
|
}
|
1
packages/services-api/src/index.ts
Normal file
1
packages/services-api/src/index.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export * from './lib/services-api';
|
7
packages/services-api/src/lib/services-api.spec.ts
Normal file
7
packages/services-api/src/lib/services-api.spec.ts
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
import { servicesApi } from './services-api';
|
||||||
|
|
||||||
|
describe('servicesApi', () => {
|
||||||
|
it('should work', () => {
|
||||||
|
expect(servicesApi()).toEqual('services-api');
|
||||||
|
});
|
||||||
|
});
|
3
packages/services-api/src/lib/services-api.ts
Normal file
3
packages/services-api/src/lib/services-api.ts
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
export function servicesApi(): string {
|
||||||
|
return 'services-api';
|
||||||
|
}
|
22
packages/services-api/tsconfig.json
Normal file
22
packages/services-api/tsconfig.json
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
{
|
||||||
|
"extends": "../../tsconfig.base.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"module": "CommonJS",
|
||||||
|
"forceConsistentCasingInFileNames": true,
|
||||||
|
"strict": true,
|
||||||
|
"noImplicitOverride": true,
|
||||||
|
"noPropertyAccessFromIndexSignature": true,
|
||||||
|
"noImplicitReturns": true,
|
||||||
|
"noFallthroughCasesInSwitch": true
|
||||||
|
},
|
||||||
|
"files": [],
|
||||||
|
"include": [],
|
||||||
|
"references": [
|
||||||
|
{
|
||||||
|
"path": "./tsconfig.lib.json"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "./tsconfig.spec.json"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
@ -1,10 +1,13 @@
|
|||||||
{
|
{
|
||||||
"extends": ["../../.eslintrc.json"],
|
"extends": ["../../.eslintrc.json"],
|
||||||
"ignorePatterns": ["!**/*"],
|
"ignorePatterns": ["!**/*"],
|
||||||
"overrides": [
|
"overrides": [{
|
||||||
{
|
|
||||||
"files": ["*.ts", "*.tsx", "*.js", "*.jsx"],
|
"files": ["*.ts", "*.tsx", "*.js", "*.jsx"],
|
||||||
"rules": {}
|
"rules": {
|
||||||
|
"@typescript-eslint/no-explicit-any": "off",
|
||||||
|
"@typescript-eslint/no-unused-vars": "off",
|
||||||
|
"@typescript-eslint/triple-slash-reference": "off"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"files": ["*.ts", "*.tsx"],
|
"files": ["*.ts", "*.tsx"],
|
||||||
|
3
packages/services/.inspirecloudignore
Normal file
3
packages/services/.inspirecloudignore
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
**/node_modules
|
||||||
|
.*/**
|
||||||
|
src
|
38
packages/services/gulpfile.js
Normal file
38
packages/services/gulpfile.js
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
const {src,dest,parallel,watch,series} = require('gulp');
|
||||||
|
const ts = require('gulp-typescript');
|
||||||
|
const tsProject = ts.createProject("tsconfig.app.json");
|
||||||
|
const shell = require('gulp-shell');
|
||||||
|
const nodemon = require('gulp-nodemon');
|
||||||
|
|
||||||
|
function buildTs () {
|
||||||
|
return src('./src/**/*.ts')
|
||||||
|
.pipe(tsProject())
|
||||||
|
.pipe(dest('./dist'));
|
||||||
|
}
|
||||||
|
|
||||||
|
function start (done) {
|
||||||
|
nodemon({
|
||||||
|
exec: 'inspirecloud dev',
|
||||||
|
ext: 'js,ts',
|
||||||
|
ignore: './dist',
|
||||||
|
delay: 50,
|
||||||
|
tasks: ['buildTs'],
|
||||||
|
done
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function watchServe () {
|
||||||
|
return watch('src/**/*.ts', {
|
||||||
|
delay: 100
|
||||||
|
}, series(buildTs))
|
||||||
|
}
|
||||||
|
|
||||||
|
function serve () {
|
||||||
|
return series(buildTs, start)()
|
||||||
|
}
|
||||||
|
|
||||||
|
function deploy () {
|
||||||
|
return series(buildTs, shell.task('inspirecloud deploy'))()
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {serve,deploy,buildTs}
|
3
packages/services/index.js
Normal file
3
packages/services/index.js
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
const { app } = require('./dist/app')
|
||||||
|
// 导出 HTTP handler, koa 对象不可直接作为 HTTP handler, 需要调用 callback() 获取
|
||||||
|
module.exports = app.callback()
|
4
packages/services/inspirecloud.json
Normal file
4
packages/services/inspirecloud.json
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"main": "index.js",
|
||||||
|
"watch": ["index.js"]
|
||||||
|
}
|
5580
packages/services/package-lock.json
generated
Normal file
5580
packages/services/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
@ -1,5 +1,33 @@
|
|||||||
{
|
{
|
||||||
"name": "@bitdance/services",
|
"name": "@bitdance/services",
|
||||||
"version": "0.0.1",
|
"version": "0.0.1",
|
||||||
"type": "commonjs"
|
"type": "commonjs",
|
||||||
|
"scripts": {
|
||||||
|
"dev": "gulp serve",
|
||||||
|
"deploy": "gulp deploy",
|
||||||
|
"test": "jest"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@byteinspire/api": "^1.0.12",
|
||||||
|
"@byteinspire/cli": "^2.2.23",
|
||||||
|
"@koa/router": "^10.1.1",
|
||||||
|
"koa": "^2.13.4",
|
||||||
|
"koa-body": "^4.2.0",
|
||||||
|
"koa-bunyan-logger": "^2.1.0",
|
||||||
|
"koa-helmet": "^6.1.0",
|
||||||
|
"koa-static": "^5.0.0",
|
||||||
|
"koa-swagger-decorator": "^1.8.2",
|
||||||
|
"koa2-cors": "^2.0.6",
|
||||||
|
"lodash": "^4.17.21"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/koa-bunyan-logger": "^2.1.4",
|
||||||
|
"@types/koa2-cors": "^2.0.2",
|
||||||
|
"gulp": "^4.0.2",
|
||||||
|
"gulp-cli": "^2.3.0",
|
||||||
|
"gulp-nodemon": "^2.5.0",
|
||||||
|
"gulp-shell": "^0.8.0",
|
||||||
|
"gulp-typescript": "^6.0.0-alpha.1",
|
||||||
|
"nodemon": "^2.0.15"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,33 +1,4 @@
|
|||||||
{
|
{
|
||||||
"root": "packages/services",
|
"root": "packages/services",
|
||||||
"sourceRoot": "packages/services/src",
|
|
||||||
"projectType": "library",
|
|
||||||
"targets": {
|
|
||||||
"build": {
|
|
||||||
"executor": "@nrwl/js:tsc",
|
|
||||||
"outputs": ["{options.outputPath}"],
|
|
||||||
"options": {
|
|
||||||
"outputPath": "dist/packages/services",
|
|
||||||
"main": "packages/services/src/index.ts",
|
|
||||||
"tsConfig": "packages/services/tsconfig.lib.json",
|
|
||||||
"assets": ["packages/services/*.md"]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"lint": {
|
|
||||||
"executor": "@nrwl/linter:eslint",
|
|
||||||
"outputs": ["{options.outputFile}"],
|
|
||||||
"options": {
|
|
||||||
"lintFilePatterns": ["packages/services/**/*.ts"]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"test": {
|
|
||||||
"executor": "@nrwl/jest:jest",
|
|
||||||
"outputs": ["coverage/packages/services"],
|
|
||||||
"options": {
|
|
||||||
"jestConfig": "packages/services/jest.config.js",
|
|
||||||
"passWithNoTests": true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"tags": []
|
"tags": []
|
||||||
}
|
}
|
||||||
|
55
packages/services/src/app.ts
Normal file
55
packages/services/src/app.ts
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
import Koa from 'koa'
|
||||||
|
import koaBody from 'koa-body';
|
||||||
|
import router from './router';
|
||||||
|
import helmet from 'koa-helmet'
|
||||||
|
import koaBunyanLogger from 'koa-bunyan-logger'
|
||||||
|
import koaCors from 'koa2-cors';
|
||||||
|
import { errorHandler } from './utils/response'
|
||||||
|
|
||||||
|
const app = new Koa();
|
||||||
|
|
||||||
|
// 跨域请求设置
|
||||||
|
app.use(koaCors({
|
||||||
|
origin: function (ctx) { //设置允许来自指定域名请求
|
||||||
|
return '*'
|
||||||
|
},
|
||||||
|
maxAge: 5, //指定本次预检请求的有效期,单位为秒。
|
||||||
|
credentials: true, //是否允许发送Cookie
|
||||||
|
allowMethods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'], //设置所允许的HTTP请求方法'
|
||||||
|
allowHeaders: ['Content-Type', 'Authorization', 'Accept', 'x-tt-session-v2'], //设置服务器支持的所有头信息字段
|
||||||
|
exposeHeaders: ['WWW-Authenticate', 'Server-Authorization'] //设置获取其他自定义字段
|
||||||
|
}))
|
||||||
|
|
||||||
|
// 过滤不安全的请求内容
|
||||||
|
// app.use(helmet.contentSecurityPolicy());
|
||||||
|
app.use(helmet.dnsPrefetchControl());
|
||||||
|
app.use(helmet.expectCt());
|
||||||
|
app.use(helmet.frameguard());
|
||||||
|
app.use(helmet.hidePoweredBy());
|
||||||
|
app.use(helmet.hsts());
|
||||||
|
app.use(helmet.ieNoOpen());
|
||||||
|
app.use(helmet.noSniff());
|
||||||
|
app.use(helmet.permittedCrossDomainPolicies());
|
||||||
|
app.use(helmet.referrerPolicy());
|
||||||
|
app.use(helmet.xssFilter());
|
||||||
|
|
||||||
|
// 解析不同类别的请求
|
||||||
|
app.use(koaBody({
|
||||||
|
multipart: true,
|
||||||
|
formidable: {
|
||||||
|
maxFileSize: 30 * 1024 * 1024
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
// 请求日志
|
||||||
|
app.use(koaBunyanLogger());
|
||||||
|
app.use(koaBunyanLogger.requestIdContext());
|
||||||
|
app.use(koaBunyanLogger.requestLogger());
|
||||||
|
|
||||||
|
|
||||||
|
// 若后面的路由抛错,则封装为错误响应返回
|
||||||
|
app.use(errorHandler);
|
||||||
|
|
||||||
|
// 为应用使用路由定义
|
||||||
|
app.use(router.routes()).use(router.allowedMethods());
|
||||||
|
export { app }
|
56
packages/services/src/controllers/auth.ts
Normal file
56
packages/services/src/controllers/auth.ts
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
import { getCurrentUser, login, logout, register } from '../services/user';
|
||||||
|
import { Context, description, middlewares, request, security, securityAll, summary, swaggerClass, swaggerProperty, tagsAll } from 'koa-swagger-decorator';
|
||||||
|
import { authenticate, swaggerBody } from '../utils/swagger';
|
||||||
|
import { isNil } from 'lodash';
|
||||||
|
import { authentication } from '../utils/response';
|
||||||
|
@swaggerClass()
|
||||||
|
export class LoginRegDto {
|
||||||
|
@swaggerProperty({ type: "string", required: true }) username = "";
|
||||||
|
@swaggerProperty({ type: "string", required: true }) password = "";
|
||||||
|
};
|
||||||
|
|
||||||
|
@tagsAll('认证系统')
|
||||||
|
export default class Auth {
|
||||||
|
|
||||||
|
@request('POST', '/auth/register')
|
||||||
|
@summary('注册一个用户')
|
||||||
|
@swaggerBody(LoginRegDto)
|
||||||
|
static async register(ctx: Context) {
|
||||||
|
const params = ctx.validatedBody as LoginRegDto;
|
||||||
|
const data = await register(
|
||||||
|
ctx,
|
||||||
|
params.username,
|
||||||
|
params.password
|
||||||
|
);
|
||||||
|
ctx.body = { code: 200, msg: '' }
|
||||||
|
}
|
||||||
|
|
||||||
|
@request('POST', '/auth/login')
|
||||||
|
@summary('登录一个用户')
|
||||||
|
@swaggerBody(LoginRegDto)
|
||||||
|
static async login(ctx: Context) {
|
||||||
|
const params = ctx.validatedBody as LoginRegDto;
|
||||||
|
const data = await login(
|
||||||
|
ctx,
|
||||||
|
params.username,
|
||||||
|
params.password
|
||||||
|
);
|
||||||
|
ctx.body = { code: 200, msg: '' }
|
||||||
|
}
|
||||||
|
|
||||||
|
@request('GET', '/auth/info')
|
||||||
|
@summary('获取当前的用户信息')
|
||||||
|
@authenticate
|
||||||
|
static async info(ctx: Context) {
|
||||||
|
const data = await getCurrentUser(ctx)
|
||||||
|
ctx.body = { code: 200, msg: '', data };
|
||||||
|
}
|
||||||
|
|
||||||
|
@request('POST', '/auth/logout')
|
||||||
|
@summary('登出当前系统')
|
||||||
|
@security([{ session: [] }])
|
||||||
|
static async logout(ctx: Context) {
|
||||||
|
await logout(ctx)
|
||||||
|
ctx.body = { code: 200, msg: '' };
|
||||||
|
}
|
||||||
|
}
|
29
packages/services/src/controllers/files.ts
Normal file
29
packages/services/src/controllers/files.ts
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
import { getCurrentUser, login, logout, register } from '../services/user';
|
||||||
|
import { Context, description, middlewares, formData, request, security, securityAll, summary, swaggerClass, swaggerProperty, tagsAll } from 'koa-swagger-decorator';
|
||||||
|
import { swaggerBody } from '../utils/swagger';
|
||||||
|
import { isArray, isNil, uniqueId } from 'lodash';
|
||||||
|
import { authentication } from '../utils/response';
|
||||||
|
import { uploadFile } from '../services/files';
|
||||||
|
import { readFileSync } from 'fs'
|
||||||
|
|
||||||
|
@tagsAll('文件系统')
|
||||||
|
export default class RemoteFile {
|
||||||
|
|
||||||
|
@request('POST', '/files')
|
||||||
|
@summary('上传文件')
|
||||||
|
@formData({ file: { type: 'file', required: true, description: '文件内容' } })
|
||||||
|
@security([{ session: [] }])
|
||||||
|
@middlewares([authentication])
|
||||||
|
static async upload(ctx: Context) {
|
||||||
|
const file = ctx.request.files?.['file']
|
||||||
|
|
||||||
|
if (isNil(file)) throw { code: 400, msg: '未上传任何文件' }
|
||||||
|
if (isArray(file)) throw { code: 400, msg: '只能上传一个文件' }
|
||||||
|
|
||||||
|
const content = readFileSync(file.path)
|
||||||
|
const data = await uploadFile(file.name || uniqueId('file'), content)
|
||||||
|
|
||||||
|
ctx.body = { code: 200, msg: '', data }
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
19
packages/services/src/controllers/index.ts
Normal file
19
packages/services/src/controllers/index.ts
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
import { Context, description, middlewares, request, security, summary, swaggerClass, swaggerProperty, tagsAll } from 'koa-swagger-decorator';
|
||||||
|
import { authentication } from '../utils/response';
|
||||||
|
import { authenticate, swaggerBody } from '../utils/swagger';
|
||||||
|
|
||||||
|
@tagsAll('测试')
|
||||||
|
export default class Index {
|
||||||
|
@request('GET', '/test')
|
||||||
|
@summary('测试服务器是否正常')
|
||||||
|
static async test(ctx: Context) {
|
||||||
|
ctx.body = { code: 200, msg: '', data: new Date() }
|
||||||
|
}
|
||||||
|
|
||||||
|
@request('GET', '/authtest')
|
||||||
|
@summary('测试服务器是否正常')
|
||||||
|
@authenticate
|
||||||
|
static async authtest(ctx: Context) {
|
||||||
|
ctx.body = { code: 200, msg: '', data: new Date() }
|
||||||
|
}
|
||||||
|
}
|
50
packages/services/src/controllers/memo.ts
Normal file
50
packages/services/src/controllers/memo.ts
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
import { getCurrentUser } from './../services/user';
|
||||||
|
import { Context, description, middlewares, path, request, security, summary, swaggerClass, swaggerProperty, tagsAll } from 'koa-swagger-decorator';
|
||||||
|
import { authentication } from '../utils/response';
|
||||||
|
import { authenticate, swaggerBody } from '../utils/swagger';
|
||||||
|
import { ObjectId, UserContext } from '../models/base';
|
||||||
|
import memoService from '../services/memo';
|
||||||
|
import { multipageHttpQuery } from '../utils/multipage';
|
||||||
|
import { User } from '../models/user';
|
||||||
|
|
||||||
|
export class CreateMemoDto {
|
||||||
|
@swaggerProperty({ type: "string", required: true }) content = "";
|
||||||
|
};
|
||||||
|
|
||||||
|
@tagsAll('备忘录')
|
||||||
|
export default class Memo {
|
||||||
|
|
||||||
|
@request('GET', '/memos')
|
||||||
|
@summary('获取个人所有的备忘录')
|
||||||
|
@authenticate
|
||||||
|
@multipageHttpQuery()
|
||||||
|
static async getAll(ctx: UserContext) {
|
||||||
|
const data = await memoService.findByUserPage(ctx.user._id, ctx.validatedQuery)
|
||||||
|
ctx.body = { code: 200, msg: '', data }
|
||||||
|
}
|
||||||
|
|
||||||
|
@request('POST', '/memos')
|
||||||
|
@summary('创建一个备忘录')
|
||||||
|
@authenticate
|
||||||
|
@swaggerBody(CreateMemoDto)
|
||||||
|
static async createOne(ctx: UserContext) {
|
||||||
|
// ctx.user 获取的只是一个JSON字段,不支持数据库关联
|
||||||
|
const user = await memoService.db.table<User>('_user').where({ _id: new ObjectId(ctx.user._id) }).projection({ username: 1, lastLogin: 1, status: 1 }).findOne()
|
||||||
|
|
||||||
|
const data = await memoService.createOne({
|
||||||
|
content: ctx.validatedBody.content,
|
||||||
|
user
|
||||||
|
})
|
||||||
|
ctx.body = { code: 200, msg: '', data }
|
||||||
|
}
|
||||||
|
|
||||||
|
@request('DELETE', '/memos/{id}')
|
||||||
|
@summary('删除一个备忘录')
|
||||||
|
@authenticate
|
||||||
|
@path({ id: { type: 'string', required: true, description: 'ID' } })
|
||||||
|
static async deleteOne(ctx: UserContext) {
|
||||||
|
await memoService.deleteOne(ctx.validatedParams.id)
|
||||||
|
ctx.body = { code: 200, msg: '' }
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -1 +0,0 @@
|
|||||||
export * from './lib/services';
|
|
@ -1,7 +0,0 @@
|
|||||||
import { services } from './services';
|
|
||||||
|
|
||||||
describe('services', () => {
|
|
||||||
it('should work', () => {
|
|
||||||
expect(services()).toEqual('services');
|
|
||||||
});
|
|
||||||
});
|
|
@ -1,3 +0,0 @@
|
|||||||
export function services(): string {
|
|
||||||
return 'services';
|
|
||||||
}
|
|
15
packages/services/src/models/base.ts
Normal file
15
packages/services/src/models/base.ts
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
import { Context } from 'koa-swagger-decorator';
|
||||||
|
export type WithId<T extends object> = T & { _id: string }
|
||||||
|
|
||||||
|
import inspirecloud from '@byteinspire/api';
|
||||||
|
import { User } from './user';
|
||||||
|
|
||||||
|
export const ObjectId = inspirecloud.db.ObjectId
|
||||||
|
|
||||||
|
export type ObjectIdType = InstanceType<typeof ObjectId>
|
||||||
|
|
||||||
|
export interface PageQuery { page: number, pageSize: number }
|
||||||
|
|
||||||
|
export interface UserContext extends Context {
|
||||||
|
user: WithId<User>
|
||||||
|
}
|
8
packages/services/src/models/memo.ts
Normal file
8
packages/services/src/models/memo.ts
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
import { IEntity } from '@byteinspire/db';
|
||||||
|
import { User } from './user';
|
||||||
|
import { ObjectIdType } from './base';
|
||||||
|
|
||||||
|
export interface Memo {
|
||||||
|
content: string;
|
||||||
|
user: User
|
||||||
|
}
|
17
packages/services/src/models/user.ts
Normal file
17
packages/services/src/models/user.ts
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
export interface User {
|
||||||
|
avatar: string | null;
|
||||||
|
email: string | null;
|
||||||
|
phoneNumber: string | null;
|
||||||
|
intro: string | null;
|
||||||
|
lastLogin: number;
|
||||||
|
loginCount: number;
|
||||||
|
lastIp: string;
|
||||||
|
status: boolean;
|
||||||
|
createAt: number;
|
||||||
|
createdAt: number;
|
||||||
|
updatedAt: number;
|
||||||
|
username: string;
|
||||||
|
firstProvider: string;
|
||||||
|
loginAt: number;
|
||||||
|
expireAt: number;
|
||||||
|
}
|
32
packages/services/src/router.ts
Normal file
32
packages/services/src/router.ts
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
import '@koa/router'
|
||||||
|
|
||||||
|
import { SwaggerRouter } from 'koa-swagger-decorator'
|
||||||
|
import path from 'path'
|
||||||
|
|
||||||
|
const router = new SwaggerRouter({ prefix: '/api' }) // extends from koa-router
|
||||||
|
|
||||||
|
// swagger docs avaliable at http://localhost:3000/api/swagger-html
|
||||||
|
router.swagger({
|
||||||
|
title: 'BitDance浏览器插件后台服务',
|
||||||
|
description: '请求认证头为 `x-tt-session-v2`',
|
||||||
|
version: '1.0.0',
|
||||||
|
prefix: '/api',
|
||||||
|
swaggerHtmlEndpoint: '/swagger-html',
|
||||||
|
swaggerJsonEndpoint: '/swagger-json',
|
||||||
|
swaggerOptions: {
|
||||||
|
securityDefinitions: {
|
||||||
|
session: {
|
||||||
|
type: 'apiKey',
|
||||||
|
in: 'header',
|
||||||
|
name: 'x-tt-session-v2',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
// mapDir will scan the input dir, and automatically call router.map to all Router Class
|
||||||
|
router.mapDir(path.resolve(__dirname, 'controllers'), {
|
||||||
|
ignore: ["**.spec.ts", "**.d.ts"],
|
||||||
|
})
|
||||||
|
|
||||||
|
export default router
|
108
packages/services/src/services/base.ts
Normal file
108
packages/services/src/services/base.ts
Normal file
@ -0,0 +1,108 @@
|
|||||||
|
import { isNil } from 'lodash';
|
||||||
|
import { IEntity, ITable, API } from '@byteinspire/db';
|
||||||
|
import { Memo } from '../models/memo';
|
||||||
|
import { Context } from 'koa-swagger-decorator';
|
||||||
|
import inspirecloud from '@byteinspire/api'
|
||||||
|
import { multipageDbQuery } from '../utils/multipage';
|
||||||
|
import { PageQuery, ObjectIdType, ObjectId } from '../models/base';
|
||||||
|
|
||||||
|
export default class BaseDbService<T> {
|
||||||
|
table: ITable<T>
|
||||||
|
db: API = inspirecloud.db
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param name 表名
|
||||||
|
*/
|
||||||
|
constructor(name: string) {
|
||||||
|
this.table = inspirecloud.db.table<T>(name)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查找多个
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
findMany(query: any): Promise<(T & IEntity)[]> {
|
||||||
|
return query.find()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查找全部
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
findAll(): Promise<(T & IEntity)[]> {
|
||||||
|
return this.findMany(this.table.where())
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查找单个
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
findOne(id: string): Promise<(T & IEntity)> {
|
||||||
|
const result = this.table.where({ _id: new ObjectId(id) }).findOne()
|
||||||
|
if (isNil(result)) throw { status: 404, message: 'Not Found' }
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 分页查找
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
findPage(pageArg: PageQuery, query: any) {
|
||||||
|
return multipageDbQuery(pageArg, query)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 分页全部查找
|
||||||
|
* @param pageArg
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
findAllPage(pageArg: PageQuery) {
|
||||||
|
return this.findPage(pageArg, this.table.where())
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建单个
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
createOne(item: T): Promise<(T & IEntity)> {
|
||||||
|
return this.table.save(item)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新单个
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
async updateOne(item: Partial<T> & { _id: string }): Promise<T & IEntity> {
|
||||||
|
const result = await this.findOne(item._id)
|
||||||
|
|
||||||
|
for (const [k, v] of Object.entries(item) as [keyof (T & IEntity), any][]) {
|
||||||
|
if (k === '_id' || k === 'createdAt' || k === 'updatedAt') continue
|
||||||
|
result[k] = v
|
||||||
|
}
|
||||||
|
return this.table.save(result)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除数据
|
||||||
|
* @param id
|
||||||
|
*/
|
||||||
|
async deleteOne(id: string) {
|
||||||
|
const result = await this.table.where({ _id: new ObjectId(id) }).findOne()
|
||||||
|
if (isNil(result)) throw { status: 404, message: 'Not Found' }
|
||||||
|
return await this.table.delete([result])
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除多个数据
|
||||||
|
* @param ids
|
||||||
|
* @returns 成功删除数据的数目
|
||||||
|
*/
|
||||||
|
async deleteMany(ids: string[]) {
|
||||||
|
const result = await this.table.where({ _id: this.db.in(ids) }).find()
|
||||||
|
const delRes = await this.table.delete(result)
|
||||||
|
return (delRes as any).deletedCount
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
14
packages/services/src/services/files.ts
Normal file
14
packages/services/src/services/files.ts
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
import inspirecloud from '@byteinspire/api';
|
||||||
|
|
||||||
|
const fileService = inspirecloud.file
|
||||||
|
|
||||||
|
const uploadFile = fileService.upload.bind(fileService) as any as (
|
||||||
|
name: string,
|
||||||
|
buffer: Buffer | string | { url: string },
|
||||||
|
) => Promise<{ url: string; id: string }>;
|
||||||
|
|
||||||
|
const downloadFile = fileService.download.bind(fileService)
|
||||||
|
|
||||||
|
const removeFile = fileService.delete.bind(fileService)
|
||||||
|
|
||||||
|
export { uploadFile, downloadFile, removeFile }
|
19
packages/services/src/services/memo.ts
Normal file
19
packages/services/src/services/memo.ts
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
import { getCurrentUser } from './user';
|
||||||
|
import { ObjectId } from './../models/base';
|
||||||
|
import { Memo } from './../models/memo';
|
||||||
|
import BaseDbService from './base'
|
||||||
|
import { IEntity, ITable, API } from '@byteinspire/db';
|
||||||
|
import { PageQuery } from '../models/base';
|
||||||
|
|
||||||
|
class MemoService extends BaseDbService<Memo> {
|
||||||
|
findByUserPage(uid: string, pageArg: PageQuery) {
|
||||||
|
return this.findPage(pageArg, this.table.where({ user: new ObjectId(uid) }).populate({
|
||||||
|
ref: 'user',
|
||||||
|
projection: ['_id', 'username', 'nickname', 'lastLogin', 'status']
|
||||||
|
} as any))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const memoService = new MemoService('memo')
|
||||||
|
|
||||||
|
export default memoService
|
25
packages/services/src/services/user.ts
Normal file
25
packages/services/src/services/user.ts
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
import { User } from './../models/user';
|
||||||
|
import { WithId } from '../models/base';
|
||||||
|
import { Context } from 'koa-swagger-decorator';
|
||||||
|
import inspirecloud from '@byteinspire/api';
|
||||||
|
import { isNil } from 'lodash';
|
||||||
|
|
||||||
|
const userService = (inspirecloud as any).user
|
||||||
|
|
||||||
|
export const login = userService.login as (ctx: Context, username: string, password: string) => Promise<undefined>
|
||||||
|
|
||||||
|
export const register = userService.register as (ctx: Context, username: string, password: string) => Promise<undefined>
|
||||||
|
|
||||||
|
export const getCurrentUser = userService.current as (ctx: Context) => Promise<WithId<User> | undefined>
|
||||||
|
|
||||||
|
export const logout = userService.logout as (ctx: Context) => Promise<undefined>
|
||||||
|
|
||||||
|
export const updateNewPassword = userService.changePassword as (context: Context, newPassword: string, originPassword?: string) => Promise<any>;
|
||||||
|
|
||||||
|
export async function isLogin(ctx: Context) {
|
||||||
|
try {
|
||||||
|
return !isNil(await getCurrentUser(ctx))
|
||||||
|
} catch (error) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
27
packages/services/src/utils/multipage.ts
Normal file
27
packages/services/src/utils/multipage.ts
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
import { Context, query } from 'koa-swagger-decorator';
|
||||||
|
import { PageQuery } from '../models/base';
|
||||||
|
|
||||||
|
// 用于分页的数据库查询辅助函数
|
||||||
|
export async function multipageDbQuery<T = any>(pageArg: PageQuery, query: any) {
|
||||||
|
const { page = 1, pageSize = 10 } = pageArg
|
||||||
|
|
||||||
|
if (page < 1 || pageSize < 1) throw { status: 400, message: '分页参数不合法' }
|
||||||
|
|
||||||
|
const total = await query.count();
|
||||||
|
const content = await query.skip((page - 1) * pageSize).limit(pageSize).find();
|
||||||
|
return {
|
||||||
|
total,
|
||||||
|
page,
|
||||||
|
pageSize,
|
||||||
|
content: (content || []) as T[]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//用于分页的HTTP Query查询
|
||||||
|
export function multipageHttpQuery() {
|
||||||
|
return query(
|
||||||
|
{
|
||||||
|
page: { type: 'number', required: false, default: 1, description: 'type' },
|
||||||
|
pageSize: { type: 'number', required: false, default: 10, description: 'type' }
|
||||||
|
})
|
||||||
|
}
|
35
packages/services/src/utils/response.ts
Normal file
35
packages/services/src/utils/response.ts
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
import { Context } from 'koa-swagger-decorator';
|
||||||
|
import { UserContext } from '../models/base';
|
||||||
|
import { getCurrentUser, isLogin } from '../services/user';
|
||||||
|
|
||||||
|
// 处理请求中的错误
|
||||||
|
export async function errorHandler(ctx: Context, next: any) {
|
||||||
|
try {
|
||||||
|
await next();
|
||||||
|
} catch (err) {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
const error = err as any
|
||||||
|
// 抛出的错误可以附带 status 字段,代表 http 状态码
|
||||||
|
// 若没有提供,则默认状态码为 500,代表服务器内部错误
|
||||||
|
ctx.status = error.status || 500;
|
||||||
|
ctx.body = { code: ctx.status, msg: error.message };
|
||||||
|
console.error(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 对当前的请求进行登录验证,并注入用户信息到 ctx.user
|
||||||
|
* @param ctx 请求上下文
|
||||||
|
* @param next
|
||||||
|
*/
|
||||||
|
export async function authentication(ctx: UserContext, next: any) {
|
||||||
|
try {
|
||||||
|
const user = await getCurrentUser(ctx)
|
||||||
|
if (!user) throw ""
|
||||||
|
ctx.user = user
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error)
|
||||||
|
throw { status: 401, message: "用户未登录" }
|
||||||
|
}
|
||||||
|
await next()
|
||||||
|
}
|
24
packages/services/src/utils/swagger.ts
Normal file
24
packages/services/src/utils/swagger.ts
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
import { body, security } from 'koa-swagger-decorator'
|
||||||
|
import { authentication } from './response'
|
||||||
|
|
||||||
|
export function swaggerDocument(cls: any) {
|
||||||
|
return (cls as any).swaggerDocument
|
||||||
|
}
|
||||||
|
|
||||||
|
export function swaggerBody<T extends object>(cls: T) {
|
||||||
|
return body(swaggerDocument(cls))
|
||||||
|
}
|
||||||
|
|
||||||
|
export function authenticate(
|
||||||
|
target: any,
|
||||||
|
name: string,
|
||||||
|
descriptor: PropertyDescriptor
|
||||||
|
) {
|
||||||
|
|
||||||
|
if (!descriptor.value.middlewares) descriptor.value.middlewares = []
|
||||||
|
descriptor.value.middlewares.push(authentication)
|
||||||
|
|
||||||
|
// call other decorators
|
||||||
|
security([{ session: [] }])(target, name, descriptor)
|
||||||
|
return descriptor;
|
||||||
|
};
|
10
packages/services/tsconfig.app.json
Normal file
10
packages/services/tsconfig.app.json
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
"extends": "./tsconfig.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"outDir": "./dist",
|
||||||
|
"experimentalDecorators": true,
|
||||||
|
"types": []
|
||||||
|
},
|
||||||
|
"include": ["./src/**/*.ts"],
|
||||||
|
"exclude": ["**/*.spec.ts"]
|
||||||
|
}
|
@ -5,18 +5,18 @@
|
|||||||
"forceConsistentCasingInFileNames": true,
|
"forceConsistentCasingInFileNames": true,
|
||||||
"strict": true,
|
"strict": true,
|
||||||
"noImplicitOverride": true,
|
"noImplicitOverride": true,
|
||||||
|
"strictFunctionTypes": false,
|
||||||
|
"experimentalDecorators": true,
|
||||||
"noPropertyAccessFromIndexSignature": true,
|
"noPropertyAccessFromIndexSignature": true,
|
||||||
"noImplicitReturns": true,
|
"noImplicitReturns": true,
|
||||||
"noFallthroughCasesInSwitch": true
|
"noFallthroughCasesInSwitch": true,
|
||||||
|
"declaration": true,
|
||||||
|
"emitDecoratorMetadata": true,
|
||||||
|
"esModuleInterop": true
|
||||||
},
|
},
|
||||||
"files": [],
|
// "files": [],
|
||||||
"include": [],
|
"include": ["src/**/*.ts"],
|
||||||
"references": [
|
"references": [{
|
||||||
{
|
"path": "./tsconfig.app.json"
|
||||||
"path": "./tsconfig.lib.json"
|
}]
|
||||||
},
|
|
||||||
{
|
|
||||||
"path": "./tsconfig.spec.json"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
"composite": true,
|
"composite": true,
|
||||||
"sourceMap": true,
|
"sourceMap": true,
|
||||||
"declaration": false,
|
"declaration": false,
|
||||||
|
"esModuleInterop": true,
|
||||||
"moduleResolution": "node",
|
"moduleResolution": "node",
|
||||||
"emitDecoratorMetadata": true,
|
"emitDecoratorMetadata": true,
|
||||||
"experimentalDecorators": true,
|
"experimentalDecorators": true,
|
||||||
@ -16,7 +17,7 @@
|
|||||||
"skipDefaultLibCheck": true,
|
"skipDefaultLibCheck": true,
|
||||||
"baseUrl": ".",
|
"baseUrl": ".",
|
||||||
"paths": {
|
"paths": {
|
||||||
"@bitdance/services": ["packages/services/src/index.ts"],
|
"@bitdance/services-api": ["packages/services-api/src/index.ts"],
|
||||||
"@bitdance/shared": ["packages/shared/src/index.ts"]
|
"@bitdance/shared": ["packages/shared/src/index.ts"]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
"projects": {
|
"projects": {
|
||||||
"plugin-ui": "packages/plugin-ui",
|
"plugin-ui": "packages/plugin-ui",
|
||||||
"services": "packages/services",
|
"services": "packages/services",
|
||||||
|
"services-api": "packages/services-api",
|
||||||
"shared": "packages/shared",
|
"shared": "packages/shared",
|
||||||
"shell-chrome": "packages\\shell-chrome"
|
"shell-chrome": "packages\\shell-chrome"
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user