Add forced update check against Gitea releases.

Block startup when a newer Release exists; keep offline pharmacies working.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Exest 2026-07-31 12:58:36 +03:00
parent 09535e9b02
commit d4e7ab3b37
12 changed files with 697 additions and 20 deletions

24
docs/gitea-updates.md Normal file
View File

@ -0,0 +1,24 @@
# Gitea + принудительные обновления десктопа
Полная инструкция по установке Gitea: в репозитории `source``docs/gitea/`.
Клиент при старте (`ElectroPharmacy_Load`) вызывает Gitea API:
`GET {UpdateCheckBaseUrl}/api/v1/repos/{UpdateRepo}/releases/latest`
Настройки (можно сменить без пересборки через user settings):
| Ключ | По умолчанию |
|------|----------------|
| `UpdateCheckBaseUrl` | `https://git.24pharmdata.ru` |
| `UpdateRepo` | `pharmdata/elfisa-pharmacy` |
| `UpdateApiToken` | пусто (нужен для private releases) |
## Регламент релиза
1. Поднять `AssemblyVersion` / `AssemblyFileVersion` в `Properties/AssemblyInfo.cs`.
2. `git tag vX.Y.Z && git push gitea master --tags`
3. `powershell -File dist\build-release.ps1`
4. Gitea → Releases → New Release → прикрепить ZIP.
Старый клиент с меньшей версией увидит модалку «Доступно новое обновление» и не пустит в работу.

View File

@ -42,6 +42,15 @@
<setting name="InvoiceExportPath" serializeAs="String"> <setting name="InvoiceExportPath" serializeAs="String">
<value /> <value />
</setting> </setting>
<setting name="UpdateCheckBaseUrl" serializeAs="String">
<value>https://git.24pharmdata.ru</value>
</setting>
<setting name="UpdateRepo" serializeAs="String">
<value>pharmdata/elfisa-pharmacy</value>
</setting>
<setting name="UpdateApiToken" serializeAs="String">
<value />
</setting>
</Электронная_Фармация.Properties.Settings> </Электронная_Фармация.Properties.Settings>
</userSettings> </userSettings>
</configuration> </configuration>

View File

@ -10,6 +10,8 @@ namespace Электронная_Фармация.Classes
public static class AppConfig public static class AppConfig
{ {
public const string DefaultApiBaseUrl = "https://24pharmdata.ru"; 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 LegacyApiBaseUrl = "http://195.34.241.84:9988";
private const string ApiBaseUrlEnvVar = "ELF_API_BASE_URL"; private const string ApiBaseUrlEnvVar = "ELF_API_BASE_URL";
private static bool _sqliteInteropPrepared; private static bool _sqliteInteropPrepared;
@ -126,6 +128,69 @@ namespace Электронная_Фармация.Classes
Settings.Default.Save(); 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() private static void EnsureSQLiteInteropInBaseDir()
{ {
// System.Data.SQLite ожидает SQLite.Interop.dll рядом с exe. // System.Data.SQLite ожидает SQLite.Interop.dll рядом с exe.

View File

@ -0,0 +1,302 @@
using System;
using System.Linq;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Reflection;
using System.Text.Json;
using System.Text.Json.Serialization;
using System.Threading.Tasks;
namespace Электроннаяармация.Classes
{
public sealed class UpdateCheckResult
{
public bool CheckedOk { get; set; }
public bool UpdateRequired { get; set; }
public string LocalVersion { get; set; }
public string LatestVersion { get; set; }
public string ReleaseUrl { get; set; }
public string DownloadUrl { get; set; }
public string ErrorMessage { get; set; }
}
/// <summary>
/// Checks Gitea Releases API for a newer desktop build.
/// </summary>
public static class UpdateChecker
{
private static readonly JsonSerializerOptions JsonOptions = new JsonSerializerOptions
{
PropertyNameCaseInsensitive = true
};
public static UpdateCheckResult CheckForUpdate()
{
return CheckForUpdate(null, null, null, null);
}
/// <summary>
/// Overload for tests / smoke scripts (null = use AppConfig).
/// </summary>
public static UpdateCheckResult CheckForUpdate(
string baseUrl,
string repo,
string token,
string localVersionOverride)
{
try
{
return CheckForUpdateAsync(baseUrl, repo, token, localVersionOverride)
.GetAwaiter().GetResult();
}
catch (Exception ex)
{
AppDebugLog.Info("UpdateChecker", $"Проверка обновлений пропущена: {ex.Message}");
return new UpdateCheckResult
{
CheckedOk = false,
UpdateRequired = false,
LocalVersion = localVersionOverride ?? GetLocalVersionString(),
ErrorMessage = ex.Message
};
}
}
public static async Task<UpdateCheckResult> CheckForUpdateAsync()
{
return await CheckForUpdateAsync(null, null, null, null).ConfigureAwait(false);
}
public static async Task<UpdateCheckResult> CheckForUpdateAsync(
string baseUrlOverride,
string repoOverride,
string tokenOverride,
string localVersionOverride)
{
var localVersion = string.IsNullOrWhiteSpace(localVersionOverride)
? GetLocalVersionString()
: NormalizeVersion(localVersionOverride);
var result = new UpdateCheckResult
{
LocalVersion = localVersion
};
var baseUrl = (baseUrlOverride ?? AppConfig.UpdateCheckBaseUrl).TrimEnd('/');
var repo = (repoOverride ?? AppConfig.UpdateRepo).Trim('/');
if (string.IsNullOrWhiteSpace(baseUrl) || string.IsNullOrWhiteSpace(repo))
{
result.CheckedOk = false;
result.ErrorMessage = "UpdateCheckBaseUrl/UpdateRepo не заданы";
return result;
}
var url = $"{baseUrl}/api/v1/repos/{repo}/releases/latest";
using (var client = new HttpClient { Timeout = TimeSpan.FromSeconds(8) })
{
var token = tokenOverride ?? AppConfig.UpdateApiToken;
if (!string.IsNullOrWhiteSpace(token))
{
client.DefaultRequestHeaders.Authorization =
new AuthenticationHeaderValue("token", token.Trim());
}
client.DefaultRequestHeaders.Accept.Add(
new MediaTypeWithQualityHeaderValue("application/json"));
AppDebugLog.Info("UpdateChecker", $"GET {url}");
HttpResponseMessage response;
try
{
response = await client.GetAsync(url).ConfigureAwait(false);
}
catch (Exception ex)
{
// Offline pharmacies must continue working.
AppDebugLog.Info("UpdateChecker", $"Сеть недоступна, обновление не блокируем: {ex.Message}");
result.CheckedOk = false;
result.ErrorMessage = ex.Message;
return result;
}
var body = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
if (!response.IsSuccessStatusCode)
{
AppDebugLog.Info("UpdateChecker",
$"releases/latest HTTP {(int)response.StatusCode}: {Truncate(body, 200)}");
result.CheckedOk = false;
result.ErrorMessage = $"HTTP {(int)response.StatusCode}";
return result;
}
var release = JsonSerializer.Deserialize<GiteaRelease>(body, JsonOptions);
if (release == null || string.IsNullOrWhiteSpace(release.TagName))
{
result.CheckedOk = false;
result.ErrorMessage = "Пустой ответ releases/latest";
return result;
}
var latest = NormalizeVersion(release.TagName);
result.LatestVersion = latest;
result.ReleaseUrl = string.IsNullOrWhiteSpace(release.HtmlUrl)
? $"{baseUrl}/{repo}/releases/tag/{release.TagName}"
: release.HtmlUrl;
result.DownloadUrl = PickZipAssetUrl(release) ?? result.ReleaseUrl;
result.CheckedOk = true;
result.UpdateRequired = IsRemoteNewer(latest, localVersion);
AppDebugLog.Info("UpdateChecker",
$"Локально={localVersion}, latest={latest}, updateRequired={result.UpdateRequired}");
return result;
}
}
public static string GetLocalVersionString()
{
try
{
var asm = Assembly.GetExecutingAssembly();
var file = asm.GetCustomAttribute<AssemblyFileVersionAttribute>()?.Version;
if (!string.IsNullOrWhiteSpace(file))
{
return NormalizeVersion(file);
}
return NormalizeVersion(asm.GetName().Version?.ToString() ?? "0.0.0.0");
}
catch
{
return "0.0.0.0";
}
}
/// <summary>
/// Returns true when remoteTag is strictly newer than localVersion.
/// Accepts "v1.0.9", "1.0.9", "1.0.9.0".
/// </summary>
public static bool IsRemoteNewer(string remoteTag, string localVersion)
{
if (!TryParseVersion(remoteTag, out var remote) ||
!TryParseVersion(localVersion, out var local))
{
return false;
}
return remote > local;
}
public static string NormalizeVersion(string value)
{
if (string.IsNullOrWhiteSpace(value))
{
return "0.0.0.0";
}
value = value.Trim();
if (value.StartsWith("v", StringComparison.OrdinalIgnoreCase))
{
value = value.Substring(1);
}
// Drop pre-release suffix: 1.0.9-beta -> 1.0.9
var dash = value.IndexOf('-');
if (dash > 0)
{
value = value.Substring(0, dash);
}
if (TryParseVersion(value, out var v))
{
return v.ToString();
}
return value;
}
public static bool TryParseVersion(string value, out Version version)
{
version = null;
if (string.IsNullOrWhiteSpace(value))
{
return false;
}
value = value.Trim();
if (value.StartsWith("v", StringComparison.OrdinalIgnoreCase))
{
value = value.Substring(1);
}
var dash = value.IndexOf('-');
if (dash > 0)
{
value = value.Substring(0, dash);
}
var parts = value.Split('.');
if (parts.Length == 1)
{
value = value + ".0.0.0";
}
else if (parts.Length == 2)
{
value = value + ".0.0";
}
else if (parts.Length == 3)
{
value = value + ".0";
}
return Version.TryParse(value, out version);
}
private static string PickZipAssetUrl(GiteaRelease release)
{
if (release?.Assets == null || release.Assets.Length == 0)
{
return null;
}
var zip = release.Assets.FirstOrDefault(a =>
!string.IsNullOrWhiteSpace(a?.Name) &&
a.Name.EndsWith(".zip", StringComparison.OrdinalIgnoreCase));
if (zip == null)
{
zip = release.Assets.FirstOrDefault(a => !string.IsNullOrWhiteSpace(a?.BrowserDownloadUrl));
}
return zip?.BrowserDownloadUrl;
}
private static string Truncate(string s, int max)
{
if (string.IsNullOrEmpty(s) || s.Length <= max)
{
return s ?? string.Empty;
}
return s.Substring(0, max) + "...";
}
private sealed class GiteaRelease
{
[JsonPropertyName("tag_name")]
public string TagName { get; set; }
[JsonPropertyName("html_url")]
public string HtmlUrl { get; set; }
[JsonPropertyName("assets")]
public GiteaAsset[] Assets { get; set; }
}
private sealed class GiteaAsset
{
[JsonPropertyName("name")]
public string Name { get; set; }
[JsonPropertyName("browser_download_url")]
public string BrowserDownloadUrl { get; set; }
}
}
}

View File

@ -129,6 +129,7 @@
<Compile Include="Classes\SuppliersItem.cs" /> <Compile Include="Classes\SuppliersItem.cs" />
<Compile Include="Classes\SupplierResponse.cs" /> <Compile Include="Classes\SupplierResponse.cs" />
<Compile Include="Classes\ToastNotification.cs" /> <Compile Include="Classes\ToastNotification.cs" />
<Compile Include="Classes\UpdateChecker.cs" />
<Compile Include="Form1.cs"> <Compile Include="Form1.cs">
<SubType>Form</SubType> <SubType>Form</SubType>
</Compile> </Compile>
@ -201,6 +202,12 @@
<Compile Include="HelpForms\HF_Settings.Designer.cs"> <Compile Include="HelpForms\HF_Settings.Designer.cs">
<DependentUpon>HF_Settings.cs</DependentUpon> <DependentUpon>HF_Settings.cs</DependentUpon>
</Compile> </Compile>
<Compile Include="HelpForms\HF_UpdateAvailable.cs">
<SubType>Form</SubType>
</Compile>
<Compile Include="HelpForms\HF_UpdateAvailable.Designer.cs">
<DependentUpon>HF_UpdateAvailable.cs</DependentUpon>
</Compile>
<Compile Include="HelpForms\HF_UploadDataToServer.cs"> <Compile Include="HelpForms\HF_UploadDataToServer.cs">
<SubType>Form</SubType> <SubType>Form</SubType>
</Compile> </Compile>

View File

@ -67,6 +67,28 @@ namespace Электронная_Фармация
{ {
FinalizeModernShell(); FinalizeModernShell();
// Принудительное обновление: если на Gitea есть более новый Release — не пускаем в работу.
// При недоступной сети не блокируем (офлайн-аптека должна открываться).
try
{
var update = UpdateChecker.CheckForUpdate();
if (update != null && update.UpdateRequired)
{
using (var dlg = new HF_UpdateAvailable(update))
{
UiThemeHelper.ApplyToControlTree(dlg);
dlg.ShowDialog(this);
}
Application.Exit();
return;
}
}
catch (Exception ex)
{
AppDebugLog.Info("App", $"Проверка обновлений пропущена: {ex.Message}");
}
FCheckDatabaseIntegrary fCheckDatabaseIntegrary = new FCheckDatabaseIntegrary(); FCheckDatabaseIntegrary fCheckDatabaseIntegrary = new FCheckDatabaseIntegrary();
UiThemeHelper.ApplyToControlTree(fCheckDatabaseIntegrary); UiThemeHelper.ApplyToControlTree(fCheckDatabaseIntegrary);
fCheckDatabaseIntegrary.ShowDialog(); fCheckDatabaseIntegrary.ShowDialog();

View File

@ -0,0 +1,107 @@
namespace Электроннаяармация.HelpForms
{
partial class HF_UpdateAvailable
{
private System.ComponentModel.IContainer components = null;
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
private void InitializeComponent()
{
this.panelHeader = new System.Windows.Forms.Panel();
this.lblTitle = new System.Windows.Forms.Label();
this.lblMessage = new System.Windows.Forms.Label();
this.btnDownload = new Elfisa.UI.Controls.ModernButton();
this.btnExit = new Elfisa.UI.Controls.ModernButton();
this.panelHeader.SuspendLayout();
this.SuspendLayout();
//
// panelHeader
//
this.panelHeader.Controls.Add(this.lblTitle);
this.panelHeader.Dock = System.Windows.Forms.DockStyle.Top;
this.panelHeader.Location = new System.Drawing.Point(0, 0);
this.panelHeader.Name = "panelHeader";
this.panelHeader.Size = new System.Drawing.Size(520, 56);
this.panelHeader.TabIndex = 0;
//
// lblTitle
//
this.lblTitle.Font = new System.Drawing.Font("Segoe UI Semibold", 13.8F, System.Drawing.FontStyle.Bold);
this.lblTitle.ForeColor = System.Drawing.Color.White;
this.lblTitle.Location = new System.Drawing.Point(12, 10);
this.lblTitle.Name = "lblTitle";
this.lblTitle.Size = new System.Drawing.Size(480, 36);
this.lblTitle.TabIndex = 0;
this.lblTitle.Text = "Доступно новое обновление";
this.lblTitle.TextAlign = System.Drawing.ContentAlignment.MiddleLeft;
//
// lblMessage
//
this.lblMessage.Font = new System.Drawing.Font("Segoe UI", 11F);
this.lblMessage.Location = new System.Drawing.Point(24, 72);
this.lblMessage.Name = "lblMessage";
this.lblMessage.Size = new System.Drawing.Size(472, 120);
this.lblMessage.TabIndex = 1;
this.lblMessage.Text = "Доступно новое обновление";
//
// btnDownload
//
this.btnDownload.Font = new System.Drawing.Font("Segoe UI", 11F);
this.btnDownload.Location = new System.Drawing.Point(24, 220);
this.btnDownload.Name = "btnDownload";
this.btnDownload.Size = new System.Drawing.Size(180, 40);
this.btnDownload.TabIndex = 2;
this.btnDownload.Text = "Скачать";
this.btnDownload.Click += new System.EventHandler(this.btnDownload_Click);
//
// btnExit
//
this.btnExit.Font = new System.Drawing.Font("Segoe UI", 11F);
this.btnExit.Location = new System.Drawing.Point(316, 220);
this.btnExit.Name = "btnExit";
this.btnExit.Size = new System.Drawing.Size(180, 40);
this.btnExit.TabIndex = 3;
this.btnExit.Text = "Выход";
this.btnExit.Click += new System.EventHandler(this.btnExit_Click);
//
// HF_UpdateAvailable
//
this.AutoScaleDimensions = new System.Drawing.SizeF(8F, 16F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(520, 290);
this.Controls.Add(this.btnExit);
this.Controls.Add(this.btnDownload);
this.Controls.Add(this.lblMessage);
this.Controls.Add(this.panelHeader);
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog;
this.MaximizeBox = false;
this.MinimizeBox = false;
this.Name = "HF_UpdateAvailable";
this.ShowIcon = false;
this.ShowInTaskbar = false;
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
this.Text = "Обновление";
this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.HF_UpdateAvailable_FormClosing);
this.panelHeader.ResumeLayout(false);
this.ResumeLayout(false);
}
#endregion
private System.Windows.Forms.Panel panelHeader;
private System.Windows.Forms.Label lblTitle;
private System.Windows.Forms.Label lblMessage;
private Elfisa.UI.Controls.ModernButton btnDownload;
private Elfisa.UI.Controls.ModernButton btnExit;
}
}

View File

@ -0,0 +1,65 @@
using System;
using System.Diagnostics;
using System.Windows.Forms;
using Электроннаяармация.Classes;
namespace Электроннаяармация.HelpForms
{
public partial class HF_UpdateAvailable : Form
{
private readonly UpdateCheckResult _result;
public HF_UpdateAvailable(UpdateCheckResult result)
{
_result = result ?? throw new ArgumentNullException(nameof(result));
InitializeComponent();
UiThemeHelper.ApplyToControlTree(this);
var latest = string.IsNullOrWhiteSpace(_result.LatestVersion) ? "?" : _result.LatestVersion;
var local = string.IsNullOrWhiteSpace(_result.LocalVersion) ? "?" : _result.LocalVersion;
lblMessage.Text =
$"Доступно новое обновление {latest} (у вас {local}).{Environment.NewLine}{Environment.NewLine}" +
"Для продолжения работы необходимо установить новую версию.";
}
private void btnDownload_Click(object sender, EventArgs e)
{
var url = !string.IsNullOrWhiteSpace(_result.DownloadUrl)
? _result.DownloadUrl
: _result.ReleaseUrl;
if (string.IsNullOrWhiteSpace(url))
{
UiDialogs.ShowError("Ссылка на обновление не задана.", "Обновление", this);
return;
}
try
{
Process.Start(new ProcessStartInfo
{
FileName = url,
UseShellExecute = true
});
}
catch (Exception ex)
{
UiDialogs.ShowError($"Не удалось открыть ссылку:{Environment.NewLine}{ex.Message}", "Обновление", this);
}
}
private void btnExit_Click(object sender, EventArgs e)
{
DialogResult = DialogResult.Abort;
Close();
}
private void HF_UpdateAvailable_FormClosing(object sender, FormClosingEventArgs e)
{
// Крестик / Alt+F4 — тоже выход из приложения (не пускать в работу).
if (DialogResult != DialogResult.Abort)
{
DialogResult = DialogResult.Abort;
}
}
}
}

View File

@ -1,36 +1,36 @@
using System.Reflection; using System.Reflection;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
// Общие сведения об этой сборке предоставляются следующим набором // Общие сведения об этой сборке предоставляются следующим набором
// набора атрибутов. Измените значения этих атрибутов для изменения сведений, // набора атрибутов. Измените значения этих атрибутов для изменения сведений,
// связанных со сборкой. // связанных со сборкой.
[assembly: AssemblyTitle("Электронная Фармация")] [assembly: AssemblyTitle("Электронная Фармация")]
[assembly: AssemblyDescription("")] [assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")] [assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")] [assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("Электронная Фармация")] [assembly: AssemblyProduct("Электронная Фармация")]
[assembly: AssemblyCopyright("Copyright © 2023")] [assembly: AssemblyCopyright("Copyright © 2023")]
[assembly: AssemblyTrademark("")] [assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")] [assembly: AssemblyCulture("")]
// Установка значения False для параметра ComVisible делает типы в этой сборке невидимыми // Установка значения False для параметра ComVisible делает типы в этой сборке невидимыми
// для компонентов COM. Если необходимо обратиться к типу в этой сборке через // для компонентов COM. Если необходимо обратиться к типу в этой сборке через
// COM, следует установить атрибут ComVisible в TRUE для этого типа. // COM, следует установить атрибут ComVisible в TRUE для этого типа.
[assembly: ComVisible(false)] [assembly: ComVisible(false)]
// Следующий GUID служит для идентификации библиотеки типов, если этот проект будет видимым для COM // Следующий GUID служит для идентификации библиотеки типов, если этот проект будет видимым для COM
[assembly: Guid("145d8060-4187-4871-b4fb-8df795727b66")] [assembly: Guid("145d8060-4187-4871-b4fb-8df795727b66")]
// Сведения о версии сборки состоят из указанных ниже четырех значений: // Сведения о версии сборки состоят из указанных ниже четырех значений:
// //
// Основной номер версии // Основной номер версии
// Дополнительный номер версии // Дополнительный номер версии
// Номер сборки // Номер сборки
// Редакция // Редакция
// //
// Можно задать все значения или принять номера сборки и редакции по умолчанию // Можно задать все значения или принять номера сборки и редакции по умолчанию
// используя "*", как показано ниже: // используя "*", как показано ниже:
// [assembly: AssemblyVersion("1.0.*")] // [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.3.0")] [assembly: AssemblyVersion("1.0.10.0")]
[assembly: AssemblyFileVersion("1.0.3.0")] [assembly: AssemblyFileVersion("1.0.10.0")]

View File

@ -167,5 +167,41 @@ namespace Электронная_Фармация.Properties {
} }
} }
[global::System.Configuration.UserScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("https://git.24pharmdata.ru")]
public string UpdateCheckBaseUrl {
get {
return ((string)(this["UpdateCheckBaseUrl"]));
}
set {
this["UpdateCheckBaseUrl"] = value;
}
}
[global::System.Configuration.UserScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("pharmdata/elfisa-pharmacy")]
public string UpdateRepo {
get {
return ((string)(this["UpdateRepo"]));
}
set {
this["UpdateRepo"] = value;
}
}
[global::System.Configuration.UserScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("")]
public string UpdateApiToken {
get {
return ((string)(this["UpdateApiToken"]));
}
set {
this["UpdateApiToken"] = value;
}
}
} }
} }

View File

@ -38,5 +38,14 @@
<Setting Name="LastPriceReminderUtc" Type="System.DateTime" Scope="User"> <Setting Name="LastPriceReminderUtc" Type="System.DateTime" Scope="User">
<Value Profile="(Default)">01.01.0001 00:00:00</Value> <Value Profile="(Default)">01.01.0001 00:00:00</Value>
</Setting> </Setting>
<Setting Name="UpdateCheckBaseUrl" Type="System.String" Scope="User">
<Value Profile="(Default)">https://git.24pharmdata.ru</Value>
</Setting>
<Setting Name="UpdateRepo" Type="System.String" Scope="User">
<Value Profile="(Default)">pharmdata/elfisa-pharmacy</Value>
</Setting>
<Setting Name="UpdateApiToken" Type="System.String" Scope="User">
<Value Profile="(Default)" />
</Setting>
</Settings> </Settings>
</SettingsFile> </SettingsFile>

View File

@ -0,0 +1,31 @@
using System;
using Электроннаяармация.Classes;
namespace UpdateCheckSmoke
{
internal static class Program
{
private static int Main(string[] args)
{
// args: baseUrl repo localVersion
var baseUrl = args.Length > 0 ? args[0] : "http://127.0.0.1:18765";
var repo = args.Length > 1 ? args[1] : "pharmdata/elfisa-pharmacy";
var local = args.Length > 2 ? args[2] : "1.0.3.0";
var result = UpdateChecker.CheckForUpdate(baseUrl, repo, null, local);
Console.WriteLine("CheckedOk=" + result.CheckedOk);
Console.WriteLine("UpdateRequired=" + result.UpdateRequired);
Console.WriteLine("Local=" + result.LocalVersion);
Console.WriteLine("Latest=" + result.LatestVersion);
Console.WriteLine("Download=" + result.DownloadUrl);
Console.WriteLine("Error=" + result.ErrorMessage);
if (!result.CheckedOk)
{
return 2;
}
return result.UpdateRequired ? 0 : 1;
}
}
}