elfisa-pharmacy/src/ElectronicPharmacy/Classes/AppConfig.cs
Exest 3039c324be Add in-app auto-updater that preserves local database.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-31 13:44:44 +03:00

261 lines
8.8 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using System;
using System.IO;
using Электроннаяармация.Properties;
namespace Электроннаяармация.Classes
{
/// <summary>
/// Shared application paths and API settings.
/// </summary>
public static class AppConfig
{
public const string DefaultApiBaseUrl = "https://24pharmdata.ru";
public const string DefaultUpdateCheckBaseUrl = "https://git.24pharmdata.ru";
public const string DefaultUpdateRepo = "pharmdata/elfisa-pharmacy";
private const string LegacyApiBaseUrl = "http://195.34.241.84:9988";
private const string ApiBaseUrlEnvVar = "ELF_API_BASE_URL";
private static bool _sqliteInteropPrepared;
private static readonly object _sqliteInteropLock = new object();
/// <summary>
/// Replaces known dead API URLs in saved user settings.
/// </summary>
/// <summary>
/// Carries user settings across AssemblyVersion bumps (new user.config folder).
/// </summary>
public static void UpgradeUserSettingsIfNeeded()
{
try
{
if (Settings.Default.UpgradeSettings)
{
Settings.Default.Upgrade();
Settings.Default.UpgradeSettings = false;
Settings.Default.Save();
AppDebugLog.Info("AppConfig", "User settings upgraded from previous version");
}
}
catch (Exception ex)
{
AppDebugLog.Info("AppConfig", $"Upgrade settings skipped: {ex.Message}");
}
}
public static void MigrateLegacySettings()
{
var current = Settings.Default.ApiBaseUrl;
if (string.IsNullOrWhiteSpace(current))
{
return;
}
var normalized = NormalizeApiBaseUrl(current);
if (string.Equals(normalized, LegacyApiBaseUrl, StringComparison.OrdinalIgnoreCase) ||
string.Equals(normalized, "http://195.34.241.84:9988", StringComparison.OrdinalIgnoreCase))
{
Settings.Default.ApiBaseUrl = DefaultApiBaseUrl;
Settings.Default.stringToken = string.Empty;
Settings.Default.Save();
AppDebugLog.Info("AppConfig", $"ApiBaseUrl мигрирован: {LegacyApiBaseUrl} -> {DefaultApiBaseUrl}");
}
}
public static string ApiBaseUrl
{
get
{
var fromSettings = Settings.Default.ApiBaseUrl;
if (!string.IsNullOrWhiteSpace(fromSettings))
{
return NormalizeApiBaseUrl(fromSettings);
}
var fromEnv = Environment.GetEnvironmentVariable(ApiBaseUrlEnvVar);
if (!string.IsNullOrWhiteSpace(fromEnv))
{
return NormalizeApiBaseUrl(fromEnv);
}
return DefaultApiBaseUrl;
}
}
public static string NormalizeApiBaseUrl(string url)
{
if (string.IsNullOrWhiteSpace(url))
{
return DefaultApiBaseUrl;
}
url = url.Trim().TrimEnd('/');
if (!url.StartsWith("http://", StringComparison.OrdinalIgnoreCase) &&
!url.StartsWith("https://", StringComparison.OrdinalIgnoreCase))
{
url = "http://" + url;
}
return url;
}
public static string SqliteDbPath
{
get
{
var baseDir = AppDomain.CurrentDomain.BaseDirectory;
var root = Path.Combine(baseDir, "efClient.db");
var data = Path.Combine(baseDir, "Data", "efClient.db");
if (File.Exists(root))
{
return root;
}
if (File.Exists(data))
{
return data;
}
return root;
}
}
public static string SqliteConnectionString
{
get
{
EnsureSQLiteInteropInBaseDir();
return $"Data Source={SqliteDbPath};Version=3;New=False;";
}
}
public static string LocationId
{
get { return Settings.Default.LocationId ?? string.Empty; }
}
public static void SetLocationId(string locationId)
{
Settings.Default.LocationId = (locationId ?? string.Empty).Trim();
Settings.Default.Save();
}
public static string InvoiceExportPath
{
get { return Settings.Default.InvoiceExportPath ?? string.Empty; }
}
public static void SetInvoiceExportPath(string path)
{
Settings.Default.InvoiceExportPath = (path ?? string.Empty).Trim();
Settings.Default.Save();
}
/// <summary>
/// Gitea base URL for forced update checks (no trailing slash).
/// </summary>
public static string UpdateCheckBaseUrl
{
get
{
var fromEnv = Environment.GetEnvironmentVariable("ELF_UPDATE_CHECK_BASE_URL");
if (!string.IsNullOrWhiteSpace(fromEnv))
{
return NormalizeApiBaseUrl(fromEnv);
}
var fromSettings = Settings.Default.UpdateCheckBaseUrl;
if (!string.IsNullOrWhiteSpace(fromSettings))
{
return NormalizeApiBaseUrl(fromSettings);
}
return DefaultUpdateCheckBaseUrl;
}
}
/// <summary>
/// owner/repo on Gitea, e.g. pharmdata/elfisa-pharmacy.
/// </summary>
public static string UpdateRepo
{
get
{
var fromEnv = Environment.GetEnvironmentVariable("ELF_UPDATE_REPO");
if (!string.IsNullOrWhiteSpace(fromEnv))
{
return fromEnv.Trim().Trim('/');
}
var fromSettings = Settings.Default.UpdateRepo;
if (!string.IsNullOrWhiteSpace(fromSettings))
{
return fromSettings.Trim().Trim('/');
}
return DefaultUpdateRepo;
}
}
/// <summary>
/// Optional read-only Gitea token for private release repos.
/// </summary>
public static string UpdateApiToken
{
get
{
var fromEnv = Environment.GetEnvironmentVariable("ELF_UPDATE_API_TOKEN");
if (!string.IsNullOrWhiteSpace(fromEnv))
{
return fromEnv.Trim();
}
return Settings.Default.UpdateApiToken ?? string.Empty;
}
}
private static void EnsureSQLiteInteropInBaseDir()
{
// System.Data.SQLite ожидает SQLite.Interop.dll рядом с exe.
// Важно: разрядность DLL должна совпадать с процессом (Prefer32Bit => x86).
if (_sqliteInteropPrepared)
{
return;
}
lock (_sqliteInteropLock)
{
if (_sqliteInteropPrepared)
{
return;
}
var baseDir = AppDomain.CurrentDomain.BaseDirectory;
var targetDll = Path.Combine(baseDir, "SQLite.Interop.dll");
var archFolder = Environment.Is64BitProcess ? "x64" : "x86";
var sourceDll = Path.Combine(baseDir, archFolder, "SQLite.Interop.dll");
var sourcePdb = Path.Combine(baseDir, archFolder, "SQLite.Interop.pdb");
try
{
if (File.Exists(sourceDll))
{
// Всегда подкладываем DLL нужной разрядности (даже если в корне уже лежит "чужая").
File.Copy(sourceDll, targetDll, overwrite: true);
var targetPdb = Path.Combine(baseDir, "SQLite.Interop.pdb");
if (File.Exists(sourcePdb))
{
File.Copy(sourcePdb, targetPdb, overwrite: true);
}
}
}
catch
{
// Если копирование не удалось (права/readonly/и т.п.), дальше будет явное исключение от SQLite.
}
_sqliteInteropPrepared = true;
}
}
}
}