Skip to content

Commit 47bfb64

Browse files
committed
chore: finalize migration prep, legacy split, and docs updates
1 parent a91a9c5 commit 47bfb64

312 files changed

Lines changed: 1501 additions & 1160 deletions

File tree

Some content is hidden

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

.gitignore

Lines changed: 34 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,36 @@
11
*.user
2-
obj
3-
bin
42
*.suo
5-
packages
6-
/.vs/
7-
*.dtbcache.json
3+
*.userosscache
4+
*.sln.docstates
5+
*.rsuser
6+
7+
[Bb]in/
8+
[Oo]bj/
9+
[Dd]ebug/
10+
[Rr]elease/
11+
x64/
12+
x86/
13+
[Aa][Rr][Mm]/
14+
[Aa][Rr][Mm]64/
15+
16+
packages/
17+
*.nupkg
18+
*.snupkg
19+
20+
TestResults/
21+
*.trx
22+
*.coverage
23+
*.coveragexml
24+
25+
.vs/
26+
.vscode/
27+
.idea/
28+
29+
.DS_Store
30+
Thumbs.db
31+
32+
*.log
33+
!docs/migration/logs/*.log
34+
*.tmp
35+
*.cache
36+
*.dtbcache.json

Console/ServMon/ServMon.csproj

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,6 @@
2323
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
2424
</ItemGroup>
2525
<ItemGroup>
26-
<Reference Include="WCMS.Common">
27-
<HintPath>..\..\Libraries\WCMS.Common.dll</HintPath>
28-
</Reference>
26+
<ProjectReference Include="..\..\Shared\WCMS.Common\WCMS.Common.csproj" />
2927
</ItemGroup>
30-
</Project>
28+
</Project>

README.md

Lines changed: 58 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,59 @@
11
# ServMon
2-
ASP.NET service monitoring application
2+
3+
ServMon is a configurable service and endpoint monitor with a background agent and web dashboard for status and alerting.
4+
5+
## Project layout
6+
7+
- `ServMon.sln`: modern/default solution (`Console/ServMon`, `WebApp`, `Shared/WCMS.Common`, `Shared/WCMS.Common.Tests`)
8+
- `legacy/ServMon.Legacy.sln`: legacy solution (`legacy/ServMonWebCore`, `legacy/ServMonWeb`, `legacy/ServMonWeb/ServMonWeb.Tests`)
9+
- `Console/ServMon`: monitoring agent (`net8.0`)
10+
- `WebApp`: ASP.NET Core MVC UI (`net8.0`, branded `ServMon`; project file `ServMonWeb.csproj`)
11+
- `legacy/ServMonWebCore`: older ASP.NET Core MVC UI (`net8.0`, legacy)
12+
- `legacy/ServMonWeb`: legacy ASP.NET MVC 5 app (`.NET Framework 4.8.1`)
13+
- `legacy/ServMonWeb/ServMonWeb.Tests`: legacy MVC test project (`.NET Framework 4.8.1`)
14+
15+
## Migration and validation (.NET 10 + cross-platform)
16+
17+
Status snapshot date: **2026-04-12**.
18+
19+
- Active `net8.0` projects build successfully using .NET SDK `10.0.103`.
20+
- On macOS (arm64), the modern/default `ServMon.sln` builds successfully.
21+
- Legacy projects are isolated in `legacy/ServMon.Legacy.sln` and remain separate from the default modern build path.
22+
- Cross-platform runtime support is **not fully safe yet** because some code paths remain Windows-specific.
23+
- Full migration details, blockers, validation commands, gates, and rollback notes: [docs/DOTNET10_CROSS_PLATFORM_CHECKLIST.md](docs/DOTNET10_CROSS_PLATFORM_CHECKLIST.md).
24+
25+
## Quickstart (default provider)
26+
27+
```bash
28+
dotnet build WebApp/ServMonWeb.csproj -c Release
29+
dotnet run --project WebApp/ServMonWeb.csproj
30+
```
31+
32+
## Database provider setup (MSSQL + PostgreSQL)
33+
34+
`WebApp` (brand: `ServMon`, technical project: `ServMonWeb`) and legacy `legacy/ServMonWebCore` support two EF Core providers:
35+
36+
- `Postgres` (default)
37+
- `SqlServer`
38+
39+
Configuration keys:
40+
41+
- `DatabaseProvider`:
42+
- `Postgres` (default; also accepts `PostgreSql`/`Npgsql`)
43+
- `SqlServer`
44+
- `ConnectionStrings:DefaultConnection` (SQL Server)
45+
- `ConnectionStrings:PostgresConnection` (PostgreSQL)
46+
47+
Example PostgreSQL override (zsh/bash):
48+
49+
```bash
50+
DatabaseProvider=Postgres \
51+
ConnectionStrings__PostgresConnection="Host=localhost;Port=5432;Database=servmon;Username=postgres;Password=postgres" \
52+
dotnet run --project WebApp/ServMonWeb.csproj
53+
```
54+
55+
### Important migration note
56+
57+
Existing checked-in Identity migrations were originally scaffolded for SQL Server.
58+
With PostgreSQL as the default provider, generate/apply provider-specific PostgreSQL migrations before production use.
59+
If SQL Server remains in use, maintain a separate SQL Server migration path as well.

ServMon.sln

Lines changed: 68 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,47 +1,88 @@
1+
12
Microsoft Visual Studio Solution File, Format Version 12.00
23
# Visual Studio Version 17
3-
VisualStudioVersion = 17.10.35013.160
4+
VisualStudioVersion = 17.0.31903.59
45
MinimumVisualStudioVersion = 10.0.40219.1
5-
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ServMon", "Console\ServMon\ServMon.csproj", "{2B689AC8-A03C-496F-BA63-C83A015A07B5}"
6+
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Console", "Console", "{09D8FB36-4898-7C8E-B3F7-02AC6C3AAC25}"
67
EndProject
7-
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ServMonWeb", "Web\ServMonWeb\ServMonWeb\ServMonWeb.csproj", "{582E5196-AB7A-4E08-B96E-B589BE2B4401}"
8+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ServMon", "Console\ServMon\ServMon.csproj", "{E7107F43-471F-4DE8-8E6C-C871F5E1F5DE}"
89
EndProject
9-
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ServMonWeb.Tests", "Web\ServMonWeb\ServMonWeb.Tests\ServMonWeb.Tests.csproj", "{C35FBCED-1C6A-41B8-9E54-BD41B6F3DE50}"
10+
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Shared", "Shared", "{4F52FD11-658E-A102-6CD3-7D7C16FFA15B}"
1011
EndProject
11-
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ServMonWebCore", "ServMonWebV4\ServMonWebCore.csproj", "{DA1FD07B-ABB9-457C-8692-7BBCC49D946E}"
12+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WCMS.Common", "Shared\WCMS.Common\WCMS.Common.csproj", "{D5A47D8D-0F09-48D8-A298-1A24EE2F1E61}"
1213
EndProject
13-
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ServMonWebV5", "ServMonWebV5\ServMonWebV5.csproj", "{C31098FE-4446-433E-96FA-719CE92382BA}"
14+
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "WebApp", "WebApp", "{7E50A06F-A38B-7BE7-1137-409223B8AEF0}"
15+
EndProject
16+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ServMonWeb", "WebApp\ServMonWeb.csproj", "{781D34E3-DD87-478D-8C64-A6A055BEB8BD}"
17+
EndProject
18+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WCMS.Common.Tests", "Shared\WCMS.Common.Tests\WCMS.Common.Tests.csproj", "{2216B4F1-89BC-4F4C-A2AA-8C3439BE24F0}"
1419
EndProject
1520
Global
1621
GlobalSection(SolutionConfigurationPlatforms) = preSolution
1722
Debug|Any CPU = Debug|Any CPU
23+
Debug|x64 = Debug|x64
24+
Debug|x86 = Debug|x86
1825
Release|Any CPU = Release|Any CPU
26+
Release|x64 = Release|x64
27+
Release|x86 = Release|x86
1928
EndGlobalSection
2029
GlobalSection(ProjectConfigurationPlatforms) = postSolution
21-
{2B689AC8-A03C-496F-BA63-C83A015A07B5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
22-
{2B689AC8-A03C-496F-BA63-C83A015A07B5}.Release|Any CPU.ActiveCfg = Release|Any CPU
23-
{2B689AC8-A03C-496F-BA63-C83A015A07B5}.Release|Any CPU.Build.0 = Release|Any CPU
24-
{582E5196-AB7A-4E08-B96E-B589BE2B4401}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
25-
{582E5196-AB7A-4E08-B96E-B589BE2B4401}.Debug|Any CPU.Build.0 = Debug|Any CPU
26-
{582E5196-AB7A-4E08-B96E-B589BE2B4401}.Release|Any CPU.ActiveCfg = Release|Any CPU
27-
{582E5196-AB7A-4E08-B96E-B589BE2B4401}.Release|Any CPU.Build.0 = Release|Any CPU
28-
{C35FBCED-1C6A-41B8-9E54-BD41B6F3DE50}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
29-
{C35FBCED-1C6A-41B8-9E54-BD41B6F3DE50}.Debug|Any CPU.Build.0 = Debug|Any CPU
30-
{C35FBCED-1C6A-41B8-9E54-BD41B6F3DE50}.Release|Any CPU.ActiveCfg = Release|Any CPU
31-
{C35FBCED-1C6A-41B8-9E54-BD41B6F3DE50}.Release|Any CPU.Build.0 = Release|Any CPU
32-
{DA1FD07B-ABB9-457C-8692-7BBCC49D946E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
33-
{DA1FD07B-ABB9-457C-8692-7BBCC49D946E}.Debug|Any CPU.Build.0 = Debug|Any CPU
34-
{DA1FD07B-ABB9-457C-8692-7BBCC49D946E}.Release|Any CPU.ActiveCfg = Release|Any CPU
35-
{DA1FD07B-ABB9-457C-8692-7BBCC49D946E}.Release|Any CPU.Build.0 = Release|Any CPU
36-
{C31098FE-4446-433E-96FA-719CE92382BA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
37-
{C31098FE-4446-433E-96FA-719CE92382BA}.Debug|Any CPU.Build.0 = Debug|Any CPU
38-
{C31098FE-4446-433E-96FA-719CE92382BA}.Release|Any CPU.ActiveCfg = Release|Any CPU
39-
{C31098FE-4446-433E-96FA-719CE92382BA}.Release|Any CPU.Build.0 = Release|Any CPU
30+
{E7107F43-471F-4DE8-8E6C-C871F5E1F5DE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
31+
{E7107F43-471F-4DE8-8E6C-C871F5E1F5DE}.Debug|Any CPU.Build.0 = Debug|Any CPU
32+
{E7107F43-471F-4DE8-8E6C-C871F5E1F5DE}.Debug|x64.ActiveCfg = Debug|Any CPU
33+
{E7107F43-471F-4DE8-8E6C-C871F5E1F5DE}.Debug|x64.Build.0 = Debug|Any CPU
34+
{E7107F43-471F-4DE8-8E6C-C871F5E1F5DE}.Debug|x86.ActiveCfg = Debug|Any CPU
35+
{E7107F43-471F-4DE8-8E6C-C871F5E1F5DE}.Debug|x86.Build.0 = Debug|Any CPU
36+
{E7107F43-471F-4DE8-8E6C-C871F5E1F5DE}.Release|Any CPU.ActiveCfg = Release|Any CPU
37+
{E7107F43-471F-4DE8-8E6C-C871F5E1F5DE}.Release|Any CPU.Build.0 = Release|Any CPU
38+
{E7107F43-471F-4DE8-8E6C-C871F5E1F5DE}.Release|x64.ActiveCfg = Release|Any CPU
39+
{E7107F43-471F-4DE8-8E6C-C871F5E1F5DE}.Release|x64.Build.0 = Release|Any CPU
40+
{E7107F43-471F-4DE8-8E6C-C871F5E1F5DE}.Release|x86.ActiveCfg = Release|Any CPU
41+
{E7107F43-471F-4DE8-8E6C-C871F5E1F5DE}.Release|x86.Build.0 = Release|Any CPU
42+
{D5A47D8D-0F09-48D8-A298-1A24EE2F1E61}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
43+
{D5A47D8D-0F09-48D8-A298-1A24EE2F1E61}.Debug|Any CPU.Build.0 = Debug|Any CPU
44+
{D5A47D8D-0F09-48D8-A298-1A24EE2F1E61}.Debug|x64.ActiveCfg = Debug|Any CPU
45+
{D5A47D8D-0F09-48D8-A298-1A24EE2F1E61}.Debug|x64.Build.0 = Debug|Any CPU
46+
{D5A47D8D-0F09-48D8-A298-1A24EE2F1E61}.Debug|x86.ActiveCfg = Debug|Any CPU
47+
{D5A47D8D-0F09-48D8-A298-1A24EE2F1E61}.Debug|x86.Build.0 = Debug|Any CPU
48+
{D5A47D8D-0F09-48D8-A298-1A24EE2F1E61}.Release|Any CPU.ActiveCfg = Release|Any CPU
49+
{D5A47D8D-0F09-48D8-A298-1A24EE2F1E61}.Release|Any CPU.Build.0 = Release|Any CPU
50+
{D5A47D8D-0F09-48D8-A298-1A24EE2F1E61}.Release|x64.ActiveCfg = Release|Any CPU
51+
{D5A47D8D-0F09-48D8-A298-1A24EE2F1E61}.Release|x64.Build.0 = Release|Any CPU
52+
{D5A47D8D-0F09-48D8-A298-1A24EE2F1E61}.Release|x86.ActiveCfg = Release|Any CPU
53+
{D5A47D8D-0F09-48D8-A298-1A24EE2F1E61}.Release|x86.Build.0 = Release|Any CPU
54+
{781D34E3-DD87-478D-8C64-A6A055BEB8BD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
55+
{781D34E3-DD87-478D-8C64-A6A055BEB8BD}.Debug|Any CPU.Build.0 = Debug|Any CPU
56+
{781D34E3-DD87-478D-8C64-A6A055BEB8BD}.Debug|x64.ActiveCfg = Debug|Any CPU
57+
{781D34E3-DD87-478D-8C64-A6A055BEB8BD}.Debug|x64.Build.0 = Debug|Any CPU
58+
{781D34E3-DD87-478D-8C64-A6A055BEB8BD}.Debug|x86.ActiveCfg = Debug|Any CPU
59+
{781D34E3-DD87-478D-8C64-A6A055BEB8BD}.Debug|x86.Build.0 = Debug|Any CPU
60+
{781D34E3-DD87-478D-8C64-A6A055BEB8BD}.Release|Any CPU.ActiveCfg = Release|Any CPU
61+
{781D34E3-DD87-478D-8C64-A6A055BEB8BD}.Release|Any CPU.Build.0 = Release|Any CPU
62+
{781D34E3-DD87-478D-8C64-A6A055BEB8BD}.Release|x64.ActiveCfg = Release|Any CPU
63+
{781D34E3-DD87-478D-8C64-A6A055BEB8BD}.Release|x64.Build.0 = Release|Any CPU
64+
{781D34E3-DD87-478D-8C64-A6A055BEB8BD}.Release|x86.ActiveCfg = Release|Any CPU
65+
{781D34E3-DD87-478D-8C64-A6A055BEB8BD}.Release|x86.Build.0 = Release|Any CPU
66+
{2216B4F1-89BC-4F4C-A2AA-8C3439BE24F0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
67+
{2216B4F1-89BC-4F4C-A2AA-8C3439BE24F0}.Debug|Any CPU.Build.0 = Debug|Any CPU
68+
{2216B4F1-89BC-4F4C-A2AA-8C3439BE24F0}.Debug|x64.ActiveCfg = Debug|Any CPU
69+
{2216B4F1-89BC-4F4C-A2AA-8C3439BE24F0}.Debug|x64.Build.0 = Debug|Any CPU
70+
{2216B4F1-89BC-4F4C-A2AA-8C3439BE24F0}.Debug|x86.ActiveCfg = Debug|Any CPU
71+
{2216B4F1-89BC-4F4C-A2AA-8C3439BE24F0}.Debug|x86.Build.0 = Debug|Any CPU
72+
{2216B4F1-89BC-4F4C-A2AA-8C3439BE24F0}.Release|Any CPU.ActiveCfg = Release|Any CPU
73+
{2216B4F1-89BC-4F4C-A2AA-8C3439BE24F0}.Release|Any CPU.Build.0 = Release|Any CPU
74+
{2216B4F1-89BC-4F4C-A2AA-8C3439BE24F0}.Release|x64.ActiveCfg = Release|Any CPU
75+
{2216B4F1-89BC-4F4C-A2AA-8C3439BE24F0}.Release|x64.Build.0 = Release|Any CPU
76+
{2216B4F1-89BC-4F4C-A2AA-8C3439BE24F0}.Release|x86.ActiveCfg = Release|Any CPU
77+
{2216B4F1-89BC-4F4C-A2AA-8C3439BE24F0}.Release|x86.Build.0 = Release|Any CPU
4078
EndGlobalSection
4179
GlobalSection(SolutionProperties) = preSolution
4280
HideSolutionNode = FALSE
4381
EndGlobalSection
44-
GlobalSection(ExtensibilityGlobals) = postSolution
45-
SolutionGuid = {E853E35A-6818-4F07-9077-03943E744BB3}
82+
GlobalSection(NestedProjects) = preSolution
83+
{E7107F43-471F-4DE8-8E6C-C871F5E1F5DE} = {09D8FB36-4898-7C8E-B3F7-02AC6C3AAC25}
84+
{D5A47D8D-0F09-48D8-A298-1A24EE2F1E61} = {4F52FD11-658E-A102-6CD3-7D7C16FFA15B}
85+
{781D34E3-DD87-478D-8C64-A6A055BEB8BD} = {7E50A06F-A38B-7BE7-1137-409223B8AEF0}
86+
{2216B4F1-89BC-4F4C-A2AA-8C3439BE24F0} = {4F52FD11-658E-A102-6CD3-7D7C16FFA15B}
4687
EndGlobalSection
4788
EndGlobal

ServMonWebV4/Program.cs

Lines changed: 0 additions & 44 deletions
This file was deleted.
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
using System;
2+
using System.IO;
3+
using System.Text;
4+
using System.Xml;
5+
using WCMS.Common;
6+
using WCMS.Common.Utilities;
7+
using Xunit;
8+
9+
namespace WCMS.Common.Tests;
10+
11+
public class UnitTest1
12+
{
13+
[Fact]
14+
public void DataUtil_GetInt32_ParsesAndFallsBack()
15+
{
16+
Assert.Equal(42, DataUtil.GetInt32("42", 0));
17+
Assert.Equal(7, DataUtil.GetInt32("not-a-number", 7));
18+
Assert.Equal(1, DataUtil.GetInt32(true, 0));
19+
}
20+
21+
[Fact]
22+
public void DataUtil_GetBool_ParsesCommonValues()
23+
{
24+
Assert.True(DataUtil.GetBool("1", false));
25+
Assert.False(DataUtil.GetBool("false", true));
26+
Assert.True(DataUtil.GetBool((string)null, true));
27+
}
28+
29+
[Fact]
30+
public void XmlUtil_GetValue_ReadsChildAndAttribute()
31+
{
32+
var xml = "<Root attr=\"yes\"><Interval>15</Interval><Mail><Host>smtp.example.com</Host></Mail></Root>";
33+
var doc = new XmlDocument();
34+
doc.LoadXml(xml);
35+
var root = doc.DocumentElement;
36+
37+
Assert.Equal("15", XmlUtil.GetValue(root, "Interval"));
38+
Assert.Equal("smtp.example.com", XmlUtil.GetValue(root, "Mail/Host"));
39+
Assert.Equal("yes", XmlUtil.GetValue(root, "attr"));
40+
Assert.Equal("yes", XmlUtil.GetValue(root, "@attr"));
41+
}
42+
43+
[Fact]
44+
public void Substituter_Substitute_UsesNamedValueProvider()
45+
{
46+
var values = new NamedValueProvider();
47+
values.Add("From", "SrvMon");
48+
values.Add("Number", "6590001111");
49+
values.Add("Message", "Server down");
50+
values.Add("Today", "2026-04-12");
51+
52+
var result = Substituter.Substitute(
53+
"from=$(From)&to=$(Number)&text=$(Message)&date=$(Today|System.DateTime|yyyy-MM-dd)",
54+
values);
55+
56+
Assert.Equal("from=SrvMon&to=6590001111&text=Server down&date=2026-04-12", result);
57+
}
58+
59+
[Fact]
60+
public void FileHelper_WriteReadAndGetFolder_Works()
61+
{
62+
var tempRoot = Path.Combine(Path.GetTempPath(), "servmon-wcms-tests", Guid.NewGuid().ToString("N"));
63+
var filePath = Path.Combine(tempRoot, "services.json");
64+
65+
try
66+
{
67+
Assert.True(FileHelper.WriteFile("{\"ok\":true}", filePath, Encoding.UTF8));
68+
Assert.Equal("{\"ok\":true}", FileHelper.ReadFile(filePath));
69+
Assert.Equal(tempRoot, FileHelper.GetFolder(filePath));
70+
}
71+
finally
72+
{
73+
if (Directory.Exists(tempRoot))
74+
{
75+
Directory.Delete(tempRoot, true);
76+
}
77+
}
78+
}
79+
80+
[Fact]
81+
public void LogHelper_WriteLog_WritesToSpecificFile()
82+
{
83+
var tempRoot = Path.Combine(Path.GetTempPath(), "servmon-wcms-tests", Guid.NewGuid().ToString("N"));
84+
var logPath = Path.Combine(tempRoot, "custom.log");
85+
86+
try
87+
{
88+
LogHelper.WriteLog(logPath, "Error {0}", 123);
89+
Assert.True(File.Exists(logPath));
90+
Assert.Contains("Error 123", File.ReadAllText(logPath));
91+
}
92+
finally
93+
{
94+
if (Directory.Exists(tempRoot))
95+
{
96+
Directory.Delete(tempRoot, true);
97+
}
98+
}
99+
}
100+
}

0 commit comments

Comments
 (0)