@@ -2,94 +2,247 @@ package controller
2
2
3
3
import (
4
4
"net/http"
5
+ "sort"
6
+ "strconv"
7
+ "strings"
5
8
9
+ "github.com/docker/docker/api/types/container"
10
+ "github.com/docker/docker/api/types/filters"
6
11
"github.com/go-fuego/fuego"
7
- "github.com/raghavyuva/nixopus-api/internal/features/container/types"
12
+ containertypes "github.com/raghavyuva/nixopus-api/internal/features/container/types"
8
13
"github.com/raghavyuva/nixopus-api/internal/features/logger"
9
14
shared_types "github.com/raghavyuva/nixopus-api/internal/types"
10
15
)
11
16
12
- func (c * ContainerController ) ListContainers (f fuego.ContextNoBody ) (* shared_types.Response , error ) {
13
- containers , err := c .dockerService .ListAllContainers ()
17
+ func (c * ContainerController ) ListContainers (fuegoCtx fuego.ContextNoBody ) (* shared_types.Response , error ) {
18
+ // normalize query params
19
+ params := parseContainerListParams (fuegoCtx .Request ())
20
+
21
+ // Get pre-filtered summaries from Docker
22
+ containers , err := c .dockerService .ListContainers (container.ListOptions {
23
+ All : true ,
24
+ Filters : buildDockerFilters (params ),
25
+ })
14
26
if err != nil {
15
27
c .logger .Log (logger .Error , err .Error (), "" )
16
28
return nil , fuego.HTTPError {
17
29
Err : err ,
18
30
Status : http .StatusInternalServerError ,
19
31
}
20
32
}
33
+ // Build summaries, then search/sort/paginate
34
+ rows := summarizeContainers (containers )
35
+ pageRows , totalCount := applySearchSortPaginate (rows , params )
21
36
22
- var result []types.Container
23
- for _ , container := range containers {
24
- containerInfo , err := c .dockerService .GetContainerById (container .ID )
25
- if err != nil {
26
- c .logger .Log (logger .Error , "Error inspecting container" , container .ID )
27
- continue
28
- }
37
+ result := c .appendContainerInfo (pageRows , containers )
29
38
30
- containerData := types.Container {
31
- ID : container .ID ,
32
- Name : "" ,
33
- Image : container .Image ,
34
- Status : container .Status ,
35
- State : container .State ,
36
- Created : containerInfo .Created ,
37
- Labels : container .Labels ,
38
- Command : "" ,
39
- IPAddress : containerInfo .NetworkSettings .IPAddress ,
40
- HostConfig : types.HostConfig {
41
- Memory : containerInfo .HostConfig .Memory ,
42
- MemorySwap : containerInfo .HostConfig .MemorySwap ,
43
- CPUShares : containerInfo .HostConfig .CPUShares ,
44
- },
45
- }
39
+ return & shared_types.Response {
40
+ Status : "success" ,
41
+ Message : "Containers fetched successfully" ,
42
+ Data : map [string ]interface {}{
43
+ "containers" : result ,
44
+ "total_count" : totalCount ,
45
+ "page" : params .Page ,
46
+ "page_size" : params .PageSize ,
47
+ "sort_by" : params .SortBy ,
48
+ "sort_order" : params .SortOrder ,
49
+ "search" : params .Search ,
50
+ "status" : params .Status ,
51
+ "name" : params .Name ,
52
+ "image" : params .Image ,
53
+ },
54
+ }, nil
55
+ }
56
+
57
+ func parseContainerListParams (r * http.Request ) containertypes.ContainerListParams {
58
+ q := r .URL .Query ()
59
+ pageStr := q .Get ("page" )
60
+ pageSizeStr := q .Get ("page_size" )
61
+ sortBy := strings .ToLower (strings .TrimSpace (q .Get ("sort_by" )))
62
+ sortOrder := strings .ToLower (strings .TrimSpace (q .Get ("sort_order" )))
63
+
64
+ if pageStr == "" {
65
+ pageStr = "1"
66
+ }
67
+ if pageSizeStr == "" {
68
+ pageSizeStr = "10"
69
+ }
70
+ if sortBy == "" {
71
+ sortBy = "name"
72
+ }
73
+ if sortOrder == "" {
74
+ sortOrder = "asc"
75
+ }
76
+
77
+ page , _ := strconv .Atoi (pageStr )
78
+ if page < 1 {
79
+ page = 1
80
+ }
81
+ pageSize , _ := strconv .Atoi (pageSizeStr )
82
+ if pageSize < 1 {
83
+ pageSize = 10
84
+ }
85
+
86
+ return containertypes.ContainerListParams {
87
+ Page : page ,
88
+ PageSize : pageSize ,
89
+ Search : strings .TrimSpace (q .Get ("search" )),
90
+ SortBy : sortBy ,
91
+ SortOrder : sortOrder ,
92
+ Status : strings .TrimSpace (q .Get ("status" )),
93
+ Name : strings .TrimSpace (q .Get ("name" )),
94
+ Image : strings .TrimSpace (q .Get ("image" )),
95
+ }
96
+ }
97
+
98
+ func buildDockerFilters (p containertypes.ContainerListParams ) filters.Args {
99
+ f := filters .NewArgs ()
100
+ if p .Status != "" {
101
+ f .Add ("status" , p .Status )
102
+ }
103
+ if p .Name != "" {
104
+ f .Add ("name" , p .Name )
105
+ }
106
+ if p .Image != "" {
107
+ f .Add ("ancestor" , p .Image )
108
+ }
109
+ return f
110
+ }
46
111
47
- if container .Names != nil && len (container .Names ) > 0 {
48
- name := container .Names [0 ]
49
- if len (name ) > 1 {
50
- containerData .Name = name [1 :]
112
+ func summarizeContainers (summaries []container.Summary ) []containertypes.ContainerListRow {
113
+ rows := make ([]containertypes.ContainerListRow , 0 , len (summaries ))
114
+ for _ , csum := range summaries {
115
+ name := ""
116
+ if len (csum .Names ) > 0 {
117
+ n := csum .Names [0 ]
118
+ if len (n ) > 1 {
119
+ name = n [1 :]
51
120
} else {
52
- containerData . Name = name
121
+ name = n
53
122
}
54
123
}
124
+ rows = append (rows , containertypes.ContainerListRow {
125
+ ID : csum .ID ,
126
+ Name : name ,
127
+ Image : csum .Image ,
128
+ Status : csum .Status ,
129
+ State : csum .State ,
130
+ Created : csum .Created ,
131
+ Labels : csum .Labels ,
132
+ })
133
+ }
134
+ return rows
135
+ }
55
136
56
- if containerInfo .Config != nil && containerInfo .Config .Cmd != nil && len (containerInfo .Config .Cmd ) > 0 {
57
- containerData .Command = containerInfo .Config .Cmd [0 ]
137
+ func applySearchSortPaginate (rows []containertypes.ContainerListRow , p containertypes.ContainerListParams ) ([]containertypes.ContainerListRow , int ) {
138
+ if p .Search != "" {
139
+ lower := strings .ToLower (p .Search )
140
+ filtered := make ([]containertypes.ContainerListRow , 0 , len (rows ))
141
+ for _ , r := range rows {
142
+ if strings .Contains (strings .ToLower (r .Name ), lower ) ||
143
+ strings .Contains (strings .ToLower (r .Image ), lower ) ||
144
+ strings .Contains (strings .ToLower (r .Status ), lower ) {
145
+ filtered = append (filtered , r )
146
+ }
58
147
}
148
+ rows = filtered
149
+ }
59
150
60
- for _ , port := range container .Ports {
61
- containerData .Ports = append (containerData .Ports , types.Port {
62
- PrivatePort : int (port .PrivatePort ),
63
- PublicPort : int (port .PublicPort ),
64
- Type : port .Type ,
65
- })
151
+ sort .SliceStable (rows , func (i , j int ) bool {
152
+ switch p .SortBy {
153
+ case "status" :
154
+ a := strings .ToLower (rows [i ].Status )
155
+ b := strings .ToLower (rows [j ].Status )
156
+ if p .SortOrder == "desc" {
157
+ return a > b
158
+ }
159
+ return a < b
160
+ case "name" :
161
+ a := strings .ToLower (rows [i ].Name )
162
+ b := strings .ToLower (rows [j ].Name )
163
+ if p .SortOrder == "desc" {
164
+ return a > b
165
+ }
166
+ return a < b
167
+ default :
168
+ ai := rows [i ].Created
169
+ aj := rows [j ].Created
170
+ if p .SortOrder == "desc" {
171
+ return ai > aj
172
+ }
173
+ return ai < aj
66
174
}
175
+ })
67
176
68
- for _ , mount := range containerInfo .Mounts {
69
- containerData .Mounts = append (containerData .Mounts , types.Mount {
70
- Type : string (mount .Type ),
71
- Source : mount .Source ,
72
- Destination : mount .Destination ,
73
- Mode : mount .Mode ,
177
+ totalCount := len (rows )
178
+ start := (p .Page - 1 ) * p .PageSize
179
+ if start > totalCount {
180
+ start = totalCount
181
+ }
182
+ end := start + p .PageSize
183
+ if end > totalCount {
184
+ end = totalCount
185
+ }
186
+ return rows [start :end ], totalCount
187
+ }
188
+
189
+ func (c * ContainerController ) appendContainerInfo (pageRows []containertypes.ContainerListRow , summaries []container.Summary ) []containertypes.Container {
190
+ result := make ([]containertypes.Container , 0 , len (pageRows ))
191
+ for _ , r := range pageRows {
192
+ info , err := c .dockerService .GetContainerById (r .ID )
193
+ if err != nil {
194
+ c .logger .Log (logger .Error , "Error inspecting container" , r .ID )
195
+ continue
196
+ }
197
+ cd := containertypes.Container {
198
+ ID : r .ID ,
199
+ Name : r .Name ,
200
+ Image : r .Image ,
201
+ Status : r .Status ,
202
+ State : r .State ,
203
+ Created : info .Created ,
204
+ Labels : r .Labels ,
205
+ Command : "" ,
206
+ IPAddress : info .NetworkSettings .IPAddress ,
207
+ HostConfig : containertypes.HostConfig {
208
+ Memory : info .HostConfig .Memory ,
209
+ MemorySwap : info .HostConfig .MemorySwap ,
210
+ CPUShares : info .HostConfig .CPUShares ,
211
+ },
212
+ }
213
+ if info .Config != nil && info .Config .Cmd != nil && len (info .Config .Cmd ) > 0 {
214
+ cd .Command = info .Config .Cmd [0 ]
215
+ }
216
+ for _ , s := range summaries {
217
+ if s .ID == r .ID {
218
+ for _ , p := range s .Ports {
219
+ cd .Ports = append (cd .Ports , containertypes.Port {
220
+ PrivatePort : int (p .PrivatePort ),
221
+ PublicPort : int (p .PublicPort ),
222
+ Type : p .Type ,
223
+ })
224
+ }
225
+ break
226
+ }
227
+ }
228
+ for _ , m := range info .Mounts {
229
+ cd .Mounts = append (cd .Mounts , containertypes.Mount {
230
+ Type : string (m .Type ),
231
+ Source : m .Source ,
232
+ Destination : m .Destination ,
233
+ Mode : m .Mode ,
74
234
})
75
235
}
76
-
77
- for name , network := range containerInfo .NetworkSettings .Networks {
78
- containerData .Networks = append (containerData .Networks , types.Network {
236
+ for name , network := range info .NetworkSettings .Networks {
237
+ cd .Networks = append (cd .Networks , containertypes.Network {
79
238
Name : name ,
80
239
IPAddress : network .IPAddress ,
81
240
Gateway : network .Gateway ,
82
241
MacAddress : network .MacAddress ,
83
242
Aliases : network .Aliases ,
84
243
})
85
244
}
86
-
87
- result = append (result , containerData )
245
+ result = append (result , cd )
88
246
}
89
-
90
- return & shared_types.Response {
91
- Status : "success" ,
92
- Message : "Containers fetched successfully" ,
93
- Data : result ,
94
- }, nil
247
+ return result
95
248
}
0 commit comments