Skip to content

Commit 4cd203b

Browse files
committed
Merge branch 'master' of https://github.com/raghavyuva/nixopus
2 parents 1e559fd + 8cc2b28 commit 4cd203b

File tree

76 files changed

+3188
-711
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

76 files changed

+3188
-711
lines changed

CHANGELOG.md

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,48 @@
1+
# [0.1.0-alpha.54](https://github.com/raghavyuva/nixopus/compare/v0.1.0-alpha.53...v0.1.0-alpha.54) (2025-10-28)
2+
3+
4+
### Features
5+
6+
* configurable dashboard widgets with topbar ([#541](https://github.com/raghavyuva/nixopus/issues/541)) ([b150d69](https://github.com/raghavyuva/nixopus/commit/b150d6937db92fa288b40bffe54f4579a95f252a))
7+
8+
9+
10+
# [0.1.0-alpha.53](https://github.com/raghavyuva/nixopus/compare/v0.1.0-alpha.52...v0.1.0-alpha.53) (2025-10-27)
11+
12+
13+
### Features
14+
15+
* dashboard with draggable layout, charts, and extended system metrics ([#536](https://github.com/raghavyuva/nixopus/issues/536)) ([e13c24a](https://github.com/raghavyuva/nixopus/commit/e13c24aeefeb67fb64da1809bc23695f3076bf46))
16+
17+
18+
19+
# [0.1.0-alpha.52](https://github.com/raghavyuva/nixopus/compare/v0.1.0-alpha.51...v0.1.0-alpha.52) (2025-10-25)
20+
21+
22+
### Bug Fixes
23+
24+
* disable just in time compilation (JIT) of postgres ([#539](https://github.com/raghavyuva/nixopus/issues/539)) ([b2c35bd](https://github.com/raghavyuva/nixopus/commit/b2c35bd29349f565aa66617a164d687faf060778))
25+
26+
27+
28+
# [0.1.0-alpha.51](https://github.com/raghavyuva/nixopus/compare/v0.1.0-alpha.50...v0.1.0-alpha.51) (2025-10-25)
29+
30+
31+
### Bug Fixes
32+
33+
* menu for closed sidebar items on hover ([#526](https://github.com/raghavyuva/nixopus/issues/526)) ([ca423ed](https://github.com/raghavyuva/nixopus/commit/ca423ed2e3a53c5a1e96048914316399274afcf0))
34+
35+
36+
37+
# [0.1.0-alpha.50](https://github.com/raghavyuva/nixopus/compare/v0.1.0-alpha.49...v0.1.0-alpha.50) (2025-10-25)
38+
39+
40+
### Features
41+
42+
* update command to not reference .env ([af13242](https://github.com/raghavyuva/nixopus/commit/af13242c25e2cc8b02e965e8b3645df84e372c9b))
43+
44+
45+
146
# [0.1.0-alpha.49](https://github.com/raghavyuva/nixopus/compare/v0.1.0-alpha.48...v0.1.0-alpha.49) (2025-10-22)
247

348

@@ -729,3 +774,4 @@
729774
* **vulnerability:** fixes CVE-2024-21538 (HIGH) and CVE-2025-30204 (HIGH) ([c25e0c7](https://github.com/raghavyuva/nixopus/commit/c25e0c7fa9ad742a25f95ae7e2a780a881cad573))
730775

731776

777+

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55

66
<p align="center">
7-
Open Source Server management platform with Terminal integration, and Self Hosting capabilities.
7+
Open Source alternative to vercel, heroku, netlify with Terminal integration, and Self Hosting capabilities.
88
</p>
99

1010
<p align="center">

api/api/versions.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
{
44
"version": "v1",
55
"status": "active",
6-
"release_date": "2025-10-22T16:45:33.662757+05:30",
6+
"release_date": "2025-10-28T22:04:41.913593+05:30",
77
"end_of_life": "0001-01-01T00:00:00Z",
88
"changes": [
99
"Initial API version"

api/internal/features/dashboard/system_stats.go

Lines changed: 99 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import (
1313
"github.com/shirou/gopsutil/v3/disk"
1414
"github.com/shirou/gopsutil/v3/host"
1515
"github.com/shirou/gopsutil/v3/mem"
16+
"github.com/shirou/gopsutil/v3/net"
1617
)
1718

1819
const (
@@ -31,6 +32,8 @@ func formatBytes(bytes uint64, unit string) string {
3132
}
3233
}
3334

35+
// TODO: Add support for multi server management
36+
// solution: create a bridge between the gopsutil and the ssh client
3437
func (m *DashboardMonitor) GetSystemStats() {
3538
osType, err := m.getCommandOutput("uname -s")
3639
if err != nil {
@@ -42,24 +45,49 @@ func (m *DashboardMonitor) GetSystemStats() {
4245
stats := SystemStats{
4346
OSType: osType,
4447
Timestamp: time.Now(),
48+
CPU: CPUStats{PerCore: []CPUCore{}},
4549
Memory: MemoryStats{},
4650
Load: LoadStats{},
4751
Disk: DiskStats{AllMounts: []DiskMount{}},
52+
Network: NetworkStats{Interfaces: []NetworkInterface{}},
4853
}
4954

55+
if hostname, err := m.getCommandOutput("hostname"); err == nil {
56+
stats.Hostname = strings.TrimSpace(hostname)
57+
}
58+
59+
if kernelVersion, err := m.getCommandOutput("uname -r"); err == nil {
60+
stats.KernelVersion = strings.TrimSpace(kernelVersion)
61+
}
62+
63+
if architecture, err := m.getCommandOutput("uname -m"); err == nil {
64+
stats.Architecture = strings.TrimSpace(architecture)
65+
}
66+
67+
var uptime string
5068
if hostInfo, err := host.Info(); err == nil {
51-
stats.Load.Uptime = time.Duration(hostInfo.Uptime * uint64(time.Second)).String()
69+
uptime = time.Duration(hostInfo.Uptime * uint64(time.Second)).String()
5270
}
5371

5472
if loadAvg, err := m.getCommandOutput("uptime"); err == nil {
5573
loadAvgStr := strings.TrimSpace(loadAvg)
5674
stats.Load = parseLoadAverage(loadAvgStr)
5775
}
5876

77+
stats.Load.Uptime = uptime
78+
5979
if cpuInfo, err := cpu.Info(); err == nil && len(cpuInfo) > 0 {
6080
stats.CPUInfo = cpuInfo[0].ModelName
6181
}
6282

83+
if stats.CPUCores == 0 {
84+
if coreCount, err := cpu.Counts(true); err == nil {
85+
stats.CPUCores = coreCount
86+
}
87+
}
88+
89+
stats.CPU = m.getCPUStats()
90+
6391
if memInfo, err := mem.VirtualMemory(); err == nil {
6492
stats.Memory = MemoryStats{
6593
Total: float64(memInfo.Total) / bytesInGB,
@@ -103,6 +131,8 @@ func (m *DashboardMonitor) GetSystemStats() {
103131
stats.Disk = diskStats
104132
}
105133

134+
stats.Network = m.getNetworkStats()
135+
106136
m.Broadcast(string(GetSystemStats), stats)
107137
}
108138

@@ -126,6 +156,74 @@ func parseLoadAverage(loadStr string) LoadStats {
126156
return loadStats
127157
}
128158

159+
func (m *DashboardMonitor) getCPUStats() CPUStats {
160+
cpuStats := CPUStats{
161+
Overall: 0.0,
162+
PerCore: []CPUCore{},
163+
}
164+
165+
perCorePercent, err := cpu.Percent(time.Second, true)
166+
if err == nil && len(perCorePercent) > 0 {
167+
cpuStats.PerCore = make([]CPUCore, len(perCorePercent))
168+
var totalUsage float64 = 0
169+
170+
for i, usage := range perCorePercent {
171+
cpuStats.PerCore[i] = CPUCore{
172+
CoreID: i,
173+
Usage: usage,
174+
}
175+
totalUsage += usage
176+
}
177+
178+
cpuStats.Overall = totalUsage / float64(len(perCorePercent))
179+
} else {
180+
181+
if overallPercent, err := cpu.Percent(time.Second, false); err == nil && len(overallPercent) > 0 {
182+
cpuStats.Overall = overallPercent[0]
183+
}
184+
}
185+
186+
return cpuStats
187+
}
188+
189+
func (m *DashboardMonitor) getNetworkStats() NetworkStats {
190+
networkStats := NetworkStats{
191+
Interfaces: []NetworkInterface{},
192+
}
193+
194+
if ioCounters, err := net.IOCounters(true); err == nil {
195+
var totalSent, totalRecv, totalPacketsSent, totalPacketsRecv uint64
196+
197+
for _, counter := range ioCounters {
198+
interfaces := NetworkInterface{
199+
Name: counter.Name,
200+
BytesSent: counter.BytesSent,
201+
BytesRecv: counter.BytesRecv,
202+
PacketsSent: counter.PacketsSent,
203+
PacketsRecv: counter.PacketsRecv,
204+
ErrorIn: counter.Errin,
205+
ErrorOut: counter.Errout,
206+
DropIn: counter.Dropin,
207+
DropOut: counter.Dropout,
208+
}
209+
210+
networkStats.Interfaces = append(networkStats.Interfaces, interfaces)
211+
212+
totalSent += counter.BytesSent
213+
totalRecv += counter.BytesRecv
214+
totalPacketsSent += counter.PacketsSent
215+
totalPacketsRecv += counter.PacketsRecv
216+
}
217+
218+
networkStats.TotalBytesSent = totalSent
219+
networkStats.TotalBytesRecv = totalRecv
220+
networkStats.TotalPacketsSent = totalPacketsSent
221+
networkStats.TotalPacketsRecv = totalPacketsRecv
222+
}
223+
224+
return networkStats
225+
}
226+
129227
func (m *DashboardMonitor) getCommandOutput(cmd string) (string, error) {
130228
session, err := m.client.NewSession()
131229
if err != nil {

api/internal/features/dashboard/types.go

Lines changed: 44 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -43,12 +43,28 @@ type DashboardMonitor struct {
4343
}
4444

4545
type SystemStats struct {
46-
OSType string `json:"os_type"`
47-
CPUInfo string `json:"cpu_info"`
48-
Memory MemoryStats `json:"memory"`
49-
Load LoadStats `json:"load"`
50-
Disk DiskStats `json:"disk"`
51-
Timestamp time.Time `json:"timestamp"`
46+
OSType string `json:"os_type"`
47+
Hostname string `json:"hostname"`
48+
CPUInfo string `json:"cpu_info"`
49+
CPUCores int `json:"cpu_cores"`
50+
CPU CPUStats `json:"cpu"`
51+
Memory MemoryStats `json:"memory"`
52+
Load LoadStats `json:"load"`
53+
Disk DiskStats `json:"disk"`
54+
Network NetworkStats `json:"network"`
55+
KernelVersion string `json:"kernel_version"`
56+
Architecture string `json:"architecture"`
57+
Timestamp time.Time `json:"timestamp"`
58+
}
59+
60+
type CPUCore struct {
61+
CoreID int `json:"core_id"`
62+
Usage float64 `json:"usage"`
63+
}
64+
65+
type CPUStats struct {
66+
Overall float64 `json:"overall"`
67+
PerCore []CPUCore `json:"per_core"`
5268
}
5369

5470
type MemoryStats struct {
@@ -82,3 +98,25 @@ type DiskStats struct {
8298
MountPoint string `json:"mountPoint"`
8399
AllMounts []DiskMount `json:"allMounts"`
84100
}
101+
102+
type NetworkInterface struct {
103+
Name string `json:"name"`
104+
BytesSent uint64 `json:"bytesSent"`
105+
BytesRecv uint64 `json:"bytesRecv"`
106+
PacketsSent uint64 `json:"packetsSent"`
107+
PacketsRecv uint64 `json:"packetsRecv"`
108+
ErrorIn uint64 `json:"errorIn"`
109+
ErrorOut uint64 `json:"errorOut"`
110+
DropIn uint64 `json:"dropIn"`
111+
DropOut uint64 `json:"dropOut"`
112+
}
113+
114+
type NetworkStats struct {
115+
TotalBytesSent uint64 `json:"totalBytesSent"`
116+
TotalBytesRecv uint64 `json:"totalBytesRecv"`
117+
TotalPacketsSent uint64 `json:"totalPacketsSent"`
118+
TotalPacketsRecv uint64 `json:"totalPacketsRecv"`
119+
Interfaces []NetworkInterface `json:"interfaces"`
120+
UploadSpeed float64 `json:"uploadSpeed"`
121+
DownloadSpeed float64 `json:"downloadSpeed"`
122+
}

api/internal/features/deploy/proxy/caddy.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -80,12 +80,12 @@ func (c *Caddy) Serve() error {
8080

8181
// Replace the route for our domain
8282
server := config.Apps.HTTP.Servers["nixopus"]
83-
83+
8484
// Ensure server has listen directive
8585
if len(server.Listen) == 0 {
8686
server.Listen = []string{":80"}
8787
}
88-
88+
8989
var newRoutes []Route
9090
for _, route := range server.Routes {
9191
if len(route.Match) > 0 && len(route.Match[0].Host) > 0 && route.Match[0].Host[0] != c.Domain {

cli/app/commands/service/base.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,14 +39,13 @@ def build_command(action: str, name: str = "all", env_file: str = None, compose_
3939
cmd = ["docker", "compose"]
4040
if compose_file:
4141
cmd.extend(["-f", compose_file])
42+
if env_file:
43+
cmd.extend(["--env-file", env_file])
4244
cmd.append(action)
4345

4446
if action == "up" and kwargs.get("detach", False):
4547
cmd.append("-d")
4648

47-
if env_file:
48-
cmd.extend(["--env-file", env_file])
49-
5049
if name != "all":
5150
cmd.append(name)
5251

@@ -58,11 +57,14 @@ def build_cleanup_command(
5857
remove_images: str = "all",
5958
remove_volumes: bool = True,
6059
remove_orphans: bool = True,
60+
env_file: str = None,
6161
) -> list[str]:
6262
"""Build a docker compose cleanup command (down with prune flags)."""
6363
cmd = ["docker", "compose"]
6464
if compose_file:
6565
cmd.extend(["-f", compose_file])
66+
if env_file:
67+
cmd.extend(["--env-file", env_file])
6668
cmd.append("down")
6769

6870
if remove_images:

cli/app/commands/update/run.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,13 +28,12 @@ def __init__(self, logger: Logger):
2828
def run(self):
2929
compose_file = self.config.get_yaml_value(DEFAULT_COMPOSE_FILE)
3030
compose_file_path = self.config.get_yaml_value(NIXOPUS_CONFIG_DIR) + "/" + compose_file
31-
env_file_path = self.config.get_yaml_value(NIXOPUS_CONFIG_DIR) + "/.env"
3231
self.logger.info(updating_nixopus)
3332

3433
docker_service = BaseDockerService(self.logger, "pull")
3534

3635
self.logger.debug(pulling_latest_images)
37-
success, output = docker_service.execute_services(compose_file=compose_file_path, env_file=env_file_path)
36+
success, output = docker_service.execute_services(compose_file=compose_file_path)
3837

3938
if not success:
4039
self.logger.error(failed_to_pull_images.format(error=output))
@@ -44,7 +43,7 @@ def run(self):
4443

4544
docker_service_up = BaseDockerService(self.logger, "up")
4645
self.logger.debug(starting_services)
47-
success, output = docker_service_up.execute_services(compose_file=compose_file_path, env_file=env_file_path, detach=True)
46+
success, output = docker_service_up.execute_services(compose_file=compose_file_path, detach=True)
4847

4948
if not success:
5049
self.logger.error(failed_to_start_services.format(error=output))

cli/pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[tool.poetry]
22
name = "nixopus"
3-
version = "0.1.18"
3+
version = "0.1.20"
44
description = "A CLI for Nixopus"
55
authors = ["Nixopus <[email protected]>"]
66
readme = "README.md"

docker-compose.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ services:
3232
- POSTGRES_PASSWORD=${PASSWORD}
3333
- POSTGRES_DB=${DB_NAME}
3434
- POSTGRES_HOST_AUTH_METHOD=trust
35-
35+
command: ["postgres", "-c", "jit=0"]
3636
ports:
3737
- "${DB_PORT:-5432}:5432"
3838
volumes:

0 commit comments

Comments
 (0)