feat: add audited one-shot prepare gate
This commit is contained in:
@@ -2,6 +2,7 @@ using System.Runtime.InteropServices;
|
||||
using MBN_STOCK_WEBVIEW.Infrastructure;
|
||||
using MBN_STOCK_WEBVIEW.LegacyApplication;
|
||||
using MBN_STOCK_WEBVIEW.LegacyBridge;
|
||||
using MBN_STOCK_WEBVIEW.Playout.Safety;
|
||||
using Microsoft.UI.Windowing;
|
||||
using Microsoft.Web.WebView2.Core;
|
||||
using MMoneyCoderSharp.Data;
|
||||
@@ -21,6 +22,7 @@ public sealed partial class MainWindow : Window
|
||||
|
||||
private readonly CancellationTokenSource _lifetimeCancellation = new();
|
||||
private readonly SemaphoreSlim _intentGate = new(1, 1);
|
||||
private readonly PlayoutLaunchAuthorization _playoutLaunchAuthorization;
|
||||
private readonly WindowSubclassProcedure _windowSubclassProcedure;
|
||||
private readonly LegacyOperatorController _controller;
|
||||
private readonly LegacyIndustrySelectionWorkflow _industryWorkflow;
|
||||
@@ -36,6 +38,10 @@ public sealed partial class MainWindow : Window
|
||||
|
||||
public MainWindow()
|
||||
{
|
||||
// Capture and remove Gate A bearer material before XAML can create WebView2.
|
||||
// Only the native process keeps the capability digest for this launch.
|
||||
_playoutLaunchAuthorization =
|
||||
PlayoutLaunchAuthorization.CaptureFromEnvironment();
|
||||
_windowSubclassProcedure = OnWindowSubclassMessage;
|
||||
InitializeComponent();
|
||||
EnsureCloseConfirmationAttached();
|
||||
@@ -72,6 +78,8 @@ public sealed partial class MainWindow : Window
|
||||
{
|
||||
_databaseRuntime = DatabaseRuntime.CreateDefault();
|
||||
DataQueryExecutor.Configure(_databaseRuntime.Executor);
|
||||
var databaseMutationAuthorization =
|
||||
new LaunchDatabaseMutationAuthorization(_playoutLaunchAuthorization);
|
||||
stockLookup = new LegacyParityStockSearchService(_databaseRuntime.Executor);
|
||||
industrySelectionService = new LegacyIndustrySelectionService(
|
||||
_databaseRuntime.Executor);
|
||||
@@ -91,17 +99,23 @@ public sealed partial class MainWindow : Window
|
||||
_databaseRuntime.Executor,
|
||||
new OracleManualFinancialMutationExecutor(
|
||||
_databaseRuntime.ConnectionFactory,
|
||||
_databaseRuntime.Options.Resilience));
|
||||
_databaseRuntime.Options.Resilience,
|
||||
errorDetector: null,
|
||||
mutationAuthorization: databaseMutationAuthorization));
|
||||
manualFinancialStockSearchService = new LegacyStockSearchService(
|
||||
_databaseRuntime.Executor);
|
||||
namedPlaylistPersistenceService = new LegacyNamedPlaylistPersistenceService(
|
||||
_databaseRuntime.Executor,
|
||||
new OracleNamedPlaylistMutationExecutor(
|
||||
_databaseRuntime.ConnectionFactory,
|
||||
_databaseRuntime.Options.Resilience));
|
||||
_databaseRuntime.Options.Resilience,
|
||||
errorDetector: null,
|
||||
mutationAuthorization: databaseMutationAuthorization));
|
||||
var operatorCatalogMutationExecutor = new OperatorCatalogMutationExecutor(
|
||||
_databaseRuntime.ConnectionFactory,
|
||||
_databaseRuntime.Options.Resilience);
|
||||
_databaseRuntime.Options.Resilience,
|
||||
errorDetector: null,
|
||||
mutationAuthorization: databaseMutationAuthorization);
|
||||
themeCatalogPersistenceService = new LegacyThemeCatalogPersistenceService(
|
||||
_databaseRuntime.Executor,
|
||||
operatorCatalogMutationExecutor);
|
||||
@@ -377,6 +391,15 @@ public sealed partial class MainWindow : Window
|
||||
return;
|
||||
}
|
||||
|
||||
if (_playoutLaunchAuthorization.IsGateA &&
|
||||
IsPersistentWriteIntent(intent))
|
||||
{
|
||||
PostState(ReportIntentFailure(
|
||||
intent,
|
||||
"Gate A 검증 회차에서는 DB 및 로컬 영구 저장 변경이 허용되지 않습니다."));
|
||||
return;
|
||||
}
|
||||
|
||||
if (_controller.Current.Dialog?.IsConfirmation == true &&
|
||||
intent is not LegacyReadyIntent and
|
||||
not LegacyConfirmNativeDialogIntent and
|
||||
@@ -469,6 +492,7 @@ public sealed partial class MainWindow : Window
|
||||
LegacySaveCurrentNamedPlaylistToIntent or
|
||||
LegacyDeleteSelectedNamedPlaylistIntent or
|
||||
LegacyExecutePlayoutIntent or
|
||||
LegacyGateAPrepareIntent or
|
||||
LegacySetFadeDurationIntent or
|
||||
LegacyToggleBackgroundIntent or
|
||||
LegacyChooseBackgroundIntent)
|
||||
@@ -961,6 +985,10 @@ public sealed partial class MainWindow : Window
|
||||
await _controller.ConfirmManualListsAsync(cancellationToken),
|
||||
LegacyImportManualListsIntent =>
|
||||
await _controller.ImportLegacyManualListsAsync(cancellationToken),
|
||||
LegacyGateAPrepareIntent prepare =>
|
||||
await ExecuteGateAPrepareAsync(
|
||||
prepare.Capability,
|
||||
cancellationToken),
|
||||
LegacyExecutePlayoutIntent playout =>
|
||||
await ExecuteOperatorPlayoutAsync(
|
||||
playout.Command,
|
||||
@@ -1038,6 +1066,25 @@ public sealed partial class MainWindow : Window
|
||||
outcome is LegacyNamedPlaylistMutationOutcome.CommittedFresh or
|
||||
LegacyNamedPlaylistMutationOutcome.CommittedOptimistic;
|
||||
|
||||
private sealed class LaunchDatabaseMutationAuthorization
|
||||
: IDatabaseMutationAuthorization
|
||||
{
|
||||
private readonly PlayoutLaunchAuthorization _authorization;
|
||||
|
||||
public LaunchDatabaseMutationAuthorization(
|
||||
PlayoutLaunchAuthorization authorization)
|
||||
{
|
||||
_authorization = authorization ??
|
||||
throw new ArgumentNullException(nameof(authorization));
|
||||
}
|
||||
|
||||
public bool IsAuthorized(DatabaseMutationAuthorizationRequest request)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(request);
|
||||
return _authorization.AllowsDatabaseWrites;
|
||||
}
|
||||
}
|
||||
|
||||
private void PostState(LegacyOperatorSnapshot snapshot)
|
||||
{
|
||||
if (!_closing && _webViewReady && Browser.CoreWebView2 is not null)
|
||||
|
||||
Reference in New Issue
Block a user