chore: bump version to v4.3.8

Emergency release to fix critical issues in v4.3.7:
- Install script now correctly installs binary to /opt/pulse/bin/pulse
- Password changes no longer require sudo (addresses #317)

These fixes restore basic functionality for new installations
and Docker deployments.
This commit is contained in:
Pulse Monitor 2025-08-14 22:00:12 +00:00
parent 84382dc1f4
commit 527f53ee32
3 changed files with 67 additions and 35 deletions

View file

@ -1 +1 @@
4.3.7
4.3.8

View file

@ -197,26 +197,26 @@ download_pulse() {
mkdir -p "$TEMP_EXTRACT"
tar -xzf pulse.tar.gz -C "$TEMP_EXTRACT"
# Ensure install directory exists before copying
mkdir -p "$INSTALL_DIR"
# Ensure install directory and bin subdirectory exist
mkdir -p "$INSTALL_DIR/bin"
# Copy Pulse binary from bin/ directory (standard structure as of v4.3.1)
# Copy Pulse binary to the correct location (/opt/pulse/bin/pulse)
if [[ -f "$TEMP_EXTRACT/bin/pulse" ]]; then
cp "$TEMP_EXTRACT/bin/pulse" "$INSTALL_DIR/pulse"
cp "$TEMP_EXTRACT/bin/pulse" "$INSTALL_DIR/bin/pulse"
elif [[ -f "$TEMP_EXTRACT/pulse" ]]; then
# Fallback for old archives (pre-v4.3.1)
cp "$TEMP_EXTRACT/pulse" "$INSTALL_DIR/pulse"
cp "$TEMP_EXTRACT/pulse" "$INSTALL_DIR/bin/pulse"
else
print_error "Pulse binary not found in archive"
exit 1
fi
chmod +x "$INSTALL_DIR/pulse"
chmod +x "$INSTALL_DIR/bin/pulse"
chown -R pulse:pulse "$INSTALL_DIR"
# Create symlink in /usr/local/bin for PATH convenience
ln -sf "$INSTALL_DIR/pulse" /usr/local/bin/pulse
print_success "Pulse binary installed to $INSTALL_DIR/pulse"
ln -sf "$INSTALL_DIR/bin/pulse" /usr/local/bin/pulse
print_success "Pulse binary installed to $INSTALL_DIR/bin/pulse"
print_success "Symlink created at /usr/local/bin/pulse"
# Copy VERSION file if present
@ -254,7 +254,7 @@ Type=simple
User=pulse
Group=pulse
WorkingDirectory=$INSTALL_DIR
ExecStart=$INSTALL_DIR/pulse
ExecStart=$INSTALL_DIR/bin/pulse
Restart=always
RestartSec=3
StandardOutput=journal

View file

@ -892,45 +892,77 @@ PULSE_AUTH_PASS='%s'
})
} else {
// For systemd installations, use the existing script method
scriptPath := "/opt/pulse/scripts/change-password.sh"
cmd := exec.Command("sudo", scriptPath, hashedPassword)
_, err = cmd.CombinedOutput()
if err != nil {
log.Error().Err(err).Msg("Failed to change password via script")
// For non-Docker (systemd/manual), save to .env file
envPath := filepath.Join(r.config.ConfigPath, ".env")
if r.config.ConfigPath == "" {
envPath = "/etc/pulse/.env"
}
// Read existing .env file to preserve other settings
envContent := ""
existingContent, err := os.ReadFile(envPath)
if err == nil {
// Parse and update existing content
scanner := bufio.NewScanner(strings.NewReader(string(existingContent)))
for scanner.Scan() {
line := scanner.Text()
if line == "" || strings.HasPrefix(line, "#") {
envContent += line + "\n"
continue
}
// Update password line, keep others
if strings.HasPrefix(line, "PULSE_AUTH_PASS=") {
envContent += fmt.Sprintf("PULSE_AUTH_PASS='%s'\n", hashedPassword)
} else {
envContent += line + "\n"
}
}
} else {
// Create new .env if doesn't exist
envContent = fmt.Sprintf(`# Auto-generated by Pulse password change
# Generated on %s
PULSE_AUTH_USER='%s'
PULSE_AUTH_PASS='%s'
`, time.Now().Format(time.RFC3339), r.config.AuthUser, hashedPassword)
if r.config.APIToken != "" {
envContent += fmt.Sprintf("API_TOKEN='%s'\n", r.config.APIToken)
}
}
// Try to write the .env file
if err := os.WriteFile(envPath, []byte(envContent), 0600); err != nil {
log.Error().Err(err).Str("path", envPath).Msg("Failed to write .env file")
writeErrorResponse(w, http.StatusInternalServerError, "config_error",
"Failed to save new password", nil)
"Failed to save new password. You may need to update the password manually.", nil)
return
}
// Update the running config with the HASHED password
// Update the running config
r.config.AuthPass = hashedPassword
log.Info().Msg("Password changed successfully")
// Invalidate all sessions for this user (forces re-login with new password)
// Invalidate all sessions
InvalidateUserSessions(r.config.AuthUser)
// Audit log password change
// Audit log
LogAuditEvent("password_change", r.config.AuthUser, GetClientIP(req), req.URL.Path, true, "Password changed")
// Return success
// Detect service name for restart instructions
serviceName := "pulse"
if _, err := os.Stat("/etc/systemd/system/pulse-backend.service"); err == nil {
serviceName = "pulse-backend"
}
// Return success with manual restart instructions
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(map[string]interface{}{
"success": true,
"message": "Password changed successfully. Service will restart.",
"message": fmt.Sprintf("Password changed. Restart the service to apply: sudo systemctl restart %s", serviceName),
"requiresRestart": true,
"serviceName": serviceName,
})
// Trigger service restart in background for systemd
go func() {
time.Sleep(2 * time.Second)
log.Info().Msg("Restarting service to apply new password")
// Use sudo to restart the service
cmd := exec.Command("sudo", "systemctl", "restart", "pulse-backend")
if err := cmd.Run(); err != nil {
log.Error().Err(err).Msg("Failed to restart service after password change")
}
}()
}
}