Refactor setting module

This commit is contained in:
cuigh 2021-12-23 18:00:14 +08:00
parent bb48beec82
commit 62bbe9254d
9 changed files with 114 additions and 193 deletions

View File

@ -1,10 +1,8 @@
package api
import (
"bytes"
"encoding/json"
"github.com/cuigh/auxo/data"
"github.com/cuigh/auxo/net/web"
"github.com/cuigh/swirl/biz"
)
@ -18,7 +16,7 @@ type SettingHandler struct {
// NewSetting creates an instance of SettingHandler
func NewSetting(b biz.SettingBiz) *SettingHandler {
return &SettingHandler{
Load: settingLoad(b),
Load: settingLoad(b),
Save: settingSave(b),
}
}
@ -42,15 +40,8 @@ func settingSave(b biz.SettingBiz) web.HandlerFunc {
return func(ctx web.Context) (err error) {
args := &Args{}
err = ctx.Bind(args)
if err != nil {
return
}
options := data.Map{}
d := json.NewDecoder(bytes.NewBuffer(args.Options))
d.UseNumber()
if err = d.Decode(&options); err == nil {
err = b.Save(args.ID, options, ctx.User())
if err == nil {
err = b.Save(args.ID, args.Options, ctx.User())
}
return ajax(ctx, err)
}

View File

@ -1,22 +1,21 @@
package biz
import (
"bytes"
"context"
"encoding/json"
"strconv"
"time"
"github.com/cuigh/auxo/data"
"github.com/cuigh/auxo/net/web"
"github.com/cuigh/auxo/util/cast"
"github.com/cuigh/swirl/dao"
"github.com/cuigh/swirl/model"
)
type SettingBiz interface {
Find(id string) (options data.Map, err error)
Find(id string) (options interface{}, err error)
Load() (options data.Map, err error)
Save(id string, options data.Map, user web.User) (err error)
Save(id string, options interface{}, user web.User) (err error)
}
func NewSetting(d dao.Interface, eb EventBiz) SettingBiz {
@ -28,17 +27,11 @@ type settingBiz struct {
eb EventBiz
}
func (b *settingBiz) Find(id string) (options data.Map, err error) {
func (b *settingBiz) Find(id string) (options interface{}, err error) {
var setting *model.Setting
setting, err = b.d.SettingGet(context.TODO(), id)
if err != nil {
return
}
if setting != nil {
options = b.toMap(setting.Options)
} else {
options = make(data.Map)
if err == nil && setting != nil {
return b.unmarshal(setting.Options)
}
return
}
@ -53,82 +46,45 @@ func (b *settingBiz) Load() (options data.Map, err error) {
options = data.Map{}
for _, s := range settings {
options[s.ID] = b.toMap(s.Options)
var v interface{}
if v, err = b.unmarshal(s.Options); err != nil {
return
}
options[s.ID] = v
}
return
}
func (b *settingBiz) Save(id string, options data.Map, user web.User) (err error) {
func (b *settingBiz) Save(id string, options interface{}, user web.User) (err error) {
setting := &model.Setting{
ID: id,
Options: b.toOptions(options),
UpdatedAt: time.Now(),
}
if user != nil {
setting.UpdatedBy = model.Operator{ID: user.ID(), Name: user.Name()}
}
err = b.d.SettingUpdate(context.TODO(), setting)
setting.Options, err = b.marshal(options)
if err == nil {
err = b.d.SettingUpdate(context.TODO(), setting)
}
if err == nil && user != nil {
b.eb.CreateSetting(EventActionUpdate, user)
}
return
}
func (b *settingBiz) toOptions(m data.Map) []*model.SettingOption {
var opts []*model.SettingOption
for k, v := range m {
opt := &model.SettingOption{Name: k}
switch v.(type) {
case bool:
opt.Type = "bool"
opt.Value = strconv.FormatBool(v.(bool))
case json.Number:
opt.Type = "number"
opt.Value = cast.ToString(v)
case string:
opt.Type = "string"
opt.Value = v.(string)
default:
opt.Type = "json"
opt.Value = b.toJSON(v)
}
opts = append(opts, opt)
func (b *settingBiz) marshal(v interface{}) (s string, err error) {
var buf []byte
if buf, err = json.Marshal(v); err == nil {
s = string(buf)
}
return opts
return
}
func (b *settingBiz) toMap(options []*model.SettingOption) data.Map {
m := data.Map{}
for _, opt := range options {
var v interface{}
switch opt.Type {
case "bool":
v = opt.Value == "true"
case "number":
v = cast.ToInt32(opt.Value)
case "string":
v = opt.Value
default:
v = b.fromJSON(opt.Value)
}
m[opt.Name] = v
}
return m
}
func (b *settingBiz) toJSON(v interface{}) string {
d, err := json.Marshal(v)
if err != nil {
panic(err)
}
return string(d)
}
func (b *settingBiz) fromJSON(v string) interface{} {
var i interface{}
err := json.Unmarshal([]byte(v), &i)
if err != nil {
panic(err)
}
return i
func (b *settingBiz) unmarshal(s string) (v interface{}, err error) {
d := json.NewDecoder(bytes.NewBuffer([]byte(s)))
d.UseNumber()
err = d.Decode(&v)
return
}

View File

@ -79,7 +79,7 @@ func newInterface() (i Interface) {
case "bolt":
i, err = bolt.New(misc.Options.DBAddress)
default:
err = errors.New("Unknown database type: " + misc.Options.DBType)
err = errors.New("unknown database type: " + misc.Options.DBType)
}
if err != nil {

17
main.go
View File

@ -2,6 +2,7 @@ package main
import (
"embed"
"encoding/json"
"io/fs"
"net/http"
"strings"
@ -10,7 +11,7 @@ import (
"github.com/cuigh/auxo/app/container"
"github.com/cuigh/auxo/app/flag"
_ "github.com/cuigh/auxo/cache/memory"
"github.com/cuigh/auxo/config"
"github.com/cuigh/auxo/data"
"github.com/cuigh/auxo/data/valid"
"github.com/cuigh/auxo/errors"
"github.com/cuigh/auxo/log"
@ -102,13 +103,15 @@ func initSystem() error {
func loadSetting(sb biz.SettingBiz) *misc.Setting {
var (
err error
s = &misc.Setting{}
err error
opts data.Map
b []byte
s = &misc.Setting{}
)
config.AddSource(sb)
if err = config.Load(); err == nil {
err = config.Unmarshal(s)
if opts, err = sb.Load(); err == nil {
if b, err = json.Marshal(opts); err == nil {
err = json.Unmarshal(b, s)
}
}
if err != nil {
log.Get("misc").Error("failed to load setting: ", err)

View File

@ -49,28 +49,24 @@ func LoadOptions() (err error) {
// Setting represents the settings of Swirl.
type Setting struct {
System struct {
Version string
}
Region struct {
Language string `option:"lang"`
Timezone int32
}
Version string `json:"version"`
} `json:"system"`
LDAP struct {
Enabled bool
Address string
Security int32 // 0, 1, 2
Authentication string `option:"auth"` // simple, bind
BindDN string
BindPassword string `option:"bind_pwd"` // Bind DN password
BaseDN string // Base search path for users
UserDN string // Template for the DN of the user for simple auth
UserFilter string // Search filter for user
NameAttr string
EmailAttr string
}
Enabled bool `json:"enabled"`
Address string `json:"address"`
Security int32 `json:"security"` // 0, 1, 2
Authentication string `json:"auth"` // simple, bind
BindDN string `json:"bind_dn"`
BindPassword string `json:"bind_pwd"` // Bind DN password
BaseDN string `json:"base_dn"` // Base search path for users
UserDN string `json:"user_dn"` // Template for the DN of the user for simple auth
UserFilter string `json:"user_filter"` // Search filter for user
NameAttr string `json:"name_attr"`
EmailAttr string `json:"email_attr"`
} `json:"ldap"`
Metric struct {
Prometheus string
}
Prometheus string `json:"prometheus"`
} `json:"metric"`
}
func init() {

View File

@ -58,16 +58,10 @@ type Operator struct {
// Setting represents the options of swirl.
type Setting struct {
ID string `json:"id" bson:"_id"`
Options []*SettingOption `json:"options" bson:"options"`
UpdatedAt time.Time `json:"updatedAt" bson:"updated_at"`
UpdatedBy Operator `json:"updatedBy" bson:"updated_by"`
}
type SettingOption struct {
Name string `json:"name" bson:"name"`
Value string `json:"value" bson:"value"`
Type string `json:"type" bson:"type"`
ID string `json:"id" bson:"_id"`
Options string `json:"options" bson:"options"`
UpdatedAt time.Time `json:"updatedAt" bson:"updated_at"`
UpdatedBy Operator `json:"updatedBy" bson:"updated_by"`
}
type Role struct {

View File

@ -1,14 +1,17 @@
import ajax, { Result } from './ajax'
export interface Setting {
region: RegionSetting;
ldap: LdapSetting;
metric: MetricSetting;
deploy: DeployOptions;
}
export interface RegionSetting {
lang: string;
timezone: number;
export interface DeployOptions {
keys: {
name: string;
token: string;
expiry: number;
}[];
}
export interface LdapSetting {

View File

@ -1,5 +1,5 @@
<template>
<x-page-header>
<x-page-header>
<template #action>
<n-button secondary size="small" type="warning" @click="prune">
<template #icon>
@ -73,7 +73,16 @@ const columns = [
fixed: "left" as const,
render: (c: Container) => {
const node = c.labels?.find(l => l.name === 'com.docker.swarm.node.id')
return renderLink({ name: 'container_detail', params: { id: c.id, node: node?.value || '-' } }, c.name)
const name = c.name.length > 32 ? c.name.substring(0, 32) + '...' : c.name
return renderLink({ name: 'container_detail', params: { id: c.id, node: node?.value || '-' } }, name)
},
},
{
title: t('objects.service'),
key: "service",
render: (c: Container) => {
const service = c.labels?.find(l => l.name === 'com.docker.swarm.service.name')?.value
return service ? renderLink({ name: 'service_detail', params: { name: service } }, service) : ''
},
},
{

View File

@ -1,36 +1,36 @@
<template>
<x-page-header />
<n-space class="page-body" vertical :size="12">
<x-panel title="Region" divider="bottom" :collapsed="panel !== 'region'" v-if="false">
<x-panel title="Deployment" divider="bottom" :collapsed="panel !== 'deploy'" v-if="false">
<template #action>
<n-button
secondary
strong
class="toggle"
size="small"
@click="togglePanel('region')"
>{{ panel === 'region' ? t('buttons.collapse') : t('buttons.expand') }}</n-button>
@click="togglePanel('deploy')"
>{{ panel === 'deploy' ? t('buttons.collapse') : t('buttons.expand') }}</n-button>
</template>
<div style="padding: 4px 0 0 12px">
<n-form
inline
:model="setting"
ref="formRegion"
label-placement="left"
:show-feedback="false"
>
<n-form-item label="Timezone" path="region.timezone">
<n-select
v-model:value="setting.region.timezone"
:options="timezones"
style="width: 240px"
/>
<n-form :model="setting" ref="formDeploy" :show-feedback="false">
<n-form-item :label="t('fields.keys')" path="deploy.keys">
<n-dynamic-input
v-model:value="setting.deploy.keys"
#="{ index, value }"
:on-create="newKey"
>
<n-input-group>
<n-input :placeholder="t('fields.name')" v-model:value="value.name" />
<n-input :placeholder="t('fields.token')" v-model:value="value.token" />
<n-date-picker :placeholder="t('fields.expiry')" v-model:value="value.expiry" type="date" clearable style="min-width: 200px"/>
</n-input-group>
</n-dynamic-input>
</n-form-item>
</n-form>
<n-button
type="primary"
style="margin-top: 12px"
@click="() => save('region', setting.region)"
@click="() => save('deploy', setting.deploy)"
>{{ t('buttons.save') }}</n-button>
</div>
</x-panel>
@ -55,10 +55,7 @@
<n-switch v-model:value="setting.ldap.enabled" />
</n-form-item>
<n-form-item :label="t('fields.address')" path="ldap.address" label-align="right">
<n-input
:placeholder="t('tips.ldap_address')"
v-model:value="setting.ldap.address"
/>
<n-input :placeholder="t('tips.ldap_address')" v-model:value="setting.ldap.address" />
</n-form-item>
<n-form-item :label="t('fields.security')" path="ldap.security">
<n-radio-group v-model:value="setting.ldap.security">
@ -79,10 +76,7 @@
label-align="right"
v-show="setting.ldap.auth === 'simple'"
>
<n-input
:placeholder="t('tips.ldap_user_dn')"
v-model:value="setting.ldap.user_dn"
/>
<n-input :placeholder="t('tips.ldap_user_dn')" v-model:value="setting.ldap.user_dn" />
</n-form-item>
<n-form-item
:label="t('fields.bind_dn')"
@ -93,7 +87,7 @@
<n-grid :cols="2" :x-gap="24">
<n-form-item-gi path="ldap.bind_dn">
<n-input-group>
<n-input-group-label style="min-width: 60px">{{ t('fields.dn')}}</n-input-group-label>
<n-input-group-label style="min-width: 60px">{{ t('fields.dn') }}</n-input-group-label>
<n-input
:placeholder="t('tips.ldap_bind_dn')"
v-model:value="setting.ldap.bind_dn"
@ -102,17 +96,18 @@
</n-form-item-gi>
<n-form-item-gi path="ldap.bind_pwd">
<n-input-group>
<n-input-group-label style="min-width: 60px">{{ t('fields.password')}}</n-input-group-label>
<n-input type="password" :placeholder="t('tips.ldap_bind_pwd')" v-model:value="setting.ldap.bind_pwd" />
<n-input-group-label style="min-width: 60px">{{ t('fields.password') }}</n-input-group-label>
<n-input
type="password"
:placeholder="t('tips.ldap_bind_pwd')"
v-model:value="setting.ldap.bind_pwd"
/>
</n-input-group>
</n-form-item-gi>
</n-grid>
</n-form-item>
<n-form-item :label="t('fields.base_dn')" path="ldap.base_dn" label-align="right">
<n-input
:placeholder="t('tips.ldap_base_dn')"
v-model:value="setting.ldap.base_dn"
/>
<n-input :placeholder="t('tips.ldap_base_dn')" v-model:value="setting.ldap.base_dn" />
</n-form-item>
<n-form-item :label="t('fields.user_filter')" path="ldap.user_filter" label-align="right">
<n-input
@ -160,10 +155,7 @@
style="padding: 4px 0 0 12px"
>
<n-form-item label="Prometheus" path="metric.prometheus" label-align="right">
<n-input
:placeholder="t('tips.prometheus')"
v-model:value="setting.metric.prometheus"
/>
<n-input :placeholder="t('tips.prometheus')" v-model:value="setting.metric.prometheus" />
</n-form-item>
<n-button
type="primary"
@ -189,9 +181,10 @@ import {
NFormItemGi,
NRadioGroup,
NRadio,
NSelect,
NDynamicInput,
NSwitch,
NAlert,
NDatePicker,
} from "naive-ui";
import XPageHeader from "@/components/PageHeader.vue";
import XPanel from "@/components/Panel.vue";
@ -201,41 +194,13 @@ import { useI18n } from 'vue-i18n'
const { t } = useI18n()
const setting = ref({
region: {
timezone: 8,
},
ldap: {
security: 0,
auth: 'simple',
},
metric: {},
deploy: {},
} as Setting);
const timezones = [
{ label: 'UTC-11(Pacific/Midway)', value: -11 },
{ label: 'UTC-10(Pacific/Honolulu)', value: -10 },
{ label: 'UTC-9(America/Anchorage)', value: -9 },
{ label: 'UTC-8(America/Los_Angeles)', value: -8 },
{ label: 'UTC-7(America/Phoenix)', value: -7 },
{ label: 'UTC-6(America/Regina)', value: -6 },
{ label: 'UTC-5(America/Panama)', value: -5 },
{ label: 'UTC-4(America/Asuncion)', value: -4 },
{ label: 'UTC-3(America/Bahia)', value: -3 },
{ label: 'UTC-2(America/Noronha)', value: -2 },
{ label: 'UTC-1(Atlantic/Cape_Verde)', value: -1 },
{ label: 'UTC(Europe/London)', value: 0 },
{ label: 'UTC+1(Africa/Algiers)', value: 1 },
{ label: 'UTC+2(Africa/Cairo)', value: 2 },
{ label: 'UTC+3(Europe/Moscow)', value: 3 },
{ label: 'UTC+4(Indian/Mahe)', value: 4 },
{ label: 'UTC+5(Asia/Karachi)', value: 5 },
{ label: 'UTC+6(Asia/Almaty)', value: 6 },
{ label: 'UTC+7(Asia/Bangkok)', value: 7 },
{ label: 'UTC+8(Asia/Shanghai)', value: 8 },
{ label: 'UTC+9(Asia/Tokyo)', value: 9 },
{ label: 'UTC+10(Australia/Sydney)', value: 10 },
{ label: 'UTC+11(Pacific/Efate)', value: 11 },
{ label: 'UTC+12(Asia/Anadyr)', value: 12 },
]
const panel = ref('')
function togglePanel(name: string) {
@ -246,6 +211,10 @@ function togglePanel(name: string) {
}
}
function newKey() {
return { name: '', token: '', expiry: undefined }
}
async function save(id: string, options: any) {
await settingApi.save(id, options)
window.message.info(t('texts.action_success'));