Resolve service logs under interactive user profile

Resolves %LocalAppData% from the logged-on console user and separates client/backend log folders.
feature/centralized-offline-canteen
SYED MUSTUFA AHMED NAQVI 2026-06-01 15:09:47 +05:00
parent 42cd473e73
commit 4d8ee06b04
2 changed files with 123 additions and 0 deletions

View File

@ -0,0 +1,58 @@
using System.IO;
using System.Runtime.InteropServices;
using System.Text;
namespace UtopiaCanteenSystem.Services.Logging;
/// <summary>
/// Resolves the logged-on Windows user's profile paths when the app runs as a service (SYSTEM).
/// </summary>
internal static class InteractiveUserPath
{
public static string? TryGetLocalAppDataPath()
{
var profile = TryGetProfileDirectory();
if (string.IsNullOrWhiteSpace(profile))
return null;
var localAppData = Path.Combine(profile, "AppData", "Local");
return Directory.Exists(localAppData) ? localAppData : null;
}
private static string? TryGetProfileDirectory()
{
var sessionId = WTSGetActiveConsoleSessionId();
if (sessionId == 0xFFFFFFFF)
return null;
if (!WTSQueryUserToken(sessionId, out var userToken) || userToken == IntPtr.Zero)
return null;
try
{
var sb = new StringBuilder(512);
var size = sb.Capacity;
if (!GetUserProfileDirectory(userToken, sb, ref size, out _))
return null;
var path = sb.ToString();
return string.IsNullOrWhiteSpace(path) ? null : path;
}
finally
{
CloseHandle(userToken);
}
}
[DllImport("kernel32.dll")]
private static extern uint WTSGetActiveConsoleSessionId();
[DllImport("wtsapi32.dll", SetLastError = true)]
private static extern bool WTSQueryUserToken(uint sessionId, out IntPtr phToken);
[DllImport("userenv.dll", SetLastError = true, CharSet = CharSet.Unicode)]
private static extern bool GetUserProfileDirectory(IntPtr hToken, StringBuilder lpProfileDir, ref int lpcchSize, out int lpDwFlags);
[DllImport("kernel32.dll", SetLastError = true)]
private static extern bool CloseHandle(IntPtr hObject);
}

View File

@ -0,0 +1,65 @@
using System.IO;
namespace UtopiaCanteenSystem.Services.Logging;
/// <summary>
/// File log directories. Client uses the current user's LocalAppData.
/// Backend (Windows Service) uses the interactive user's LocalAppData when possible
/// so logs appear under the logged-on user's profile instead of systemprofile.
/// </summary>
public static class LogPaths
{
private static string? _backendLogsDirectoryOverride;
private static string? _cachedLocalAppDataRoot;
/// <summary>
/// Optional absolute path (e.g. from appsettings LogsDirectory). Used by the backend service only.
/// </summary>
public static void SetBackendLogsDirectory(string? directory)
{
_backendLogsDirectoryOverride = string.IsNullOrWhiteSpace(directory)
? null
: directory.Trim();
}
/// <summary>
/// Client logs folder: %LocalAppData%\UtopiaCanteenClient\Logs.
/// </summary>
public static string ClientLogsDirectory =>
Path.Combine(ResolveLocalAppDataRoot(), "UtopiaCanteenClient", "Logs");
/// <summary>
/// Backend logs folder: %LocalAppData%\UtopiaCanteenBackend\Logs
/// (interactive user when running as a service).
/// </summary>
public static string BackendLogsDirectory =>
_backendLogsDirectoryOverride ?? Path.Combine(ResolveLocalAppDataRoot(), "UtopiaCanteenBackend", "Logs");
public static string GetBackendLogFilePath(DateTime? date = null)
{
var d = date ?? DateTime.Now;
return Path.Combine(BackendLogsDirectory, $"backend-{d:yyyy-MM-dd}.log");
}
public static string GetClientLogFilePath(DateTime? date = null)
{
var d = date ?? DateTime.Now;
return Path.Combine(ClientLogsDirectory, $"client-{d:yyyy-MM-dd}.log");
}
/// <summary>
/// User's LocalAppData, or SYSTEM profile when no interactive session (service at login screen).
/// </summary>
public static string ResolveLocalAppDataRoot()
{
if (!string.IsNullOrEmpty(_cachedLocalAppDataRoot))
return _cachedLocalAppDataRoot;
var interactive = InteractiveUserPath.TryGetLocalAppDataPath();
if (!string.IsNullOrWhiteSpace(interactive))
return _cachedLocalAppDataRoot = interactive;
return _cachedLocalAppDataRoot =
Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
}
}