feat(terminal): add scheduled automatic checkout
Some checks failed
ci / verify (push) Has been cancelled
Some checks failed
ci / verify (push) Has been cancelled
This commit is contained in:
parent
4d37350757
commit
b4eb3432ae
12 changed files with 147 additions and 12 deletions
|
|
@ -1,7 +1,7 @@
|
||||||
openapi: 3.1.0
|
openapi: 3.1.0
|
||||||
info:
|
info:
|
||||||
title: Krona Staff API
|
title: Krona Staff API
|
||||||
version: 0.2.4
|
version: 0.2.5
|
||||||
description: Operational API for the Krona Staff foundation. HR domain endpoints are intentionally absent until owner requirements are approved.
|
description: Operational API for the Krona Staff foundation. HR domain endpoints are intentionally absent until owner requirements are approved.
|
||||||
license:
|
license:
|
||||||
name: Proprietary
|
name: Proprietary
|
||||||
|
|
|
||||||
|
|
@ -17,10 +17,10 @@
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
<PlatformTarget>x64</PlatformTarget>
|
<PlatformTarget>x64</PlatformTarget>
|
||||||
<Version>0.2.4</Version>
|
<Version>0.2.5</Version>
|
||||||
<AssemblyVersion>0.2.4.0</AssemblyVersion>
|
<AssemblyVersion>0.2.5.0</AssemblyVersion>
|
||||||
<FileVersion>0.2.4.0</FileVersion>
|
<FileVersion>0.2.5.0</FileVersion>
|
||||||
<InformationalVersion>0.2.4</InformationalVersion>
|
<InformationalVersion>0.2.5</InformationalVersion>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|
|
||||||
|
|
@ -225,6 +225,17 @@
|
||||||
<ToggleSwitch x:Name="StartWithWindowsSwitch" Header="Автозагрузка" OffContent="Выключена" OnContent="Запускать вместе с Windows"/>
|
<ToggleSwitch x:Name="StartWithWindowsSwitch" Header="Автозагрузка" OffContent="Выключена" OnContent="Запускать вместе с Windows"/>
|
||||||
<NumberBox x:Name="DuplicateGuardBox" Header="Защита от повторного скана, секунд" Minimum="10" Maximum="3600" SpinButtonPlacementMode="Compact"/>
|
<NumberBox x:Name="DuplicateGuardBox" Header="Защита от повторного скана, секунд" Minimum="10" Maximum="3600" SpinButtonPlacementMode="Compact"/>
|
||||||
<NumberBox x:Name="MaximumShiftHoursBox" Header="Максимальная длительность смены, часов" Minimum="1" Maximum="48" SpinButtonPlacementMode="Compact"/>
|
<NumberBox x:Name="MaximumShiftHoursBox" Header="Максимальная длительность смены, часов" Minimum="1" Maximum="48" SpinButtonPlacementMode="Compact"/>
|
||||||
|
<ToggleSwitch x:Name="AutomaticCheckoutSwitch"
|
||||||
|
Header="Автоматический уход"
|
||||||
|
OffContent="Выключен"
|
||||||
|
OnContent="Закрывать незавершённые смены ежедневно"/>
|
||||||
|
<TimePicker x:Name="AutomaticCheckoutTimePicker"
|
||||||
|
Header="Время автоматического ухода"
|
||||||
|
ClockIdentifier="24HourClock"
|
||||||
|
MinuteIncrement="5"/>
|
||||||
|
<TextBlock Text="Если сотрудник отметил приход, но не отметил уход, программа автоматически создаст уход в указанное время."
|
||||||
|
TextWrapping="Wrap"
|
||||||
|
Opacity="0.7"/>
|
||||||
<ToggleSwitch x:Name="ShowEmployeeNamesSwitch" Header="Имена в публичном журнале терминала" OffContent="Скрыты" OnContent="Показывать"/>
|
<ToggleSwitch x:Name="ShowEmployeeNamesSwitch" Header="Имена в публичном журнале терминала" OffContent="Скрыты" OnContent="Показывать"/>
|
||||||
<PasswordBox x:Name="NewAdminPinBox" Header="Новый PIN администратора" PasswordRevealMode="Peek"/>
|
<PasswordBox x:Name="NewAdminPinBox" Header="Новый PIN администратора" PasswordRevealMode="Peek"/>
|
||||||
<Button Content="Установить новый PIN" HorizontalAlignment="Left" Click="SetAdminPin_Click"/>
|
<Button Content="Установить новый PIN" HorizontalAlignment="Left" Click="SetAdminPin_Click"/>
|
||||||
|
|
|
||||||
|
|
@ -22,9 +22,11 @@ public sealed partial class MainPage : Page
|
||||||
private ContentDialog? _adminAuthDialog;
|
private ContentDialog? _adminAuthDialog;
|
||||||
private readonly DispatcherQueueTimer _adminSessionTimer;
|
private readonly DispatcherQueueTimer _adminSessionTimer;
|
||||||
private readonly DispatcherQueueTimer _updateTimer;
|
private readonly DispatcherQueueTimer _updateTimer;
|
||||||
|
private readonly DispatcherQueueTimer _automaticCheckoutTimer;
|
||||||
private readonly CancellationTokenSource _updateCancellation = new();
|
private readonly CancellationTokenSource _updateCancellation = new();
|
||||||
private AvailableUpdate? _availableUpdate;
|
private AvailableUpdate? _availableUpdate;
|
||||||
private bool _updateCheckInProgress;
|
private bool _updateCheckInProgress;
|
||||||
|
private bool _automaticCheckoutInProgress;
|
||||||
private bool _adminSessionActive;
|
private bool _adminSessionActive;
|
||||||
private bool _adminNavigationInProgress;
|
private bool _adminNavigationInProgress;
|
||||||
private bool _stateLoadFailed;
|
private bool _stateLoadFailed;
|
||||||
|
|
@ -71,6 +73,10 @@ public sealed partial class MainPage : Page
|
||||||
_updateTimer.Interval = TimeSpan.FromHours(6);
|
_updateTimer.Interval = TimeSpan.FromHours(6);
|
||||||
_updateTimer.IsRepeating = true;
|
_updateTimer.IsRepeating = true;
|
||||||
_updateTimer.Tick += (_, _) => EnqueueTask(() => CheckForUpdatesAsync(false));
|
_updateTimer.Tick += (_, _) => EnqueueTask(() => CheckForUpdatesAsync(false));
|
||||||
|
_automaticCheckoutTimer = DispatcherQueue.CreateTimer();
|
||||||
|
_automaticCheckoutTimer.Interval = TimeSpan.FromMinutes(1);
|
||||||
|
_automaticCheckoutTimer.IsRepeating = true;
|
||||||
|
_automaticCheckoutTimer.Tick += (_, _) => EnqueueTask(RunAutomaticCheckoutIfDueAsync);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async void Page_Loaded(object sender, RoutedEventArgs e)
|
private async void Page_Loaded(object sender, RoutedEventArgs e)
|
||||||
|
|
@ -95,6 +101,7 @@ public sealed partial class MainPage : Page
|
||||||
RefreshLists();
|
RefreshLists();
|
||||||
ConnectScanner();
|
ConnectScanner();
|
||||||
_updateTimer.Start();
|
_updateTimer.Start();
|
||||||
|
_automaticCheckoutTimer.Start();
|
||||||
MainTabs.SelectedItem = TerminalTab;
|
MainTabs.SelectedItem = TerminalTab;
|
||||||
LockScreenMessage.Text = SecurityConfigured
|
LockScreenMessage.Text = SecurityConfigured
|
||||||
? "Введите PIN администратора или приложите зарегистрированный палец."
|
? "Введите PIN администратора или приложите зарегистрированный палец."
|
||||||
|
|
@ -103,6 +110,7 @@ public sealed partial class MainPage : Page
|
||||||
{
|
{
|
||||||
SetStatus(_store.RecoveryNotice, InfoBarSeverity.Warning);
|
SetStatus(_store.RecoveryNotice, InfoBarSeverity.Warning);
|
||||||
}
|
}
|
||||||
|
await RunAutomaticCheckoutIfDueAsync();
|
||||||
await CheckForUpdatesAsync(false);
|
await CheckForUpdatesAsync(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -111,6 +119,7 @@ public sealed partial class MainPage : Page
|
||||||
_loaded = false;
|
_loaded = false;
|
||||||
_adminSessionTimer.Stop();
|
_adminSessionTimer.Stop();
|
||||||
_updateTimer.Stop();
|
_updateTimer.Stop();
|
||||||
|
_automaticCheckoutTimer.Stop();
|
||||||
_updateCancellation.Cancel();
|
_updateCancellation.Cancel();
|
||||||
_scanner.Dispose();
|
_scanner.Dispose();
|
||||||
}
|
}
|
||||||
|
|
@ -908,6 +917,107 @@ public sealed partial class MainPage : Page
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async Task RunAutomaticCheckoutIfDueAsync()
|
||||||
|
{
|
||||||
|
if (!_loaded || _stateLoadFailed || _automaticCheckoutInProgress ||
|
||||||
|
!_state.Settings.AutomaticCheckoutEnabled)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var nowUtc = DateTimeOffset.UtcNow;
|
||||||
|
var hour = Math.Clamp(_state.Settings.AutomaticCheckoutHour, 0, 23);
|
||||||
|
var minute = Math.Clamp(_state.Settings.AutomaticCheckoutMinute, 0, 59);
|
||||||
|
var latestScheduleUtc = LatestAutomaticCheckoutScheduleUtc(nowUtc, hour, minute);
|
||||||
|
if (_state.Settings.LastAutomaticCheckoutUtc >= latestScheduleUtc) return;
|
||||||
|
|
||||||
|
_automaticCheckoutInProgress = true;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var automaticRecords = new List<(AttendanceRecord Record, EmployeeRecord Employee)>();
|
||||||
|
foreach (var employee in _state.Employees)
|
||||||
|
{
|
||||||
|
var last = _state.Attendance
|
||||||
|
.Where(item => item.EmployeeId == employee.Id)
|
||||||
|
.OrderByDescending(item => item.OccurredAtUtc)
|
||||||
|
.FirstOrDefault();
|
||||||
|
if (last is null || last.EventType != "Приход") continue;
|
||||||
|
|
||||||
|
var checkoutUtc = NextAutomaticCheckoutAfter(last.OccurredAtUtc, hour, minute);
|
||||||
|
if (checkoutUtc > nowUtc) continue;
|
||||||
|
automaticRecords.Add((new AttendanceRecord
|
||||||
|
{
|
||||||
|
EmployeeId = employee.Id,
|
||||||
|
EmployeeName = employee.DisplayName,
|
||||||
|
OccurredAtUtc = checkoutUtc,
|
||||||
|
ReaderIndex = -1,
|
||||||
|
EventType = "Уход автоматически"
|
||||||
|
}, employee));
|
||||||
|
}
|
||||||
|
|
||||||
|
_state.Attendance.InsertRange(0, automaticRecords.Select(item => item.Record));
|
||||||
|
if (_state.Attendance.Count > 100_000)
|
||||||
|
{
|
||||||
|
_state.Attendance.RemoveRange(100_000, _state.Attendance.Count - 100_000);
|
||||||
|
}
|
||||||
|
_state.Settings.LastAutomaticCheckoutUtc = latestScheduleUtc;
|
||||||
|
AppendSecurityAudit(
|
||||||
|
"attendance.automatic_checkout",
|
||||||
|
"succeeded",
|
||||||
|
$"scheduled={latestScheduleUtc:O}; records={automaticRecords.Count}");
|
||||||
|
await _store.SaveAsync(_state);
|
||||||
|
RefreshLists();
|
||||||
|
|
||||||
|
if (automaticRecords.Count == 0) return;
|
||||||
|
TerminalMessage.Text = $"Автоматический уход\nЗакрыто смен: {automaticRecords.Count}";
|
||||||
|
SetStatus($"Автоматически закрыто смен: {automaticRecords.Count}.", InfoBarSeverity.Informational);
|
||||||
|
|
||||||
|
await Task.WhenAll(automaticRecords.Select(async item =>
|
||||||
|
{
|
||||||
|
var delivery = await _notifications.SendAttendanceAsync(
|
||||||
|
_state.Settings,
|
||||||
|
item.Employee,
|
||||||
|
item.Record.EventType,
|
||||||
|
item.Record.OccurredAtUtc.ToLocalTime());
|
||||||
|
item.Record.NotificationDelivered = delivery.Delivered;
|
||||||
|
item.Record.NotificationError = delivery.Error;
|
||||||
|
}));
|
||||||
|
await _store.SaveAsync(_state);
|
||||||
|
RefreshLists();
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
CrashLogService.Write(exception);
|
||||||
|
SetStatus($"Автоматический уход не выполнен: {exception.Message}", InfoBarSeverity.Warning);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
_automaticCheckoutInProgress = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static DateTimeOffset LatestAutomaticCheckoutScheduleUtc(DateTimeOffset nowUtc, int hour, int minute)
|
||||||
|
{
|
||||||
|
var localNow = nowUtc.ToLocalTime();
|
||||||
|
var localSchedule = localNow.Date.AddHours(hour).AddMinutes(minute);
|
||||||
|
if (localSchedule > localNow.DateTime) localSchedule = localSchedule.AddDays(-1);
|
||||||
|
return LocalScheduleToUtc(localSchedule);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static DateTimeOffset NextAutomaticCheckoutAfter(DateTimeOffset arrivalUtc, int hour, int minute)
|
||||||
|
{
|
||||||
|
var localArrival = arrivalUtc.ToLocalTime();
|
||||||
|
var localSchedule = localArrival.Date.AddHours(hour).AddMinutes(minute);
|
||||||
|
if (localSchedule <= localArrival.DateTime) localSchedule = localSchedule.AddDays(1);
|
||||||
|
return LocalScheduleToUtc(localSchedule);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static DateTimeOffset LocalScheduleToUtc(DateTime localSchedule)
|
||||||
|
{
|
||||||
|
localSchedule = DateTime.SpecifyKind(localSchedule, DateTimeKind.Unspecified);
|
||||||
|
if (TimeZoneInfo.Local.IsInvalidTime(localSchedule)) localSchedule = localSchedule.AddHours(1);
|
||||||
|
return new DateTimeOffset(TimeZoneInfo.ConvertTimeToUtc(localSchedule, TimeZoneInfo.Local));
|
||||||
|
}
|
||||||
private async void ClearAttendance_Click(object sender, RoutedEventArgs e)
|
private async void ClearAttendance_Click(object sender, RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
if (!await EnsureAdminSessionAsync("очистка журнала")) return;
|
if (!await EnsureAdminSessionAsync("очистка журнала")) return;
|
||||||
|
|
@ -1157,6 +1267,7 @@ public sealed partial class MainPage : Page
|
||||||
await _store.SaveAsync(_state, createRecoveryPoint: true);
|
await _store.SaveAsync(_state, createRecoveryPoint: true);
|
||||||
ApplyStartupSetting();
|
ApplyStartupSetting();
|
||||||
ConnectScanner();
|
ConnectScanner();
|
||||||
|
await RunAutomaticCheckoutIfDueAsync();
|
||||||
SetStatus("Настройки сохранены.", InfoBarSeverity.Success);
|
SetStatus("Настройки сохранены.", InfoBarSeverity.Success);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1293,6 +1404,9 @@ public sealed partial class MainPage : Page
|
||||||
_state.Settings.StartWithWindows = StartWithWindowsSwitch.IsOn;
|
_state.Settings.StartWithWindows = StartWithWindowsSwitch.IsOn;
|
||||||
_state.Settings.DuplicateScanGuardSeconds = double.IsNaN(DuplicateGuardBox.Value) ? 120 : Math.Clamp((int)DuplicateGuardBox.Value, 10, 3600);
|
_state.Settings.DuplicateScanGuardSeconds = double.IsNaN(DuplicateGuardBox.Value) ? 120 : Math.Clamp((int)DuplicateGuardBox.Value, 10, 3600);
|
||||||
_state.Settings.MaximumShiftHours = double.IsNaN(MaximumShiftHoursBox.Value) ? 20 : Math.Clamp((int)MaximumShiftHoursBox.Value, 1, 48);
|
_state.Settings.MaximumShiftHours = double.IsNaN(MaximumShiftHoursBox.Value) ? 20 : Math.Clamp((int)MaximumShiftHoursBox.Value, 1, 48);
|
||||||
|
_state.Settings.AutomaticCheckoutEnabled = AutomaticCheckoutSwitch.IsOn;
|
||||||
|
_state.Settings.AutomaticCheckoutHour = Math.Clamp(AutomaticCheckoutTimePicker.Time.Hours, 0, 23);
|
||||||
|
_state.Settings.AutomaticCheckoutMinute = Math.Clamp(AutomaticCheckoutTimePicker.Time.Minutes, 0, 59);
|
||||||
_state.Settings.ShowEmployeeNamesOnTerminal = ShowEmployeeNamesSwitch.IsOn;
|
_state.Settings.ShowEmployeeNamesOnTerminal = ShowEmployeeNamesSwitch.IsOn;
|
||||||
_state.Settings.TelegramEnabled = TelegramEnabledSwitch.IsOn;
|
_state.Settings.TelegramEnabled = TelegramEnabledSwitch.IsOn;
|
||||||
_state.Settings.TelegramBotToken = TelegramTokenBox.Password.Trim();
|
_state.Settings.TelegramBotToken = TelegramTokenBox.Password.Trim();
|
||||||
|
|
@ -1315,6 +1429,11 @@ public sealed partial class MainPage : Page
|
||||||
StartWithWindowsSwitch.IsOn = _state.Settings.StartWithWindows;
|
StartWithWindowsSwitch.IsOn = _state.Settings.StartWithWindows;
|
||||||
DuplicateGuardBox.Value = Math.Clamp(_state.Settings.DuplicateScanGuardSeconds, 10, 3600);
|
DuplicateGuardBox.Value = Math.Clamp(_state.Settings.DuplicateScanGuardSeconds, 10, 3600);
|
||||||
MaximumShiftHoursBox.Value = Math.Clamp(_state.Settings.MaximumShiftHours, 1, 48);
|
MaximumShiftHoursBox.Value = Math.Clamp(_state.Settings.MaximumShiftHours, 1, 48);
|
||||||
|
AutomaticCheckoutSwitch.IsOn = _state.Settings.AutomaticCheckoutEnabled;
|
||||||
|
AutomaticCheckoutTimePicker.Time = new TimeSpan(
|
||||||
|
Math.Clamp(_state.Settings.AutomaticCheckoutHour, 0, 23),
|
||||||
|
Math.Clamp(_state.Settings.AutomaticCheckoutMinute, 0, 59),
|
||||||
|
0);
|
||||||
ShowEmployeeNamesSwitch.IsOn = _state.Settings.ShowEmployeeNamesOnTerminal;
|
ShowEmployeeNamesSwitch.IsOn = _state.Settings.ShowEmployeeNamesOnTerminal;
|
||||||
TelegramEnabledSwitch.IsOn = _state.Settings.TelegramEnabled;
|
TelegramEnabledSwitch.IsOn = _state.Settings.TelegramEnabled;
|
||||||
TelegramTokenBox.Password = _state.Settings.TelegramBotToken;
|
TelegramTokenBox.Password = _state.Settings.TelegramBotToken;
|
||||||
|
|
|
||||||
|
|
@ -90,6 +90,10 @@ public sealed class TerminalSettings
|
||||||
public DateTimeOffset AdminPinBlockedUntilUtc { get; set; }
|
public DateTimeOffset AdminPinBlockedUntilUtc { get; set; }
|
||||||
public int DuplicateScanGuardSeconds { get; set; } = 120;
|
public int DuplicateScanGuardSeconds { get; set; } = 120;
|
||||||
public int MaximumShiftHours { get; set; } = 20;
|
public int MaximumShiftHours { get; set; } = 20;
|
||||||
|
public bool AutomaticCheckoutEnabled { get; set; }
|
||||||
|
public int AutomaticCheckoutHour { get; set; } = 2;
|
||||||
|
public int AutomaticCheckoutMinute { get; set; }
|
||||||
|
public DateTimeOffset LastAutomaticCheckoutUtc { get; set; }
|
||||||
public bool ShowEmployeeNamesOnTerminal { get; set; }
|
public bool ShowEmployeeNamesOnTerminal { get; set; }
|
||||||
public bool TelegramEnabled { get; set; }
|
public bool TelegramEnabled { get; set; }
|
||||||
public string TelegramBotToken { get; set; } = "";
|
public string TelegramBotToken { get; set; } = "";
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@
|
||||||
- несколько отпечатков у одного сотрудника, каждый регистрируется по трём последовательным сканам;
|
- несколько отпечатков у одного сотрудника, каждый регистрируется по трём последовательным сканам;
|
||||||
- чередование событий «Приход» и «Уход»;
|
- чередование событий «Приход» и «Уход»;
|
||||||
- настраиваемая защита от повторного сканирования, по умолчанию 120 секунд;
|
- настраиваемая защита от повторного сканирования, по умолчанию 120 секунд;
|
||||||
|
- ежедневный автоматический уход в заданное время для сотрудников, забывших отметить завершение смены;
|
||||||
- локальный журнал последних отметок;
|
- локальный журнал последних отметок;
|
||||||
- экспорт журнала в CSV и резервные копии зашифрованного состояния;
|
- экспорт журнала в CSV и резервные копии зашифрованного состояния;
|
||||||
- защищённая очистка журнала без удаления сотрудников и отпечатков;
|
- защищённая очистка журнала без удаления сотрудников и отпечатков;
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ public sealed class NotificationService
|
||||||
|
|
||||||
public async Task<(bool Delivered, string? Error)> SendAttendanceAsync(TerminalSettings settings, EmployeeRecord employee, string eventType, DateTimeOffset localTime)
|
public async Task<(bool Delivered, string? Error)> SendAttendanceAsync(TerminalSettings settings, EmployeeRecord employee, string eventType, DateTimeOffset localTime)
|
||||||
{
|
{
|
||||||
var action = eventType == "Уход" ? "уход" : "приход";
|
var action = eventType.StartsWith("Уход", StringComparison.Ordinal) ? "уход" : "приход";
|
||||||
return await SendAsync(settings, employee,
|
return await SendAsync(settings, employee,
|
||||||
$"Krona Staff: ваш {action} зарегистрирован {localTime:dd.MM.yyyy} в {localTime:HH:mm}.");
|
$"Krona Staff: ваш {action} зарегистрирован {localTime:dd.MM.yyyy} в {localTime:HH:mm}.");
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ SetCompressor /SOLID lzma
|
||||||
!include "MUI2.nsh"
|
!include "MUI2.nsh"
|
||||||
|
|
||||||
!ifndef APP_VERSION
|
!ifndef APP_VERSION
|
||||||
!define APP_VERSION "0.2.4"
|
!define APP_VERSION "0.2.5"
|
||||||
!endif
|
!endif
|
||||||
!ifndef PUBLISH_DIR
|
!ifndef PUBLISH_DIR
|
||||||
!error "PUBLISH_DIR is required"
|
!error "PUBLISH_DIR is required"
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
"$schema": "./schemas/module.schema.json",
|
"$schema": "./schemas/module.schema.json",
|
||||||
"id": "ru.krona.staff",
|
"id": "ru.krona.staff",
|
||||||
"name": "Krona Staff",
|
"name": "Krona Staff",
|
||||||
"version": "0.2.4",
|
"version": "0.2.5",
|
||||||
"min_platform_version": "3.2.0",
|
"min_platform_version": "3.2.0",
|
||||||
"dependencies": ["ru.krona.core"],
|
"dependencies": ["ru.krona.core"],
|
||||||
"optional_dependencies": [],
|
"optional_dependencies": [],
|
||||||
|
|
|
||||||
4
package-lock.json
generated
4
package-lock.json
generated
|
|
@ -1,12 +1,12 @@
|
||||||
{
|
{
|
||||||
"name": "@krona/staff",
|
"name": "@krona/staff",
|
||||||
"version": "0.2.4",
|
"version": "0.2.5",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "@krona/staff",
|
"name": "@krona/staff",
|
||||||
"version": "0.2.4",
|
"version": "0.2.5",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@fastify/helmet": "^13.0.2",
|
"@fastify/helmet": "^13.0.2",
|
||||||
"@fastify/sensible": "^6.0.3",
|
"@fastify/sensible": "^6.0.3",
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@krona/staff",
|
"name": "@krona/staff",
|
||||||
"version": "0.2.4",
|
"version": "0.2.5",
|
||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"engines": {
|
"engines": {
|
||||||
|
|
|
||||||
|
|
@ -1,2 +1,2 @@
|
||||||
export const SERVICE_NAME = 'krona-staff';
|
export const SERVICE_NAME = 'krona-staff';
|
||||||
export const SERVICE_VERSION = '0.2.4';
|
export const SERVICE_VERSION = '0.2.5';
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue