Refactor code

This commit is contained in:
cuigh 2021-12-16 20:23:08 +08:00
parent c3115b952c
commit 6705bd6d64
51 changed files with 848 additions and 1176 deletions

View File

@ -39,7 +39,7 @@ func chartSearch(b biz.ChartBiz) web.HandlerFunc {
return func(ctx web.Context) (err error) {
var (
args = &model.ChartSearchArgs{}
charts []*biz.Chart
charts []*model.Chart
total int
)
@ -85,7 +85,7 @@ func chartDelete(b biz.ChartBiz) web.HandlerFunc {
func chartSave(b biz.ChartBiz) web.HandlerFunc {
return func(ctx web.Context) error {
r := &biz.Chart{}
r := &model.Chart{}
err := ctx.Bind(r, true)
if err == nil {
if r.ID == "" {
@ -122,7 +122,7 @@ func chartFetchData(b biz.ChartBiz) web.HandlerFunc {
func chartFindDashboard(b biz.ChartBiz) web.HandlerFunc {
return func(ctx web.Context) (err error) {
var (
d *biz.Dashboard
d *model.Dashboard
name = ctx.Query("name")
key = ctx.Query("key")
)

View File

@ -26,7 +26,7 @@ func eventSearch(b biz.EventBiz) web.HandlerFunc {
return func(ctx web.Context) (err error) {
var (
args = &model.EventSearchArgs{}
events []*biz.Event
events []*model.Event
total int
)

View File

@ -3,6 +3,7 @@ package api
import (
"github.com/cuigh/auxo/net/web"
"github.com/cuigh/swirl/biz"
"github.com/cuigh/swirl/model"
)
// RegistryHandler encapsulates registry related handlers.
@ -60,7 +61,7 @@ func registryDelete(b biz.RegistryBiz) web.HandlerFunc {
func registrySave(b biz.RegistryBiz) web.HandlerFunc {
return func(ctx web.Context) error {
r := &biz.Registry{}
r := &model.Registry{}
err := ctx.Bind(r, true)
if err == nil {
if r.ID == "" {

View File

@ -3,6 +3,7 @@ package api
import (
"github.com/cuigh/auxo/net/web"
"github.com/cuigh/swirl/biz"
"github.com/cuigh/swirl/model"
)
// RoleHandler encapsulates role related handlers.
@ -61,7 +62,7 @@ func roleDelete(b biz.RoleBiz) web.HandlerFunc {
func roleSave(b biz.RoleBiz) web.HandlerFunc {
return func(ctx web.Context) error {
r := &biz.Role{}
r := &model.Role{}
err := ctx.Bind(r, true)
if err == nil {
if r.ID == "" {

View File

@ -6,6 +6,7 @@ import (
"github.com/cuigh/auxo/net/web"
"github.com/cuigh/swirl/biz"
"github.com/cuigh/swirl/docker/compose"
"github.com/cuigh/swirl/model"
)
// StackHandler encapsulates stack related handlers.
@ -53,7 +54,7 @@ func stackSearch(b biz.StackBiz) web.HandlerFunc {
return func(ctx web.Context) (err error) {
var (
args = &Args{}
stacks []*biz.Stack
stacks []*model.Stack
)
if err = ctx.Bind(args); err == nil {
@ -119,8 +120,13 @@ func stackDeploy(b biz.StackBiz) web.HandlerFunc {
}
func stackSave(b biz.StackBiz) web.HandlerFunc {
type Args struct {
ID string `json:"id"`
model.Stack
}
return func(ctx web.Context) error {
stack := &biz.Stack{}
stack := &Args{}
err := ctx.Bind(stack, true)
if err != nil {
return err
@ -133,9 +139,9 @@ func stackSave(b biz.StackBiz) web.HandlerFunc {
}
if stack.ID == "" {
err = b.Create(stack, ctx.User())
err = b.Create(&stack.Stack, ctx.User())
} else {
err = b.Update(stack, ctx.User())
err = b.Update(&stack.Stack, ctx.User())
}
return ajax(ctx, err)
}

View File

@ -6,9 +6,12 @@ import (
"github.com/cuigh/auxo/app"
"github.com/cuigh/auxo/data"
"github.com/cuigh/auxo/errors"
"github.com/cuigh/auxo/net/web"
"github.com/cuigh/swirl/biz"
"github.com/cuigh/swirl/docker"
"github.com/cuigh/swirl/misc"
"github.com/cuigh/swirl/model"
)
//var ErrSystemInitialized = errors.New("system was already initialized")
@ -22,10 +25,10 @@ type SystemHandler struct {
}
// NewSystem creates an instance of SystemHandler
func NewSystem(d *docker.Docker, b biz.SystemBiz) *SystemHandler {
func NewSystem(d *docker.Docker, b biz.SystemBiz, ub biz.UserBiz) *SystemHandler {
return &SystemHandler{
CheckState: systemCheckState(b),
CreateAdmin: systemCreateAdmin(b),
CreateAdmin: systemCreateAdmin(ub),
Version: systemVersion,
Summarize: systemSummarize(d),
}
@ -74,12 +77,21 @@ func systemSummarize(d *docker.Docker) web.HandlerFunc {
}
}
func systemCreateAdmin(b biz.SystemBiz) web.HandlerFunc {
func systemCreateAdmin(ub biz.UserBiz) web.HandlerFunc {
return func(c web.Context) (err error) {
user := &biz.User{}
if err = c.Bind(user, true); err == nil {
err = b.CreateAdmin(user)
user := &model.User{}
if err = c.Bind(user, true); err != nil {
return err
}
var count int
if count, err = ub.Count(); err == nil && count > 0 {
return errors.Coded(misc.ErrSystemInitialized, "system was already initialized")
}
user.Admin = true
user.Type = biz.UserTypeInternal
_, err = ub.Create(user, nil)
return ajax(c, err)
}
}

View File

@ -5,6 +5,7 @@ import (
"github.com/cuigh/auxo/data"
"github.com/cuigh/auxo/net/web"
"github.com/cuigh/swirl/biz"
"github.com/cuigh/swirl/model"
"github.com/cuigh/swirl/security"
)
@ -69,10 +70,9 @@ func userSignIn(auth *security.Authenticator, eb biz.EventBiz) web.HandlerFunc {
func userSave(b biz.UserBiz) web.HandlerFunc {
return func(ctx web.Context) error {
user := &biz.User{}
user := &model.User{}
err := ctx.Bind(user, true)
if err == nil {
user.Type = biz.UserTypeInternal
if user.ID == "" {
_, err = b.Create(user, ctx.User())
} else {
@ -171,7 +171,7 @@ func userModifyPassword(b biz.UserBiz) web.HandlerFunc {
func userModifyProfile(b biz.UserBiz) web.HandlerFunc {
return func(ctx web.Context) error {
u := &biz.User{}
u := &model.User{}
err := ctx.Bind(u, true)
if err == nil {
err = b.ModifyProfile(u, ctx.User())

View File

@ -11,6 +11,8 @@ import (
"github.com/cuigh/auxo/app/container"
"github.com/cuigh/auxo/data"
"github.com/cuigh/auxo/net/web"
"github.com/cuigh/swirl/model"
"go.mongodb.org/mongo-driver/bson/primitive"
)
@ -101,6 +103,14 @@ func indentJSON(raw []byte) (s string, err error) {
return
}
func now() model.Time {
return model.Time(time.Now())
}
func newOperator(user web.User) model.Operator {
return model.Operator{ID: user.ID(), Name: user.Name()}
}
func init() {
container.Put(NewNetwork)
container.Put(NewNode)

View File

@ -14,15 +14,22 @@ import (
"github.com/jinzhu/copier"
)
var builtins = []*model.Chart{
model.NewChart("service", "$cpu", "CPU", "${name}", `rate(container_cpu_user_seconds_total{container_label_com_docker_swarm_service_name="${service}"}[5m]) * 100`, "percent:100", 60),
model.NewChart("service", "$memory", "Memory", "${name}", `container_memory_usage_bytes{container_label_com_docker_swarm_service_name="${service}"}`, "size:bytes", 60),
model.NewChart("service", "$network_in", "Network Receive", "${name}", `sum(irate(container_network_receive_bytes_total{container_label_com_docker_swarm_service_name="${service}"}[5m])) by(name)`, "size:bytes", 60),
model.NewChart("service", "$network_out", "Network Send", "${name}", `sum(irate(container_network_transmit_bytes_total{container_label_com_docker_swarm_service_name="${service}"}[5m])) by(name)`, "size:bytes", 60),
}
type ChartBiz interface {
Search(args *model.ChartSearchArgs) (charts []*Chart, total int, err error)
Search(args *model.ChartSearchArgs) (charts []*model.Chart, total int, err error)
Delete(id, title string, user web.User) (err error)
Find(id string) (chart *Chart, err error)
Find(id string) (chart *model.Chart, err error)
Batch(ids ...string) (charts []*model.Chart, err error)
Create(chart *Chart, user web.User) (err error)
Update(chart *Chart, user web.User) (err error)
Create(chart *model.Chart, user web.User) (err error)
Update(chart *model.Chart, user web.User) (err error)
FetchData(key string, ids []string, period time.Duration) (data.Map, error)
FindDashboard(name, key string) (dashboard *Dashboard, err error)
FindDashboard(name, key string) (dashboard *model.Dashboard, err error)
UpdateDashboard(dashboard *model.Dashboard, user web.User) (err error)
}
@ -31,48 +38,28 @@ func NewChart(d dao.Interface, mb MetricBiz, eb EventBiz) ChartBiz {
d: d,
mb: mb,
eb: eb,
builtin: []*model.Chart{
model.NewChart("service", "$cpu", "CPU", "${name}", `rate(container_cpu_user_seconds_total{container_label_com_docker_swarm_service_name="${service}"}[5m]) * 100`, "percent:100", 60),
model.NewChart("service", "$memory", "Memory", "${name}", `container_memory_usage_bytes{container_label_com_docker_swarm_service_name="${service}"}`, "size:bytes", 60),
model.NewChart("service", "$network_in", "Network Receive", "${name}", `sum(irate(container_network_receive_bytes_total{container_label_com_docker_swarm_service_name="${service}"}[5m])) by(name)`, "size:bytes", 60),
model.NewChart("service", "$network_out", "Network Send", "${name}", `sum(irate(container_network_transmit_bytes_total{container_label_com_docker_swarm_service_name="${service}"}[5m])) by(name)`, "size:bytes", 60),
},
}
}
type chartBiz struct {
builtin []*model.Chart
d dao.Interface
mb MetricBiz
eb EventBiz
d dao.Interface
mb MetricBiz
eb EventBiz
}
func (b *chartBiz) Search(args *model.ChartSearchArgs) (charts []*Chart, total int, err error) {
var list []*model.Chart
list, total, err = b.d.ChartSearch(context.TODO(), args)
if err == nil {
charts = make([]*Chart, len(list))
for i, c := range list {
charts[i] = newChart(c)
}
}
return
func (b *chartBiz) Search(args *model.ChartSearchArgs) (charts []*model.Chart, total int, err error) {
return b.d.ChartSearch(context.TODO(), args)
}
func (b *chartBiz) Create(chart *Chart, user web.User) (err error) {
c := &model.Chart{
ID: createId(),
CreatedAt: time.Now(),
CreatedBy: model.Operator{ID: user.ID(), Name: user.Name()},
}
c.UpdatedAt = c.CreatedAt
if err = copier.CopyWithOption(c, chart, copier.Option{IgnoreEmpty: true, DeepCopy: true}); err != nil {
return err
}
err = b.d.ChartCreate(context.TODO(), c)
func (b *chartBiz) Create(chart *model.Chart, user web.User) (err error) {
chart.ID = createId()
chart.CreatedAt = now()
chart.CreatedBy = newOperator(user)
chart.UpdatedAt = chart.CreatedAt
chart.UpdatedBy = chart.CreatedBy
err = b.d.ChartCreate(context.TODO(), chart)
if err == nil {
b.eb.CreateChart(EventActionCreate, c.ID, c.Title, user)
b.eb.CreateChart(EventActionCreate, chart.ID, chart.Title, user)
}
return
}
@ -85,13 +72,8 @@ func (b *chartBiz) Delete(id, title string, user web.User) (err error) {
return
}
func (b *chartBiz) Find(id string) (chart *Chart, err error) {
var c *model.Chart
c, err = b.d.ChartGet(context.TODO(), id)
if err == nil {
chart = newChart(c)
}
return
func (b *chartBiz) Find(id string) (chart *model.Chart, err error) {
return b.d.ChartGet(context.TODO(), id)
}
func (b *chartBiz) Batch(ids ...string) (charts []*model.Chart, err error) {
@ -99,43 +81,30 @@ func (b *chartBiz) Batch(ids ...string) (charts []*model.Chart, err error) {
return
}
func (b *chartBiz) Update(chart *Chart, user web.User) (err error) {
c := &model.Chart{
UpdatedAt: time.Now(),
}
if err = copier.CopyWithOption(c, chart, copier.Option{IgnoreEmpty: true, DeepCopy: true}); err != nil {
return err
}
c.UpdatedBy.ID = user.ID()
c.UpdatedBy.Name = user.Name()
err = b.d.ChartUpdate(context.TODO(), c)
func (b *chartBiz) Update(chart *model.Chart, user web.User) (err error) {
chart.UpdatedAt = now()
chart.UpdatedBy = newOperator(user)
err = b.d.ChartUpdate(context.TODO(), chart)
if err == nil {
b.eb.CreateChart(EventActionUpdate, chart.ID, chart.Title, user)
}
return
}
func (b *chartBiz) FindDashboard(name, key string) (dashboard *Dashboard, err error) {
var d *model.Dashboard
d, err = b.d.DashboardGet(context.TODO(), name, key)
if err != nil {
func (b *chartBiz) FindDashboard(name, key string) (dashboard *model.Dashboard, err error) {
if dashboard, err = b.d.DashboardGet(context.TODO(), name, key); err != nil {
return
}
if d == nil {
if dashboard == nil {
dashboard = defaultDashboard(name, key)
} else {
dashboard = newDashboard(d)
}
err = b.fillCharts(dashboard)
return
}
func (b *chartBiz) UpdateDashboard(dashboard *model.Dashboard, user web.User) (err error) {
dashboard.UpdatedAt = time.Now()
dashboard.UpdatedBy.ID = user.ID()
dashboard.UpdatedBy.Name = user.Name()
dashboard.UpdatedAt = now()
dashboard.UpdatedBy = newOperator(user)
return b.d.DashboardUpdate(context.TODO(), dashboard)
}
@ -280,7 +249,7 @@ func (b *chartBiz) getCharts(ids []string) (charts map[string]*model.Chart, err
charts = make(map[string]*model.Chart)
for _, id := range ids {
if id[0] == '$' {
for _, c := range b.builtin {
for _, c := range builtins {
if c.ID == id {
charts[id] = c
}
@ -300,7 +269,7 @@ func (b *chartBiz) getCharts(ids []string) (charts map[string]*model.Chart, err
return
}
func (b *chartBiz) fillCharts(d *Dashboard) (err error) {
func (b *chartBiz) fillCharts(d *model.Dashboard) (err error) {
if len(d.Charts) == 0 {
return
}
@ -319,77 +288,23 @@ func (b *chartBiz) fillCharts(d *Dashboard) (err error) {
return err
}
for _, info := range d.Charts {
if c := m[info.ID]; c != nil {
info.Type = c.Type
info.Title = c.Title
info.Unit = c.Unit
if info.Width == 0 {
info.Width = c.Width
}
if info.Height == 0 {
info.Height = c.Height
}
info.Margin.Left = c.Margin.Left
info.Margin.Right = c.Margin.Right
info.Margin.Top = c.Margin.Top
info.Margin.Bottom = c.Margin.Bottom
for i := range d.Charts {
if c := m[d.Charts[i].ID]; c != nil {
_ = copier.CopyWithOption(&d.Charts[i], c, copier.Option{IgnoreEmpty: true})
}
}
return nil
}
type Dashboard struct {
Name string `json:"name"`
Key string `json:"key,omitempty"`
Period int32 `json:"period,omitempty"` // data range in minutes
Interval int32 `json:"interval,omitempty"` // refresh interval in seconds, 0 means disabled.
Charts []*Chart `json:"charts,omitempty"`
}
type Chart struct {
ID string `json:"id"`
Title string `json:"title" valid:"required"`
Description string `json:"desc,omitempty"`
Dashboard string `json:"dashboard,omitempty"`
Type string `json:"type" valid:"required"`
Unit string `json:"unit"`
Width int32 `json:"width" valid:"required"`
Height int32 `json:"height" valid:"required"`
Margin struct {
Left int32 `json:"left,omitempty"`
Right int32 `json:"right,omitempty"`
Top int32 `json:"top,omitempty"`
Bottom int32 `json:"bottom,omitempty"`
} `json:"margin"`
Metrics []model.ChartMetric `json:"metrics" valid:"required"`
Options data.Map `json:"options,omitempty"`
CreatedAt string `json:"createdAt,omitempty" copier:"-"`
UpdatedAt string `json:"updatedAt,omitempty" copier:"-"`
CreatedBy model.Operator `json:"createdBy"`
UpdatedBy model.Operator `json:"updatedBy"`
}
func newChart(c *model.Chart) *Chart {
chart := &Chart{
CreatedAt: formatTime(c.CreatedAt),
UpdatedAt: formatTime(c.UpdatedAt),
}
if err := copier.CopyWithOption(chart, c, copier.Option{IgnoreEmpty: true, DeepCopy: true}); err != nil {
panic(err)
}
return chart
}
func defaultDashboard(name, key string) *Dashboard {
d := &Dashboard{
func defaultDashboard(name, key string) *model.Dashboard {
d := &model.Dashboard{
Name: name,
Key: key,
Period: 30,
Interval: 15,
}
if name == "service" {
d.Charts = []*Chart{
d.Charts = []model.ChartInfo{
{ID: "$cpu"},
{ID: "$memory"},
{ID: "$network_in"},
@ -398,23 +313,3 @@ func defaultDashboard(name, key string) *Dashboard {
}
return d
}
func newDashboard(d *model.Dashboard) *Dashboard {
dashboard := &Dashboard{
Name: d.Name,
Key: d.Key,
Period: d.Period,
Interval: d.Interval,
}
if len(d.Charts) > 0 {
dashboard.Charts = make([]*Chart, len(d.Charts))
for i, c := range d.Charts {
dashboard.Charts[i] = &Chart{
ID: c.ID,
Width: c.Width,
Height: c.Height,
}
}
}
return dashboard
}

View File

@ -9,39 +9,6 @@ import (
"github.com/docker/docker/api/types/swarm"
)
type Config struct {
ID string `json:"id"`
Name string `json:"name,omitempty"`
Version uint64 `json:"version"`
Data string `json:"data"`
Labels data.Options `json:"labels,omitempty"`
Templating Driver `json:"templating"`
CreatedAt string `json:"createdAt"`
UpdatedAt string `json:"updatedAt"`
}
type Driver struct {
Name string `json:"name"`
Options data.Options `json:"options,omitempty"`
}
func newConfig(c *swarm.Config) *Config {
config := &Config{
ID: c.ID,
Name: c.Spec.Name,
Version: c.Version.Index,
Data: string(c.Spec.Data),
Labels: mapToOptions(c.Spec.Labels),
CreatedAt: formatTime(c.CreatedAt),
UpdatedAt: formatTime(c.UpdatedAt),
}
if c.Spec.Templating != nil {
config.Templating.Name = c.Spec.Templating.Name
config.Templating.Options = mapToOptions(c.Spec.Templating.Options)
}
return config
}
type ConfigBiz interface {
Search(name string, pageIndex, pageSize int) (configs []*Config, total int, err error)
Find(id string) (config *Config, raw string, err error)
@ -134,3 +101,36 @@ func (b *configBiz) Update(c *Config, user web.User) (err error) {
}
return
}
type Config struct {
ID string `json:"id"`
Name string `json:"name,omitempty"`
Version uint64 `json:"version"`
Data string `json:"data"`
Labels data.Options `json:"labels,omitempty"`
Templating Driver `json:"templating"`
CreatedAt string `json:"createdAt"`
UpdatedAt string `json:"updatedAt"`
}
type Driver struct {
Name string `json:"name"`
Options data.Options `json:"options,omitempty"`
}
func newConfig(c *swarm.Config) *Config {
config := &Config{
ID: c.ID,
Name: c.Spec.Name,
Version: c.Version.Index,
Data: string(c.Spec.Data),
Labels: mapToOptions(c.Spec.Labels),
CreatedAt: formatTime(c.CreatedAt),
UpdatedAt: formatTime(c.UpdatedAt),
}
if c.Spec.Templating != nil {
config.Templating.Name = c.Spec.Templating.Name
config.Templating.Options = mapToOptions(c.Spec.Templating.Options)
}
return config
}

View File

@ -11,143 +11,6 @@ import (
"github.com/docker/docker/api/types/mount"
)
type Container struct {
/* Summary */
ID string `json:"id"`
Name string `json:"name"`
Image string `json:"image,omitempty"`
Command string `json:"command,omitempty"`
CreatedAt string `json:"createdAt"`
Ports []*ContainerPort `json:"ports,omitempty"`
SizeRw int64 `json:"sizeRw"`
SizeRootFs int64 `json:"sizeRootFs"`
Labels data.Options `json:"labels"`
State string `json:"state"`
Status string `json:"status"`
NetworkMode string `json:"networkMode"`
Mounts []*ContainerMount `json:"mounts"`
//Networks map[string]*network.EndpointSettings
/* Detail */
PID int `json:"pid,omitempty"`
StartedAt string `json:"startedAt,omitempty"`
}
type ContainerPort struct {
IP string `json:"ip,omitempty"`
PrivatePort uint16 `json:"privatePort,omitempty"`
PublicPort uint16 `json:"publicPort,omitempty"`
Type string `json:"type,omitempty"`
}
type ContainerMount struct {
Type mount.Type `json:"type,omitempty"`
Name string `json:"name,omitempty"`
Source string `json:"source,omitempty"`
Destination string `json:"destination,omitempty"`
Driver string `json:"driver,omitempty"`
Mode string `json:"mode,omitempty"`
RW bool `json:"rw,omitempty"`
Propagation mount.Propagation `json:"propagation,omitempty"`
}
func newContainerMount(m types.MountPoint) *ContainerMount {
return &ContainerMount{
Type: m.Type,
Name: m.Name,
Source: m.Source,
Destination: m.Destination,
Driver: m.Driver,
Mode: m.Mode,
RW: m.RW,
Propagation: m.Propagation,
}
}
// EndpointSettings stores the network endpoint details
//type EndpointSettings struct {
// // Configurations
// IPAMConfig *EndpointIPAMConfig
// Links []string
// Aliases []string
// // Operational data
// NetworkID string
// EndpointID string
// Gateway string
// IPAddress string
// IPPrefixLen int
// IPv6Gateway string
// GlobalIPv6Address string
// GlobalIPv6PrefixLen int
// MacAddress string
// DriverOpts map[string]string
//}
func newContainerSummary(c *types.Container) *Container {
container := &Container{
ID: c.ID,
Name: c.Names[0],
Image: normalizeImage(c.Image),
Command: c.Command,
CreatedAt: formatTime(time.Unix(c.Created, 0)),
SizeRw: c.SizeRw,
SizeRootFs: c.SizeRootFs,
Labels: mapToOptions(c.Labels),
State: c.State,
Status: c.Status,
NetworkMode: c.HostConfig.NetworkMode,
}
for _, p := range c.Ports {
container.Ports = append(container.Ports, &ContainerPort{
IP: p.IP,
PrivatePort: p.PrivatePort,
PublicPort: p.PublicPort,
Type: p.Type,
})
}
for _, m := range c.Mounts {
container.Mounts = append(container.Mounts, newContainerMount(m))
}
return container
}
func newContainerDetail(c *types.ContainerJSON) *Container {
created, _ := time.Parse(time.RFC3339Nano, c.Created)
startedAt, _ := time.Parse(time.RFC3339Nano, c.State.StartedAt)
container := &Container{
ID: c.ID,
Name: c.Name,
Image: c.Image,
//Command: c.Command,
CreatedAt: formatTime(created),
Labels: mapToOptions(c.Config.Labels),
State: c.State.Status,
//Status: c.Status,
NetworkMode: string(c.HostConfig.NetworkMode),
// detail
PID: c.State.Pid,
StartedAt: formatTime(startedAt),
}
if c.SizeRw != nil {
container.SizeRw = *c.SizeRw
}
if c.SizeRootFs != nil {
container.SizeRootFs = *c.SizeRootFs
}
//for _, p := range c.Ports {
// container.Ports = append(container.Ports, &ContainerPort{
// IP: p.IP,
// PrivatePort: p.PrivatePort,
// PublicPort: p.PublicPort,
// Type: p.Type,
// })
//}
for _, m := range c.Mounts {
container.Mounts = append(container.Mounts, newContainerMount(m))
}
return container
}
type ContainerBiz interface {
Search(name, status string, pageIndex, pageSize int) ([]*Container, int, error)
Find(id string) (container *Container, raw string, err error)
@ -222,3 +85,116 @@ func (b *containerBiz) FetchLogs(id string, lines int, timestamps bool) (string,
}
return stdout.String(), stderr.String(), nil
}
type Container struct {
ID string `json:"id"`
Name string `json:"name"`
Image string `json:"image,omitempty"`
Command string `json:"command,omitempty"`
CreatedAt string `json:"createdAt"`
Ports []*ContainerPort `json:"ports,omitempty"`
SizeRw int64 `json:"sizeRw"`
SizeRootFs int64 `json:"sizeRootFs"`
Labels data.Options `json:"labels"`
State string `json:"state"`
Status string `json:"status"`
NetworkMode string `json:"networkMode"`
Mounts []*ContainerMount `json:"mounts"`
PID int `json:"pid,omitempty"`
StartedAt string `json:"startedAt,omitempty"`
}
type ContainerPort struct {
IP string `json:"ip,omitempty"`
PrivatePort uint16 `json:"privatePort,omitempty"`
PublicPort uint16 `json:"publicPort,omitempty"`
Type string `json:"type,omitempty"`
}
type ContainerMount struct {
Type mount.Type `json:"type,omitempty"`
Name string `json:"name,omitempty"`
Source string `json:"source,omitempty"`
Destination string `json:"destination,omitempty"`
Driver string `json:"driver,omitempty"`
Mode string `json:"mode,omitempty"`
RW bool `json:"rw,omitempty"`
Propagation mount.Propagation `json:"propagation,omitempty"`
}
func newContainerMount(m types.MountPoint) *ContainerMount {
return &ContainerMount{
Type: m.Type,
Name: m.Name,
Source: m.Source,
Destination: m.Destination,
Driver: m.Driver,
Mode: m.Mode,
RW: m.RW,
Propagation: m.Propagation,
}
}
func newContainerSummary(c *types.Container) *Container {
container := &Container{
ID: c.ID,
Name: c.Names[0],
Image: normalizeImage(c.Image),
Command: c.Command,
CreatedAt: formatTime(time.Unix(c.Created, 0)),
SizeRw: c.SizeRw,
SizeRootFs: c.SizeRootFs,
Labels: mapToOptions(c.Labels),
State: c.State,
Status: c.Status,
NetworkMode: c.HostConfig.NetworkMode,
}
for _, p := range c.Ports {
container.Ports = append(container.Ports, &ContainerPort{
IP: p.IP,
PrivatePort: p.PrivatePort,
PublicPort: p.PublicPort,
Type: p.Type,
})
}
for _, m := range c.Mounts {
container.Mounts = append(container.Mounts, newContainerMount(m))
}
return container
}
func newContainerDetail(c *types.ContainerJSON) *Container {
created, _ := time.Parse(time.RFC3339Nano, c.Created)
startedAt, _ := time.Parse(time.RFC3339Nano, c.State.StartedAt)
container := &Container{
ID: c.ID,
Name: c.Name,
Image: c.Image,
//Command: c.Command,
CreatedAt: formatTime(created),
Labels: mapToOptions(c.Config.Labels),
State: c.State.Status,
//Status: c.Status,
NetworkMode: string(c.HostConfig.NetworkMode),
PID: c.State.Pid,
StartedAt: formatTime(startedAt),
}
if c.SizeRw != nil {
container.SizeRw = *c.SizeRw
}
if c.SizeRootFs != nil {
container.SizeRootFs = *c.SizeRootFs
}
//for _, p := range c.Ports {
// container.Ports = append(container.Ports, &ContainerPort{
// IP: p.IP,
// PrivatePort: p.PrivatePort,
// PublicPort: p.PublicPort,
// Type: p.Type,
// })
//}
for _, m := range c.Mounts {
container.Mounts = append(container.Mounts, newContainerMount(m))
}
return container
}

View File

@ -2,13 +2,11 @@ package biz
import (
"context"
"time"
"github.com/cuigh/auxo/log"
"github.com/cuigh/auxo/net/web"
"github.com/cuigh/swirl/dao"
"github.com/cuigh/swirl/model"
"github.com/jinzhu/copier"
"go.mongodb.org/mongo-driver/bson/primitive"
)
@ -46,28 +44,8 @@ const (
EventActionPrune EventAction = "Prune"
)
type Event struct {
ID string `json:"id"`
Type EventType `json:"type"`
Action EventAction `json:"action"`
Code string `json:"code"`
Name string `json:"name"`
UserID string `json:"userId"`
Username string `json:"username"`
Time string `json:"time"`
}
func newEvent(e *model.Event) *Event {
event := &Event{
Time: formatTime(e.Time),
ID: e.ID.Hex(),
}
_ = copier.CopyWithOption(event, e, copier.Option{IgnoreEmpty: true})
return event
}
type EventBiz interface {
Search(args *model.EventSearchArgs) (events []*Event, total int, err error)
Search(args *model.EventSearchArgs) (events []*model.Event, total int, err error)
CreateRegistry(action EventAction, id, name string, user web.User)
CreateNode(action EventAction, id, name string, user web.User)
CreateNetwork(action EventAction, id, name string, user web.User)
@ -91,16 +69,8 @@ type eventBiz struct {
d dao.Interface
}
func (b *eventBiz) Search(args *model.EventSearchArgs) (events []*Event, total int, err error) {
var list []*model.Event
list, total, err = b.d.EventSearch(context.TODO(), args)
if err == nil && len(list) > 0 {
events = make([]*Event, len(list))
for i, e := range list {
events[i] = newEvent(e)
}
}
return
func (b *eventBiz) Search(args *model.EventSearchArgs) (events []*model.Event, total int, err error) {
return b.d.EventSearch(context.TODO(), args)
}
func (b *eventBiz) create(et EventType, ea EventAction, code, name string, user web.User) {
@ -112,9 +82,8 @@ func (b *eventBiz) create(et EventType, ea EventAction, code, name string, user
Name: name,
UserID: user.ID(),
Username: user.Name(),
Time: time.Now(),
Time: now(),
}
err := b.d.EventCreate(context.TODO(), event)
if err != nil {
log.Get("event").Errorf("failed to create event `%+v`: %v", event, err)

View File

@ -11,6 +11,63 @@ import (
"github.com/docker/docker/api/types/image"
)
type ImageBiz interface {
Search(name string, pageIndex, pageSize int) ([]*Image, int, error)
Find(name string) (image *Image, raw string, err error)
Delete(id string, user web.User) (err error)
}
func NewImage(d *docker.Docker) ImageBiz {
return &imageBiz{d: d}
}
type imageBiz struct {
d *docker.Docker
}
func (b *imageBiz) Find(id string) (img *Image, raw string, err error) {
var (
i types.ImageInspect
r []byte
histories []image.HistoryResponseItem
ctx = context.TODO()
)
if i, r, err = b.d.ImageInspect(ctx, id); err == nil {
raw, err = indentJSON(r)
}
if err == nil {
histories, err = b.d.ImageHistory(ctx, id)
}
if err == nil {
img = newImageDetail(&i, histories)
}
return
}
func (b *imageBiz) Search(name string, pageIndex, pageSize int) (images []*Image, total int, err error) {
list, total, err := b.d.ImageList(context.TODO(), name, pageIndex, pageSize)
if err != nil {
return nil, 0, err
}
images = make([]*Image, len(list))
for i, nr := range list {
images[i] = newImageSummary(&nr)
}
return images, total, nil
}
func (b *imageBiz) Delete(id string, user web.User) (err error) {
err = b.d.ImageRemove(context.TODO(), id)
//if err == nil {
// Event.CreateImage(model.EventActionDelete, id, user)
//}
return
}
type Image struct {
/* Summary */
ID string `json:"id"`
@ -123,60 +180,3 @@ func newImageDetail(is *types.ImageInspect, items []image.HistoryResponseItem) *
}
return i
}
type ImageBiz interface {
Search(name string, pageIndex, pageSize int) ([]*Image, int, error)
Find(name string) (image *Image, raw string, err error)
Delete(id string, user web.User) (err error)
}
func NewImage(d *docker.Docker) ImageBiz {
return &imageBiz{d: d}
}
type imageBiz struct {
d *docker.Docker
}
func (b *imageBiz) Find(id string) (img *Image, raw string, err error) {
var (
i types.ImageInspect
r []byte
histories []image.HistoryResponseItem
ctx = context.TODO()
)
if i, r, err = b.d.ImageInspect(ctx, id); err == nil {
raw, err = indentJSON(r)
}
if err == nil {
histories, err = b.d.ImageHistory(ctx, id)
}
if err == nil {
img = newImageDetail(&i, histories)
}
return
}
func (b *imageBiz) Search(name string, pageIndex, pageSize int) (images []*Image, total int, err error) {
list, total, err := b.d.ImageList(context.TODO(), name, pageIndex, pageSize)
if err != nil {
return nil, 0, err
}
images = make([]*Image, len(list))
for i, nr := range list {
images[i] = newImageSummary(&nr)
}
return images, total, nil
}
func (b *imageBiz) Delete(id string, user web.User) (err error) {
err = b.d.ImageRemove(context.TODO(), id)
//if err == nil {
// Event.CreateImage(model.EventActionDelete, id, user)
//}
return
}

View File

@ -10,77 +10,6 @@ import (
"github.com/docker/docker/api/types/network"
)
type Network struct {
ID string `json:"id"`
Name string `json:"name"`
Created string `json:"created"`
Driver string `json:"driver"`
Scope string `json:"scope"`
Internal bool `json:"internal"`
Attachable bool `json:"attachable"`
Ingress bool `json:"ingress"`
IPv6 bool `json:"ipv6"`
IPAM struct {
Driver string `json:"driver"`
Options data.Options `json:"options"`
Config []*IPAMConfig `json:"config"`
} `json:"ipam"`
Options data.Options `json:"options"`
Labels data.Options `json:"labels"`
Containers []*NetworkContainer `json:"containers"`
}
type IPAMConfig struct {
Subnet string `json:"subnet,omitempty"`
Gateway string `json:"gateway,omitempty"`
Range string `json:"range,omitempty"`
}
type NetworkContainer struct {
ID string `json:"id"` // container id
Name string `json:"name"` // container name
Mac string `json:"mac"` // mac address
IPv4 string `json:"ipv4"` // IPv4 address
IPv6 string `json:"ipv6"` // IPv6 address
}
func newNetwork(nr *types.NetworkResource) *Network {
n := &Network{
ID: nr.ID,
Name: nr.Name,
Created: formatTime(nr.Created),
Driver: nr.Driver,
Scope: nr.Scope,
Internal: nr.Internal,
Attachable: nr.Attachable,
Ingress: nr.Ingress,
IPv6: nr.EnableIPv6,
Options: mapToOptions(nr.Options),
Labels: mapToOptions(nr.Labels),
}
n.IPAM.Driver = nr.IPAM.Driver
n.IPAM.Options = mapToOptions(nr.IPAM.Options)
n.IPAM.Config = make([]*IPAMConfig, len(nr.IPAM.Config))
for i, c := range nr.IPAM.Config {
n.IPAM.Config[i] = &IPAMConfig{
Subnet: c.Subnet,
Gateway: c.Gateway,
Range: c.IPRange,
}
}
n.Containers = make([]*NetworkContainer, 0, len(nr.Containers))
for id, ep := range nr.Containers {
n.Containers = append(n.Containers, &NetworkContainer{
ID: id,
Name: ep.Name,
Mac: ep.MacAddress,
IPv4: ep.IPv4Address,
IPv6: ep.IPv6Address,
})
}
return n
}
type NetworkBiz interface {
Search() ([]*Network, error)
Find(name string) (network *Network, raw string, err error)
@ -167,3 +96,74 @@ func (b *networkBiz) Disconnect(networkId, networkName, container string, user w
}
return
}
type Network struct {
ID string `json:"id"`
Name string `json:"name"`
Created string `json:"created"`
Driver string `json:"driver"`
Scope string `json:"scope"`
Internal bool `json:"internal"`
Attachable bool `json:"attachable"`
Ingress bool `json:"ingress"`
IPv6 bool `json:"ipv6"`
IPAM struct {
Driver string `json:"driver"`
Options data.Options `json:"options"`
Config []*IPAMConfig `json:"config"`
} `json:"ipam"`
Options data.Options `json:"options"`
Labels data.Options `json:"labels"`
Containers []*NetworkContainer `json:"containers"`
}
type IPAMConfig struct {
Subnet string `json:"subnet,omitempty"`
Gateway string `json:"gateway,omitempty"`
Range string `json:"range,omitempty"`
}
type NetworkContainer struct {
ID string `json:"id"` // container id
Name string `json:"name"` // container name
Mac string `json:"mac"` // mac address
IPv4 string `json:"ipv4"` // IPv4 address
IPv6 string `json:"ipv6"` // IPv6 address
}
func newNetwork(nr *types.NetworkResource) *Network {
n := &Network{
ID: nr.ID,
Name: nr.Name,
Created: formatTime(nr.Created),
Driver: nr.Driver,
Scope: nr.Scope,
Internal: nr.Internal,
Attachable: nr.Attachable,
Ingress: nr.Ingress,
IPv6: nr.EnableIPv6,
Options: mapToOptions(nr.Options),
Labels: mapToOptions(nr.Labels),
}
n.IPAM.Driver = nr.IPAM.Driver
n.IPAM.Options = mapToOptions(nr.IPAM.Options)
n.IPAM.Config = make([]*IPAMConfig, len(nr.IPAM.Config))
for i, c := range nr.IPAM.Config {
n.IPAM.Config[i] = &IPAMConfig{
Subnet: c.Subnet,
Gateway: c.Gateway,
Range: c.IPRange,
}
}
n.Containers = make([]*NetworkContainer, 0, len(nr.Containers))
for id, ep := range nr.Containers {
n.Containers = append(n.Containers, &NetworkContainer{
ID: id,
Name: ep.Name,
Mac: ep.MacAddress,
IPv4: ep.IPv4Address,
IPv6: ep.IPv6Address,
})
}
return n
}

View File

@ -9,61 +9,6 @@ import (
"github.com/docker/docker/api/types/swarm"
)
type Node struct {
ID string `json:"id"`
Name string `json:"name,omitempty"`
Hostname string `json:"hostname"`
Version uint64 `json:"version"`
Role swarm.NodeRole `json:"role"`
Availability swarm.NodeAvailability `json:"availability"`
EngineVersion string `json:"engineVersion"`
Architecture string `json:"arch"`
OS string `json:"os"`
CPU int64 `json:"cpu"`
Memory float32 `json:"memory"`
Address string `json:"address"`
State swarm.NodeState `json:"state"`
Manager *NodeManager `json:"manager,omitempty"`
Labels data.Options `json:"labels,omitempty"`
CreatedAt string `json:"createdAt"`
UpdatedAt string `json:"updatedAt"`
}
type NodeManager struct {
Leader bool `json:"leader,omitempty"`
Reachability swarm.Reachability `json:"reachability,omitempty"`
Addr string `json:"addr,omitempty"`
}
func newNode(sn *swarm.Node) *Node {
n := &Node{
ID: sn.ID,
Name: sn.Spec.Name,
Hostname: sn.Description.Hostname,
Version: sn.Version.Index,
Role: sn.Spec.Role,
Availability: sn.Spec.Availability,
EngineVersion: sn.Description.Engine.EngineVersion,
Architecture: sn.Description.Platform.Architecture,
OS: sn.Description.Platform.OS,
CPU: sn.Description.Resources.NanoCPUs / 1e9,
Memory: float32(sn.Description.Resources.MemoryBytes>>20) / 1024,
Address: sn.Status.Addr,
State: sn.Status.State,
Labels: mapToOptions(sn.Spec.Labels),
CreatedAt: formatTime(sn.CreatedAt),
UpdatedAt: formatTime(sn.UpdatedAt),
}
if n.Role == swarm.NodeRoleManager {
n.Manager = &NodeManager{
Leader: sn.ManagerStatus.Leader,
Reachability: sn.ManagerStatus.Reachability,
Addr: sn.ManagerStatus.Addr,
}
}
return n
}
type NodeBiz interface {
Search() ([]*Node, error)
Find(id string) (node *Node, raw string, err error)
@ -129,3 +74,58 @@ func (b *nodeBiz) Update(n *Node, user web.User) (err error) {
}
return
}
type Node struct {
ID string `json:"id"`
Name string `json:"name,omitempty"`
Hostname string `json:"hostname"`
Version uint64 `json:"version"`
Role swarm.NodeRole `json:"role"`
Availability swarm.NodeAvailability `json:"availability"`
EngineVersion string `json:"engineVersion"`
Architecture string `json:"arch"`
OS string `json:"os"`
CPU int64 `json:"cpu"`
Memory float32 `json:"memory"`
Address string `json:"address"`
State swarm.NodeState `json:"state"`
Manager *NodeManager `json:"manager,omitempty"`
Labels data.Options `json:"labels,omitempty"`
CreatedAt string `json:"createdAt"`
UpdatedAt string `json:"updatedAt"`
}
type NodeManager struct {
Leader bool `json:"leader,omitempty"`
Reachability swarm.Reachability `json:"reachability,omitempty"`
Addr string `json:"addr,omitempty"`
}
func newNode(sn *swarm.Node) *Node {
n := &Node{
ID: sn.ID,
Name: sn.Spec.Name,
Hostname: sn.Description.Hostname,
Version: sn.Version.Index,
Role: sn.Spec.Role,
Availability: sn.Spec.Availability,
EngineVersion: sn.Description.Engine.EngineVersion,
Architecture: sn.Description.Platform.Architecture,
OS: sn.Description.Platform.OS,
CPU: sn.Description.Resources.NanoCPUs / 1e9,
Memory: float32(sn.Description.Resources.MemoryBytes>>20) / 1024,
Address: sn.Status.Addr,
State: sn.Status.State,
Labels: mapToOptions(sn.Spec.Labels),
CreatedAt: formatTime(sn.CreatedAt),
UpdatedAt: formatTime(sn.UpdatedAt),
}
if n.Role == swarm.NodeRoleManager {
n.Manager = &NodeManager{
Leader: sn.ManagerStatus.Leader,
Reachability: sn.ManagerStatus.Reachability,
Addr: sn.ManagerStatus.Addr,
}
}
return n
}

View File

@ -4,7 +4,6 @@ import (
"context"
"encoding/base64"
"encoding/json"
"time"
"github.com/cuigh/auxo/net/web"
"github.com/cuigh/swirl/dao"
@ -13,12 +12,12 @@ import (
)
type RegistryBiz interface {
Search() ([]*Registry, error)
Find(id string) (*Registry, error)
Search() ([]*model.Registry, error)
Find(id string) (*model.Registry, error)
GetAuth(url string) (auth string, err error)
Delete(id, name string, user web.User) (err error)
Create(registry *Registry, user web.User) (err error)
Update(registry *Registry, user web.User) (err error)
Create(registry *model.Registry, user web.User) (err error)
Update(registry *model.Registry, user web.User) (err error)
}
func NewRegistry(d dao.Interface, eb EventBiz) RegistryBiz {
@ -30,13 +29,11 @@ type registryBiz struct {
eb EventBiz
}
func (b *registryBiz) Create(registry *Registry, user web.User) (err error) {
r := registry.Convert()
func (b *registryBiz) Create(r *model.Registry, user web.User) (err error) {
r.ID = createId()
r.CreatedAt = time.Now()
r.CreatedAt = now()
r.UpdatedAt = r.CreatedAt
r.CreatedBy.ID = user.ID()
r.CreatedBy.Name = user.Name()
r.CreatedBy = newOperator(user)
r.UpdatedBy = r.CreatedBy
err = b.d.RegistryCreate(context.TODO(), r)
@ -46,32 +43,30 @@ func (b *registryBiz) Create(registry *Registry, user web.User) (err error) {
return
}
func (b *registryBiz) Update(registry *Registry, user web.User) (err error) {
r := registry.Convert()
r.UpdatedAt = time.Now()
r.UpdatedBy.ID = user.ID()
r.UpdatedBy.Name = user.Name()
func (b *registryBiz) Update(r *model.Registry, user web.User) (err error) {
r.UpdatedAt = now()
r.UpdatedBy = newOperator(user)
err = b.d.RegistryUpdate(context.TODO(), r)
if err == nil {
b.eb.CreateRegistry(EventActionUpdate, registry.ID, registry.Name, user)
b.eb.CreateRegistry(EventActionUpdate, r.ID, r.Name, user)
}
return
}
func (b *registryBiz) Search() (registries []*Registry, err error) {
var list []*model.Registry
if list, err = b.d.RegistryGetAll(context.TODO()); err == nil {
for _, r := range list {
registries = append(registries, newRegistry(r))
func (b *registryBiz) Search() (registries []*model.Registry, err error) {
registries, err = b.d.RegistryGetAll(context.TODO())
if err == nil {
for _, r := range registries {
r.Password = ""
}
}
return
}
func (b *registryBiz) Find(id string) (registry *Registry, err error) {
var r *model.Registry
if r, err = b.d.RegistryGet(context.TODO(), id); err == nil {
registry = newRegistry(r)
func (b *registryBiz) Find(id string) (registry *model.Registry, err error) {
registry, err = b.d.RegistryGet(context.TODO(), id)
if err == nil {
registry.Password = ""
}
return
}
@ -101,43 +96,3 @@ func (b *registryBiz) Delete(id, name string, user web.User) (err error) {
}
return
}
type Registry struct {
ID string `json:"id,omitempty"`
Name string `json:"name" valid:"required"`
URL string `json:"url" valid:"required,url"`
Username string `json:"username" valid:"required"`
Password string `json:"password" copier:"-"`
CreatedAt string `json:"createdAt,omitempty" copier:"-"`
UpdatedAt string `json:"updatedAt,omitempty" copier:"-"`
CreatedBy model.Operator `json:"createdBy" bson:"created_by"`
UpdatedBy model.Operator `json:"updatedBy" bson:"updated_by"`
}
func newRegistry(r *model.Registry) *Registry {
if r == nil {
return nil
}
return &Registry{
ID: r.ID,
Name: r.Name,
URL: r.URL,
Username: r.Username,
CreatedAt: formatTime(r.CreatedAt),
UpdatedAt: formatTime(r.UpdatedAt),
CreatedBy: r.CreatedBy,
UpdatedBy: r.UpdatedBy,
//Password: r.Password, // omit password
}
}
func (r *Registry) Convert() *model.Registry {
return &model.Registry{
ID: r.ID,
Name: r.Name,
URL: r.URL,
Username: r.Username,
Password: r.Password,
}
}

View File

@ -2,43 +2,18 @@ package biz
import (
"context"
"time"
"github.com/cuigh/auxo/net/web"
"github.com/cuigh/swirl/dao"
"github.com/cuigh/swirl/model"
)
type Role struct {
ID string `json:"id,omitempty"`
Name string `json:"name,omitempty" valid:"required"`
Description string `json:"desc,omitempty"`
Perms []string `json:"perms,omitempty"`
CreatedAt string `json:"createdAt,omitempty"`
UpdatedAt string `json:"updatedAt,omitempty"`
CreatedBy model.Operator `json:"createdBy" bson:"created_by"`
UpdatedBy model.Operator `json:"updatedBy" bson:"updated_by"`
}
func newRole(r *model.Role) *Role {
return &Role{
ID: r.ID,
Name: r.Name,
Description: r.Description,
Perms: r.Perms,
CreatedAt: formatTime(r.CreatedAt),
UpdatedAt: formatTime(r.UpdatedAt),
CreatedBy: r.CreatedBy,
UpdatedBy: r.UpdatedBy,
}
}
type RoleBiz interface {
Search(name string) ([]*Role, error)
Find(id string) (role *Role, err error)
Create(role *Role, user web.User) (err error)
Search(name string) ([]*model.Role, error)
Find(id string) (role *model.Role, err error)
Create(role *model.Role, user web.User) (err error)
Delete(id, name string, user web.User) (err error)
Update(r *Role, user web.User) (err error)
Update(r *model.Role, user web.User) (err error)
}
func NewRole(d dao.Interface, eb EventBiz) RoleBiz {
@ -50,26 +25,21 @@ type roleBiz struct {
eb EventBiz
}
func (b *roleBiz) Search(name string) (roles []*Role, err error) {
var list []*model.Role
list, err = b.d.RoleSearch(context.TODO(), name)
if err == nil {
for _, r := range list {
roles = append(roles, newRole(r))
}
}
return
func (b *roleBiz) Search(name string) (roles []*model.Role, err error) {
return b.d.RoleSearch(context.TODO(), name)
}
func (b *roleBiz) Create(role *Role, user web.User) (err error) {
func (b *roleBiz) Create(role *model.Role, user web.User) (err error) {
r := &model.Role{
ID: createId(),
Name: role.Name,
Description: role.Description,
Perms: role.Perms,
CreatedAt: time.Now(),
CreatedAt: now(),
CreatedBy: newOperator(user),
}
r.UpdatedAt = r.CreatedAt
r.UpdatedBy = r.CreatedBy
err = b.d.RoleCreate(context.TODO(), r)
if err == nil {
b.eb.CreateRole(EventActionCreate, r.ID, role.Name, user)
@ -85,25 +55,19 @@ func (b *roleBiz) Delete(id, name string, user web.User) (err error) {
return
}
func (b *roleBiz) Find(id string) (role *Role, err error) {
var r *model.Role
r, err = b.d.RoleGet(context.TODO(), id)
if r != nil {
role = newRole(r)
}
return
func (b *roleBiz) Find(id string) (role *model.Role, err error) {
return b.d.RoleGet(context.TODO(), id)
}
func (b *roleBiz) Update(role *Role, user web.User) (err error) {
func (b *roleBiz) Update(role *model.Role, user web.User) (err error) {
r := &model.Role{
ID: role.ID,
Name: role.Name,
Description: role.Description,
Perms: role.Perms,
UpdatedAt: time.Now(),
UpdatedAt: now(),
UpdatedBy: newOperator(user),
}
r.UpdatedBy.ID = user.ID()
r.UpdatedBy.Name = user.Name()
err = b.d.RoleUpdate(context.TODO(), r)
if err == nil {
b.eb.CreateRole(EventActionUpdate, role.ID, role.Name, user)

View File

@ -9,39 +9,6 @@ import (
"github.com/docker/docker/api/types/swarm"
)
type Secret struct {
ID string `json:"id"`
Name string `json:"name,omitempty"`
Version uint64 `json:"version"`
Data string `json:"data"`
Labels data.Options `json:"labels,omitempty"`
Driver Driver `json:"driver"`
Templating Driver `json:"templating"`
CreatedAt string `json:"createdAt"`
UpdatedAt string `json:"updatedAt"`
}
func newSecret(c *swarm.Secret) *Secret {
secret := &Secret{
ID: c.ID,
Name: c.Spec.Name,
Version: c.Version.Index,
Data: string(c.Spec.Data),
Labels: mapToOptions(c.Spec.Labels),
CreatedAt: formatTime(c.CreatedAt),
UpdatedAt: formatTime(c.UpdatedAt),
}
if c.Spec.Driver != nil {
secret.Driver.Name = c.Spec.Driver.Name
secret.Driver.Options = mapToOptions(c.Spec.Driver.Options)
}
if c.Spec.Templating != nil {
secret.Templating.Name = c.Spec.Templating.Name
secret.Templating.Options = mapToOptions(c.Spec.Templating.Options)
}
return secret
}
type SecretBiz interface {
Search(name string, pageIndex, pageSize int) (secrets []*Secret, total int, err error)
Find(id string) (secret *Secret, raw string, err error)
@ -146,3 +113,36 @@ func (b *secretBiz) Update(c *Secret, user web.User) (err error) {
}
return
}
type Secret struct {
ID string `json:"id"`
Name string `json:"name,omitempty"`
Version uint64 `json:"version"`
Data string `json:"data"`
Labels data.Options `json:"labels,omitempty"`
Driver Driver `json:"driver"`
Templating Driver `json:"templating"`
CreatedAt string `json:"createdAt"`
UpdatedAt string `json:"updatedAt"`
}
func newSecret(c *swarm.Secret) *Secret {
secret := &Secret{
ID: c.ID,
Name: c.Spec.Name,
Version: c.Version.Index,
Data: string(c.Spec.Data),
Labels: mapToOptions(c.Spec.Labels),
CreatedAt: formatTime(c.CreatedAt),
UpdatedAt: formatTime(c.UpdatedAt),
}
if c.Spec.Driver != nil {
secret.Driver.Name = c.Spec.Driver.Name
secret.Driver.Options = mapToOptions(c.Spec.Driver.Options)
}
if c.Spec.Templating != nil {
secret.Templating.Name = c.Spec.Templating.Name
secret.Templating.Options = mapToOptions(c.Spec.Templating.Options)
}
return secret
}

View File

@ -3,7 +3,6 @@ package biz
import (
"context"
"strings"
"time"
"github.com/cuigh/auxo/errors"
"github.com/cuigh/auxo/net/web"
@ -15,13 +14,13 @@ import (
)
type StackBiz interface {
Search(name, filter string) (stacks []*Stack, err error)
Find(name string) (stack *Stack, err error)
Search(name, filter string) (stacks []*model.Stack, err error)
Find(name string) (stack *model.Stack, err error)
Delete(name string, user web.User) (err error)
Shutdown(name string, user web.User) (err error)
Deploy(name string, user web.User) (err error)
Create(s *Stack, user web.User) (err error)
Update(s *Stack, user web.User) (err error)
Create(s *model.Stack, user web.User) (err error)
Update(s *model.Stack, user web.User) (err error)
}
func NewStack(d *docker.Docker, s dao.Interface, eb EventBiz) StackBiz {
@ -34,7 +33,7 @@ type stackBiz struct {
eb EventBiz
}
func (b *stackBiz) Search(name, filter string) (stacks []*Stack, err error) {
func (b *stackBiz) Search(name, filter string) (stacks []*model.Stack, err error) {
var (
activeStacks map[string][]string
internalStacks []*model.Stack
@ -53,8 +52,8 @@ func (b *stackBiz) Search(name, filter string) (stacks []*Stack, err error) {
}
// merge stacks and definitions
for _, s := range internalStacks {
stack := newStack(s)
for i := range internalStacks {
stack := internalStacks[i]
if services, ok := activeStacks[stack.Name]; ok {
stack.Services = services
delete(activeStacks, stack.Name)
@ -64,7 +63,7 @@ func (b *stackBiz) Search(name, filter string) (stacks []*Stack, err error) {
}
}
for n, services := range activeStacks {
stack := &Stack{Name: n, Services: services}
stack := &model.Stack{Name: n, Services: services}
if !b.filter(stack, name, filter) {
stacks = append(stacks, stack)
}
@ -72,17 +71,17 @@ func (b *stackBiz) Search(name, filter string) (stacks []*Stack, err error) {
return
}
func (b *stackBiz) Find(name string) (stack *Stack, err error) {
s, err := b.s.StackGet(context.TODO(), name)
func (b *stackBiz) Find(name string) (s *model.Stack, err error) {
s, err = b.s.StackGet(context.TODO(), name)
if err != nil {
return nil, err
} else if s == nil {
return &Stack{ID: name, Name: name}, nil
s = &model.Stack{Name: name}
}
return newStack(s), nil
return
}
func (b *stackBiz) filter(stack *Stack, name, filter string) bool {
func (b *stackBiz) filter(stack *model.Stack, name, filter string) bool {
if name != "" {
if !strings.Contains(strings.ToLower(stack.Name), strings.ToLower(name)) {
return true
@ -107,15 +106,12 @@ func (b *stackBiz) filter(stack *Stack, name, filter string) bool {
return false
}
func (b *stackBiz) Create(s *Stack, user web.User) (err error) {
func (b *stackBiz) Create(s *model.Stack, user web.User) (err error) {
stack := &model.Stack{
Name: s.Name,
Content: s.Content,
CreatedAt: time.Now(),
CreatedBy: model.Operator{
ID: user.ID(),
Name: user.Name(),
},
CreatedAt: now(),
CreatedBy: newOperator(user),
}
stack.UpdatedAt = stack.CreatedAt
stack.UpdatedBy = stack.CreatedBy
@ -126,18 +122,13 @@ func (b *stackBiz) Create(s *Stack, user web.User) (err error) {
return
}
func (b *stackBiz) Get(name string) (stack *model.Stack, err error) {
return b.s.StackGet(context.TODO(), name)
}
func (b *stackBiz) Update(s *Stack, user web.User) (err error) {
func (b *stackBiz) Update(s *model.Stack, user web.User) (err error) {
stack := &model.Stack{
Name: s.Name,
Content: s.Content,
UpdatedAt: time.Now(),
UpdatedAt: now(),
UpdatedBy: newOperator(user),
}
stack.UpdatedBy.ID = user.ID()
stack.UpdatedBy.Name = user.Name()
err = b.s.StackUpdate(context.TODO(), stack)
if err == nil {
b.eb.CreateStack(EventActionUpdate, stack.Name, user)
@ -200,28 +191,3 @@ func (b *stackBiz) Deploy(name string, user web.User) (err error) {
}
return
}
type Stack struct {
ID string `json:"id,omitempty"`
Name string `json:"name,omitempty"`
Content string `json:"content,omitempty"`
Services []string `json:"services,omitempty"`
Internal bool `json:"internal"`
CreatedAt string `json:"createdAt,omitempty"`
UpdatedAt string `json:"updatedAt,omitempty"`
CreatedBy model.Operator `json:"createdBy,omitempty"`
UpdatedBy model.Operator `json:"updatedBy,omitempty"`
}
func newStack(s *model.Stack) *Stack {
return &Stack{
ID: s.Name,
Name: s.Name,
Content: s.Content,
Internal: true,
CreatedAt: formatTime(s.CreatedAt),
UpdatedAt: formatTime(s.UpdatedAt),
CreatedBy: s.CreatedBy,
UpdatedBy: s.UpdatedBy,
}
}

View File

@ -3,7 +3,6 @@ package biz
import (
"github.com/cuigh/auxo/app"
"github.com/cuigh/auxo/data"
"github.com/cuigh/auxo/errors"
"github.com/cuigh/swirl/dao"
"github.com/cuigh/swirl/misc"
"github.com/docker/docker/api/types/versions"
@ -12,23 +11,22 @@ import (
type SystemBiz interface {
Init() (err error)
CheckState() (state *SystemState, err error)
CreateAdmin(user *User) (err error)
}
func NewSystem(d dao.Interface, ub UserBiz, sb SettingBiz, s *misc.Setting) SystemBiz {
return &systemBiz{
s: s,
d: d,
ub: ub,
sb: sb,
s: s,
}
}
type systemBiz struct {
s *misc.Setting
d dao.Interface
ub UserBiz
sb SettingBiz
s *misc.Setting
}
func (b *systemBiz) Init() (err error) {
@ -51,21 +49,6 @@ func (b *systemBiz) CheckState() (state *SystemState, err error) {
return
}
func (b *systemBiz) CreateAdmin(user *User) (err error) {
user.Admin = true
user.Type = UserTypeInternal
var count int
if count, err = b.ub.Count(); err == nil {
if count > 0 {
err = errors.Coded(1, "system was already initialized")
} else {
_, err = b.ub.Create(user, nil)
}
}
return
}
type SystemState struct {
Fresh bool `json:"fresh"`
}

View File

@ -8,6 +8,63 @@ import (
"github.com/docker/docker/api/types/swarm"
)
type TaskBiz interface {
Search(node, service, mode string, pageIndex, pageSize int) (tasks []*Task, total int, err error)
Find(id string) (task *Task, raw string, err error)
FetchLogs(id string, lines int, timestamps bool) (stdout, stderr string, err error)
}
func NewTask(d *docker.Docker) TaskBiz {
return &taskBiz{d: d}
}
type taskBiz struct {
d *docker.Docker
}
func (b *taskBiz) Find(id string) (task *Task, raw string, err error) {
var (
t swarm.Task
s swarm.Service
r []byte
)
t, r, err = b.d.TaskInspect(context.TODO(), id)
if err == nil {
raw, err = indentJSON(r)
}
if err == nil {
task = newTask(&t)
if s, _, _ = b.d.ServiceInspect(context.TODO(), t.ServiceID, false); s.Spec.Name == "" {
task.ServiceName = task.ServiceID
} else {
task.ServiceName = s.Spec.Name
}
}
return
}
func (b *taskBiz) Search(node, service, state string, pageIndex, pageSize int) (tasks []*Task, total int, err error) {
var list []swarm.Task
list, total, err = b.d.TaskList(context.TODO(), node, service, state, pageIndex, pageSize)
if err != nil {
return
}
tasks = make([]*Task, len(list))
for i, t := range list {
tasks[i] = newTask(&t)
}
return
}
func (b *taskBiz) FetchLogs(id string, lines int, timestamps bool) (string, string, error) {
stdout, stderr, err := b.d.TaskLogs(context.TODO(), id, lines, timestamps)
if err != nil {
return "", "", err
}
return stdout.String(), stderr.String(), nil
}
type Task struct {
ID string `json:"id"`
Name string `json:"name"`
@ -67,60 +124,3 @@ func newTask(t *swarm.Task) *Task {
}
return task
}
type TaskBiz interface {
Search(node, service, mode string, pageIndex, pageSize int) (tasks []*Task, total int, err error)
Find(id string) (task *Task, raw string, err error)
FetchLogs(id string, lines int, timestamps bool) (stdout, stderr string, err error)
}
func NewTask(d *docker.Docker) TaskBiz {
return &taskBiz{d: d}
}
type taskBiz struct {
d *docker.Docker
}
func (b *taskBiz) Find(id string) (task *Task, raw string, err error) {
var (
t swarm.Task
s swarm.Service
r []byte
)
t, r, err = b.d.TaskInspect(context.TODO(), id)
if err == nil {
raw, err = indentJSON(r)
}
if err == nil {
task = newTask(&t)
if s, _, _ = b.d.ServiceInspect(context.TODO(), t.ServiceID, false); s.Spec.Name == "" {
task.ServiceName = task.ServiceID
} else {
task.ServiceName = s.Spec.Name
}
}
return
}
func (b *taskBiz) Search(node, service, state string, pageIndex, pageSize int) (tasks []*Task, total int, err error) {
var list []swarm.Task
list, total, err = b.d.TaskList(context.TODO(), node, service, state, pageIndex, pageSize)
if err != nil {
return
}
tasks = make([]*Task, len(list))
for i, t := range list {
tasks[i] = newTask(&t)
}
return
}
func (b *taskBiz) FetchLogs(id string, lines int, timestamps bool) (string, string, error) {
stdout, stderr, err := b.d.TaskLogs(context.TODO(), id, lines, timestamps)
if err != nil {
return "", "", err
}
return stdout.String(), stderr.String(), nil
}

View File

@ -2,16 +2,13 @@ package biz
import (
"context"
"time"
"github.com/cuigh/auxo/data/guid"
"github.com/cuigh/auxo/errors"
"github.com/cuigh/auxo/net/web"
"github.com/cuigh/auxo/security/passwd"
"github.com/cuigh/swirl/dao"
"github.com/cuigh/swirl/misc"
"github.com/cuigh/swirl/model"
"github.com/jinzhu/copier"
)
const (
@ -29,17 +26,17 @@ const (
)
type UserBiz interface {
Search(name, loginName, filter string, pageIndex, pageSize int) (users []*User, total int, err error)
Create(user *User, ctxUser web.User) (id string, err error)
Update(user *User, ctxUser web.User) (err error)
FindByID(id string) (user *User, err error)
FindByName(loginName string) (user *User, err error)
Search(name, loginName, filter string, pageIndex, pageSize int) (users []*model.User, total int, err error)
Create(user *model.User, ctxUser web.User) (id string, err error)
Update(user *model.User, ctxUser web.User) (err error)
FindByID(id string) (user *model.User, err error)
FindByName(loginName string) (user *model.User, err error)
FindPrivacy(loginName string) (privacy *UserPrivacy, err error)
Count() (count int, err error)
Delete(id, name string, user web.User) (err error)
SetStatus(id string, status int32, user web.User) (err error)
ModifyPassword(oldPwd, newPwd string, user web.User) (err error)
ModifyProfile(user *User, ctxUser web.User) (err error)
ModifyProfile(user *model.User, ctxUser web.User) (err error)
}
func NewUser(d dao.Interface, eb EventBiz) UserBiz {
@ -51,17 +48,14 @@ type userBiz struct {
eb EventBiz
}
func (b *userBiz) Search(name, loginName, filter string, pageIndex, pageSize int) (users []*User, total int, err error) {
var (
list []*model.User
args = &model.UserSearchArgs{
Name: name,
LoginName: loginName,
Status: -1,
PageIndex: pageIndex,
PageSize: pageSize,
}
)
func (b *userBiz) Search(name, loginName, filter string, pageIndex, pageSize int) (users []*model.User, total int, err error) {
var args = &model.UserSearchArgs{
Name: name,
LoginName: loginName,
Status: -1,
PageIndex: pageIndex,
PageSize: pageSize,
}
switch filter {
case "admins":
@ -72,31 +66,15 @@ func (b *userBiz) Search(name, loginName, filter string, pageIndex, pageSize int
args.Status = UserStatusBlocked
}
list, total, err = b.d.UserSearch(context.TODO(), args)
if err == nil {
for _, u := range list {
users = append(users, newUser(u))
}
}
return
return b.d.UserSearch(context.TODO(), args)
}
func (b *userBiz) FindByID(id string) (user *User, err error) {
var u *model.User
u, err = b.d.UserGet(context.TODO(), id)
if u != nil {
user = newUser(u)
}
return
func (b *userBiz) FindByID(id string) (user *model.User, err error) {
return b.d.UserGet(context.TODO(), id)
}
func (b *userBiz) FindByName(loginName string) (user *User, err error) {
var u *model.User
u, err = b.d.UserGetByName(context.TODO(), loginName)
if u != nil {
user = newUser(u)
}
return
func (b *userBiz) FindByName(loginName string) (user *model.User, err error) {
return b.d.UserGetByName(context.TODO(), loginName)
}
func (b *userBiz) FindPrivacy(loginName string) (privacy *UserPrivacy, err error) {
@ -115,23 +93,17 @@ func (b *userBiz) FindPrivacy(loginName string) (privacy *UserPrivacy, err error
return
}
func (b *userBiz) Create(u *User, ctxUser web.User) (id string, err error) {
user := &model.User{
ID: createId(),
Name: u.Name,
LoginName: u.LoginName,
Email: u.Email,
Admin: u.Admin,
Type: u.Type,
Status: UserStatusActive,
Roles: u.Roles,
CreatedAt: time.Now(),
CreatedBy: model.Operator{ID: ctxUser.ID(), Name: ctxUser.Name()},
func (b *userBiz) Create(user *model.User, ctxUser web.User) (id string, err error) {
user.ID = createId()
user.Status = UserStatusActive
user.CreatedAt = now()
if ctxUser != nil {
user.CreatedBy = newOperator(ctxUser)
}
user.UpdatedAt = user.CreatedAt
user.UpdatedBy = user.CreatedBy
if user.Type == UserTypeInternal {
user.Password, user.Salt, err = passwd.Generate(u.Password)
user.Password, user.Salt, err = passwd.Generate(user.Password)
if err != nil {
return
}
@ -144,21 +116,11 @@ func (b *userBiz) Create(u *User, ctxUser web.User) (id string, err error) {
return
}
func (b *userBiz) Update(u *User, ctxUser web.User) (err error) {
user := &model.User{
ID: u.ID,
Name: u.Name,
LoginName: u.LoginName,
Email: u.Email,
Admin: u.Admin,
Type: u.Type,
Roles: u.Roles,
UpdatedAt: time.Now(),
}
user.UpdatedBy.ID = ctxUser.ID()
user.UpdatedBy.Name = ctxUser.Name()
func (b *userBiz) Update(user *model.User, ctxUser web.User) (err error) {
user.UpdatedAt = now()
user.UpdatedBy = newOperator(ctxUser)
if err = b.d.UserUpdate(context.TODO(), user); err == nil {
b.eb.CreateUser(EventActionUpdate, u.LoginName, u.Name, ctxUser)
b.eb.CreateUser(EventActionUpdate, user.LoginName, user.Name, ctxUser)
}
return
}
@ -167,8 +129,8 @@ func (b *userBiz) SetStatus(id string, status int32, user web.User) (err error)
u := &model.User{
ID: id,
Status: status,
UpdatedAt: time.Now(),
UpdatedBy: model.Operator{ID: user.ID(), Name: user.Name()},
UpdatedAt: now(),
UpdatedBy: newOperator(user),
}
return b.d.UserUpdateStatus(context.TODO(), u)
}
@ -194,81 +156,49 @@ func (b *userBiz) ModifyPassword(oldPwd, newPwd string, user web.User) (err erro
return errors.Coded(misc.ErrOldPasswordIncorrect, "current password is incorrect")
}
u.Password, u.Salt, err = passwd.Generate(newPwd)
if err != nil {
if u.Password, u.Salt, err = passwd.Generate(newPwd); err != nil {
return
}
u.UpdatedAt = time.Now()
u.UpdatedBy.ID = user.ID()
u.UpdatedBy.Name = user.Name()
err = b.d.UserUpdatePassword(context.TODO(), u)
return
u.UpdatedAt = now()
u.UpdatedBy = newOperator(user)
return b.d.UserUpdatePassword(context.TODO(), u)
}
func (b *userBiz) ModifyProfile(u *User, user web.User) (err error) {
return b.d.UserUpdateProfile(context.TODO(), &model.User{
ID: user.ID(),
Name: u.Name,
LoginName: u.LoginName,
Email: u.Email,
UpdatedAt: time.Now(),
UpdatedBy: model.Operator{ID: user.ID(), Name: user.Name()},
})
func (b *userBiz) ModifyProfile(u *model.User, user web.User) (err error) {
u.ID = user.ID()
u.UpdatedAt = now()
u.UpdatedBy = newOperator(user)
return b.d.UserUpdateProfile(context.TODO(), u)
}
func (b *userBiz) Count() (count int, err error) {
return b.d.UserCount(context.TODO())
}
func (b *userBiz) UpdateSession(id string) (token string, err error) {
session := &model.Session{
UserID: id,
Token: guid.New().String(),
UpdatedAt: time.Now(),
}
session.Expires = session.UpdatedAt.Add(time.Hour * 24)
err = b.d.SessionUpdate(context.TODO(), session)
if err == nil {
token = session.Token
}
return
}
//func (b *userBiz) UpdateSession(id string) (token string, err error) {
// session := &model.Session{
// UserID: id,
// Token: guid.New().String(),
// UpdatedAt: time.Now(),
// }
// session.Expires = session.UpdatedAt.Add(time.Hour * 24)
// err = b.d.SessionUpdate(context.TODO(), session)
// if err == nil {
// token = session.Token
// }
// return
//}
func (b *userBiz) GetSession(token string) (session *model.Session, err error) {
return b.d.SessionGet(context.TODO(), token)
}
type User struct {
ID string `json:"id"`
Name string `json:"name" valid:"required"`
LoginName string `json:"loginName" valid:"required"`
Password string `json:"password,omitempty"`
Email string `json:"email" valid:"required"`
Admin bool `json:"admin"`
Type string `json:"type"`
Status int32 `json:"status,omitempty"`
Roles []string `json:"roles,omitempty"`
CreatedAt string `json:"createdAt,omitempty"`
UpdatedAt string `json:"updatedAt,omitempty"`
CreatedBy model.Operator `json:"createdBy"`
UpdatedBy model.Operator `json:"updatedBy"`
}
type UserPrivacy struct {
ID string
Name string
Password string
Salt string
Password string `json:"-"`
Salt string `json:"-"`
Type string
Status int32
}
func newUser(u *model.User) *User {
user := &User{
CreatedAt: formatTime(u.CreatedAt),
UpdatedAt: formatTime(u.UpdatedAt),
}
_ = copier.CopyWithOption(user, u, copier.Option{IgnoreEmpty: true, DeepCopy: true})
return user
}

View File

@ -11,41 +11,6 @@ import (
"github.com/docker/docker/api/types/volume"
)
type Volume struct {
Name string `json:"name"`
Driver string `json:"driver,omitempty"`
CustomDriver string `json:"customDriver,omitempty"`
CreatedAt string `json:"createdAt"`
MountPoint string `json:"mountPoint,omitempty"`
Scope string `json:"scope"`
Labels data.Options `json:"labels,omitempty"`
Options data.Options `json:"options,omitempty"`
Status map[string]interface{} `json:"status,omitempty"`
RefCount int64 `json:"refCount"`
Size int64 `json:"size"`
}
func newVolume(v *types.Volume) *Volume {
createdAt, _ := time.Parse(time.RFC3339Nano, v.CreatedAt)
vol := &Volume{
Name: v.Name,
Driver: v.Driver,
CreatedAt: formatTime(createdAt),
MountPoint: v.Mountpoint,
Scope: v.Scope,
Status: v.Status,
Labels: mapToOptions(v.Labels),
Options: mapToOptions(v.Options),
RefCount: -1,
Size: -1,
}
if v.UsageData != nil {
vol.RefCount = v.UsageData.RefCount
vol.Size = v.UsageData.Size
}
return vol
}
type VolumeBiz interface {
Search(name string, pageIndex, pageSize int) ([]*Volume, int, error)
Find(name string) (volume *Volume, raw string, err error)
@ -129,3 +94,38 @@ func (b *volumeBiz) Prune(user web.User) (deletedVolumes []string, reclaimedSpac
}
return
}
type Volume struct {
Name string `json:"name"`
Driver string `json:"driver,omitempty"`
CustomDriver string `json:"customDriver,omitempty"`
CreatedAt string `json:"createdAt"`
MountPoint string `json:"mountPoint,omitempty"`
Scope string `json:"scope"`
Labels data.Options `json:"labels,omitempty"`
Options data.Options `json:"options,omitempty"`
Status map[string]interface{} `json:"status,omitempty"`
RefCount int64 `json:"refCount"`
Size int64 `json:"size"`
}
func newVolume(v *types.Volume) *Volume {
createdAt, _ := time.Parse(time.RFC3339Nano, v.CreatedAt)
vol := &Volume{
Name: v.Name,
Driver: v.Driver,
CreatedAt: formatTime(createdAt),
MountPoint: v.Mountpoint,
Scope: v.Scope,
Status: v.Status,
Labels: mapToOptions(v.Labels),
Options: mapToOptions(v.Options),
RefCount: -1,
Size: -1,
}
if v.UsageData != nil {
vol.RefCount = v.UsageData.RefCount
vol.Size = v.UsageData.Size
}
return vol
}

View File

@ -3,6 +3,7 @@ package bolt
import (
"context"
"sort"
"time"
"github.com/cuigh/swirl/misc"
"github.com/cuigh/swirl/model"
@ -33,7 +34,7 @@ func (d *Dao) ChartSearch(ctx context.Context, args *model.ChartSearchArgs) (cha
if err == nil {
count = len(charts)
sort.Slice(charts, func(i, j int) bool {
return charts[i].CreatedAt.After(charts[j].UpdatedAt)
return time.Time(charts[i].UpdatedAt).After(time.Time(charts[j].UpdatedAt))
})
start, end := misc.Page(count, args.PageIndex, args.PageSize)
charts = charts[start:end]

View File

@ -3,6 +3,7 @@ package bolt
import (
"context"
"sort"
"time"
"github.com/cuigh/swirl/misc"
"github.com/cuigh/swirl/model"
@ -34,7 +35,7 @@ func (d *Dao) EventSearch(ctx context.Context, args *model.EventSearchArgs) (eve
if err == nil {
count = len(events)
sort.Slice(events, func(i, j int) bool {
return events[i].Time.After(events[j].Time)
return time.Time(events[i].Time).After(time.Time(events[j].Time))
})
start, end := misc.Page(count, args.PageIndex, args.PageSize)
events = events[start:end]

View File

@ -40,7 +40,7 @@ func (d *Dao) RoleGet(ctx context.Context, id string) (role *model.Role, err err
}
func (d *Dao) RoleUpdate(ctx context.Context, role *model.Role) (err error) {
old := &model.Registry{}
old := &model.Role{}
return d.update(Role, role.ID, old, func() interface{} {
role.CreatedAt = old.CreatedAt
role.CreatedBy = old.CreatedBy

View File

@ -127,11 +127,13 @@ func (d *Dao) UserUpdatePassword(ctx context.Context, user *model.User) (err err
func (d *Dao) SessionGet(ctx context.Context, token string) (session *model.Session, err error) {
s := &model.Session{}
found, err := d.find(Session, s, func() bool { return s.Token == token })
if found {
return s, nil
err = d.get(Session, token, s)
if err == ErrNoRecords {
return nil, nil
} else if err != nil {
s = nil
}
return nil, err
return
}
func (d *Dao) SessionUpdate(ctx context.Context, session *model.Session) (err error) {

View File

@ -30,7 +30,7 @@ var (
func main() {
app.Name = "Swirl"
app.Version = "1.0.0beta3"
app.Version = "1.0.0beta4"
app.Desc = "A web management UI for Docker, focused on swarm cluster"
app.Action = func(ctx *app.Context) error {
return run.Pipeline(misc.LoadOptions, initSystem, scaler.Start, startServer)

View File

@ -7,6 +7,7 @@ const (
ErrAccountDisabled = 1002
ErrOldPasswordIncorrect = 1003
ErrExternalStack = 1004
ErrSystemInitialized = 1005
)
func Error(code int32, err error) error {

View File

@ -59,8 +59,8 @@ type Operator struct {
// Setting represents the options of swirl.
type Setting struct {
ID string `json:"id" bson:"_id"`
Options []*SettingOption `json:"options" bson:"options,omitempty"`
UpdatedAt time.Time `bson:"updated_at" json:"updatedAt,omitempty"`
Options []*SettingOption `json:"options" bson:"options"`
UpdatedAt time.Time `json:"updatedAt" bson:"updated_at"`
UpdatedBy Operator `json:"updatedBy" bson:"updated_by"`
}
@ -71,31 +71,31 @@ type SettingOption struct {
}
type Role struct {
ID string `bson:"_id" json:"id,omitempty"`
Name string `bson:"name" json:"name,omitempty" valid:"required"`
Description string `bson:"desc" json:"desc,omitempty"`
Perms []string `bson:"perms" json:"perms,omitempty"`
CreatedAt time.Time `bson:"created_at" json:"created_at,omitempty"`
UpdatedAt time.Time `bson:"updated_at" json:"updated_at,omitempty"`
CreatedBy Operator `json:"createdBy" bson:"created_by"`
UpdatedBy Operator `json:"updatedBy" bson:"updated_by"`
ID string `json:"id,omitempty" bson:"_id"`
Name string `json:"name,omitempty" bson:"name" valid:"required"`
Description string `json:"desc,omitempty" bson:"desc,omitempty"`
Perms []string `json:"perms,omitempty" bson:"perms,omitempty"`
UpdatedAt Time `json:"updatedAt" bson:"updated_at"`
CreatedAt Time `json:"createdAt" bson:"created_at"`
CreatedBy Operator `json:"createdBy" bson:"created_by"`
UpdatedBy Operator `json:"updatedBy" bson:"updated_by"`
}
type User struct {
ID string `bson:"_id" json:"id,omitempty"`
Name string `bson:"name" json:"name,omitempty" valid:"required"`
LoginName string `bson:"login_name" json:"loginName,omitempty" valid:"required"`
Password string `bson:"password" json:"-"`
Salt string `bson:"salt" json:"-"`
Email string `bson:"email" json:"email,omitempty" valid:"required"`
Admin bool `bson:"admin" json:"admin,omitempty"`
Type string `bson:"type" json:"type,omitempty"`
Status int32 `bson:"status" json:"status,omitempty"`
Roles []string `bson:"roles" json:"roles,omitempty"`
CreatedAt time.Time `bson:"created_at" json:"createdAt,omitempty"`
UpdatedAt time.Time `bson:"updated_at" json:"updatedAt,omitempty"`
CreatedBy Operator `json:"createdBy" bson:"created_by"`
UpdatedBy Operator `json:"updatedBy" bson:"updated_by"`
ID string `json:"id,omitempty" bson:"_id"`
Name string `json:"name" bson:"name" valid:"required"`
LoginName string `json:"loginName" bson:"login_name" valid:"required"`
Password string `json:"-" bson:"password"`
Salt string `json:"-" bson:"salt"`
Email string `json:"email" bson:"email" valid:"required"`
Admin bool `json:"admin" bson:"admin"`
Type string `json:"type" bson:"type"`
Status int32 `json:"status" bson:"status"`
Roles []string `json:"roles,omitempty" bson:"roles,omitempty"`
CreatedAt Time `json:"createdAt" bson:"created_at"`
UpdatedAt Time `json:"updatedAt" bson:"updated_at"`
CreatedBy Operator `json:"createdBy" bson:"created_by"`
UpdatedBy Operator `json:"updatedBy" bson:"updated_by"`
}
type UserSearchArgs struct {
@ -108,15 +108,15 @@ type UserSearchArgs struct {
}
type Registry struct {
ID string `bson:"_id" json:"id"`
Name string `bson:"name" json:"name"`
URL string `bson:"url" json:"url"`
Username string `bson:"username" json:"username"`
Password string `bson:"password" json:"-"`
CreatedAt time.Time `bson:"created_at" json:"createdAt,omitempty"`
UpdatedAt time.Time `bson:"updated_at" json:"updatedAt,omitempty"`
CreatedBy Operator `json:"createdBy" bson:"created_by"`
UpdatedBy Operator `json:"updatedBy" bson:"updated_by"`
ID string `json:"id" bson:"_id"`
Name string `json:"name" bson:"name"`
URL string `json:"url" bson:"url"`
Username string `json:"username" bson:"username"`
Password string `json:"password,omitempty" bson:"password,omitempty"`
CreatedAt Time `json:"createdAt" bson:"created_at"`
UpdatedAt Time `json:"updatedAt" bson:"updated_at"`
CreatedBy Operator `json:"createdBy" bson:"created_by"`
UpdatedBy Operator `json:"updatedBy" bson:"updated_by"`
}
func (r *Registry) Match(image string) bool {
@ -136,25 +136,25 @@ func (r *Registry) GetEncodedAuth() string {
}
type Stack struct {
Name string `bson:"_id" json:"name,omitempty"`
Content string `bson:"content" json:"content,omitempty" bind:"content=form,content=file"`
Services []string `bson:"-" json:"services,omitempty"`
Internal bool `bson:"-" json:"internal"`
CreatedAt time.Time `bson:"created_at" json:"createdAt,omitempty"`
UpdatedAt time.Time `bson:"updated_at" json:"updatedAt,omitempty"`
CreatedBy Operator `json:"createdBy" bson:"created_by"`
UpdatedBy Operator `json:"updatedBy" bson:"updated_by"`
Name string `json:"name" bson:"_id"`
Content string `json:"content" bson:"content"`
Services []string `json:"services,omitempty" bson:"-"`
Internal bool `json:"internal" bson:"-"`
CreatedAt Time `json:"createdAt" bson:"created_at"`
UpdatedAt Time `json:"updatedAt" bson:"updated_at"`
CreatedBy Operator `json:"createdBy" bson:"created_by"`
UpdatedBy Operator `json:"updatedBy" bson:"updated_by"`
}
type Event struct {
ID primitive.ObjectID `bson:"_id"`
Type string `bson:"type"`
Action string `bson:"action"`
Code string `bson:"code"`
Name string `bson:"name"`
UserID string `bson:"user_id"`
Username string `bson:"username"`
Time time.Time `bson:"time"`
ID primitive.ObjectID `json:"id" bson:"_id"`
Type string `json:"type" bson:"type"`
Action string `json:"action" bson:"action"`
Code string `json:"code" bson:"code"`
Name string `json:"name" bson:"name"`
UserID string `json:"userId" bson:"user_id"`
Username string `json:"username" bson:"username"`
Time Time `json:"time" bson:"time"`
}
type EventSearchArgs struct {
@ -166,27 +166,26 @@ type EventSearchArgs struct {
// Chart represents a dashboard chart.
type Chart struct {
ID string `json:"id" bson:"_id"` // unique, the name of build-in charts has '$' prefix.
Title string `json:"title" valid:"required"`
ID string `json:"id" bson:"_id"` // the id of built-in charts has '$' prefix.
Title string `json:"title" bson:"title" valid:"required"`
Description string `json:"desc" bson:"desc"`
Metrics []ChartMetric `json:"metrics" valid:"required"`
Dashboard string `json:"dashboard"` // home/service/task...
Type string `json:"type"` // pie/line...
Unit string `json:"unit"` // bytes/milliseconds/percent:100...
Width int32 `json:"width"` // 1-12(12 columns total)
Height int32 `json:"height"` // default 50
Options data.Map `json:"options,omitempty"`
Metrics []ChartMetric `json:"metrics" bson:"metrics" valid:"required"`
Dashboard string `json:"dashboard" bson:"dashboard"` // home/service...
Type string `json:"type" bson:"type"` // pie/line...
Unit string `json:"unit" bson:"unit"` // bytes/milliseconds/percent:100...
Width int32 `json:"width" bson:"width"` // 1-12(12 columns total)
Height int32 `json:"height" bson:"height"` // default 50
Options data.Map `json:"options,omitempty" bson:"options,omitempty"`
Margin struct {
Left int32 `json:"left,omitempty"`
Right int32 `json:"right,omitempty"`
Top int32 `json:"top,omitempty"`
Bottom int32 `json:"bottom,omitempty"`
} `json:"margin"`
//Colors []string `json:"colors"`
CreatedAt time.Time `bson:"created_at" json:"createdAt,omitempty"`
UpdatedAt time.Time `bson:"updated_at" json:"updatedAt,omitempty"`
CreatedBy Operator `json:"createdBy" bson:"created_by"`
UpdatedBy Operator `json:"updatedBy" bson:"updated_by"`
Left int32 `json:"left,omitempty" bson:"left,omitempty"`
Right int32 `json:"right,omitempty" bson:"right,omitempty"`
Top int32 `json:"top,omitempty" bson:"top,omitempty"`
Bottom int32 `json:"bottom,omitempty" bson:"bottom,omitempty"`
} `json:"margin" bson:"margin"`
CreatedAt Time `json:"createdAt" bson:"created_at"`
UpdatedAt Time `json:"updatedAt" bson:"updated_at"`
CreatedBy Operator `json:"createdBy" bson:"created_by"`
UpdatedBy Operator `json:"updatedBy" bson:"updated_by"`
}
func NewChart(dashboard, id, title, legend, query, unit string, left int32) *Chart {
@ -220,20 +219,28 @@ type ChartSearchArgs struct {
}
type Dashboard struct {
Name string `json:"name"`
Key string `json:"key,omitempty"`
Period int32 `json:"period,omitempty"` // data range in minutes
Interval int32 `json:"interval,omitempty"` // refresh interval in seconds, 0 means disabled.
Charts []ChartInfo `json:"charts,omitempty"`
UpdatedAt time.Time `bson:"updated_at" json:"updatedAt,omitempty"`
UpdatedBy Operator `json:"updatedBy" bson:"updated_by"`
Name string `json:"name" bson:"name"`
Key string `json:"key,omitempty" bson:"key,omitempty"`
Period int32 `json:"period,omitempty" bson:"period,omitempty"` // data range in minutes
Interval int32 `json:"interval,omitempty" bson:"interval,omitempty"` // refresh interval in seconds, 0 means disabled.
Charts []ChartInfo `json:"charts,omitempty" bson:"charts,omitempty"`
UpdatedAt Time `json:"-" bson:"updated_at"`
UpdatedBy Operator `json:"-" bson:"updated_by"`
}
type ChartInfo struct {
ID string `json:"id"`
Width int32 `json:"width,omitempty"`
Height int32 `json:"height,omitempty"`
//Colors []string `json:"colors,omitempty"`
ID string `json:"id" bson:"id"`
Width int32 `json:"width,omitempty" bson:"width,omitempty"`
Height int32 `json:"height,omitempty" bson:"height,omitempty"`
Title string `json:"title" bson:"-"`
Type string `json:"type" bson:"-"`
Unit string `json:"unit" bson:"-"`
Margin struct {
Left int32 `json:"left,omitempty" bson:"-"`
Right int32 `json:"right,omitempty" bson:"-"`
Top int32 `json:"top,omitempty" bson:"-"`
Bottom int32 `json:"bottom,omitempty" bson:"-"`
} `json:"margin" bson:"-"`
}
func (cd *Dashboard) ID() string {
@ -244,65 +251,12 @@ func (cd *Dashboard) ID() string {
}
type Session struct {
UserID string `bson:"_id" json:"id,omitempty"`
Token string `bson:"token" json:"token,omitempty"`
Expires time.Time `bson:"expires" json:"expires,omitempty"`
UpdatedAt time.Time `bson:"updated_at" json:"updated_at,omitempty"`
ID string `json:"id" bson:"_id"` // token
UserID string `json:"userId" bson:"user_id"`
Roles []string `json:"roles" bson:"roles"`
Perm int64 `json:"perm" bson:"perm"`
Dirty bool `json:"dirty" bson:"dirty"`
Expiry time.Time `json:"expiry" bson:"expiry"`
MaxExpiry time.Time `json:"maxExpiry" bson:"max_expiry"`
UpdatedAt time.Time `json:"updatedAt" bson:"updated_at"`
}
//type AuthUser struct {
// user *User
// roles []*Role
// perms map[string]struct{}
//}
//
//func NewAuthUser(user *User, roles []*Role) *AuthUser {
// if user == nil {
// panic(111)
// }
// u := &AuthUser{
// user: user,
// roles: roles,
// perms: make(map[string]struct{}),
// }
// for _, role := range roles {
// for _, perm := range role.Perms {
// u.perms[perm] = data.Empty
// }
// }
// return u
//}
//
//func (u *AuthUser) ID() string {
// return u.user.ID
//}
//
//func (u *AuthUser) Name() string {
// return u.user.Name
//}
//
//func (u *AuthUser) Anonymous() bool {
// return u.user.ID == ""
//}
//
//func (u *AuthUser) Admin() bool {
// return u.user.Admin
//}
//
//func (u *AuthUser) IsInRole(roleID string) bool {
// for _, role := range u.roles {
// if role.ID == roleID {
// return true
// }
// }
// return false
//}
//
//func (u *AuthUser) IsAllowed(perm string) bool {
// if u.user.Admin {
// return true
// }
//
// _, ok := u.perms[perm]
// return ok
//}

View File

@ -8,6 +8,7 @@ import (
"github.com/cuigh/auxo/security/passwd"
"github.com/cuigh/swirl/biz"
"github.com/cuigh/swirl/misc"
"github.com/cuigh/swirl/model"
)
type Authenticator struct {
@ -92,7 +93,7 @@ func ldapRealm(s *misc.Setting, ub biz.UserBiz) RealmFunc {
lu = user.(*ldap.User)
)
if u == nil {
id, err = ub.Create(&biz.User{
id, err = ub.Create(&model.User{
Type: biz.UserTypeLDAP,
LoginName: loginName,
Name: lu.Name(),

View File

@ -20,8 +20,8 @@ export interface Chart {
top: number;
bottom: number;
};
createdAt: string;
updatedAt: string;
createdAt: number;
updatedAt: number;
createdBy: {
id: string;
name: string;

View File

@ -8,7 +8,7 @@ export interface Event {
name: string;
userId: string;
username: string;
time: string;
time: number;
}
export interface SearchArgs {

View File

@ -6,8 +6,8 @@ export interface Registry {
url: string;
username: string;
password: string;
createdAt: string;
updatedAt: string;
createdAt: number;
updatedAt: number;
createdBy: {
id: string;
name: string;

View File

@ -5,8 +5,8 @@ export interface Role {
name: string;
desc: string;
perms: string[];
createdAt: string;
updatedAt: string;
createdAt: number;
updatedAt: number;
createdBy: {
id: string;
name: string;

View File

@ -6,8 +6,8 @@ export interface Stack {
content: string;
services?: string[];
internal: boolean;
createdAt: string;
updatedAt: string;
createdAt: number;
updatedAt: number;
createdBy: {
id: string;
name: string;

View File

@ -17,8 +17,8 @@ export interface User {
status: number;
email: string;
roles: string[];
createdAt: string;
updatedAt: string;
createdAt: number;
updatedAt: number;
createdBy: {
id: string;
name: string;

View File

@ -394,6 +394,7 @@ export default {
"1002": "Account is disabled",
"1003": "Old password is incorrect",
"1004": "Can not deploy external stack",
"1005": "system was already initialized",
},
"copyright": "© 2017-2021 cuigh. All rights reserved.",
}

View File

@ -394,6 +394,7 @@ export default {
"1002": "账户已禁用",
"1003": "旧密码不正确",
"1004": "不能发布外部编排",
"1005": "系统已经初始化",
},
"copyright": "© 2017-2021 cuigh. 保留所有权利。",
}

View File

@ -68,7 +68,7 @@ import XCode from "@/components/Code.vue";
import XIcon from "@/components/Icon.vue";
import chartApi from "@/api/chart";
import type { Chart } from "@/api/chart";
import { renderButtons, renderLink, renderTag } from "@/utils/render";
import { renderButtons, renderLink, renderTag, renderTime } from "@/utils/render";
import { useDataTable } from "@/utils/data-table";
import { toTitle } from "@/utils";
import { useI18n } from 'vue-i18n'
@ -119,7 +119,8 @@ const columns = [
},
{
title: t('fields.updated_at'),
key: "updatedAt"
key: "updatedAt",
render: (c: Chart) => renderTime(c.updatedAt),
},
{
title: t('fields.actions'),
@ -175,7 +176,7 @@ function importChart() {
}
function exportChart(c: Chart) {
const { id, createdAt, updatedAt, ...chart } = c
const { id, createdAt, updatedAt, createdBy, updatedBy, ...chart } = c
window.dialog.success({
showIcon: false,
title: t('dialogs.export_chart.title'),

View File

@ -57,11 +57,15 @@
<x-description-item :label="t('fields.created_by')">
<x-anchor :url="`/system/users/${model.createdBy?.id}`">{{ model.createdBy?.name }}</x-anchor>
</x-description-item>
<x-description-item :label="t('fields.created_at')">{{ model.createdAt }}</x-description-item>
<x-description-item :label="t('fields.created_at')">
<n-time :time="model.createdAt" format="y-MM-dd HH:mm:ss" />
</x-description-item>
<x-description-item :label="t('fields.updated_by')">
<x-anchor :url="`/system/users/${model.updatedBy?.id}`">{{ model.updatedBy?.name }}</x-anchor>
</x-description-item>
<x-description-item :label="t('fields.updated_at')">{{ model.updatedAt }}</x-description-item>
<x-description-item :label="t('fields.updated_at')">
<n-time :time="model.updatedAt" format="y-MM-dd HH:mm:ss" />
</x-description-item>
</x-description>
<x-panel :title="t('fields.metrics')">
<n-table size="small" :bordered="true" :single-line="false">
@ -89,6 +93,7 @@ import {
NSpace,
NIcon,
NTable,
NTime,
} from "naive-ui";
import { ArrowBackCircleOutline as BackIcon } from "@vicons/ionicons5";
import XPageHeader from "@/components/PageHeader.vue";

View File

@ -59,7 +59,7 @@ import XPageHeader from "@/components/PageHeader.vue";
import eventApi from "@/api/event";
import type { Event } from "@/api/event";
import { useDataTable } from "@/utils/data-table";
import { renderLink, renderTag } from "@/utils/render";
import { renderLink, renderTag, renderTime } from "@/utils/render";
import { useI18n } from 'vue-i18n'
const { t } = useI18n()
@ -186,7 +186,8 @@ const columns = [
},
{
title: t('fields.time'),
key: "time"
key: "time",
render: (e: Event) => renderTime(e.time),
},
];
const { state, pagination, fetchData, changePageSize } = useDataTable(eventApi.search, filter)

View File

@ -30,8 +30,12 @@
</td>
<td>{{ r.url }}</td>
<td>{{ r.username }}</td>
<td>{{ r.createdAt }}</td>
<td>{{ r.updatedAt }}</td>
<td>
<n-time :time="r.createdAt" format="y-MM-dd HH:mm:ss" />
</td>
<td>
<n-time :time="r.updatedAt" format="y-MM-dd HH:mm:ss" />
</td>
<td>
<n-button
size="tiny"
@ -60,6 +64,7 @@ import {
NTable,
NPopconfirm,
NIcon,
NTime,
} from "naive-ui";
import { AddOutline as AddIcon } from "@vicons/ionicons5";
import XPageHeader from "@/components/PageHeader.vue";

View File

@ -25,11 +25,15 @@
<x-description-item :label="t('fields.created_by')">
<x-anchor :url="`/system/users/${model.createdBy?.id}`">{{ model.createdBy?.name }}</x-anchor>
</x-description-item>
<x-description-item :label="t('fields.created_at')">{{ model.createdAt }}</x-description-item>
<x-description-item :label="t('fields.created_at')">
<n-time :time="model.createdAt" format="y-MM-dd HH:mm:ss" />
</x-description-item>
<x-description-item :label="t('fields.updated_by')">
<x-anchor :url="`/system/users/${model.updatedBy?.id}`">{{ model.updatedBy?.name }}</x-anchor>
</x-description-item>
<x-description-item :label="t('fields.updated_at')">{{ model.updatedAt }}</x-description-item>
<x-description-item :label="t('fields.updated_at')">
<n-time :time="model.updatedAt" format="y-MM-dd HH:mm:ss" />
</x-description-item>
</x-description>
</n-space>
</template>
@ -40,6 +44,7 @@ import {
NButton,
NSpace,
NIcon,
NTime,
} from "naive-ui";
import { ArrowBackCircleOutline as BackIcon } from "@vicons/ionicons5";
import XPageHeader from "@/components/PageHeader.vue";

View File

@ -33,7 +33,9 @@
</td>
<td>{{ r.name }}</td>
<td>{{ r.desc }}</td>
<td>{{ r.updatedAt }}</td>
<td>
<n-time :time="r.updatedAt" format="y-MM-dd HH:mm:ss" />
</td>
<td>
<n-popconfirm :show-icon="false" @positive-click="deleteRole(r, index)">
<template #trigger>
@ -63,6 +65,7 @@ import {
NIcon,
NTable,
NPopconfirm,
NTime,
} from "naive-ui";
import {
AddOutline as AddIcon,

View File

@ -24,11 +24,15 @@
<x-description-item :label="t('fields.created_by')">
<x-anchor :url="`/system/users/${model.createdBy?.id}`">{{ model.createdBy?.name }}</x-anchor>
</x-description-item>
<x-description-item :label="t('fields.created_at')">{{ model.createdAt }}</x-description-item>
<x-description-item :label="t('fields.created_at')">
<n-time :time="model.createdAt" format="y-MM-dd HH:mm:ss" />
</x-description-item>
<x-description-item :label="t('fields.updated_by')">
<x-anchor :url="`/system/users/${model.updatedBy?.id}`">{{ model.updatedBy?.name }}</x-anchor>
</x-description-item>
<x-description-item :label="t('fields.updated_at')">{{ model.updatedAt }}</x-description-item>
<x-description-item :label="t('fields.updated_at')">
<n-time :time="model.updatedAt" format="y-MM-dd HH:mm:ss" />
</x-description-item>
<x-description-item :span="2" :label="t('fields.perms')">
<n-grid cols="1 480:2 960:3 1440:4" x-gap="6">
<n-gi span="1" v-for="g in ps">
@ -48,6 +52,7 @@ import {
NIcon,
NGrid,
NGi,
NTime,
} from "naive-ui";
import { useRoute } from "vue-router";
import { ArrowBackCircleOutline as BackIcon } from "@vicons/ionicons5";

View File

@ -152,6 +152,7 @@ async function fetchData() {
if (name) {
let tr = await stackApi.find(name);
model.value = tr.data as Stack;
model.value.id = model.value.name
}
}

View File

@ -38,8 +38,12 @@
</n-tag>
</n-space>
</td>
<td>{{ r.createdAt }}</td>
<td>{{ r.updatedAt }}</td>
<td>
<n-time :time="r.createdAt" format="y-MM-dd HH:mm:ss" />
</td>
<td>
<n-time :time="r.updatedAt" format="y-MM-dd HH:mm:ss" />
</td>
<td>
<n-button
size="tiny"
@ -86,6 +90,7 @@ import {
NTable,
NPopconfirm,
NTag,
NTime,
} from "naive-ui";
import { AddOutline as AddIcon } from "@vicons/ionicons5";
import XPageHeader from "@/components/PageHeader.vue";

View File

@ -23,11 +23,15 @@
<x-description-item :label="t('fields.created_by')">
<x-anchor :url="`/system/users/${model.createdBy?.id}`">{{ model.createdBy?.name }}</x-anchor>
</x-description-item>
<x-description-item :label="t('fields.created_at')">{{ model.createdAt }}</x-description-item>
<x-description-item :label="t('fields.created_at')">
<n-time :time="model.createdAt" format="y-MM-dd HH:mm:ss" />
</x-description-item>
<x-description-item :label="t('fields.updated_by')">
<x-anchor :url="`/system/users/${model.updatedBy?.id}`">{{ model.updatedBy?.name }}</x-anchor>
</x-description-item>
<x-description-item :label="t('fields.updated_at')">{{ model.updatedAt }}</x-description-item>
<x-description-item :label="t('fields.updated_at')">
<n-time :time="model.updatedAt" format="y-MM-dd HH:mm:ss" />
</x-description-item>
</x-description>
<x-panel :title="t('fields.content')">
<x-code :code="model.content" language="yaml" />
@ -42,6 +46,7 @@ import {
NButton,
NSpace,
NIcon,
NTime,
} from "naive-ui";
import { ArrowBackCircleOutline as BackIcon } from "@vicons/ionicons5";
import XPageHeader from "@/components/PageHeader.vue";

View File

@ -70,7 +70,7 @@ import XPageHeader from "@/components/PageHeader.vue";
import userApi from "@/api/user";
import type { User } from "@/api/user";
import { useDataTable } from "@/utils/data-table";
import { renderButtons, renderLink, renderTag } from "@/utils/render";
import { renderButtons, renderLink, renderTag, renderTime } from "@/utils/render";
import { useI18n } from 'vue-i18n'
const { t } = useI18n()
@ -114,6 +114,7 @@ const columns = [
{
title: t('fields.updated_at'),
key: "updatedAt",
render: (row: User) => renderTime(row.updatedAt),
},
{
title: t('fields.actions'),

View File

@ -48,13 +48,17 @@
:url="`/system/users/${model.user.createdBy?.id}`"
>{{ model.user.createdBy?.name }}</x-anchor>
</x-description-item>
<x-description-item :label="t('fields.created_at')">{{ model.user.createdAt }}</x-description-item>
<x-description-item :label="t('fields.created_at')">
<n-time :time="model.user.createdAt" format="y-MM-dd HH:mm:ss" />
</x-description-item>
<x-description-item :label="t('fields.updated_by')">
<x-anchor
:url="`/system/users/${model.user.updatedBy?.id}`"
>{{ model.user.updatedBy?.name }}</x-anchor>
</x-description-item>
<x-description-item :label="t('fields.updated_at')">{{ model.user.updatedAt }}</x-description-item>
<x-description-item :label="t('fields.updated_at')">
<n-time :time="model.user.updatedAt" format="y-MM-dd HH:mm:ss" />
</x-description-item>
<x-description-item
:label="t('objects.role', 2)"
:span="2"
@ -80,6 +84,7 @@ import {
NTag,
NSpace,
NIcon,
NTime,
} from "naive-ui";
import { useRoute } from "vue-router";
import { ArrowBackCircleOutline as BackIcon } from "@vicons/ionicons5";