Skip to content

Commit

Permalink
feat(system-server): add account apis;
Browse files Browse the repository at this point in the history
  • Loading branch information
maslow committed Aug 30, 2021
1 parent a2e9a6c commit 664458e
Show file tree
Hide file tree
Showing 32 changed files with 1,595 additions and 5 deletions.
7 changes: 6 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -59,5 +59,10 @@
"sys_admin": "laf-sys",
"sys_password": "laf-sys"
}
}
},
"cSpell.words": [
"appid",
"signin",
"uids"
]
}
8 changes: 4 additions & 4 deletions packages/system-server/README.md
Original file line number Diff line number Diff line change
@@ -1,23 +1,23 @@
# less api framework - devops server
# laf - system server

## 介绍

`laf-devops-server` 是 laf 中负责在线开发和运维应用的服务:
`laf-system-server` 是 laf 中负责在线开发和运维应用的服务:

- 云函数管理
- 数据访问策略管理
- 数据库管理:集合与数据管理,备份与恢复
- 应用状态:启停、指标统计、伸缩

另配套有 `laf-devops-client` - LAF 开发运维控制台,提供 Web IDE,在线编写、调试云函数,操作数据库等。
另配套有 `laf-system-client` - LAF 开发运维控制台,提供 Web IDE,在线编写、调试云函数,操作数据库等。


## 安装部署

### 安装依赖

```sh
cd packages/devops-server
cd packages/system-server

# 安装依赖(建议使用 node 14+)
npm install
Expand Down
65 changes: 65 additions & 0 deletions packages/system-server/src/config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import * as dotenv from 'dotenv'
dotenv.config()

/**
* Configuration Collection
*/
export default class Config {
/**
* the mongodb connection configuration of sys db
*/
static get sys_db() {
if (!process.env['SYS_DB']) {
throw new Error('env: `SYS_DB` is missing')
}

if (!process.env['SYS_DB_URI']) {
throw new Error('env: `SYS_DB_URI` is missing')
}

return {
database: process.env['SYS_DB'],
uri: process.env['SYS_DB_URI'],
poolSize: (process.env['SYS_DB_POOL_LIMIT'] ?? 10) as number,
}
}

/**
* the devops server secret salt, mainly used for generating devops tokens
*/
static get SYS_SERVER_SECRET_SALT(): string {
const secret_salt = process.env['SYS_SERVER_SECRET_SALT']
if (!secret_salt) {
throw new Error('env: `SYS_SERVER_SECRET_SALT` is missing')
}
return secret_salt
}

/**
* the logger level : 'fatal', 'error', 'warning', 'info', 'debug', 'trace'
*/
static get LOG_LEVEL(): string {
return process.env['LOG_LEVEL'] ?? (this.isProd ? 'info' : 'debug')
}

/**
* the serving port, default is 9000
*/
static get PORT(): number {
return (process.env.PORT ?? 9000) as number
}

/**
* the expiration duration time of devops server token, default is 24 hours (units in hour)
*/
static get TOKEN_EXPIRED_TIME(): number {
return (process.env.TOKEN_EXPIRED_TIME ?? 24) as number
}

/**
* in production deploy or not
*/
static get isProd(): boolean {
return process.env.NODE_ENV === 'production'
}
}
81 changes: 81 additions & 0 deletions packages/system-server/src/router/account/edit.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
/*
* @Author: Maslow<wangfugen@126.com>
* @Date: 2021-07-30 10:30:29
* @LastEditTime: 2021-08-28 22:53:24
* @Description:
*/

import { Request, Response } from 'express'
import { hashPassword } from '../../lib/utils/hash'
import { DatabaseAgent } from '../../lib/db-agent'
import { Constants } from '../../constants'


/**
* The handler of editing account
*/
export async function handleEdit(req: Request, res: Response) {
const uid = req['auth']?.uid
const db = DatabaseAgent.sys_db

// check if params valid
const { password, avatar, name, roles } = req.body
if (!uid) {
return res.status(401)
}

// check if uid valid
const { data: account } = await db.collection(Constants.cn.accounts)
.where({ _id: uid })
.getOne()

if (!account) {
return res.status(422).send('account not found')
}

// check if roles are valid
const { total: valid_count } = await db.collection(Constants.cn.roles)
.where({ name: db.command.in(roles) })
.count()

if (valid_count !== roles.length) {
return res.status(422).send('invalid roles')
}

// update account
const data = {
updated_at: Date.now()
}

// update password if provided
if (password) {
data['password'] = hashPassword(password)
}

// update avatar if provided
if (avatar && avatar != account.avatar) {
data['avatar'] = avatar
}

// update name if provided
if (name && name != account.name) {
data['name'] = name
}

// update roles if provided
if (roles) {
data['roles'] = roles
}

const r = await db.collection(Constants.cn.accounts)
.where({ _id: uid })
.update(data)

return res.send({
code: 0,
data: {
...r,
uid
}
})
}
35 changes: 35 additions & 0 deletions packages/system-server/src/router/account/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/*
* @Author: Maslow<wangfugen@126.com>
* @Date: 2021-07-30 10:30:29
* @LastEditTime: 2021-08-29 11:33:26
* @Description:
*/

import { Router } from 'express'
import { handleEdit } from './edit'
import { handleProfile } from './profile'
import { handleSignIn } from './signin'
import { handleSignUp } from './signup'

export const AccountRouter = Router()

/**
* account login
*/
AccountRouter.post('/login', handleSignIn)


/**
* get account profile
*/
AccountRouter.get('/profile', handleProfile)

/**
* account
*/
AccountRouter.post('/signup', handleSignUp)

/**
* edit admin
*/
AccountRouter.post('/edit', handleEdit)
41 changes: 41 additions & 0 deletions packages/system-server/src/router/account/profile.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/*
* @Author: Maslow<wangfugen@126.com>
* @Date: 2021-07-30 10:30:29
* @LastEditTime: 2021-08-28 22:52:21
* @Description:
*/

import { Request, Response } from 'express'
import { getPermissionsByAccountId } from '../../api/permission'
import { DatabaseAgent } from '../../lib/db-agent'
import { Constants } from '../../constants'

/**
* The handler of getting profile
*/
export async function handleProfile(req: Request, res: Response) {
const uid = req['auth']?.uid
if (!uid) {
return res.status(401)
}

const db = DatabaseAgent.sys_db

const { data: account } = await db.collection(Constants.cn.accounts)
.where({ _id: uid })
.getOne()

if (!account) {
return res.status(422).send('account not found')
}

const { permissions } = await getPermissionsByAccountId(account._id)

return res.send({
code: 0,
data: {
...account,
permissions
}
})
}
66 changes: 66 additions & 0 deletions packages/system-server/src/router/account/signin.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
/*
* @Author: Maslow<wangfugen@126.com>
* @Date: 2021-07-30 10:30:29
* @LastEditTime: 2021-08-30 15:25:39
* @Description:
*/

import { Request, Response } from 'express'
import { getToken } from '../../lib/utils/token'
import { hashPassword } from '../../lib/utils/hash'
import { DatabaseAgent } from '../../lib/db-agent'
import Config from '../../config'
import { Constants } from '../../constants'
import * as assert from 'assert'

/**
* The handler of sign in
*/
export async function handleSignIn(req: Request, res: Response) {
const { username, password } = req.body

const login_failed_error = {
code: 1,
error: 'invalid username or password'
}

if (!username || !password) {
return res.send(login_failed_error)
}
const db = DatabaseAgent.sys_db
const ret = await db.collection(Constants.cn.accounts)
.withOne({
query: db
.collection(Constants.cn.password)
.where({ password: hashPassword(password), type: 'login' }),
localField: '_id',
foreignField: 'uid'
})
.where({ username })
.merge({ intersection: true })

assert.ok(ret.ok)

if (!ret.data.length) {
return res.send(login_failed_error)
}

const account = ret.data[0]

const expire = Math.floor(Date.now() / 1000) + 60 * 60 * Config.TOKEN_EXPIRED_TIME
const payload = {
uid: account._id,
exp: expire
}
const access_token = getToken(payload)

return res.send({
code: 0,
data: {
access_token,
username,
uid: account._id,
expire
}
})
}
Loading

0 comments on commit 664458e

Please sign in to comment.