Initial Commit

This commit is contained in:
Marc Rejohn Castillano 2025-10-19 16:57:07 +08:00
commit 6a756571ed
925 changed files with 62574 additions and 0 deletions

63
.gitattributes vendored Normal file
View File

@ -0,0 +1,63 @@
###############################################################################
# Set default behavior to automatically normalize line endings.
###############################################################################
* text=auto
###############################################################################
# Set default behavior for command prompt diff.
#
# This is need for earlier builds of msysgit that does not have it on by
# default for csharp files.
# Note: This is only used by command line
###############################################################################
#*.cs diff=csharp
###############################################################################
# Set the merge driver for project and solution files
#
# Merging from the command prompt will add diff markers to the files if there
# are conflicts (Merging from VS is not affected by the settings below, in VS
# the diff markers are never inserted). Diff markers may cause the following
# file extensions to fail to load in VS. An alternative would be to treat
# these files as binary and thus will always conflict and require user
# intervention with every merge. To do so, just uncomment the entries below
###############################################################################
#*.sln merge=binary
#*.csproj merge=binary
#*.vbproj merge=binary
#*.vcxproj merge=binary
#*.vcproj merge=binary
#*.dbproj merge=binary
#*.fsproj merge=binary
#*.lsproj merge=binary
#*.wixproj merge=binary
#*.modelproj merge=binary
#*.sqlproj merge=binary
#*.wwaproj merge=binary
###############################################################################
# behavior for image files
#
# image files are treated as binary by default.
###############################################################################
#*.jpg binary
#*.png binary
#*.gif binary
###############################################################################
# diff behavior for common document formats
#
# Convert binary document formats to text before diffing them. This feature
# is only available from the command line. Turn it on by uncommenting the
# entries below.
###############################################################################
#*.doc diff=astextplain
#*.DOC diff=astextplain
#*.docx diff=astextplain
#*.DOCX diff=astextplain
#*.dot diff=astextplain
#*.DOT diff=astextplain
#*.pdf diff=astextplain
#*.PDF diff=astextplain
#*.rtf diff=astextplain
#*.RTF diff=astextplain

428
.gitignore vendored Normal file
View File

@ -0,0 +1,428 @@
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
##
## Get latest from https://github.com/github/gitignore/blob/main/VisualStudio.gitignore
# User-specific files
*.rsuser
*.suo
*.user
*.userosscache
*.sln.docstates
*.env
# User-specific files (MonoDevelop/Xamarin Studio)
*.userprefs
# Mono auto generated files
mono_crash.*
# Build results
[Dd]ebug/
[Dd]ebugPublic/
[Rr]elease/
[Rr]eleases/
[Dd]ebug/x64/
[Dd]ebugPublic/x64/
[Rr]elease/x64/
[Rr]eleases/x64/
bin/x64/
obj/x64/
[Dd]ebug/x86/
[Dd]ebugPublic/x86/
[Rr]elease/x86/
[Rr]eleases/x86/
bin/x86/
obj/x86/
[Ww][Ii][Nn]32/
[Aa][Rr][Mm]/
[Aa][Rr][Mm]64/
[Aa][Rr][Mm]64[Ee][Cc]/
bld/
[Oo]bj/
[Oo]ut/
[Ll]og/
[Ll]ogs/
# Build results on 'Bin' directories
**/[Bb]in/*
# Uncomment if you have tasks that rely on *.refresh files to move binaries
# (https://github.com/github/gitignore/pull/3736)
#!**/[Bb]in/*.refresh
# Visual Studio 2015/2017 cache/options directory
.vs/
# Uncomment if you have tasks that create the project's static files in wwwroot
#wwwroot/
# Visual Studio 2017 auto generated files
Generated\ Files/
# MSTest test Results
[Tt]est[Rr]esult*/
[Bb]uild[Ll]og.*
*.trx
# NUnit
*.VisualState.xml
TestResult.xml
nunit-*.xml
# Approval Tests result files
*.received.*
# Build Results of an ATL Project
[Dd]ebugPS/
[Rr]eleasePS/
dlldata.c
# Benchmark Results
BenchmarkDotNet.Artifacts/
# .NET Core
project.lock.json
project.fragment.lock.json
artifacts/
# ASP.NET Scaffolding
ScaffoldingReadMe.txt
# StyleCop
StyleCopReport.xml
# Files built by Visual Studio
*_i.c
*_p.c
*_h.h
*.ilk
*.meta
*.obj
*.idb
*.iobj
*.pch
*.pdb
*.ipdb
*.pgc
*.pgd
*.rsp
# but not Directory.Build.rsp, as it configures directory-level build defaults
!Directory.Build.rsp
*.sbr
*.tlb
*.tli
*.tlh
*.tmp
*.tmp_proj
*_wpftmp.csproj
*.log
*.tlog
*.vspscc
*.vssscc
.builds
*.pidb
*.svclog
*.scc
# Chutzpah Test files
_Chutzpah*
# Visual C++ cache files
ipch/
*.aps
*.ncb
*.opendb
*.opensdf
*.sdf
*.cachefile
*.VC.db
*.VC.VC.opendb
# Visual Studio profiler
*.psess
*.vsp
*.vspx
*.sap
# Visual Studio Trace Files
*.e2e
# TFS 2012 Local Workspace
$tf/
# Guidance Automation Toolkit
*.gpState
# ReSharper is a .NET coding add-in
_ReSharper*/
*.[Rr]e[Ss]harper
*.DotSettings.user
# TeamCity is a build add-in
_TeamCity*
# DotCover is a Code Coverage Tool
*.dotCover
# AxoCover is a Code Coverage Tool
.axoCover/*
!.axoCover/settings.json
# Coverlet is a free, cross platform Code Coverage Tool
coverage*.json
coverage*.xml
coverage*.info
# Visual Studio code coverage results
*.coverage
*.coveragexml
# NCrunch
_NCrunch_*
.NCrunch_*
.*crunch*.local.xml
nCrunchTemp_*
# MightyMoose
*.mm.*
AutoTest.Net/
# Web workbench (sass)
.sass-cache/
# Installshield output folder
[Ee]xpress/
# DocProject is a documentation generator add-in
DocProject/buildhelp/
DocProject/Help/*.HxT
DocProject/Help/*.HxC
DocProject/Help/*.hhc
DocProject/Help/*.hhk
DocProject/Help/*.hhp
DocProject/Help/Html2
DocProject/Help/html
# Click-Once directory
publish/
# Publish Web Output
*.[Pp]ublish.xml
*.azurePubxml
# Note: Comment the next line if you want to checkin your web deploy settings,
# but database connection strings (with potential passwords) will be unencrypted
*.pubxml
*.publishproj
# Microsoft Azure Web App publish settings. Comment the next line if you want to
# checkin your Azure Web App publish settings, but sensitive information contained
# in these scripts will be unencrypted
PublishScripts/
# NuGet Packages
*.nupkg
# NuGet Symbol Packages
*.snupkg
# The packages folder can be ignored because of Package Restore
**/[Pp]ackages/*
# except build/, which is used as an MSBuild target.
!**/[Pp]ackages/build/
# Uncomment if necessary however generally it will be regenerated when needed
#!**/[Pp]ackages/repositories.config
# NuGet v3's project.json files produces more ignorable files
*.nuget.props
*.nuget.targets
# Microsoft Azure Build Output
csx/
*.build.csdef
# Microsoft Azure Emulator
ecf/
rcf/
# Windows Store app package directories and files
AppPackages/
BundleArtifacts/
Package.StoreAssociation.xml
_pkginfo.txt
*.appx
*.appxbundle
*.appxupload
# Visual Studio cache files
# files ending in .cache can be ignored
*.[Cc]ache
# but keep track of directories ending in .cache
!?*.[Cc]ache/
# Others
ClientBin/
~$*
*~
*.dbmdl
*.dbproj.schemaview
*.jfm
*.pfx
*.publishsettings
orleans.codegen.cs
# Including strong name files can present a security risk
# (https://github.com/github/gitignore/pull/2483#issue-259490424)
#*.snk
# Since there are multiple workflows, uncomment next line to ignore bower_components
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
#bower_components/
# RIA/Silverlight projects
Generated_Code/
# Backup & report files from converting an old project file
# to a newer Visual Studio version. Backup files are not needed,
# because we have git ;-)
_UpgradeReport_Files/
Backup*/
UpgradeLog*.XML
UpgradeLog*.htm
ServiceFabricBackup/
*.rptproj.bak
# SQL Server files
*.mdf
*.ldf
*.ndf
# Business Intelligence projects
*.rdl.data
*.bim.layout
*.bim_*.settings
*.rptproj.rsuser
*- [Bb]ackup.rdl
*- [Bb]ackup ([0-9]).rdl
*- [Bb]ackup ([0-9][0-9]).rdl
# Microsoft Fakes
FakesAssemblies/
# GhostDoc plugin setting file
*.GhostDoc.xml
# Node.js Tools for Visual Studio
.ntvs_analysis.dat
node_modules/
# Visual Studio 6 build log
*.plg
# Visual Studio 6 workspace options file
*.opt
# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
*.vbw
# Visual Studio 6 workspace and project file (working project files containing files to include in project)
*.dsw
*.dsp
# Visual Studio 6 technical files
*.ncb
*.aps
# Visual Studio LightSwitch build output
**/*.HTMLClient/GeneratedArtifacts
**/*.DesktopClient/GeneratedArtifacts
**/*.DesktopClient/ModelManifest.xml
**/*.Server/GeneratedArtifacts
**/*.Server/ModelManifest.xml
_Pvt_Extensions
# Paket dependency manager
**/.paket/paket.exe
paket-files/
# FAKE - F# Make
**/.fake/
# CodeRush personal settings
**/.cr/personal
# Python Tools for Visual Studio (PTVS)
**/__pycache__/
*.pyc
# Cake - Uncomment if you are using it
#tools/**
#!tools/packages.config
# Tabs Studio
*.tss
# Telerik's JustMock configuration file
*.jmconfig
# BizTalk build output
*.btp.cs
*.btm.cs
*.odx.cs
*.xsd.cs
# OpenCover UI analysis results
OpenCover/
# Azure Stream Analytics local run output
ASALocalRun/
# MSBuild Binary and Structured Log
*.binlog
MSBuild_Logs/
# AWS SAM Build and Temporary Artifacts folder
.aws-sam
# NVidia Nsight GPU debugger configuration file
*.nvuser
# MFractors (Xamarin productivity tool) working folder
**/.mfractor/
# Local History for Visual Studio
**/.localhistory/
# Visual Studio History (VSHistory) files
.vshistory/
# BeatPulse healthcheck temp database
healthchecksdb
# Backup folder for Package Reference Convert tool in Visual Studio 2017
MigrationBackup/
# Ionide (cross platform F# VS Code tools) working folder
**/.ionide/
# Fody - auto-generated XML schema
FodyWeavers.xsd
# VS Code files for those working on multiple tools
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
!.vscode/*.code-snippets
# Local History for Visual Studio Code
.history/
# Built Visual Studio Code Extensions
*.vsix
# Windows Installer files from build outputs
*.cab
*.msi
*.msix
*.msm
*.msp

Binary file not shown.

Binary file not shown.

BIN
.vs/Inventory/v17/.suo Normal file

Binary file not shown.

View File

@ -0,0 +1,58 @@
{
"Version": 1,
"WorkspaceRootPath": "D:\\Projects\\CRMC\\InventoryAgent\\",
"Documents": [
{
"AbsoluteMoniker": "D:0:0:{B0F07931-1AEA-48D9-9E4A-A60F2506D2B4}|Inventory.Agent\\Inventory.Agent.csproj|d:\\projects\\crmc\\inventoryagent\\inventory.agent\\worker.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
"RelativeMoniker": "D:0:0:{B0F07931-1AEA-48D9-9E4A-A60F2506D2B4}|Inventory.Agent\\Inventory.Agent.csproj|solutionrelative:inventory.agent\\worker.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
},
{
"AbsoluteMoniker": "D:0:0:{A22DD7C8-9AF2-465B-BD48-850455AA1296}|Inventory.AdminTool\\Inventory.AdminTool.csproj|d:\\projects\\crmc\\inventoryagent\\inventory.admintool\\program.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
"RelativeMoniker": "D:0:0:{A22DD7C8-9AF2-465B-BD48-850455AA1296}|Inventory.AdminTool\\Inventory.AdminTool.csproj|solutionrelative:inventory.admintool\\program.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
}
],
"DocumentGroupContainers": [
{
"Orientation": 1,
"VerticalTabListWidth": 256,
"DocumentGroups": [
{
"DockedHeight": 329,
"SelectedChildIndex": 1,
"Children": [
{
"$type": "Bookmark",
"Name": "ST:128:0:{116d2292-e37d-41cd-a077-ebacac4c8cc4}"
},
{
"$type": "Document",
"DocumentIndex": 0,
"Title": "Worker.cs",
"DocumentMoniker": "D:\\Projects\\CRMC\\InventoryAgent\\Inventory.Agent\\Worker.cs",
"RelativeDocumentMoniker": "Inventory.Agent\\Worker.cs",
"ToolTip": "D:\\Projects\\CRMC\\InventoryAgent\\Inventory.Agent\\Worker.cs",
"RelativeToolTip": "Inventory.Agent\\Worker.cs",
"ViewState": "AgIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2025-10-19T08:48:55.835Z",
"EditorCaption": ""
},
{
"$type": "Document",
"DocumentIndex": 1,
"Title": "Program.cs",
"DocumentMoniker": "D:\\Projects\\CRMC\\InventoryAgent\\Inventory.AdminTool\\Program.cs",
"RelativeDocumentMoniker": "Inventory.AdminTool\\Program.cs",
"ToolTip": "D:\\Projects\\CRMC\\InventoryAgent\\Inventory.AdminTool\\Program.cs",
"RelativeToolTip": "Inventory.AdminTool\\Program.cs",
"ViewState": "AgIAAA8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2025-10-19T08:48:51.263Z",
"EditorCaption": ""
}
]
}
]
}
]
}

View File

@ -0,0 +1,58 @@
{
"Version": 1,
"WorkspaceRootPath": "D:\\Projects\\CRMC\\InventoryAgent\\",
"Documents": [
{
"AbsoluteMoniker": "D:0:0:{B0F07931-1AEA-48D9-9E4A-A60F2506D2B4}|Inventory.Agent\\Inventory.Agent.csproj|d:\\projects\\crmc\\inventoryagent\\inventory.agent\\worker.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
"RelativeMoniker": "D:0:0:{B0F07931-1AEA-48D9-9E4A-A60F2506D2B4}|Inventory.Agent\\Inventory.Agent.csproj|solutionrelative:inventory.agent\\worker.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
},
{
"AbsoluteMoniker": "D:0:0:{A22DD7C8-9AF2-465B-BD48-850455AA1296}|Inventory.AdminTool\\Inventory.AdminTool.csproj|d:\\projects\\crmc\\inventoryagent\\inventory.admintool\\program.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
"RelativeMoniker": "D:0:0:{A22DD7C8-9AF2-465B-BD48-850455AA1296}|Inventory.AdminTool\\Inventory.AdminTool.csproj|solutionrelative:inventory.admintool\\program.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
}
],
"DocumentGroupContainers": [
{
"Orientation": 1,
"VerticalTabListWidth": 256,
"DocumentGroups": [
{
"DockedHeight": 329,
"SelectedChildIndex": 1,
"Children": [
{
"$type": "Bookmark",
"Name": "ST:128:0:{116d2292-e37d-41cd-a077-ebacac4c8cc4}"
},
{
"$type": "Document",
"DocumentIndex": 0,
"Title": "Worker.cs",
"DocumentMoniker": "D:\\Projects\\CRMC\\InventoryAgent\\Inventory.Agent\\Worker.cs",
"RelativeDocumentMoniker": "Inventory.Agent\\Worker.cs",
"ToolTip": "D:\\Projects\\CRMC\\InventoryAgent\\Inventory.Agent\\Worker.cs",
"RelativeToolTip": "Inventory.Agent\\Worker.cs",
"ViewState": "AgIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2025-10-19T08:48:55.835Z",
"EditorCaption": ""
},
{
"$type": "Document",
"DocumentIndex": 1,
"Title": "Program.cs",
"DocumentMoniker": "D:\\Projects\\CRMC\\InventoryAgent\\Inventory.AdminTool\\Program.cs",
"RelativeDocumentMoniker": "Inventory.AdminTool\\Program.cs",
"ToolTip": "D:\\Projects\\CRMC\\InventoryAgent\\Inventory.AdminTool\\Program.cs",
"RelativeToolTip": "Inventory.AdminTool\\Program.cs",
"ViewState": "AgIAAA8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2025-10-19T08:48:51.263Z",
"EditorCaption": ""
}
]
}
]
}
]
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,23 @@
<Project Sdk="Microsoft.NET.Sdk">
<ItemGroup>
<ProjectReference Include="..\Inventory.Core\Inventory.Core.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="DotNetEnv" Version="3.1.1" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="9.0.0-preview.5.24306.3">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="Microsoft.Extensions.Hosting" Version="8.0.0" />
</ItemGroup>
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net9.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
</Project>

View File

@ -0,0 +1,373 @@
using System;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Security.Principal;
using System.Text.Json;
using System.Threading.Tasks;
using Inventory.Core;
using Microsoft.Extensions.Logging;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using DotNetEnv;
namespace Inventory.AdminTool
{
class Program
{
static async Task Main(string[] args)
{
if (OperatingSystem.IsWindows() && !IsAdministrator())
{
Console.WriteLine("This tool requires administrator privileges to collect all system information.");
Console.WriteLine("Attempting to restart with elevated permissions...");
try
{
var exeName = Process.GetCurrentProcess().MainModule.FileName;
var startInfo = new ProcessStartInfo(exeName)
{
Verb = "runas",
Arguments = string.Join(" ", args),
UseShellExecute = true
};
Process.Start(startInfo);
return; // Exit the non-elevated instance
}
catch (Exception ex)
{
Console.WriteLine($"Failed to restart with admin privileges. Please run your terminal as an administrator and try again. Error: {ex.Message}");
return;
}
}
var host = CreateHostBuilder(args).Build();
await host.RunAsync();
}
private static IHostBuilder CreateHostBuilder(string[] args)
{
Env.TraversePath().Load();
string? dbCon = Env.GetString("DB_CONNECTION_STRING");
if (string.IsNullOrWhiteSpace(dbCon))
{
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine("FATAL ERROR: DB_CONNECTION_STRING is not set in the .env file.");
Console.WriteLine("Please ensure a .env file exists in the project directory and contains the connection string.");
Console.ResetColor();
Environment.Exit(1); // Exit with an error code
}
return Host.CreateDefaultBuilder(args)
.ConfigureLogging(logging =>
{
// Suppress informational logs from Entity Framework Core
logging.AddFilter("Microsoft.EntityFrameworkCore", LogLevel.Warning);
})
.ConfigureServices((hostContext, services) =>
{
services.AddDbContext<InventoryContext>(options =>
options.UseSqlServer(dbCon));
services.AddHostedService<AppHostService>();
services.AddHttpClient();
services.AddSingleton<SystemInfoCollector>();
services.AddScoped<DatabaseUpdater>();
services.AddScoped<HealthMonitor>();
services.AddScoped<SlurpitClient>();
services.AddScoped<UpdateWorkflow>();
});
}
private static bool IsAdministrator()
{
if (!OperatingSystem.IsWindows())
{
// On non-Windows systems, we can assume root access is handled differently (e.g., via sudo).
return true;
}
var identity = WindowsIdentity.GetCurrent();
var principal = new WindowsPrincipal(identity);
return principal.IsInRole(WindowsBuiltInRole.Administrator);
}
}
public class AppHostService : IHostedService
{
private readonly IServiceProvider _serviceProvider;
public AppHostService(IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
}
public async Task StartAsync(CancellationToken cancellationToken)
{
bool errorOccurred = false;
try
{
using var scope = _serviceProvider.CreateScope();
var services = scope.ServiceProvider;
// Apply migrations on startup
var dbContext = services.GetRequiredService<InventoryContext>();
await dbContext.Database.MigrateAsync(cancellationToken);
var collector = services.GetRequiredService<SystemInfoCollector>();
var healthMonitor = services.GetRequiredService<HealthMonitor>();
DisplaySystemInfo(collector, healthMonitor);
await HandleLocationUpdate(services);
Console.Write("\nDo you want to force an upsert now? (y/n): ");
var response = Console.ReadLine();
if (response?.Equals("y", StringComparison.OrdinalIgnoreCase) == true)
{
var updateWorkflow = services.GetRequiredService<UpdateWorkflow>();
await updateWorkflow.Run();
Console.WriteLine("Upsert completed successfully.");
}
}
catch (Exception ex)
{
errorOccurred = true;
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine("\n\nAn unexpected error occurred:");
Console.WriteLine(ex.ToString());
Console.ResetColor();
}
finally
{
Console.WriteLine(errorOccurred
? "\nAn error occurred. The application will remain open. Press any key to exit."
: "\nExecution finished. Press any key to exit.");
Console.ReadKey();
}
var lifetime = _serviceProvider.GetRequiredService<IHostApplicationLifetime>();
lifetime.StopApplication();
}
public Task StopAsync(CancellationToken cancellationToken)
{
return Task.CompletedTask;
}
private static string FormatBytes(ulong bytes)
{
if (bytes == 0) return "0 B";
const int scale = 1024;
string[] orders = { "B", "KB", "MB", "GB", "TB", "PB" };
var orderIndex = (int)Math.Floor(Math.Log(bytes, scale));
if (orderIndex >= orders.Length) orderIndex = orders.Length - 1;
double num = Math.Round(bytes / Math.Pow(scale, orderIndex), 2);
return $"{num} {orders[orderIndex]}";
}
private void DisplaySystemInfo(SystemInfoCollector collector, HealthMonitor healthMonitor)
{
Console.WriteLine("--- System Information ---");
Console.WriteLine($"Computer Name: {collector.GetComputerName()}");
Console.WriteLine($"Device Type: {collector.GetDeviceType()}");
Console.WriteLine($"Serial Number: {collector.GetSystemSerialNumber()}");
Console.WriteLine($"Motherboard Serial Number: {collector.GetMotherboardSerialNumber()}");
Console.WriteLine($"System UUID: {collector.GetSystemUUID()}");
Console.WriteLine($"Processor: {collector.GetProcessor()}");
Console.WriteLine($"RAM: {collector.GetTotalRAM()}");
Console.WriteLine($"OS Version: {collector.GetOSVersion()}");
Console.WriteLine($"OS Install Date: {collector.GetOSInstallDate()}");
Console.WriteLine($"OS License Key: {collector.GetOSLicenseKey()}");
var (ips, mac) = collector.GetNetworkInfo();
Console.WriteLine("IP Addresses:");
if (ips.Any())
{
foreach (var ip in ips)
{
Console.WriteLine($" - {ip}");
}
}
Console.WriteLine($"MAC Address: {mac}");
Console.WriteLine($"Optical Drive: {collector.HasOpticalDrive()}");
Console.WriteLine("\n--- GPUs ---");
var gpus = collector.GetGPUs();
if (gpus.Any())
{
gpus.ForEach(gpu => Console.WriteLine($" - {gpu}"));
}
else
{
Console.WriteLine(" No GPUs found.");
}
Console.WriteLine("\n--- Storage Devices ---");
var storage = collector.GetStorage();
if (storage.Any())
{
foreach (var drive in storage)
{
Console.WriteLine($" - Model: {drive.Model ?? "N/A"}");
Console.WriteLine($" Serial: {drive.SerialNumber ?? "N/A"}");
Console.WriteLine($" Type: {drive.MediaType} ({drive.InterfaceType})");
Console.WriteLine($" Size: {FormatBytes(drive.Size)}");
}
}
else
{
Console.WriteLine(" No storage devices found.");
}
Console.WriteLine("\n--- Monitors ---");
var monitors = collector.GetMonitors();
if (monitors.Any())
{
foreach (var monitor in monitors)
{
Console.WriteLine($" - Model: {monitor.Model ?? "N/A"}");
Console.WriteLine($" Serial: {monitor.SerialNumber ?? "N/A"}");
Console.WriteLine($" PnPDeviceID: {monitor.PnPDeviceID ?? "N/A"}");
}
}
else
{
Console.WriteLine(" No monitors found.");
}
Console.WriteLine("\n--- Printers ---");
var printers = collector.GetPrinters();
if (printers.Any())
{
foreach (var printer in printers)
{
string type = printer.IsNetwork ? "Network" : "Local";
Console.WriteLine($" - Name: {printer.Name ?? "N/A"} ({type})");
Console.WriteLine($" Driver: {printer.DriverName ?? "N/A"}");
Console.WriteLine($" Port: {printer.PortName ?? "N/A"}");
Console.WriteLine($" Host: {printer.HostName ?? "N/A"}");
}
}
else
{
Console.WriteLine(" No physical printers found.");
}
Console.WriteLine("\n--- Local Admins ---");
var admins = collector.GetLocalAdmins();
if (admins.Any())
{
foreach (var admin in admins.OrderBy(a => a.Source).ThenBy(a => a.Name))
{
string status = admin.IsEnabled == false ? " (Disabled)" : (admin.IsLockedOut == true ? " (Locked)" : "");
Console.WriteLine($" - {admin.Name} [{admin.AccountType} / {admin.Source}]{status}");
}
}
else
{
Console.WriteLine(" Could not retrieve local administrators.");
}
Console.WriteLine("\n--- Health Metrics (Requires Admin) ---");
try
{
var report = healthMonitor.CollectHealthMetrics();
Console.WriteLine($"CPU Temp: {(report.CpuTemp.HasValue ? $"{report.CpuTemp.Value}°C" : "N/A")}");
Console.WriteLine($"CPU Load: {(report.CpuLoad.HasValue ? $"{Math.Round(report.CpuLoad.Value, 2)}%" : "N/A")}");
Console.WriteLine($"CPU Power: {(report.CpuPower.HasValue ? $"{Math.Round(report.CpuPower.Value, 2)}W" : "N/A")}");
Console.WriteLine($"GPU Temp: {(report.GpuTemp.HasValue ? $"{report.GpuTemp.Value}°C" : "N/A")}");
Console.WriteLine($"GPU Load: {(report.GpuLoad.HasValue ? $"{Math.Round(report.GpuLoad.Value, 2)}%" : "N/A")}");
Console.WriteLine($"GPU Power: {(report.GpuPower.HasValue ? $"{Math.Round(report.GpuPower.Value, 2)}W" : "N/A")}");
Console.WriteLine($"GPU Clock: {(report.GpuClock.HasValue ? $"{Math.Round(report.GpuClock.Value, 0)}MHz" : "N/A")}");
Console.WriteLine($"RAM Load: {(report.RamLoad.HasValue ? $"{Math.Round(report.RamLoad.Value, 2)}%" : "N/A")}");
Console.WriteLine($"Battery Health: {(report.BatteryHealth.HasValue ? $"{report.BatteryHealth.Value}%" : "N/A")}");
Console.WriteLine("Fan Speeds:");
if (report.FanSpeeds.Any())
{
foreach (var fan in report.FanSpeeds)
{
Console.WriteLine($" - {fan.Name}: {(fan.Rpm.HasValue ? $"{Math.Round(fan.Rpm.Value, 0)} RPM" : "N/A")}");
}
}
Console.WriteLine("Drive Health:");
if (report.DriveHealth.Any())
{
foreach (var driveHealth in report.DriveHealth)
{
string failing = driveHealth.IsFailing == true ? " - PREDICTING FAILURE!" : "";
string health = driveHealth.HealthPercentage.HasValue ? $"Health: {driveHealth.HealthPercentage}% | " : "";
Console.WriteLine($" - {driveHealth.Model}: {health}Temp: {(driveHealth.Temperature.HasValue ? $"{driveHealth.Temperature.Value}°C" : "N/A")}{failing}");
}
}
else
{
Console.WriteLine(" No drive health sensors found.");
}
}
catch (Exception ex)
{
Console.WriteLine($"Could not retrieve health metrics. Ensure the tool is run as Administrator. Error: {ex.Message}");
}
Console.WriteLine("--------------------------");
}
private async Task HandleLocationUpdate(IServiceProvider services)
{
using var scope = services.CreateScope();
var dbContext = scope.ServiceProvider.GetRequiredService<InventoryContext>();
var collector = scope.ServiceProvider.GetRequiredService<SystemInfoCollector>();
string? localIdentifier = GetLocalIdentifier(collector);
if (string.IsNullOrWhiteSpace(localIdentifier))
{
Console.WriteLine("\nCould not determine a unique identifier for this machine. Cannot update location.");
return;
}
var device = await dbContext.Devices.FirstOrDefaultAsync(d => d.HardwareIdentifier == localIdentifier);
Console.WriteLine("\n--- Location Information ---");
Console.WriteLine($"Current Location: {device?.Location ?? "Not set"}");
Console.Write("Enter new location (leave blank to keep current): ");
string? newLocation = Console.ReadLine();
if (!string.IsNullOrWhiteSpace(newLocation))
{
if (device == null)
{
// This case is unlikely if the update workflow runs first, but good to have.
device = new Device { HardwareIdentifier = localIdentifier };
dbContext.Devices.Add(device);
}
device.Location = newLocation;
await dbContext.SaveChangesAsync();
Console.WriteLine($"Location updated to '{newLocation}'.");
}
}
private string? GetLocalIdentifier(SystemInfoCollector collector)
{
// This logic is duplicated from DatabaseUpdater to be used here.
var identifiers = new[]
{
collector.GetMotherboardSerialNumber(),
collector.GetSystemUUID(),
collector.GetNetworkInfo().MACAddress
};
return identifiers.FirstOrDefault(id =>
!string.IsNullOrWhiteSpace(id) &&
!id.Equals("To be filled by O.E.M.", StringComparison.OrdinalIgnoreCase));
}
}
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,13 @@
{
"runtimeOptions": {
"tfm": "net8.0",
"framework": {
"name": "Microsoft.NETCore.App",
"version": "8.0.0"
},
"configProperties": {
"System.Reflection.NullabilityInfoContext.IsSupported": true,
"System.Runtime.Serialization.EnableUnsafeBinaryFormatterSerialization": false
}
}
}

Binary file not shown.

Some files were not shown because too many files have changed in this diff Show More