Skip to content

Commit 7e60b05

Browse files
committed
Merge branch 'master' of https://github.com/MachDatum/ThingConnect.Pulse into history-loading
2 parents 0bb2e00 + 81d8487 commit 7e60b05

File tree

10 files changed

+372
-267
lines changed

10 files changed

+372
-267
lines changed

ThingConnect.Pulse.Server/ThingConnect.Pulse.Server.csproj

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,5 +54,10 @@
5454
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
5555
</None>
5656
</ItemGroup>
57+
58+
<!-- Prevent auto-generation of web.config during publish -->
59+
<PropertyGroup>
60+
<IsTransformWebConfigDisabled>true</IsTransformWebConfigDisabled>
61+
</PropertyGroup>
5762

5863
</Project>

ThingConnect.Pulse.Server/appsettings.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@
4545
}
4646
},
4747
"AllowedHosts": "*",
48+
"Urls": "http://[::]:8090",
4849
"ConnectionStrings": {
4950
"DefaultConnection": "Data Source=C:\\ProgramData\\ThingConnect.Pulse\\data\\pulse.db"
5051
},

favicon.ico

108 KB
Binary file not shown.

install-service.ps1

Lines changed: 34 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,29 @@ $DataDir = "$ProgramDataRoot\data"
1818

1919
# Get the current script directory
2020
$ScriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path
21-
$BinaryPath = Join-Path $ScriptDir "ThingConnect.Pulse.Server\bin\Debug\net8.0\ThingConnect.Pulse.Server.exe"
21+
22+
# Determine binary path - check if we're in an installed location or development
23+
$InstalledBinaryPath = Join-Path $ScriptDir "ThingConnect.Pulse.Server.exe"
24+
$DevBinaryPath = Join-Path $ScriptDir "ThingConnect.Pulse.Server\bin\Release\net8.0\ThingConnect.Pulse.Server.exe"
25+
$DevDebugBinaryPath = Join-Path $ScriptDir "ThingConnect.Pulse.Server\bin\Debug\net8.0\ThingConnect.Pulse.Server.exe"
26+
27+
if (Test-Path $InstalledBinaryPath) {
28+
$BinaryPath = $InstalledBinaryPath
29+
Write-Host "Using installed binary: $BinaryPath" -ForegroundColor Cyan
30+
} elseif (Test-Path $DevBinaryPath) {
31+
$BinaryPath = $DevBinaryPath
32+
Write-Host "Using development Release binary: $BinaryPath" -ForegroundColor Cyan
33+
} elseif (Test-Path $DevDebugBinaryPath) {
34+
$BinaryPath = $DevDebugBinaryPath
35+
Write-Host "Using development Debug binary: $BinaryPath" -ForegroundColor Cyan
36+
} else {
37+
Write-Error "Could not find ThingConnect.Pulse.Server.exe in any expected location"
38+
Write-Host "Searched locations:" -ForegroundColor Yellow
39+
Write-Host " - $InstalledBinaryPath" -ForegroundColor Gray
40+
Write-Host " - $DevBinaryPath" -ForegroundColor Gray
41+
Write-Host " - $DevDebugBinaryPath" -ForegroundColor Gray
42+
exit 1
43+
}
2244

2345
# Ensure we have admin privileges
2446
if (-NOT ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator")) {
@@ -78,17 +100,18 @@ switch ($Action.ToLower()) {
78100
# Create directory structure
79101
Initialize-DirectoryStructure
80102

81-
# Build the application first
82-
Write-Host "Building application..." -ForegroundColor Yellow
83-
dotnet build ThingConnect.Pulse.Server --configuration Release
84-
if ($LASTEXITCODE -ne 0) {
85-
Write-Error "Build failed!"
86-
exit 1
103+
# Only build if we're in development mode (not using installed binary)
104+
if ($BinaryPath -ne $InstalledBinaryPath) {
105+
Write-Host "Building application..." -ForegroundColor Yellow
106+
dotnet build ThingConnect.Pulse.Server --configuration Release
107+
if ($LASTEXITCODE -ne 0) {
108+
Write-Error "Build failed!"
109+
exit 1
110+
}
111+
} else {
112+
Write-Host "Using pre-built installed binary" -ForegroundColor Cyan
87113
}
88114

89-
# Update binary path for Release build
90-
$BinaryPath = Join-Path $ScriptDir "ThingConnect.Pulse.Server\bin\Release\net8.0\ThingConnect.Pulse.Server.exe"
91-
92115
# Stop service if it exists
93116
$service = Get-Service -Name $ServiceName -ErrorAction SilentlyContinue
94117
if ($service) {
@@ -109,7 +132,7 @@ switch ($Action.ToLower()) {
109132
Write-Host "Configuration: $ConfigDir\config.yaml" -ForegroundColor Cyan
110133
Write-Host "Database: $DataDir\pulse.db" -ForegroundColor Cyan
111134
Write-Host "Logs: $LogsDir\" -ForegroundColor Cyan
112-
Write-Host "Web interface: http://localhost:8080" -ForegroundColor Cyan
135+
Write-Host "Web interface: http://localhost:8090" -ForegroundColor Cyan
113136
}
114137

115138
"uninstall" {

setup.iss

Lines changed: 43 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@
33

44
#define AppName "ThingConnect Pulse"
55
#define AppVersion "1.0.0"
6-
#define AppPublisher "MachDatum"
7-
#define AppURL "https://github.com/MachDatum/ThingConnect.Pulse"
6+
#define AppPublisher "ThingConnect"
7+
#define AppURL "https://thingconnect.io/pulse"
88
#define AppExeName "ThingConnect.Pulse.Server.exe"
99
#define ServiceName "ThingConnectPulseSvc"
1010
#define ServiceDisplayName "ThingConnect Pulse Server"
@@ -23,7 +23,7 @@ DefaultDirName={autopf}\ThingConnect.Pulse
2323
DefaultGroupName={#AppName}
2424
DisableProgramGroupPage=yes
2525
OutputDir=installer
26-
OutputBaseFilename=ThingConnect.Pulse.Setup
26+
OutputBaseFilename=ThingConnect Pulse - Setup {#AppVersion}
2727
Compression=lzma
2828
SolidCompression=yes
2929
WizardStyle=modern
@@ -35,6 +35,12 @@ DisableReadyPage=no
3535
MinVersion=10.0.17763
3636
ArchitecturesAllowed=x64
3737

38+
; Enable installation logging
39+
SetupLogging=yes
40+
41+
; Icon configuration
42+
SetupIconFile=brand\favicon.ico
43+
3844
[Languages]
3945
Name: "english"; MessagesFile: "compiler:Default.isl"
4046

@@ -43,17 +49,23 @@ Name: "english"; MessagesFile: "compiler:Default.isl"
4349
Source: "publish\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs
4450

4551
[Icons]
46-
Name: "{group}\{#AppName}"; Filename: "http://localhost:8080"; IconFilename: "{app}\{#AppExeName}"
47-
Name: "{group}\Configuration"; Filename: "{commonappdata}\ThingConnect.Pulse\config\config.yaml"
48-
Name: "{group}\Logs Directory"; Filename: "{commonappdata}\ThingConnect.Pulse\logs"
52+
Name: "{group}\{#AppName}"; Filename: "http://localhost:8090"; IconFilename: "{app}\wwwroot\favicon.ico"
53+
Name: "{group}\Logs Directory"; Filename: "{commonappdata}\ThingConnect.Pulse\logs";
54+
Name: "{group}\Installation Log"; Filename: "{log}"
55+
Name: "{group}\Documentation"; Filename: "https://docs.thingconnect.io/pulse"
4956
Name: "{group}\{cm:UninstallProgram,{#AppName}}"; Filename: "{uninstallexe}"
50-
57+
5158
[Run]
52-
Filename: "http://localhost:8080"; Description: "Open {#AppName} Web Interface"; Flags: nowait postinstall shellexec skipifsilent
59+
; Install and start the Windows service
60+
Filename: "{sys}\sc.exe"; Parameters: "create ""{#ServiceName}"" start= auto DisplayName= ""{#ServiceDisplayName}"" binPath= ""{app}\{#AppExeName}"""; Flags: runhidden; StatusMsg: "Creating Windows service..."
61+
Filename: "{sys}\sc.exe"; Parameters: "description ""{#ServiceName}"" ""{#ServiceDescription}"""; Flags: runhidden; StatusMsg: "Setting service description..."
62+
Filename: "{sys}\sc.exe"; Parameters: "failure ""{#ServiceName}"" reset= 86400 actions= restart/5000/restart/5000/restart/5000"; Flags: runhidden; StatusMsg: "Configuring service recovery..."
63+
Filename: "{sys}\sc.exe"; Parameters: "start ""{#ServiceName}"""; Flags: runhidden; StatusMsg: "Starting Windows service..."
64+
Filename: "http://localhost:8090"; Description: "Open {#AppName} Web Interface"; Flags: nowait postinstall shellexec skipifsilent
5365

5466
[UninstallRun]
55-
Filename: "sc.exe"; Parameters: "stop ""{#ServiceName}"""; Flags: runhidden
56-
Filename: "sc.exe"; Parameters: "delete ""{#ServiceName}"""; Flags: runhidden
67+
Filename: "{sys}\sc.exe"; Parameters: "stop ""{#ServiceName}"""; Flags: runhidden
68+
Filename: "{sys}\sc.exe"; Parameters: "delete ""{#ServiceName}"""; Flags: runhidden
5769

5870
[Code]
5971
function IsServiceInstalled(): Boolean;
@@ -83,30 +95,7 @@ begin
8395
Sleep(1000); // Wait for service removal
8496
end;
8597
86-
function InstallService(): Boolean;
87-
var
88-
ServicePath: String;
89-
ResultCode: Integer;
90-
begin
91-
ServicePath := '"' + ExpandConstant('{app}') + '\{#AppExeName}"';
92-
Log('Installing service: {#ServiceName} at ' + ServicePath);
93-
94-
Result := Exec('sc.exe', Format('create "{#ServiceName}" binPath= "%s" DisplayName= "{#ServiceDisplayName}" start= auto', [ServicePath]), '', SW_HIDE, ewWaitUntilTerminated, ResultCode);
95-
96-
if Result then
97-
begin
98-
// Set service description
99-
Exec('sc.exe', 'description "{#ServiceName}" "{#ServiceDescription}"', '', SW_HIDE, ewWaitUntilTerminated, ResultCode);
100-
end;
101-
end;
102-
103-
function StartService(): Boolean;
104-
var
105-
ResultCode: Integer;
106-
begin
107-
Log('Starting service: {#ServiceName}');
108-
Result := Exec('sc.exe', 'start "{#ServiceName}"', '', SW_HIDE, ewWaitUntilTerminated, ResultCode);
109-
end;
98+
// Service installation is now handled by [Run] section
11099
111100
procedure CreateDirectoryStructure();
112101
var
@@ -120,73 +109,50 @@ begin
120109
VersionsDir := ProgramDataRoot + '\versions';
121110
LogsDir := ProgramDataRoot + '\logs';
122111
DataDir := ProgramDataRoot + '\data';
123-
ConfigFile := ConfigDir + '\config.yaml';
124112
125113
Log('Creating directory structure at: ' + ProgramDataRoot);
126114
127115
ForceDirectories(ConfigDir);
128116
ForceDirectories(VersionsDir);
129117
ForceDirectories(LogsDir);
130118
ForceDirectories(DataDir);
131-
132-
// Create default config file if it doesn't exist
133-
if not FileExists(ConfigFile) then
134-
begin
135-
DefaultConfig :=
136-
'# ThingConnect Pulse Configuration' + #13#10 +
137-
'# This is the main configuration file for network monitoring' + #13#10 +
138-
'# ' + #13#10 +
139-
'# For configuration syntax and examples, see:' + #13#10 +
140-
'# https://github.com/MachDatum/ThingConnect.Pulse/blob/main/docs/config.schema.json' + #13#10 +
141-
'' + #13#10 +
142-
'# Example configuration:' + #13#10 +
143-
'# targets:' + #13#10 +
144-
'# - name: "Router"' + #13#10 +
145-
'# endpoints:' + #13#10 +
146-
'# - host: "192.168.1.1"' + #13#10 +
147-
'# type: "icmp"' + #13#10 +
148-
'# - name: "Web Services"' + #13#10 +
149-
'# endpoints:' + #13#10 +
150-
'# - host: "www.example.com"' + #13#10 +
151-
'# type: "http"' + #13#10 +
152-
'# path: "/health"' + #13#10 +
153-
'' + #13#10 +
154-
'# Empty configuration - add your monitoring targets above' + #13#10 +
155-
'targets: []' + #13#10;
156-
157-
SaveStringToFile(ConfigFile, DefaultConfig, False);
158-
Log('Created default config file');
159-
end;
160119
end;
161120
162121
procedure CurStepChanged(CurStep: TSetupStep);
163122
begin
164123
case CurStep of
165124
ssInstall:
166125
begin
126+
Log('=== Installation Step: Preparing installation ===');
127+
Log('Installation target directory: ' + ExpandConstant('{app}'));
128+
167129
// Stop and remove existing service if present
168130
if IsServiceInstalled() then
169131
begin
170-
StopService();
171-
RemoveService();
132+
Log('Existing service detected, will be stopped and removed...');
133+
if StopService() then
134+
Log('Existing service stopped successfully')
135+
else
136+
Log('WARNING: Failed to stop existing service');
137+
138+
if RemoveService() then
139+
Log('Existing service removed successfully')
140+
else
141+
Log('WARNING: Failed to remove existing service');
142+
end else
143+
begin
144+
Log('No existing service found');
172145
end;
173146
end;
174147
175148
ssPostInstall:
176149
begin
150+
Log('=== Post-Install Step: Creating directory structure ===');
151+
177152
// Create directory structure
153+
Log('Creating application data directories...');
178154
CreateDirectoryStructure();
179-
180-
// Install and start the Windows service
181-
if InstallService() then
182-
begin
183-
Log('Service installed successfully');
184-
if not StartService() then
185-
MsgBox('Service installed but failed to start. You can start it manually from Services.msc', mbInformation, MB_OK);
186-
end else
187-
begin
188-
MsgBox('Failed to install Windows service. You may need to install manually using install-service.ps1', mbError, MB_OK);
189-
end;
155+
Log('Directory structure created successfully');
190156
end;
191157
end;
192158
end;

thingconnect.pulse.client/src/components/status/StatusFilters.tsx

Lines changed: 14 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -142,31 +142,23 @@ export function StatusFilters({
142142
openOnClick
143143
>
144144
<Combobox.Control>
145-
<Combobox.Input placeholder='Select Group...' _dark={{ borderColor: 'gray.200' }} />
145+
<Combobox.Input placeholder='Select Group...' />
146146
<Combobox.IndicatorGroup>
147147
<Combobox.ClearTrigger onClick={clearFilter} cursor={'pointer'} />
148148
<Combobox.Trigger />
149149
</Combobox.IndicatorGroup>
150150
</Combobox.Control>
151151
<Combobox.Positioner>
152-
<Combobox.Content _dark={{ borderWidth: 1, borderColor: 'gray.200' }}>
152+
<Combobox.Content>
153153
<Combobox.Empty>No groups found</Combobox.Empty>
154-
<Combobox.Item
155-
key='allgroups'
156-
item={{ label: 'All Groups', value: '' }}
157-
_dark={{ _hover: { bg: 'gray.400' } }}
158-
>
154+
<Combobox.Item key='allgroups' item={{ label: 'All Groups', value: '' }}>
159155
<HStack justify='space-between' textStyle='sm'>
160156
All Groups
161157
</HStack>
162158
</Combobox.Item>
163159
{collection.items.map(item => {
164160
return (
165-
<Combobox.Item
166-
key={item.value}
167-
item={item}
168-
_dark={{ _hover: { bg: 'gray.400' } }}
169-
>
161+
<Combobox.Item key={item.value} item={item}>
170162
<HStack justify='space-between' textStyle='sm'>
171163
{item.label}
172164
</HStack>
@@ -193,7 +185,6 @@ export function StatusFilters({
193185
placeholder='Search endpoints by name or host...'
194186
pl='10'
195187
pr={searchTerm ? '10' : '4'}
196-
borderColor='gray.300'
197188
value={searchTerm}
198189
onChange={e => handleSearchChange(e.target.value)}
199190
data-testid='search-input'
@@ -218,27 +209,26 @@ export function StatusFilters({
218209
{/* Group By Dropdown */}
219210
<Menu.Root>
220211
<Menu.Trigger asChild>
221-
<Button variant='outline' borderColor='gray.300'>
222-
{groupByOptions.length > 0
223-
? groupByOptions
224-
.map(opt => (opt === 'status' ? 'Status' : opt === 'group' ? 'Group' : opt))
225-
.join(' + ')
226-
: 'Group By'}
212+
<Button variant='outline'>
213+
<Text fontSize='sm'>
214+
{groupByOptions.length > 0
215+
? groupByOptions
216+
.map(opt => (opt === 'status' ? 'Status' : opt === 'group' ? 'Group' : opt))
217+
.join(' + ')
218+
: 'Group By'}
219+
</Text>
227220
<MdExpandMore />
228221
</Button>
229222
</Menu.Trigger>
230223
<Menu.Positioner px={4}>
231-
<Menu.Content
232-
minWidth='200px'
233-
borderColor='gray.300'
234-
_dark={{ borderWidth: 1, borderColor: 'gray.200' }}
235-
>
224+
<Menu.Content minWidth='200px'>
236225
<Flex
237226
justify='flex-end'
238227
px={2}
239228
py={1}
240229
borderBottom='1px solid'
241230
borderColor='gray.200'
231+
_dark={{ borderColor: 'gray.600' }}
242232
>
243233
<HStack gap={2}>
244234
<Button
@@ -276,7 +266,6 @@ export function StatusFilters({
276266
onToggleGroupBy &&
277267
onToggleGroupBy('status', !groupByOptions.includes('status'));
278268
}}
279-
_dark={{ _hover: { bg: 'gray.400' } }}
280269
>
281270
<Flex w='full' justify='flex-start' align='center' gap={3}>
282271
<Text as='span'>Group by Status</Text>
@@ -290,7 +279,6 @@ export function StatusFilters({
290279
onCheckedChange={() => {
291280
onToggleGroupBy && onToggleGroupBy('group', !groupByOptions.includes('group'));
292281
}}
293-
_dark={{ _hover: { bg: 'gray.400' } }}
294282
>
295283
<Flex w='full' justify='flex-start' align='center' gap={3}>
296284
<Text as='span'>Group by Group</Text>

thingconnect.pulse.client/src/components/status/StatusTable.tsx

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -92,12 +92,7 @@ export function StatusTable({ items, isLoading }: StatusTableProps) {
9292
py={1}
9393
borderRadius='md'
9494
fontSize='10px'
95-
bg={`${getStatusColor(item.status)}.200`}
96-
color={`${getStatusColor(item.status)}.600`}
97-
_dark={{
98-
bg: `${getStatusColor(item.status)}.700`,
99-
color: `${getStatusColor(item.status)}.200`,
100-
}}
95+
colorPalette={getStatusColor(item.status)}
10196
>
10297
{item.status.toUpperCase()}
10398
</Badge>

thingconnect.pulse.client/src/components/status/TrendBlocks.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ const TrendBlocks = ({ data }: { data: SparklinePoint[] }) => {
1414
h='5'
1515
borderRadius='sm'
1616
bg={point.s === 'd' ? 'red.500' : 'green.500'}
17+
_dark={{ bg: point.s === 'd' ? 'red.600' : 'green.600' }}
1718
/>
1819
))}
1920
</HStack>

0 commit comments

Comments
 (0)