-
Notifications
You must be signed in to change notification settings - Fork 13
Expand file tree
/
Copy pathStartup.cs
More file actions
214 lines (181 loc) · 9.23 KB
/
Copy pathStartup.cs
File metadata and controls
214 lines (181 loc) · 9.23 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
namespace Opc.Ua.Cloud.Publisher
{
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.DataProtection;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Opc.Ua.Cloud.Publisher.Configuration;
using Opc.Ua.Cloud.Publisher.Interfaces;
using Radzen;
using System;
using System.IO;
using System.Threading.Tasks;
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
// basic authentication for the UI is mandatory: fail fast if the credentials are not configured
string basicAuthUsername = Environment.GetEnvironmentVariable(BasicAuthMiddleware.UsernameEnvVar);
string basicAuthPassword = Environment.GetEnvironmentVariable(BasicAuthMiddleware.PasswordEnvVar);
if (string.IsNullOrEmpty(basicAuthUsername) || string.IsNullOrEmpty(basicAuthPassword))
{
throw new InvalidOperationException(
$"Basic authentication credentials are not configured. Please set the '{BasicAuthMiddleware.UsernameEnvVar}' and '{BasicAuthMiddleware.PasswordEnvVar}' environment variables.");
}
// persist the Data Protection key ring to a stable location so session cookies remain
// decryptable across app/container restarts
string keyRingPath = Path.Combine(Directory.GetCurrentDirectory(), "settings", "dataprotection-keys");
Directory.CreateDirectory(keyRingPath);
services.AddDataProtection()
.PersistKeysToFileSystem(new DirectoryInfo(keyRingPath))
.SetApplicationName("UACloudPublisher");
services.AddSession(options =>
{
options.IdleTimeout = TimeSpan.FromMinutes(5);
// use a distinct cookie name so stale cookies issued before Data Protection key persistence
// was configured are ignored (rather than failing to unprotect and logging warnings), and
// set standard hardening flags
options.Cookie.Name = ".UACloudPublisher.Session";
options.Cookie.HttpOnly = true;
options.Cookie.IsEssential = true;
});
services.AddControllersWithViews();
services.AddRazorPages();
services.AddServerSideBlazor();
services.AddRadzenComponents();
services.AddHttpClient();
// add our singletons
services.AddSingleton<IUAApplication, UAApplication>();
services.AddSingleton<IUAClient, UAClient>();
services.AddSingleton<KafkaClient>();
services.AddSingleton<MQTTClient>();
services.AddSingleton<Settings.BrokerResolver>(serviceProvider => key =>
{
switch (key)
{
case "MQTT":
return serviceProvider.GetService<MQTTClient>();
case "Kafka":
return serviceProvider.GetService<KafkaClient>();
default:
return null;
}
});
services.AddSingleton<IPublishedNodesFileHandler, PublishedNodesFileHandler>();
services.AddSingleton<ICommandProcessor, CommandProcessor>();
// add our message processing engine
services.AddSingleton<IMessageProcessor, MessageProcessor>();
services.AddSingleton<IMessageSource, MonitoredItemNotification>();
services.AddSingleton<IMessageEncoder, PubSubTelemetryEncoder>();
services.AddSingleton<IMessagePublisher, StoreForwardPublisher>();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app,
IWebHostEnvironment env,
ILogger<Startup> logger,
ILoggerFactory loggerFactory,
IUAApplication uaApp,
IMessageProcessor engine,
IMessagePublisher messagePublisher,
Settings.BrokerResolver brokerResolver,
IPublishedNodesFileHandler publishedNodesFileHandler)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Browser/Error");
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
// enforce mandatory HTTP Basic authentication for the whole application
app.UseMiddleware<BasicAuthMiddleware>();
app.UseSession();
app.UseRouting();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
endpoints.MapBlazorHub();
});
// do all further initialization on a background thread to load the webserver independently
_ = Task.Run(async () =>
{
try
{
// kick off the task to show periodic diagnostic info (fire-and-forget)
_ = Task.Run(async () => await Diagnostics.Singleton.RunAsync().ConfigureAwait(false));
// create our app
await uaApp.CreateAsync().ConfigureAwait(false);
IBrokerClient broker;
IBrokerClient altBroker;
if (Settings.Instance.UseKafka)
{
broker = brokerResolver("Kafka");
}
else
{
broker = brokerResolver("MQTT");
}
// connect to broker
await broker.ConnectAsync().ConfigureAwait(false);
// check if we need a second broker (for receiving UA over MQTT,
// or for sending metadata via a different broker kind than the primary)
bool altKindDiffersForMetadata = Settings.Instance.UseAltBrokerForMetadata && (Settings.Instance.UseKafkaForAlt != Settings.Instance.UseKafka);
if (altKindDiffersForMetadata)
{
altBroker = brokerResolver(Settings.Instance.UseKafkaForAlt ? "Kafka" : "MQTT");
await altBroker.ConnectAsync(true).ConfigureAwait(false);
if (altKindDiffersForMetadata)
{
messagePublisher.ApplyAltClient(altBroker);
}
}
// run the telemetry engine (fire-and-forget)
_ = Task.Run(async () => await engine.RunAsync().ConfigureAwait(false));
// load our persistency file
if (Settings.Instance.AutoLoadPersistedNodes)
{
try
{
string persistencyFilePath = Path.Combine(Directory.GetCurrentDirectory(), "settings", "persistency.json");
if (!File.Exists(persistencyFilePath))
{
// no file persisted yet
logger.LogInformation("No persistency file found at {Path}; skipping auto-load.", persistencyFilePath);
}
else
{
byte[] persistencyFile = await File.ReadAllBytesAsync(persistencyFilePath).ConfigureAwait(false);
// parse the file (fire-and-forget)
_ = Task.Run(async () => await publishedNodesFileHandler.ParseFileAsync(persistencyFile).ConfigureAwait(false));
}
}
catch (Exception ex)
{
logger.LogError(ex, "Failed to auto-load persisted nodes.");
}
}
}
catch (Exception ex)
{
logger.LogError(ex, "Background initialization failed.");
}
});
}
}
}