From dbac55648b6d9249668d555b5fa663f178cfa204 Mon Sep 17 00:00:00 2001 From: tgstation-server Date: Tue, 9 Jun 2020 02:11:58 +0000 Subject: [PATCH] Deploy code docs to GitHub Pages for Travis build 3467 Commit: e60ccadd73f2a53d8e17e67bad338afe4dcea819 --- ...12___p_g_create_8_designer_8cs_source.html | 2 +- 20200516111712___p_g_create_8cs_source.html | 4 +- ...___m_y_fix_foreign_key_8_designer_8cs.html | 122 +++++++++ ...fix_foreign_key_8_designer_8cs_source.html | 111 +++++++++ 20200529171541___m_y_fix_foreign_key_8cs.html | 122 +++++++++ ...1541___m_y_fix_foreign_key_8cs_source.html | 112 +++++++++ _application_8cs_source.html | 3 +- _database_configuration_8cs_source.html | 2 +- _database_context_8cs_source.html | 38 +-- _error_code_8cs_source.html | 2 +- _my_sql_database_context_8cs_source.html | 8 +- ...ase_context_model_snapshot_8cs_source.html | 2 +- ...tgres_sql_database_context_8cs_source.html | 10 +- ...ase_context_model_snapshot_8cs_source.html | 4 +- ...gn_time_db_context_factory_8cs_source.html | 2 +- _r_e_a_d_m_e_8md_source.html | 2 +- _reattach_info_handler_8cs_source.html | 4 +- _server_factory_8cs_source.html | 3 +- _setup_wizard_8cs_source.html | 24 +- annotated.html | 33 +-- class_migration.html | 27 +- class_migration__inherit__graph.map | 27 +- class_migration__inherit__graph.md5 | 2 +- class_migration__inherit__graph.png | Bin 276819 -> 259669 bytes ...s_1_1_internal_1_1_server_information.html | 2 +- ...1_1_session_1_1_reattach_info_handler.html | 4 +- ...ation_1_1_control_panel_configuration.html | 4 +- ...on_1_1_database_configuration-members.html | 6 +- ...figuration_1_1_database_configuration.html | 68 ++--- ...ration_1_1_file_logging_configuration.html | 2 +- ...nfiguration_1_1_general_configuration.html | 6 +- ...ver_1_1_host_1_1_core_1_1_application.html | 5 +- ...c2b516c6696a0390b37c4983a52a235_cgraph.map | 1 - ...c2b516c6696a0390b37c4983a52a235_cgraph.md5 | 2 +- ...c2b516c6696a0390b37c4983a52a235_cgraph.png | Bin 8107 -> 4991 bytes ...database_1_1_database_context-members.html | 29 +-- ...ost_1_1_database_1_1_database_context.html | 164 +++++++----- ...base_1_1_database_context__coll__graph.map | 32 +-- ...base_1_1_database_context__coll__graph.md5 | 2 +- ...base_1_1_database_context__coll__graph.png | Bin 168613 -> 178779 bytes ...tions_1_1_m_y_fix_foreign_key-members.html | 114 +++++++++ ..._1_migrations_1_1_m_y_fix_foreign_key.html | 233 ++++++++++++++++++ ...s_1_1_m_y_fix_foreign_key__coll__graph.map | 3 + ...s_1_1_m_y_fix_foreign_key__coll__graph.md5 | 1 + ...s_1_1_m_y_fix_foreign_key__coll__graph.png | Bin 0 -> 5121 bytes ..._1_m_y_fix_foreign_key__inherit__graph.map | 3 + ..._1_m_y_fix_foreign_key__inherit__graph.md5 | 1 + ..._1_m_y_fix_foreign_key__inherit__graph.png | Bin 0 -> 5121 bytes ...y_sql_database_context_model_snapshot.html | 2 +- ...atabase_1_1_migrations_1_1_p_g_create.html | 8 +- ...e_1_1_my_sql_database_context-members.html | 17 +- ..._database_1_1_my_sql_database_context.html | 71 ++++-- ...1_my_sql_database_context__coll__graph.map | 24 +- ...1_my_sql_database_context__coll__graph.md5 | 2 +- ...1_my_sql_database_context__coll__graph.png | Bin 152423 -> 190309 bytes ...postgres_sql_database_context-members.html | 17 +- ...ase_1_1_postgres_sql_database_context.html | 73 ++++-- ...gres_sql_database_context__coll__graph.map | 24 +- ...gres_sql_database_context__coll__graph.md5 | 2 +- ...gres_sql_database_context__coll__graph.png | Bin 152471 -> 190278 bytes ...1_sql_server_database_context-members.html | 19 +- ...abase_1_1_sql_server_database_context.html | 40 +-- ...l_server_database_context__coll__graph.map | 34 +-- ...l_server_database_context__coll__graph.md5 | 2 +- ...l_server_database_context__coll__graph.png | Bin 152282 -> 162291 bytes ...e_1_1_sqlite_database_context-members.html | 19 +- ..._database_1_1_sqlite_database_context.html | 40 +-- ...1_sqlite_database_context__coll__graph.map | 34 +-- ...1_sqlite_database_context__coll__graph.md5 | 2 +- ...1_sqlite_database_context__coll__graph.png | Bin 152141 -> 162097 bytes ...ons_1_1_service_collection_extensions.html | 2 - ...s_sql_database_context_model_snapshot.html | 2 +- ...rver_1_1_host_1_1_models_1_1_instance.html | 8 +- ..._1_server_1_1_host_1_1_server_factory.html | 5 +- ...r_1_1_host_1_1_setup_1_1_setup_wizard.html | 62 ++--- classes.html | 182 +++++++------- dir_6a8d19239e99965ecc1391ebd049009a.html | 4 + files.html | 10 +- functions_b.html | 1 + functions_d.html | 5 +- functions_func_b.html | 1 + functions_func_d.html | 3 +- functions_func_u.html | 5 +- functions_m.html | 3 - functions_prop_m.html | 3 - functions_prop_s.html | 1 + functions_r.html | 13 +- functions_s.html | 1 + functions_t.html | 4 +- functions_u.html | 5 +- functions_vars_r.html | 5 + hierarchy.html | 27 +- index.html | 2 +- inherit_graph_8.map | 29 +-- inherit_graph_8.md5 | 2 +- inherit_graph_8.png | Bin 276755 -> 259550 bytes inherits.html | 29 +-- ..._1_host_1_1_core_1_1_i_server_control.html | 12 - ...t_1_1_setup_1_1_i_post_setup_services.html | 2 +- ...station_1_1_server_1_1_api_1_1_models.html | 2 +- ..._1_1_host_1_1_database_1_1_migrations.html | 3 + search/all_0.js | 4 +- search/all_10.js | 6 +- search/all_12.js | 21 +- search/all_13.js | 2 +- search/all_14.js | 6 +- search/all_15.js | 8 +- search/all_2.js | 2 +- search/all_4.js | 2 +- search/all_9.js | 2 +- search/all_a.js | 2 +- search/all_d.js | 4 +- search/classes_10.js | 4 +- search/classes_12.js | 2 +- search/classes_13.js | 2 +- search/classes_9.js | 2 +- search/classes_c.js | 1 + search/files_0.js | 4 +- search/functions_1.js | 2 +- search/functions_13.js | 2 +- search/functions_3.js | 2 +- search/properties_10.js | 2 +- search/properties_b.js | 3 +- search/variables_11.js | 1 + search/variables_13.js | 2 +- search/variables_f.js | 2 +- 126 files changed, 1628 insertions(+), 673 deletions(-) create mode 100644 20200529171541___m_y_fix_foreign_key_8_designer_8cs.html create mode 100644 20200529171541___m_y_fix_foreign_key_8_designer_8cs_source.html create mode 100644 20200529171541___m_y_fix_foreign_key_8cs.html create mode 100644 20200529171541___m_y_fix_foreign_key_8cs_source.html create mode 100644 class_tgstation_1_1_server_1_1_host_1_1_database_1_1_migrations_1_1_m_y_fix_foreign_key-members.html create mode 100644 class_tgstation_1_1_server_1_1_host_1_1_database_1_1_migrations_1_1_m_y_fix_foreign_key.html create mode 100644 class_tgstation_1_1_server_1_1_host_1_1_database_1_1_migrations_1_1_m_y_fix_foreign_key__coll__graph.map create mode 100644 class_tgstation_1_1_server_1_1_host_1_1_database_1_1_migrations_1_1_m_y_fix_foreign_key__coll__graph.md5 create mode 100644 class_tgstation_1_1_server_1_1_host_1_1_database_1_1_migrations_1_1_m_y_fix_foreign_key__coll__graph.png create mode 100644 class_tgstation_1_1_server_1_1_host_1_1_database_1_1_migrations_1_1_m_y_fix_foreign_key__inherit__graph.map create mode 100644 class_tgstation_1_1_server_1_1_host_1_1_database_1_1_migrations_1_1_m_y_fix_foreign_key__inherit__graph.md5 create mode 100644 class_tgstation_1_1_server_1_1_host_1_1_database_1_1_migrations_1_1_m_y_fix_foreign_key__inherit__graph.png diff --git a/20200516111712___p_g_create_8_designer_8cs_source.html b/20200516111712___p_g_create_8_designer_8cs_source.html index 7f6510183d..8725ce7799 100644 --- a/20200516111712___p_g_create_8_designer_8cs_source.html +++ b/20200516111712___p_g_create_8_designer_8cs_source.html @@ -94,7 +94,7 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
20200516111712_PGCreate.Designer.cs
-Go to the documentation of this file.
1 // <auto-generated />
2 using System;
3 using Microsoft.EntityFrameworkCore;
4 using Microsoft.EntityFrameworkCore.Infrastructure;
5 using Microsoft.EntityFrameworkCore.Migrations;
6 using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
7 
8 namespace Tgstation.Server.Host.Database.Migrations
9 {
10  [DbContext(typeof(PostgresSqlDatabaseContext))]
11  [Migration("20200516111712_PGCreate")]
12  partial class PGCreate
13  {
15  protected override void BuildTargetModel(ModelBuilder modelBuilder)
16  {
17 #pragma warning disable 612, 618
18  modelBuilder
19  .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn)
20  .HasAnnotation("ProductVersion", "3.1.4")
21  .HasAnnotation("Relational:MaxIdentifierLength", 63);
22 
23  modelBuilder.Entity("Tgstation.Server.Host.Models.ChatBot", b =>
24  {
25  b.Property<long>("Id")
26  .ValueGeneratedOnAdd()
27  .HasColumnType("bigint")
28  .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
29 
30  b.Property<int>("ChannelLimit")
31  .HasColumnType("integer");
32 
33  b.Property<string>("ConnectionString")
34  .IsRequired()
35  .HasColumnType("character varying(10000)")
36  .HasMaxLength(10000);
37 
38  b.Property<bool?>("Enabled")
39  .HasColumnType("boolean");
40 
41  b.Property<long>("InstanceId")
42  .HasColumnType("bigint");
43 
44  b.Property<string>("Name")
45  .IsRequired()
46  .HasColumnType("character varying(100)")
47  .HasMaxLength(100);
48 
49  b.Property<int>("Provider")
50  .HasColumnType("integer");
51 
52  b.Property<long>("ReconnectionInterval")
53  .HasColumnType("bigint");
54 
55  b.HasKey("Id");
56 
57  b.HasIndex("InstanceId", "Name")
58  .IsUnique();
59 
60  b.ToTable("ChatBots");
61  });
62 
63  modelBuilder.Entity("Tgstation.Server.Host.Models.ChatChannel", b =>
64  {
65  b.Property<long>("Id")
66  .ValueGeneratedOnAdd()
67  .HasColumnType("bigint")
68  .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
69 
70  b.Property<long>("ChatSettingsId")
71  .HasColumnType("bigint");
72 
73  b.Property<decimal?>("DiscordChannelId")
74  .HasColumnType("numeric(20,0)");
75 
76  b.Property<string>("IrcChannel")
77  .HasColumnType("character varying(100)")
78  .HasMaxLength(100);
79 
80  b.Property<bool?>("IsAdminChannel")
81  .IsRequired()
82  .HasColumnType("boolean");
83 
84  b.Property<bool?>("IsUpdatesChannel")
85  .IsRequired()
86  .HasColumnType("boolean");
87 
88  b.Property<bool?>("IsWatchdogChannel")
89  .IsRequired()
90  .HasColumnType("boolean");
91 
92  b.Property<string>("Tag")
93  .HasColumnType("character varying(10000)")
94  .HasMaxLength(10000);
95 
96  b.HasKey("Id");
97 
98  b.HasIndex("ChatSettingsId", "DiscordChannelId")
99  .IsUnique();
100 
101  b.HasIndex("ChatSettingsId", "IrcChannel")
102  .IsUnique();
103 
104  b.ToTable("ChatChannels");
105  });
106 
107  modelBuilder.Entity("Tgstation.Server.Host.Models.CompileJob", b =>
108  {
109  b.Property<long>("Id")
110  .ValueGeneratedOnAdd()
111  .HasColumnType("bigint")
112  .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
113 
114  b.Property<string>("ByondVersion")
115  .IsRequired()
116  .HasColumnType("text");
117 
118  b.Property<int?>("DMApiMajorVersion")
119  .HasColumnType("integer");
120 
121  b.Property<int?>("DMApiMinorVersion")
122  .HasColumnType("integer");
123 
124  b.Property<int?>("DMApiPatchVersion")
125  .HasColumnType("integer");
126 
127  b.Property<Guid?>("DirectoryName")
128  .IsRequired()
129  .HasColumnType("uuid");
130 
131  b.Property<string>("DmeName")
132  .IsRequired()
133  .HasColumnType("text");
134 
135  b.Property<long>("JobId")
136  .HasColumnType("bigint");
137 
138  b.Property<int>("MinimumSecurityLevel")
139  .HasColumnType("integer");
140 
141  b.Property<string>("Output")
142  .IsRequired()
143  .HasColumnType("text");
144 
145  b.Property<long>("RevisionInformationId")
146  .HasColumnType("bigint");
147 
148  b.HasKey("Id");
149 
150  b.HasIndex("DirectoryName");
151 
152  b.HasIndex("JobId")
153  .IsUnique();
154 
155  b.HasIndex("RevisionInformationId");
156 
157  b.ToTable("CompileJobs");
158  });
159 
160  modelBuilder.Entity("Tgstation.Server.Host.Models.DreamDaemonSettings", b =>
161  {
162  b.Property<long>("Id")
163  .ValueGeneratedOnAdd()
164  .HasColumnType("bigint")
165  .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
166 
167  b.Property<bool?>("AllowWebClient")
168  .IsRequired()
169  .HasColumnType("boolean");
170 
171  b.Property<bool?>("AutoStart")
172  .IsRequired()
173  .HasColumnType("boolean");
174 
175  b.Property<long>("HeartbeatSeconds")
176  .HasColumnType("bigint");
177 
178  b.Property<long>("InstanceId")
179  .HasColumnType("bigint");
180 
181  b.Property<int>("PrimaryPort")
182  .HasColumnType("integer");
183 
184  b.Property<int>("SecondaryPort")
185  .HasColumnType("integer");
186 
187  b.Property<int>("SecurityLevel")
188  .HasColumnType("integer");
189 
190  b.Property<long>("StartupTimeout")
191  .HasColumnType("bigint");
192 
193  b.HasKey("Id");
194 
195  b.HasIndex("InstanceId")
196  .IsUnique();
197 
198  b.ToTable("DreamDaemonSettings");
199  });
200 
201  modelBuilder.Entity("Tgstation.Server.Host.Models.DreamMakerSettings", b =>
202  {
203  b.Property<long>("Id")
204  .ValueGeneratedOnAdd()
205  .HasColumnType("bigint")
206  .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
207 
208  b.Property<int>("ApiValidationPort")
209  .HasColumnType("integer");
210 
211  b.Property<int>("ApiValidationSecurityLevel")
212  .HasColumnType("integer");
213 
214  b.Property<long>("InstanceId")
215  .HasColumnType("bigint");
216 
217  b.Property<string>("ProjectName")
218  .HasColumnType("character varying(10000)")
219  .HasMaxLength(10000);
220 
221  b.HasKey("Id");
222 
223  b.HasIndex("InstanceId")
224  .IsUnique();
225 
226  b.ToTable("DreamMakerSettings");
227  });
228 
229  modelBuilder.Entity("Tgstation.Server.Host.Models.DualReattachInformation", b =>
230  {
231  b.Property<long>("Id")
232  .ValueGeneratedOnAdd()
233  .HasColumnType("bigint")
234  .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
235 
236  b.Property<long?>("AlphaId")
237  .HasColumnType("bigint");
238 
239  b.Property<bool>("AlphaIsActive")
240  .HasColumnType("boolean");
241 
242  b.Property<long?>("BravoId")
243  .HasColumnType("bigint");
244 
245  b.Property<long>("InstanceId")
246  .HasColumnType("bigint");
247 
248  b.HasKey("Id");
249 
250  b.HasIndex("AlphaId");
251 
252  b.HasIndex("BravoId");
253 
254  b.HasIndex("InstanceId")
255  .IsUnique();
256 
257  b.ToTable("WatchdogReattachInformations");
258  });
259 
260  modelBuilder.Entity("Tgstation.Server.Host.Models.Instance", b =>
261  {
262  b.Property<long>("Id")
263  .ValueGeneratedOnAdd()
264  .HasColumnType("bigint")
265  .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
266 
267  b.Property<long>("AutoUpdateInterval")
268  .HasColumnType("bigint");
269 
270  b.Property<int>("ChatBotLimit")
271  .HasColumnType("integer");
272 
273  b.Property<int>("ConfigurationType")
274  .HasColumnType("integer");
275 
276  b.Property<string>("Name")
277  .IsRequired()
278  .HasColumnType("character varying(10000)")
279  .HasMaxLength(10000);
280 
281  b.Property<bool?>("Online")
282  .IsRequired()
283  .HasColumnType("boolean");
284 
285  b.Property<string>("Path")
286  .IsRequired()
287  .HasColumnType("text");
288 
289  b.HasKey("Id");
290 
291  b.HasIndex("Path")
292  .IsUnique();
293 
294  b.ToTable("Instances");
295  });
296 
297  modelBuilder.Entity("Tgstation.Server.Host.Models.InstanceUser", b =>
298  {
299  b.Property<long>("Id")
300  .ValueGeneratedOnAdd()
301  .HasColumnType("bigint")
302  .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
303 
304  b.Property<decimal>("ByondRights")
305  .HasColumnType("numeric(20,0)");
306 
307  b.Property<decimal>("ChatBotRights")
308  .HasColumnType("numeric(20,0)");
309 
310  b.Property<decimal>("ConfigurationRights")
311  .HasColumnType("numeric(20,0)");
312 
313  b.Property<decimal>("DreamDaemonRights")
314  .HasColumnType("numeric(20,0)");
315 
316  b.Property<decimal>("DreamMakerRights")
317  .HasColumnType("numeric(20,0)");
318 
319  b.Property<long>("InstanceId")
320  .HasColumnType("bigint");
321 
322  b.Property<decimal>("InstanceUserRights")
323  .HasColumnType("numeric(20,0)");
324 
325  b.Property<decimal>("RepositoryRights")
326  .HasColumnType("numeric(20,0)");
327 
328  b.Property<long?>("UserId")
329  .IsRequired()
330  .HasColumnType("bigint");
331 
332  b.HasKey("Id");
333 
334  b.HasIndex("InstanceId");
335 
336  b.HasIndex("UserId", "InstanceId")
337  .IsUnique();
338 
339  b.ToTable("InstanceUsers");
340  });
341 
342  modelBuilder.Entity("Tgstation.Server.Host.Models.Job", b =>
343  {
344  b.Property<long>("Id")
345  .ValueGeneratedOnAdd()
346  .HasColumnType("bigint")
347  .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
348 
349  b.Property<decimal?>("CancelRight")
350  .HasColumnType("numeric(20,0)");
351 
352  b.Property<decimal?>("CancelRightsType")
353  .HasColumnType("numeric(20,0)");
354 
355  b.Property<bool?>("Cancelled")
356  .IsRequired()
357  .HasColumnType("boolean");
358 
359  b.Property<long?>("CancelledById")
360  .HasColumnType("bigint");
361 
362  b.Property<string>("Description")
363  .IsRequired()
364  .HasColumnType("text");
365 
366  b.Property<long?>("ErrorCode")
367  .HasColumnType("bigint");
368 
369  b.Property<string>("ExceptionDetails")
370  .HasColumnType("text");
371 
372  b.Property<long>("InstanceId")
373  .HasColumnType("bigint");
374 
375  b.Property<DateTimeOffset?>("StartedAt")
376  .IsRequired()
377  .HasColumnType("timestamp with time zone");
378 
379  b.Property<long>("StartedById")
380  .HasColumnType("bigint");
381 
382  b.Property<DateTimeOffset?>("StoppedAt")
383  .HasColumnType("timestamp with time zone");
384 
385  b.HasKey("Id");
386 
387  b.HasIndex("CancelledById");
388 
389  b.HasIndex("InstanceId");
390 
391  b.HasIndex("StartedById");
392 
393  b.ToTable("Jobs");
394  });
395 
396  modelBuilder.Entity("Tgstation.Server.Host.Models.ReattachInformation", b =>
397  {
398  b.Property<long>("Id")
399  .ValueGeneratedOnAdd()
400  .HasColumnType("bigint")
401  .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
402 
403  b.Property<string>("AccessIdentifier")
404  .IsRequired()
405  .HasColumnType("text");
406 
407  b.Property<long>("CompileJobId")
408  .HasColumnType("bigint");
409 
410  b.Property<bool>("IsPrimary")
411  .HasColumnType("boolean");
412 
413  b.Property<int>("LaunchSecurityLevel")
414  .HasColumnType("integer");
415 
416  b.Property<int>("Port")
417  .HasColumnType("integer");
418 
419  b.Property<int>("ProcessId")
420  .HasColumnType("integer");
421 
422  b.Property<int>("RebootState")
423  .HasColumnType("integer");
424 
425  b.HasKey("Id");
426 
427  b.HasIndex("CompileJobId");
428 
429  b.ToTable("ReattachInformations");
430  });
431 
432  modelBuilder.Entity("Tgstation.Server.Host.Models.RepositorySettings", b =>
433  {
434  b.Property<long>("Id")
435  .ValueGeneratedOnAdd()
436  .HasColumnType("bigint")
437  .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
438 
439  b.Property<string>("AccessToken")
440  .HasColumnType("character varying(10000)")
441  .HasMaxLength(10000);
442 
443  b.Property<string>("AccessUser")
444  .HasColumnType("character varying(10000)")
445  .HasMaxLength(10000);
446 
447  b.Property<bool?>("AutoUpdatesKeepTestMerges")
448  .IsRequired()
449  .HasColumnType("boolean");
450 
451  b.Property<bool?>("AutoUpdatesSynchronize")
452  .IsRequired()
453  .HasColumnType("boolean");
454 
455  b.Property<string>("CommitterEmail")
456  .IsRequired()
457  .HasColumnType("character varying(10000)")
458  .HasMaxLength(10000);
459 
460  b.Property<string>("CommitterName")
461  .IsRequired()
462  .HasColumnType("character varying(10000)")
463  .HasMaxLength(10000);
464 
465  b.Property<long>("InstanceId")
466  .HasColumnType("bigint");
467 
468  b.Property<bool?>("PostTestMergeComment")
469  .IsRequired()
470  .HasColumnType("boolean");
471 
472  b.Property<bool?>("PushTestMergeCommits")
473  .IsRequired()
474  .HasColumnType("boolean");
475 
476  b.Property<bool?>("ShowTestMergeCommitters")
477  .IsRequired()
478  .HasColumnType("boolean");
479 
480  b.HasKey("Id");
481 
482  b.HasIndex("InstanceId")
483  .IsUnique();
484 
485  b.ToTable("RepositorySettings");
486  });
487 
488  modelBuilder.Entity("Tgstation.Server.Host.Models.RevInfoTestMerge", b =>
489  {
490  b.Property<long>("Id")
491  .ValueGeneratedOnAdd()
492  .HasColumnType("bigint")
493  .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
494 
495  b.Property<long>("RevisionInformationId")
496  .HasColumnType("bigint");
497 
498  b.Property<long>("TestMergeId")
499  .HasColumnType("bigint");
500 
501  b.HasKey("Id");
502 
503  b.HasIndex("RevisionInformationId");
504 
505  b.HasIndex("TestMergeId");
506 
507  b.ToTable("RevInfoTestMerges");
508  });
509 
510  modelBuilder.Entity("Tgstation.Server.Host.Models.RevisionInformation", b =>
511  {
512  b.Property<long>("Id")
513  .ValueGeneratedOnAdd()
514  .HasColumnType("bigint")
515  .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
516 
517  b.Property<string>("CommitSha")
518  .IsRequired()
519  .HasColumnType("character varying(40)")
520  .HasMaxLength(40);
521 
522  b.Property<long>("InstanceId")
523  .HasColumnType("bigint");
524 
525  b.Property<string>("OriginCommitSha")
526  .IsRequired()
527  .HasColumnType("character varying(40)")
528  .HasMaxLength(40);
529 
530  b.HasKey("Id");
531 
532  b.HasIndex("InstanceId", "CommitSha")
533  .IsUnique();
534 
535  b.ToTable("RevisionInformations");
536  });
537 
538  modelBuilder.Entity("Tgstation.Server.Host.Models.TestMerge", b =>
539  {
540  b.Property<long>("Id")
541  .ValueGeneratedOnAdd()
542  .HasColumnType("bigint")
543  .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
544 
545  b.Property<string>("Author")
546  .IsRequired()
547  .HasColumnType("text");
548 
549  b.Property<string>("BodyAtMerge")
550  .IsRequired()
551  .HasColumnType("text");
552 
553  b.Property<string>("Comment")
554  .HasColumnType("character varying(10000)")
555  .HasMaxLength(10000);
556 
557  b.Property<DateTimeOffset>("MergedAt")
558  .HasColumnType("timestamp with time zone");
559 
560  b.Property<long>("MergedById")
561  .HasColumnType("bigint");
562 
563  b.Property<int>("Number")
564  .HasColumnType("integer");
565 
566  b.Property<long?>("PrimaryRevisionInformationId")
567  .IsRequired()
568  .HasColumnType("bigint");
569 
570  b.Property<string>("PullRequestRevision")
571  .IsRequired()
572  .HasColumnType("character varying(40)")
573  .HasMaxLength(40);
574 
575  b.Property<string>("TitleAtMerge")
576  .IsRequired()
577  .HasColumnType("text");
578 
579  b.Property<string>("Url")
580  .IsRequired()
581  .HasColumnType("text");
582 
583  b.HasKey("Id");
584 
585  b.HasIndex("MergedById");
586 
587  b.HasIndex("PrimaryRevisionInformationId")
588  .IsUnique();
589 
590  b.ToTable("TestMerges");
591  });
592 
593  modelBuilder.Entity("Tgstation.Server.Host.Models.User", b =>
594  {
595  b.Property<long?>("Id")
596  .ValueGeneratedOnAdd()
597  .HasColumnType("bigint")
598  .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
599 
600  b.Property<decimal>("AdministrationRights")
601  .HasColumnType("numeric(20,0)");
602 
603  b.Property<string>("CanonicalName")
604  .IsRequired()
605  .HasColumnType("text");
606 
607  b.Property<DateTimeOffset?>("CreatedAt")
608  .IsRequired()
609  .HasColumnType("timestamp with time zone");
610 
611  b.Property<long?>("CreatedById")
612  .HasColumnType("bigint");
613 
614  b.Property<bool?>("Enabled")
615  .IsRequired()
616  .HasColumnType("boolean");
617 
618  b.Property<decimal>("InstanceManagerRights")
619  .HasColumnType("numeric(20,0)");
620 
621  b.Property<DateTimeOffset?>("LastPasswordUpdate")
622  .HasColumnType("timestamp with time zone");
623 
624  b.Property<string>("Name")
625  .IsRequired()
626  .HasColumnType("character varying(10000)")
627  .HasMaxLength(10000);
628 
629  b.Property<string>("PasswordHash")
630  .HasColumnType("text");
631 
632  b.Property<string>("SystemIdentifier")
633  .HasColumnType("text");
634 
635  b.HasKey("Id");
636 
637  b.HasIndex("CanonicalName")
638  .IsUnique();
639 
640  b.HasIndex("CreatedById");
641 
642  b.HasIndex("SystemIdentifier")
643  .IsUnique();
644 
645  b.ToTable("Users");
646  });
647 
648  modelBuilder.Entity("Tgstation.Server.Host.Models.ChatBot", b =>
649  {
650  b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
651  .WithMany("ChatSettings")
652  .HasForeignKey("InstanceId")
653  .OnDelete(DeleteBehavior.Cascade)
654  .IsRequired();
655  });
656 
657  modelBuilder.Entity("Tgstation.Server.Host.Models.ChatChannel", b =>
658  {
659  b.HasOne("Tgstation.Server.Host.Models.ChatBot", "ChatSettings")
660  .WithMany("Channels")
661  .HasForeignKey("ChatSettingsId")
662  .OnDelete(DeleteBehavior.Cascade)
663  .IsRequired();
664  });
665 
666  modelBuilder.Entity("Tgstation.Server.Host.Models.CompileJob", b =>
667  {
668  b.HasOne("Tgstation.Server.Host.Models.Job", "Job")
669  .WithOne()
670  .HasForeignKey("Tgstation.Server.Host.Models.CompileJob", "JobId")
671  .OnDelete(DeleteBehavior.Cascade)
672  .IsRequired();
673 
674  b.HasOne("Tgstation.Server.Host.Models.RevisionInformation", "RevisionInformation")
675  .WithMany("CompileJobs")
676  .HasForeignKey("RevisionInformationId")
677  .OnDelete(DeleteBehavior.ClientNoAction)
678  .IsRequired();
679  });
680 
681  modelBuilder.Entity("Tgstation.Server.Host.Models.DreamDaemonSettings", b =>
682  {
683  b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
684  .WithOne("DreamDaemonSettings")
685  .HasForeignKey("Tgstation.Server.Host.Models.DreamDaemonSettings", "InstanceId")
686  .OnDelete(DeleteBehavior.Cascade)
687  .IsRequired();
688  });
689 
690  modelBuilder.Entity("Tgstation.Server.Host.Models.DreamMakerSettings", b =>
691  {
692  b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
693  .WithOne("DreamMakerSettings")
694  .HasForeignKey("Tgstation.Server.Host.Models.DreamMakerSettings", "InstanceId")
695  .OnDelete(DeleteBehavior.Cascade)
696  .IsRequired();
697  });
698 
699  modelBuilder.Entity("Tgstation.Server.Host.Models.DualReattachInformation", b =>
700  {
701  b.HasOne("Tgstation.Server.Host.Models.ReattachInformation", "Alpha")
702  .WithMany()
703  .HasForeignKey("AlphaId");
704 
705  b.HasOne("Tgstation.Server.Host.Models.ReattachInformation", "Bravo")
706  .WithMany()
707  .HasForeignKey("BravoId");
708 
709  b.HasOne("Tgstation.Server.Host.Models.Instance", null)
710  .WithOne("WatchdogReattachInformation")
711  .HasForeignKey("Tgstation.Server.Host.Models.DualReattachInformation", "InstanceId")
712  .OnDelete(DeleteBehavior.Cascade)
713  .IsRequired();
714  });
715 
716  modelBuilder.Entity("Tgstation.Server.Host.Models.InstanceUser", b =>
717  {
718  b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
719  .WithMany("InstanceUsers")
720  .HasForeignKey("InstanceId")
721  .OnDelete(DeleteBehavior.Cascade)
722  .IsRequired();
723 
724  b.HasOne("Tgstation.Server.Host.Models.User", null)
725  .WithMany("InstanceUsers")
726  .HasForeignKey("UserId")
727  .OnDelete(DeleteBehavior.Cascade)
728  .IsRequired();
729  });
730 
731  modelBuilder.Entity("Tgstation.Server.Host.Models.Job", b =>
732  {
733  b.HasOne("Tgstation.Server.Host.Models.User", "CancelledBy")
734  .WithMany()
735  .HasForeignKey("CancelledById");
736 
737  b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
738  .WithMany("Jobs")
739  .HasForeignKey("InstanceId")
740  .OnDelete(DeleteBehavior.Cascade)
741  .IsRequired();
742 
743  b.HasOne("Tgstation.Server.Host.Models.User", "StartedBy")
744  .WithMany()
745  .HasForeignKey("StartedById")
746  .OnDelete(DeleteBehavior.Cascade)
747  .IsRequired();
748  });
749 
750  modelBuilder.Entity("Tgstation.Server.Host.Models.ReattachInformation", b =>
751  {
752  b.HasOne("Tgstation.Server.Host.Models.CompileJob", "CompileJob")
753  .WithMany()
754  .HasForeignKey("CompileJobId")
755  .OnDelete(DeleteBehavior.Cascade)
756  .IsRequired();
757  });
758 
759  modelBuilder.Entity("Tgstation.Server.Host.Models.RepositorySettings", b =>
760  {
761  b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
762  .WithOne("RepositorySettings")
763  .HasForeignKey("Tgstation.Server.Host.Models.RepositorySettings", "InstanceId")
764  .OnDelete(DeleteBehavior.Cascade)
765  .IsRequired();
766  });
767 
768  modelBuilder.Entity("Tgstation.Server.Host.Models.RevInfoTestMerge", b =>
769  {
770  b.HasOne("Tgstation.Server.Host.Models.RevisionInformation", "RevisionInformation")
771  .WithMany("ActiveTestMerges")
772  .HasForeignKey("RevisionInformationId")
773  .OnDelete(DeleteBehavior.Cascade)
774  .IsRequired();
775 
776  b.HasOne("Tgstation.Server.Host.Models.TestMerge", "TestMerge")
777  .WithMany("RevisonInformations")
778  .HasForeignKey("TestMergeId")
779  .OnDelete(DeleteBehavior.ClientNoAction)
780  .IsRequired();
781  });
782 
783  modelBuilder.Entity("Tgstation.Server.Host.Models.RevisionInformation", b =>
784  {
785  b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
786  .WithMany("RevisionInformations")
787  .HasForeignKey("InstanceId")
788  .OnDelete(DeleteBehavior.Cascade)
789  .IsRequired();
790  });
791 
792  modelBuilder.Entity("Tgstation.Server.Host.Models.TestMerge", b =>
793  {
794  b.HasOne("Tgstation.Server.Host.Models.User", "MergedBy")
795  .WithMany("TestMerges")
796  .HasForeignKey("MergedById")
797  .OnDelete(DeleteBehavior.Restrict)
798  .IsRequired();
799 
800  b.HasOne("Tgstation.Server.Host.Models.RevisionInformation", "PrimaryRevisionInformation")
801  .WithOne("PrimaryTestMerge")
802  .HasForeignKey("Tgstation.Server.Host.Models.TestMerge", "PrimaryRevisionInformationId")
803  .OnDelete(DeleteBehavior.Cascade)
804  .IsRequired();
805  });
806 
807  modelBuilder.Entity("Tgstation.Server.Host.Models.User", b =>
808  {
809  b.HasOne("Tgstation.Server.Host.Models.User", "CreatedBy")
810  .WithMany("CreatedUsers")
811  .HasForeignKey("CreatedById");
812  });
813 #pragma warning restore 612, 618
814  }
815  }
816 }
+Go to the documentation of this file.
1 // <auto-generated />
2 using System;
3 using Microsoft.EntityFrameworkCore;
4 using Microsoft.EntityFrameworkCore.Infrastructure;
5 using Microsoft.EntityFrameworkCore.Migrations;
6 using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
7 
8 namespace Tgstation.Server.Host.Database.Migrations
9 {
10  [DbContext(typeof(PostgresSqlDatabaseContext))]
11  [Migration("20200516111712_PGCreate")]
12  partial class PGCreate
13  {
15  protected override void BuildTargetModel(ModelBuilder modelBuilder)
16  {
17 #pragma warning disable 612, 618
18  modelBuilder
19  .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn)
20  .HasAnnotation("ProductVersion", "3.1.4")
21  .HasAnnotation("Relational:MaxIdentifierLength", 63);
22 
23  modelBuilder.Entity("Tgstation.Server.Host.Models.ChatBot", b =>
24  {
25  b.Property<long>("Id")
26  .ValueGeneratedOnAdd()
27  .HasColumnType("bigint")
28  .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
29 
30  b.Property<int>("ChannelLimit")
31  .HasColumnType("integer");
32 
33  b.Property<string>("ConnectionString")
34  .IsRequired()
35  .HasColumnType("character varying(10000)")
36  .HasMaxLength(10000);
37 
38  b.Property<bool?>("Enabled")
39  .HasColumnType("boolean");
40 
41  b.Property<long>("InstanceId")
42  .HasColumnType("bigint");
43 
44  b.Property<string>("Name")
45  .IsRequired()
46  .HasColumnType("character varying(100)")
47  .HasMaxLength(100);
48 
49  b.Property<int>("Provider")
50  .HasColumnType("integer");
51 
52  b.Property<long>("ReconnectionInterval")
53  .HasColumnType("bigint");
54 
55  b.HasKey("Id");
56 
57  b.HasIndex("InstanceId", "Name")
58  .IsUnique();
59 
60  b.ToTable("ChatBots");
61  });
62 
63  modelBuilder.Entity("Tgstation.Server.Host.Models.ChatChannel", b =>
64  {
65  b.Property<long>("Id")
66  .ValueGeneratedOnAdd()
67  .HasColumnType("bigint")
68  .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
69 
70  b.Property<long>("ChatSettingsId")
71  .HasColumnType("bigint");
72 
73  b.Property<decimal?>("DiscordChannelId")
74  .HasColumnType("numeric(20,0)");
75 
76  b.Property<string>("IrcChannel")
77  .HasColumnType("character varying(100)")
78  .HasMaxLength(100);
79 
80  b.Property<bool?>("IsAdminChannel")
81  .IsRequired()
82  .HasColumnType("boolean");
83 
84  b.Property<bool?>("IsUpdatesChannel")
85  .IsRequired()
86  .HasColumnType("boolean");
87 
88  b.Property<bool?>("IsWatchdogChannel")
89  .IsRequired()
90  .HasColumnType("boolean");
91 
92  b.Property<string>("Tag")
93  .HasColumnType("character varying(10000)")
94  .HasMaxLength(10000);
95 
96  b.HasKey("Id");
97 
98  b.HasIndex("ChatSettingsId", "DiscordChannelId")
99  .IsUnique();
100 
101  b.HasIndex("ChatSettingsId", "IrcChannel")
102  .IsUnique();
103 
104  b.ToTable("ChatChannels");
105  });
106 
107  modelBuilder.Entity("Tgstation.Server.Host.Models.CompileJob", b =>
108  {
109  b.Property<long>("Id")
110  .ValueGeneratedOnAdd()
111  .HasColumnType("bigint")
112  .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
113 
114  b.Property<string>("ByondVersion")
115  .IsRequired()
116  .HasColumnType("text");
117 
118  b.Property<int?>("DMApiMajorVersion")
119  .HasColumnType("integer");
120 
121  b.Property<int?>("DMApiMinorVersion")
122  .HasColumnType("integer");
123 
124  b.Property<int?>("DMApiPatchVersion")
125  .HasColumnType("integer");
126 
127  b.Property<Guid?>("DirectoryName")
128  .IsRequired()
129  .HasColumnType("uuid");
130 
131  b.Property<string>("DmeName")
132  .IsRequired()
133  .HasColumnType("text");
134 
135  b.Property<long>("JobId")
136  .HasColumnType("bigint");
137 
138  b.Property<int>("MinimumSecurityLevel")
139  .HasColumnType("integer");
140 
141  b.Property<string>("Output")
142  .IsRequired()
143  .HasColumnType("text");
144 
145  b.Property<long>("RevisionInformationId")
146  .HasColumnType("bigint");
147 
148  b.HasKey("Id");
149 
150  b.HasIndex("DirectoryName");
151 
152  b.HasIndex("JobId")
153  .IsUnique();
154 
155  b.HasIndex("RevisionInformationId");
156 
157  b.ToTable("CompileJobs");
158  });
159 
160  modelBuilder.Entity("Tgstation.Server.Host.Models.DreamDaemonSettings", b =>
161  {
162  b.Property<long>("Id")
163  .ValueGeneratedOnAdd()
164  .HasColumnType("bigint")
165  .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
166 
167  b.Property<bool?>("AllowWebClient")
168  .IsRequired()
169  .HasColumnType("boolean");
170 
171  b.Property<bool?>("AutoStart")
172  .IsRequired()
173  .HasColumnType("boolean");
174 
175  b.Property<long>("HeartbeatSeconds")
176  .HasColumnType("bigint");
177 
178  b.Property<long>("InstanceId")
179  .HasColumnType("bigint");
180 
181  b.Property<int>("PrimaryPort")
182  .HasColumnType("integer");
183 
184  b.Property<int>("SecondaryPort")
185  .HasColumnType("integer");
186 
187  b.Property<int>("SecurityLevel")
188  .HasColumnType("integer");
189 
190  b.Property<long>("StartupTimeout")
191  .HasColumnType("bigint");
192 
193  b.HasKey("Id");
194 
195  b.HasIndex("InstanceId")
196  .IsUnique();
197 
198  b.ToTable("DreamDaemonSettings");
199  });
200 
201  modelBuilder.Entity("Tgstation.Server.Host.Models.DreamMakerSettings", b =>
202  {
203  b.Property<long>("Id")
204  .ValueGeneratedOnAdd()
205  .HasColumnType("bigint")
206  .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
207 
208  b.Property<int>("ApiValidationPort")
209  .HasColumnType("integer");
210 
211  b.Property<int>("ApiValidationSecurityLevel")
212  .HasColumnType("integer");
213 
214  b.Property<long>("InstanceId")
215  .HasColumnType("bigint");
216 
217  b.Property<string>("ProjectName")
218  .HasColumnType("character varying(10000)")
219  .HasMaxLength(10000);
220 
221  b.HasKey("Id");
222 
223  b.HasIndex("InstanceId")
224  .IsUnique();
225 
226  b.ToTable("DreamMakerSettings");
227  });
228 
229  modelBuilder.Entity("Tgstation.Server.Host.Models.DualReattachInformation", b =>
230  {
231  b.Property<long>("Id")
232  .ValueGeneratedOnAdd()
233  .HasColumnType("bigint")
234  .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
235 
236  b.Property<long?>("AlphaId")
237  .HasColumnType("bigint");
238 
239  b.Property<bool>("AlphaIsActive")
240  .HasColumnType("boolean");
241 
242  b.Property<long?>("BravoId")
243  .HasColumnType("bigint");
244 
245  b.Property<long>("InstanceId")
246  .HasColumnType("bigint");
247 
248  b.HasKey("Id");
249 
250  b.HasIndex("AlphaId");
251 
252  b.HasIndex("BravoId");
253 
254  b.HasIndex("InstanceId")
255  .IsUnique();
256 
257  b.ToTable("WatchdogReattachInformations");
258  });
259 
260  modelBuilder.Entity("Tgstation.Server.Host.Models.Instance", b =>
261  {
262  b.Property<long>("Id")
263  .ValueGeneratedOnAdd()
264  .HasColumnType("bigint")
265  .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
266 
267  b.Property<long>("AutoUpdateInterval")
268  .HasColumnType("bigint");
269 
270  b.Property<int>("ChatBotLimit")
271  .HasColumnType("integer");
272 
273  b.Property<int>("ConfigurationType")
274  .HasColumnType("integer");
275 
276  b.Property<string>("Name")
277  .IsRequired()
278  .HasColumnType("character varying(10000)")
279  .HasMaxLength(10000);
280 
281  b.Property<bool?>("Online")
282  .IsRequired()
283  .HasColumnType("boolean");
284 
285  b.Property<string>("Path")
286  .IsRequired()
287  .HasColumnType("text");
288 
289  b.HasKey("Id");
290 
291  b.HasIndex("Path")
292  .IsUnique();
293 
294  b.ToTable("Instances");
295  });
296 
297  modelBuilder.Entity("Tgstation.Server.Host.Models.InstanceUser", b =>
298  {
299  b.Property<long>("Id")
300  .ValueGeneratedOnAdd()
301  .HasColumnType("bigint")
302  .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
303 
304  b.Property<decimal>("ByondRights")
305  .HasColumnType("numeric(20,0)");
306 
307  b.Property<decimal>("ChatBotRights")
308  .HasColumnType("numeric(20,0)");
309 
310  b.Property<decimal>("ConfigurationRights")
311  .HasColumnType("numeric(20,0)");
312 
313  b.Property<decimal>("DreamDaemonRights")
314  .HasColumnType("numeric(20,0)");
315 
316  b.Property<decimal>("DreamMakerRights")
317  .HasColumnType("numeric(20,0)");
318 
319  b.Property<long>("InstanceId")
320  .HasColumnType("bigint");
321 
322  b.Property<decimal>("InstanceUserRights")
323  .HasColumnType("numeric(20,0)");
324 
325  b.Property<decimal>("RepositoryRights")
326  .HasColumnType("numeric(20,0)");
327 
328  b.Property<long?>("UserId")
329  .IsRequired()
330  .HasColumnType("bigint");
331 
332  b.HasKey("Id");
333 
334  b.HasIndex("InstanceId");
335 
336  b.HasIndex("UserId", "InstanceId")
337  .IsUnique();
338 
339  b.ToTable("InstanceUsers");
340  });
341 
342  modelBuilder.Entity("Tgstation.Server.Host.Models.Job", b =>
343  {
344  b.Property<long>("Id")
345  .ValueGeneratedOnAdd()
346  .HasColumnType("bigint")
347  .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
348 
349  b.Property<decimal?>("CancelRight")
350  .HasColumnType("numeric(20,0)");
351 
352  b.Property<decimal?>("CancelRightsType")
353  .HasColumnType("numeric(20,0)");
354 
355  b.Property<bool?>("Cancelled")
356  .IsRequired()
357  .HasColumnType("boolean");
358 
359  b.Property<long?>("CancelledById")
360  .HasColumnType("bigint");
361 
362  b.Property<string>("Description")
363  .IsRequired()
364  .HasColumnType("text");
365 
366  b.Property<long?>("ErrorCode")
367  .HasColumnType("bigint");
368 
369  b.Property<string>("ExceptionDetails")
370  .HasColumnType("text");
371 
372  b.Property<long>("InstanceId")
373  .HasColumnType("bigint");
374 
375  b.Property<DateTimeOffset?>("StartedAt")
376  .IsRequired()
377  .HasColumnType("timestamp with time zone");
378 
379  b.Property<long>("StartedById")
380  .HasColumnType("bigint");
381 
382  b.Property<DateTimeOffset?>("StoppedAt")
383  .HasColumnType("timestamp with time zone");
384 
385  b.HasKey("Id");
386 
387  b.HasIndex("CancelledById");
388 
389  b.HasIndex("InstanceId");
390 
391  b.HasIndex("StartedById");
392 
393  b.ToTable("Jobs");
394  });
395 
396  modelBuilder.Entity("Tgstation.Server.Host.Models.ReattachInformation", b =>
397  {
398  b.Property<long>("Id")
399  .ValueGeneratedOnAdd()
400  .HasColumnType("bigint")
401  .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
402 
403  b.Property<string>("AccessIdentifier")
404  .IsRequired()
405  .HasColumnType("text");
406 
407  b.Property<long>("CompileJobId")
408  .HasColumnType("bigint");
409 
410  b.Property<bool>("IsPrimary")
411  .HasColumnType("boolean");
412 
413  b.Property<int>("LaunchSecurityLevel")
414  .HasColumnType("integer");
415 
416  b.Property<int>("Port")
417  .HasColumnType("integer");
418 
419  b.Property<int>("ProcessId")
420  .HasColumnType("integer");
421 
422  b.Property<int>("RebootState")
423  .HasColumnType("integer");
424 
425  b.HasKey("Id");
426 
427  b.HasIndex("CompileJobId");
428 
429  b.ToTable("ReattachInformations");
430  });
431 
432  modelBuilder.Entity("Tgstation.Server.Host.Models.RepositorySettings", b =>
433  {
434  b.Property<long>("Id")
435  .ValueGeneratedOnAdd()
436  .HasColumnType("bigint")
437  .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
438 
439  b.Property<string>("AccessToken")
440  .HasColumnType("character varying(10000)")
441  .HasMaxLength(10000);
442 
443  b.Property<string>("AccessUser")
444  .HasColumnType("character varying(10000)")
445  .HasMaxLength(10000);
446 
447  b.Property<bool?>("AutoUpdatesKeepTestMerges")
448  .IsRequired()
449  .HasColumnType("boolean");
450 
451  b.Property<bool?>("AutoUpdatesSynchronize")
452  .IsRequired()
453  .HasColumnType("boolean");
454 
455  b.Property<string>("CommitterEmail")
456  .IsRequired()
457  .HasColumnType("character varying(10000)")
458  .HasMaxLength(10000);
459 
460  b.Property<string>("CommitterName")
461  .IsRequired()
462  .HasColumnType("character varying(10000)")
463  .HasMaxLength(10000);
464 
465  b.Property<long>("InstanceId")
466  .HasColumnType("bigint");
467 
468  b.Property<bool?>("PostTestMergeComment")
469  .IsRequired()
470  .HasColumnType("boolean");
471 
472  b.Property<bool?>("PushTestMergeCommits")
473  .IsRequired()
474  .HasColumnType("boolean");
475 
476  b.Property<bool?>("ShowTestMergeCommitters")
477  .IsRequired()
478  .HasColumnType("boolean");
479 
480  b.HasKey("Id");
481 
482  b.HasIndex("InstanceId")
483  .IsUnique();
484 
485  b.ToTable("RepositorySettings");
486  });
487 
488  modelBuilder.Entity("Tgstation.Server.Host.Models.RevInfoTestMerge", b =>
489  {
490  b.Property<long>("Id")
491  .ValueGeneratedOnAdd()
492  .HasColumnType("bigint")
493  .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
494 
495  b.Property<long>("RevisionInformationId")
496  .HasColumnType("bigint");
497 
498  b.Property<long>("TestMergeId")
499  .HasColumnType("bigint");
500 
501  b.HasKey("Id");
502 
503  b.HasIndex("RevisionInformationId");
504 
505  b.HasIndex("TestMergeId");
506 
507  b.ToTable("RevInfoTestMerges");
508  });
509 
510  modelBuilder.Entity("Tgstation.Server.Host.Models.RevisionInformation", b =>
511  {
512  b.Property<long>("Id")
513  .ValueGeneratedOnAdd()
514  .HasColumnType("bigint")
515  .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
516 
517  b.Property<string>("CommitSha")
518  .IsRequired()
519  .HasColumnType("character varying(40)")
520  .HasMaxLength(40);
521 
522  b.Property<long>("InstanceId")
523  .HasColumnType("bigint");
524 
525  b.Property<string>("OriginCommitSha")
526  .IsRequired()
527  .HasColumnType("character varying(40)")
528  .HasMaxLength(40);
529 
530  b.HasKey("Id");
531 
532  b.HasIndex("InstanceId", "CommitSha")
533  .IsUnique();
534 
535  b.ToTable("RevisionInformations");
536  });
537 
538  modelBuilder.Entity("Tgstation.Server.Host.Models.TestMerge", b =>
539  {
540  b.Property<long>("Id")
541  .ValueGeneratedOnAdd()
542  .HasColumnType("bigint")
543  .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
544 
545  b.Property<string>("Author")
546  .IsRequired()
547  .HasColumnType("text");
548 
549  b.Property<string>("BodyAtMerge")
550  .IsRequired()
551  .HasColumnType("text");
552 
553  b.Property<string>("Comment")
554  .HasColumnType("character varying(10000)")
555  .HasMaxLength(10000);
556 
557  b.Property<DateTimeOffset>("MergedAt")
558  .HasColumnType("timestamp with time zone");
559 
560  b.Property<long>("MergedById")
561  .HasColumnType("bigint");
562 
563  b.Property<int>("Number")
564  .HasColumnType("integer");
565 
566  b.Property<long?>("PrimaryRevisionInformationId")
567  .IsRequired()
568  .HasColumnType("bigint");
569 
570  b.Property<string>("PullRequestRevision")
571  .IsRequired()
572  .HasColumnType("character varying(40)")
573  .HasMaxLength(40);
574 
575  b.Property<string>("TitleAtMerge")
576  .IsRequired()
577  .HasColumnType("text");
578 
579  b.Property<string>("Url")
580  .IsRequired()
581  .HasColumnType("text");
582 
583  b.HasKey("Id");
584 
585  b.HasIndex("MergedById");
586 
587  b.HasIndex("PrimaryRevisionInformationId")
588  .IsUnique();
589 
590  b.ToTable("TestMerges");
591  });
592 
593  modelBuilder.Entity("Tgstation.Server.Host.Models.User", b =>
594  {
595  b.Property<long?>("Id")
596  .ValueGeneratedOnAdd()
597  .HasColumnType("bigint")
598  .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
599 
600  b.Property<decimal>("AdministrationRights")
601  .HasColumnType("numeric(20,0)");
602 
603  b.Property<string>("CanonicalName")
604  .IsRequired()
605  .HasColumnType("text");
606 
607  b.Property<DateTimeOffset?>("CreatedAt")
608  .IsRequired()
609  .HasColumnType("timestamp with time zone");
610 
611  b.Property<long?>("CreatedById")
612  .HasColumnType("bigint");
613 
614  b.Property<bool?>("Enabled")
615  .IsRequired()
616  .HasColumnType("boolean");
617 
618  b.Property<decimal>("InstanceManagerRights")
619  .HasColumnType("numeric(20,0)");
620 
621  b.Property<DateTimeOffset?>("LastPasswordUpdate")
622  .HasColumnType("timestamp with time zone");
623 
624  b.Property<string>("Name")
625  .IsRequired()
626  .HasColumnType("character varying(10000)")
627  .HasMaxLength(10000);
628 
629  b.Property<string>("PasswordHash")
630  .HasColumnType("text");
631 
632  b.Property<string>("SystemIdentifier")
633  .HasColumnType("text");
634 
635  b.HasKey("Id");
636 
637  b.HasIndex("CanonicalName")
638  .IsUnique();
639 
640  b.HasIndex("CreatedById");
641 
642  b.HasIndex("SystemIdentifier")
643  .IsUnique();
644 
645  b.ToTable("Users");
646  });
647 
648  modelBuilder.Entity("Tgstation.Server.Host.Models.ChatBot", b =>
649  {
650  b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
651  .WithMany("ChatSettings")
652  .HasForeignKey("InstanceId")
653  .OnDelete(DeleteBehavior.Cascade)
654  .IsRequired();
655  });
656 
657  modelBuilder.Entity("Tgstation.Server.Host.Models.ChatChannel", b =>
658  {
659  b.HasOne("Tgstation.Server.Host.Models.ChatBot", "ChatSettings")
660  .WithMany("Channels")
661  .HasForeignKey("ChatSettingsId")
662  .OnDelete(DeleteBehavior.Cascade)
663  .IsRequired();
664  });
665 
666  modelBuilder.Entity("Tgstation.Server.Host.Models.CompileJob", b =>
667  {
668  b.HasOne("Tgstation.Server.Host.Models.Job", "Job")
669  .WithOne()
670  .HasForeignKey("Tgstation.Server.Host.Models.CompileJob", "JobId")
671  .OnDelete(DeleteBehavior.Cascade)
672  .IsRequired();
673 
674  b.HasOne("Tgstation.Server.Host.Models.RevisionInformation", "RevisionInformation")
675  .WithMany("CompileJobs")
676  .HasForeignKey("RevisionInformationId")
677  .OnDelete(DeleteBehavior.Cascade)
678  .IsRequired();
679  });
680 
681  modelBuilder.Entity("Tgstation.Server.Host.Models.DreamDaemonSettings", b =>
682  {
683  b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
684  .WithOne("DreamDaemonSettings")
685  .HasForeignKey("Tgstation.Server.Host.Models.DreamDaemonSettings", "InstanceId")
686  .OnDelete(DeleteBehavior.Cascade)
687  .IsRequired();
688  });
689 
690  modelBuilder.Entity("Tgstation.Server.Host.Models.DreamMakerSettings", b =>
691  {
692  b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
693  .WithOne("DreamMakerSettings")
694  .HasForeignKey("Tgstation.Server.Host.Models.DreamMakerSettings", "InstanceId")
695  .OnDelete(DeleteBehavior.Cascade)
696  .IsRequired();
697  });
698 
699  modelBuilder.Entity("Tgstation.Server.Host.Models.DualReattachInformation", b =>
700  {
701  b.HasOne("Tgstation.Server.Host.Models.ReattachInformation", "Alpha")
702  .WithMany()
703  .HasForeignKey("AlphaId");
704 
705  b.HasOne("Tgstation.Server.Host.Models.ReattachInformation", "Bravo")
706  .WithMany()
707  .HasForeignKey("BravoId");
708 
709  b.HasOne("Tgstation.Server.Host.Models.Instance", null)
710  .WithOne("WatchdogReattachInformation")
711  .HasForeignKey("Tgstation.Server.Host.Models.DualReattachInformation", "InstanceId")
712  .OnDelete(DeleteBehavior.Cascade)
713  .IsRequired();
714  });
715 
716  modelBuilder.Entity("Tgstation.Server.Host.Models.InstanceUser", b =>
717  {
718  b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
719  .WithMany("InstanceUsers")
720  .HasForeignKey("InstanceId")
721  .OnDelete(DeleteBehavior.Cascade)
722  .IsRequired();
723 
724  b.HasOne("Tgstation.Server.Host.Models.User", null)
725  .WithMany("InstanceUsers")
726  .HasForeignKey("UserId")
727  .OnDelete(DeleteBehavior.Cascade)
728  .IsRequired();
729  });
730 
731  modelBuilder.Entity("Tgstation.Server.Host.Models.Job", b =>
732  {
733  b.HasOne("Tgstation.Server.Host.Models.User", "CancelledBy")
734  .WithMany()
735  .HasForeignKey("CancelledById");
736 
737  b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
738  .WithMany("Jobs")
739  .HasForeignKey("InstanceId")
740  .OnDelete(DeleteBehavior.Cascade)
741  .IsRequired();
742 
743  b.HasOne("Tgstation.Server.Host.Models.User", "StartedBy")
744  .WithMany()
745  .HasForeignKey("StartedById")
746  .OnDelete(DeleteBehavior.Cascade)
747  .IsRequired();
748  });
749 
750  modelBuilder.Entity("Tgstation.Server.Host.Models.ReattachInformation", b =>
751  {
752  b.HasOne("Tgstation.Server.Host.Models.CompileJob", "CompileJob")
753  .WithMany()
754  .HasForeignKey("CompileJobId")
755  .OnDelete(DeleteBehavior.Cascade)
756  .IsRequired();
757  });
758 
759  modelBuilder.Entity("Tgstation.Server.Host.Models.RepositorySettings", b =>
760  {
761  b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
762  .WithOne("RepositorySettings")
763  .HasForeignKey("Tgstation.Server.Host.Models.RepositorySettings", "InstanceId")
764  .OnDelete(DeleteBehavior.Cascade)
765  .IsRequired();
766  });
767 
768  modelBuilder.Entity("Tgstation.Server.Host.Models.RevInfoTestMerge", b =>
769  {
770  b.HasOne("Tgstation.Server.Host.Models.RevisionInformation", "RevisionInformation")
771  .WithMany("ActiveTestMerges")
772  .HasForeignKey("RevisionInformationId")
773  .OnDelete(DeleteBehavior.Cascade)
774  .IsRequired();
775 
776  b.HasOne("Tgstation.Server.Host.Models.TestMerge", "TestMerge")
777  .WithMany("RevisonInformations")
778  .HasForeignKey("TestMergeId")
779  .OnDelete(DeleteBehavior.ClientNoAction)
780  .IsRequired();
781  });
782 
783  modelBuilder.Entity("Tgstation.Server.Host.Models.RevisionInformation", b =>
784  {
785  b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
786  .WithMany("RevisionInformations")
787  .HasForeignKey("InstanceId")
788  .OnDelete(DeleteBehavior.Cascade)
789  .IsRequired();
790  });
791 
792  modelBuilder.Entity("Tgstation.Server.Host.Models.TestMerge", b =>
793  {
794  b.HasOne("Tgstation.Server.Host.Models.User", "MergedBy")
795  .WithMany("TestMerges")
796  .HasForeignKey("MergedById")
797  .OnDelete(DeleteBehavior.Restrict)
798  .IsRequired();
799 
800  b.HasOne("Tgstation.Server.Host.Models.RevisionInformation", "PrimaryRevisionInformation")
801  .WithOne("PrimaryTestMerge")
802  .HasForeignKey("Tgstation.Server.Host.Models.TestMerge", "PrimaryRevisionInformationId")
803  .OnDelete(DeleteBehavior.Cascade)
804  .IsRequired();
805  });
806 
807  modelBuilder.Entity("Tgstation.Server.Host.Models.User", b =>
808  {
809  b.HasOne("Tgstation.Server.Host.Models.User", "CreatedBy")
810  .WithMany("CreatedUsers")
811  .HasForeignKey("CreatedById");
812  });
813 #pragma warning restore 612, 618
814  }
815  }
816 }
diff --git a/20200516111712___p_g_create_8cs_source.html b/20200516111712___p_g_create_8cs_source.html index bc55141a2e..ecd5be4aba 100644 --- a/20200516111712___p_g_create_8cs_source.html +++ b/20200516111712___p_g_create_8cs_source.html @@ -94,7 +94,7 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
20200516111712_PGCreate.cs
-Go to the documentation of this file.
1 using System;
2 using Microsoft.EntityFrameworkCore.Migrations;
3 using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
4 
5 namespace Tgstation.Server.Host.Database.Migrations
6 {
10  public partial class PGCreate : Migration
11  {
13  protected override void Up(MigrationBuilder migrationBuilder)
14  {
15  if (migrationBuilder == null)
16  throw new ArgumentNullException(nameof(migrationBuilder));
17 
18  migrationBuilder.CreateTable(
19  name: "Instances",
20  columns: table => new
21  {
22  Id = table.Column<long>(nullable: false)
23  .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
24  Name = table.Column<string>(maxLength: 10000, nullable: false),
25  Path = table.Column<string>(nullable: false),
26  Online = table.Column<bool>(nullable: false),
27  ConfigurationType = table.Column<int>(nullable: false),
28  AutoUpdateInterval = table.Column<long>(nullable: false),
29  ChatBotLimit = table.Column<int>(nullable: false)
30  },
31  constraints: table =>
32  {
33  table.PrimaryKey("PK_Instances", x => x.Id);
34  });
35 
36  migrationBuilder.CreateTable(
37  name: "Users",
38  columns: table => new
39  {
40  Id = table.Column<long>(nullable: false)
41  .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
42  Enabled = table.Column<bool>(nullable: false),
43  CreatedAt = table.Column<DateTimeOffset>(nullable: false),
44  SystemIdentifier = table.Column<string>(nullable: true),
45  Name = table.Column<string>(maxLength: 10000, nullable: false),
46  AdministrationRights = table.Column<decimal>(nullable: false),
47  InstanceManagerRights = table.Column<decimal>(nullable: false),
48  PasswordHash = table.Column<string>(nullable: true),
49  CreatedById = table.Column<long>(nullable: true),
50  CanonicalName = table.Column<string>(nullable: false),
51  LastPasswordUpdate = table.Column<DateTimeOffset>(nullable: true)
52  },
53  constraints: table =>
54  {
55  table.PrimaryKey("PK_Users", x => x.Id);
56  table.ForeignKey(
57  name: "FK_Users_Users_CreatedById",
58  column: x => x.CreatedById,
59  principalTable: "Users",
60  principalColumn: "Id",
61  onDelete: ReferentialAction.Restrict);
62  });
63 
64  migrationBuilder.CreateTable(
65  name: "ChatBots",
66  columns: table => new
67  {
68  Id = table.Column<long>(nullable: false)
69  .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
70  Name = table.Column<string>(maxLength: 100, nullable: false),
71  Enabled = table.Column<bool>(nullable: true),
72  ReconnectionInterval = table.Column<long>(nullable: false),
73  ChannelLimit = table.Column<int>(nullable: false),
74  Provider = table.Column<int>(nullable: false),
75  ConnectionString = table.Column<string>(maxLength: 10000, nullable: false),
76  InstanceId = table.Column<long>(nullable: false)
77  },
78  constraints: table =>
79  {
80  table.PrimaryKey("PK_ChatBots", x => x.Id);
81  table.ForeignKey(
82  name: "FK_ChatBots_Instances_InstanceId",
83  column: x => x.InstanceId,
84  principalTable: "Instances",
85  principalColumn: "Id",
86  onDelete: ReferentialAction.Cascade);
87  });
88 
89  migrationBuilder.CreateTable(
90  name: "DreamDaemonSettings",
91  columns: table => new
92  {
93  Id = table.Column<long>(nullable: false)
94  .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
95  AllowWebClient = table.Column<bool>(nullable: false),
96  SecurityLevel = table.Column<int>(nullable: false),
97  PrimaryPort = table.Column<int>(nullable: false),
98  SecondaryPort = table.Column<int>(nullable: false),
99  StartupTimeout = table.Column<long>(nullable: false),
100  HeartbeatSeconds = table.Column<long>(nullable: false),
101  AutoStart = table.Column<bool>(nullable: false),
102  InstanceId = table.Column<long>(nullable: false)
103  },
104  constraints: table =>
105  {
106  table.PrimaryKey("PK_DreamDaemonSettings", x => x.Id);
107  table.ForeignKey(
108  name: "FK_DreamDaemonSettings_Instances_InstanceId",
109  column: x => x.InstanceId,
110  principalTable: "Instances",
111  principalColumn: "Id",
112  onDelete: ReferentialAction.Cascade);
113  });
114 
115  migrationBuilder.CreateTable(
116  name: "DreamMakerSettings",
117  columns: table => new
118  {
119  Id = table.Column<long>(nullable: false)
120  .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
121  ProjectName = table.Column<string>(maxLength: 10000, nullable: true),
122  ApiValidationPort = table.Column<int>(nullable: false),
123  ApiValidationSecurityLevel = table.Column<int>(nullable: false),
124  InstanceId = table.Column<long>(nullable: false)
125  },
126  constraints: table =>
127  {
128  table.PrimaryKey("PK_DreamMakerSettings", x => x.Id);
129  table.ForeignKey(
130  name: "FK_DreamMakerSettings_Instances_InstanceId",
131  column: x => x.InstanceId,
132  principalTable: "Instances",
133  principalColumn: "Id",
134  onDelete: ReferentialAction.Cascade);
135  });
136 
137  migrationBuilder.CreateTable(
138  name: "RepositorySettings",
139  columns: table => new
140  {
141  Id = table.Column<long>(nullable: false)
142  .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
143  CommitterName = table.Column<string>(maxLength: 10000, nullable: false),
144  CommitterEmail = table.Column<string>(maxLength: 10000, nullable: false),
145  AccessUser = table.Column<string>(maxLength: 10000, nullable: true),
146  AccessToken = table.Column<string>(maxLength: 10000, nullable: true),
147  PushTestMergeCommits = table.Column<bool>(nullable: false),
148  ShowTestMergeCommitters = table.Column<bool>(nullable: false),
149  AutoUpdatesKeepTestMerges = table.Column<bool>(nullable: false),
150  AutoUpdatesSynchronize = table.Column<bool>(nullable: false),
151  PostTestMergeComment = table.Column<bool>(nullable: false),
152  InstanceId = table.Column<long>(nullable: false)
153  },
154  constraints: table =>
155  {
156  table.PrimaryKey("PK_RepositorySettings", x => x.Id);
157  table.ForeignKey(
158  name: "FK_RepositorySettings_Instances_InstanceId",
159  column: x => x.InstanceId,
160  principalTable: "Instances",
161  principalColumn: "Id",
162  onDelete: ReferentialAction.Cascade);
163  });
164 
165  migrationBuilder.CreateTable(
166  name: "RevisionInformations",
167  columns: table => new
168  {
169  Id = table.Column<long>(nullable: false)
170  .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
171  CommitSha = table.Column<string>(maxLength: 40, nullable: false),
172  OriginCommitSha = table.Column<string>(maxLength: 40, nullable: false),
173  InstanceId = table.Column<long>(nullable: false)
174  },
175  constraints: table =>
176  {
177  table.PrimaryKey("PK_RevisionInformations", x => x.Id);
178  table.ForeignKey(
179  name: "FK_RevisionInformations_Instances_InstanceId",
180  column: x => x.InstanceId,
181  principalTable: "Instances",
182  principalColumn: "Id",
183  onDelete: ReferentialAction.Cascade);
184  });
185 
186  migrationBuilder.CreateTable(
187  name: "InstanceUsers",
188  columns: table => new
189  {
190  Id = table.Column<long>(nullable: false)
191  .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
192  UserId = table.Column<long>(nullable: false),
193  InstanceUserRights = table.Column<decimal>(nullable: false),
194  ByondRights = table.Column<decimal>(nullable: false),
195  DreamDaemonRights = table.Column<decimal>(nullable: false),
196  DreamMakerRights = table.Column<decimal>(nullable: false),
197  RepositoryRights = table.Column<decimal>(nullable: false),
198  ChatBotRights = table.Column<decimal>(nullable: false),
199  ConfigurationRights = table.Column<decimal>(nullable: false),
200  InstanceId = table.Column<long>(nullable: false)
201  },
202  constraints: table =>
203  {
204  table.PrimaryKey("PK_InstanceUsers", x => x.Id);
205  table.ForeignKey(
206  name: "FK_InstanceUsers_Instances_InstanceId",
207  column: x => x.InstanceId,
208  principalTable: "Instances",
209  principalColumn: "Id",
210  onDelete: ReferentialAction.Cascade);
211  table.ForeignKey(
212  name: "FK_InstanceUsers_Users_UserId",
213  column: x => x.UserId,
214  principalTable: "Users",
215  principalColumn: "Id",
216  onDelete: ReferentialAction.Cascade);
217  });
218 
219  migrationBuilder.CreateTable(
220  name: "Jobs",
221  columns: table => new
222  {
223  Id = table.Column<long>(nullable: false)
224  .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
225  Description = table.Column<string>(nullable: false),
226  ErrorCode = table.Column<long>(nullable: true),
227  ExceptionDetails = table.Column<string>(nullable: true),
228  StartedAt = table.Column<DateTimeOffset>(nullable: false),
229  StoppedAt = table.Column<DateTimeOffset>(nullable: true),
230  Cancelled = table.Column<bool>(nullable: false),
231  CancelRightsType = table.Column<decimal>(nullable: true),
232  CancelRight = table.Column<decimal>(nullable: true),
233  StartedById = table.Column<long>(nullable: false),
234  CancelledById = table.Column<long>(nullable: true),
235  InstanceId = table.Column<long>(nullable: false)
236  },
237  constraints: table =>
238  {
239  table.PrimaryKey("PK_Jobs", x => x.Id);
240  table.ForeignKey(
241  name: "FK_Jobs_Users_CancelledById",
242  column: x => x.CancelledById,
243  principalTable: "Users",
244  principalColumn: "Id",
245  onDelete: ReferentialAction.Restrict);
246  table.ForeignKey(
247  name: "FK_Jobs_Instances_InstanceId",
248  column: x => x.InstanceId,
249  principalTable: "Instances",
250  principalColumn: "Id",
251  onDelete: ReferentialAction.Cascade);
252  table.ForeignKey(
253  name: "FK_Jobs_Users_StartedById",
254  column: x => x.StartedById,
255  principalTable: "Users",
256  principalColumn: "Id",
257  onDelete: ReferentialAction.Cascade);
258  });
259 
260  migrationBuilder.CreateTable(
261  name: "ChatChannels",
262  columns: table => new
263  {
264  Id = table.Column<long>(nullable: false)
265  .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
266  IrcChannel = table.Column<string>(maxLength: 100, nullable: true),
267  DiscordChannelId = table.Column<decimal>(nullable: true),
268  IsAdminChannel = table.Column<bool>(nullable: false),
269  IsWatchdogChannel = table.Column<bool>(nullable: false),
270  IsUpdatesChannel = table.Column<bool>(nullable: false),
271  Tag = table.Column<string>(maxLength: 10000, nullable: true),
272  ChatSettingsId = table.Column<long>(nullable: false)
273  },
274  constraints: table =>
275  {
276  table.PrimaryKey("PK_ChatChannels", x => x.Id);
277  table.ForeignKey(
278  name: "FK_ChatChannels_ChatBots_ChatSettingsId",
279  column: x => x.ChatSettingsId,
280  principalTable: "ChatBots",
281  principalColumn: "Id",
282  onDelete: ReferentialAction.Cascade);
283  });
284 
285  migrationBuilder.CreateTable(
286  name: "TestMerges",
287  columns: table => new
288  {
289  Id = table.Column<long>(nullable: false)
290  .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
291  Number = table.Column<int>(nullable: false),
292  PullRequestRevision = table.Column<string>(maxLength: 40, nullable: false),
293  Comment = table.Column<string>(maxLength: 10000, nullable: true),
294  TitleAtMerge = table.Column<string>(nullable: false),
295  BodyAtMerge = table.Column<string>(nullable: false),
296  Url = table.Column<string>(nullable: false),
297  Author = table.Column<string>(nullable: false),
298  MergedAt = table.Column<DateTimeOffset>(nullable: false),
299  MergedById = table.Column<long>(nullable: false),
300  PrimaryRevisionInformationId = table.Column<long>(nullable: false)
301  },
302  constraints: table =>
303  {
304  table.PrimaryKey("PK_TestMerges", x => x.Id);
305  table.ForeignKey(
306  name: "FK_TestMerges_Users_MergedById",
307  column: x => x.MergedById,
308  principalTable: "Users",
309  principalColumn: "Id",
310  onDelete: ReferentialAction.Restrict);
311  table.ForeignKey(
312  name: "FK_TestMerges_RevisionInformations_PrimaryRevisionInformationId",
313  column: x => x.PrimaryRevisionInformationId,
314  principalTable: "RevisionInformations",
315  principalColumn: "Id",
316  onDelete: ReferentialAction.Cascade);
317  });
318 
319  migrationBuilder.CreateTable(
320  name: "CompileJobs",
321  columns: table => new
322  {
323  Id = table.Column<long>(nullable: false)
324  .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
325  DmeName = table.Column<string>(nullable: false),
326  Output = table.Column<string>(nullable: false),
327  DirectoryName = table.Column<Guid>(nullable: false),
328  MinimumSecurityLevel = table.Column<int>(nullable: false),
329  JobId = table.Column<long>(nullable: false),
330  RevisionInformationId = table.Column<long>(nullable: false),
331  ByondVersion = table.Column<string>(nullable: false),
332  DMApiMajorVersion = table.Column<int>(nullable: true),
333  DMApiMinorVersion = table.Column<int>(nullable: true),
334  DMApiPatchVersion = table.Column<int>(nullable: true)
335  },
336  constraints: table =>
337  {
338  table.PrimaryKey("PK_CompileJobs", x => x.Id);
339  table.ForeignKey(
340  name: "FK_CompileJobs_Jobs_JobId",
341  column: x => x.JobId,
342  principalTable: "Jobs",
343  principalColumn: "Id",
344  onDelete: ReferentialAction.Cascade);
345  table.ForeignKey(
346  name: "FK_CompileJobs_RevisionInformations_RevisionInformationId",
347  column: x => x.RevisionInformationId,
348  principalTable: "RevisionInformations",
349  principalColumn: "Id");
350  });
351 
352  migrationBuilder.CreateTable(
353  name: "RevInfoTestMerges",
354  columns: table => new
355  {
356  Id = table.Column<long>(nullable: false)
357  .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
358  TestMergeId = table.Column<long>(nullable: false),
359  RevisionInformationId = table.Column<long>(nullable: false)
360  },
361  constraints: table =>
362  {
363  table.PrimaryKey("PK_RevInfoTestMerges", x => x.Id);
364  table.ForeignKey(
365  name: "FK_RevInfoTestMerges_RevisionInformations_RevisionInformationId",
366  column: x => x.RevisionInformationId,
367  principalTable: "RevisionInformations",
368  principalColumn: "Id",
369  onDelete: ReferentialAction.Cascade);
370  table.ForeignKey(
371  name: "FK_RevInfoTestMerges_TestMerges_TestMergeId",
372  column: x => x.TestMergeId,
373  principalTable: "TestMerges",
374  principalColumn: "Id");
375  });
376 
377  migrationBuilder.CreateTable(
378  name: "ReattachInformations",
379  columns: table => new
380  {
381  Id = table.Column<long>(nullable: false)
382  .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
383  AccessIdentifier = table.Column<string>(nullable: false),
384  ProcessId = table.Column<int>(nullable: false),
385  IsPrimary = table.Column<bool>(nullable: false),
386  Port = table.Column<int>(nullable: false),
387  RebootState = table.Column<int>(nullable: false),
388  LaunchSecurityLevel = table.Column<int>(nullable: false),
389  CompileJobId = table.Column<long>(nullable: false)
390  },
391  constraints: table =>
392  {
393  table.PrimaryKey("PK_ReattachInformations", x => x.Id);
394  table.ForeignKey(
395  name: "FK_ReattachInformations_CompileJobs_CompileJobId",
396  column: x => x.CompileJobId,
397  principalTable: "CompileJobs",
398  principalColumn: "Id",
399  onDelete: ReferentialAction.Cascade);
400  });
401 
402  migrationBuilder.CreateTable(
403  name: "WatchdogReattachInformations",
404  columns: table => new
405  {
406  Id = table.Column<long>(nullable: false)
407  .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
408  AlphaIsActive = table.Column<bool>(nullable: false),
409  InstanceId = table.Column<long>(nullable: false),
410  AlphaId = table.Column<long>(nullable: true),
411  BravoId = table.Column<long>(nullable: true)
412  },
413  constraints: table =>
414  {
415  table.PrimaryKey("PK_WatchdogReattachInformations", x => x.Id);
416  table.ForeignKey(
417  name: "FK_WatchdogReattachInformations_ReattachInformations_AlphaId",
418  column: x => x.AlphaId,
419  principalTable: "ReattachInformations",
420  principalColumn: "Id",
421  onDelete: ReferentialAction.Restrict);
422  table.ForeignKey(
423  name: "FK_WatchdogReattachInformations_ReattachInformations_BravoId",
424  column: x => x.BravoId,
425  principalTable: "ReattachInformations",
426  principalColumn: "Id",
427  onDelete: ReferentialAction.Restrict);
428  table.ForeignKey(
429  name: "FK_WatchdogReattachInformations_Instances_InstanceId",
430  column: x => x.InstanceId,
431  principalTable: "Instances",
432  principalColumn: "Id",
433  onDelete: ReferentialAction.Cascade);
434  });
435 
436  migrationBuilder.CreateIndex(
437  name: "IX_ChatBots_InstanceId_Name",
438  table: "ChatBots",
439  columns: new[] { "InstanceId", "Name" },
440  unique: true);
441 
442  migrationBuilder.CreateIndex(
443  name: "IX_ChatChannels_ChatSettingsId_DiscordChannelId",
444  table: "ChatChannels",
445  columns: new[] { "ChatSettingsId", "DiscordChannelId" },
446  unique: true);
447 
448  migrationBuilder.CreateIndex(
449  name: "IX_ChatChannels_ChatSettingsId_IrcChannel",
450  table: "ChatChannels",
451  columns: new[] { "ChatSettingsId", "IrcChannel" },
452  unique: true);
453 
454  migrationBuilder.CreateIndex(
455  name: "IX_CompileJobs_DirectoryName",
456  table: "CompileJobs",
457  column: "DirectoryName");
458 
459  migrationBuilder.CreateIndex(
460  name: "IX_CompileJobs_JobId",
461  table: "CompileJobs",
462  column: "JobId",
463  unique: true);
464 
465  migrationBuilder.CreateIndex(
466  name: "IX_CompileJobs_RevisionInformationId",
467  table: "CompileJobs",
468  column: "RevisionInformationId");
469 
470  migrationBuilder.CreateIndex(
471  name: "IX_DreamDaemonSettings_InstanceId",
472  table: "DreamDaemonSettings",
473  column: "InstanceId",
474  unique: true);
475 
476  migrationBuilder.CreateIndex(
477  name: "IX_DreamMakerSettings_InstanceId",
478  table: "DreamMakerSettings",
479  column: "InstanceId",
480  unique: true);
481 
482  migrationBuilder.CreateIndex(
483  name: "IX_Instances_Path",
484  table: "Instances",
485  column: "Path",
486  unique: true);
487 
488  migrationBuilder.CreateIndex(
489  name: "IX_InstanceUsers_InstanceId",
490  table: "InstanceUsers",
491  column: "InstanceId");
492 
493  migrationBuilder.CreateIndex(
494  name: "IX_InstanceUsers_UserId_InstanceId",
495  table: "InstanceUsers",
496  columns: new[] { "UserId", "InstanceId" },
497  unique: true);
498 
499  migrationBuilder.CreateIndex(
500  name: "IX_Jobs_CancelledById",
501  table: "Jobs",
502  column: "CancelledById");
503 
504  migrationBuilder.CreateIndex(
505  name: "IX_Jobs_InstanceId",
506  table: "Jobs",
507  column: "InstanceId");
508 
509  migrationBuilder.CreateIndex(
510  name: "IX_Jobs_StartedById",
511  table: "Jobs",
512  column: "StartedById");
513 
514  migrationBuilder.CreateIndex(
515  name: "IX_ReattachInformations_CompileJobId",
516  table: "ReattachInformations",
517  column: "CompileJobId");
518 
519  migrationBuilder.CreateIndex(
520  name: "IX_RepositorySettings_InstanceId",
521  table: "RepositorySettings",
522  column: "InstanceId",
523  unique: true);
524 
525  migrationBuilder.CreateIndex(
526  name: "IX_RevInfoTestMerges_RevisionInformationId",
527  table: "RevInfoTestMerges",
528  column: "RevisionInformationId");
529 
530  migrationBuilder.CreateIndex(
531  name: "IX_RevInfoTestMerges_TestMergeId",
532  table: "RevInfoTestMerges",
533  column: "TestMergeId");
534 
535  migrationBuilder.CreateIndex(
536  name: "IX_RevisionInformations_InstanceId_CommitSha",
537  table: "RevisionInformations",
538  columns: new[] { "InstanceId", "CommitSha" },
539  unique: true);
540 
541  migrationBuilder.CreateIndex(
542  name: "IX_TestMerges_MergedById",
543  table: "TestMerges",
544  column: "MergedById");
545 
546  migrationBuilder.CreateIndex(
547  name: "IX_TestMerges_PrimaryRevisionInformationId",
548  table: "TestMerges",
549  column: "PrimaryRevisionInformationId",
550  unique: true);
551 
552  migrationBuilder.CreateIndex(
553  name: "IX_Users_CanonicalName",
554  table: "Users",
555  column: "CanonicalName",
556  unique: true);
557 
558  migrationBuilder.CreateIndex(
559  name: "IX_Users_CreatedById",
560  table: "Users",
561  column: "CreatedById");
562 
563  migrationBuilder.CreateIndex(
564  name: "IX_Users_SystemIdentifier",
565  table: "Users",
566  column: "SystemIdentifier",
567  unique: true);
568 
569  migrationBuilder.CreateIndex(
570  name: "IX_WatchdogReattachInformations_AlphaId",
571  table: "WatchdogReattachInformations",
572  column: "AlphaId");
573 
574  migrationBuilder.CreateIndex(
575  name: "IX_WatchdogReattachInformations_BravoId",
576  table: "WatchdogReattachInformations",
577  column: "BravoId");
578 
579  migrationBuilder.CreateIndex(
580  name: "IX_WatchdogReattachInformations_InstanceId",
581  table: "WatchdogReattachInformations",
582  column: "InstanceId",
583  unique: true);
584  }
585 
587  protected override void Down(MigrationBuilder migrationBuilder)
588  {
589  if (migrationBuilder == null)
590  throw new ArgumentNullException(nameof(migrationBuilder));
591 
592  migrationBuilder.DropTable(
593  name: "ChatChannels");
594 
595  migrationBuilder.DropTable(
596  name: "DreamDaemonSettings");
597 
598  migrationBuilder.DropTable(
599  name: "DreamMakerSettings");
600 
601  migrationBuilder.DropTable(
602  name: "InstanceUsers");
603 
604  migrationBuilder.DropTable(
605  name: "RepositorySettings");
606 
607  migrationBuilder.DropTable(
608  name: "RevInfoTestMerges");
609 
610  migrationBuilder.DropTable(
611  name: "WatchdogReattachInformations");
612 
613  migrationBuilder.DropTable(
614  name: "ChatBots");
615 
616  migrationBuilder.DropTable(
617  name: "TestMerges");
618 
619  migrationBuilder.DropTable(
620  name: "ReattachInformations");
621 
622  migrationBuilder.DropTable(
623  name: "CompileJobs");
624 
625  migrationBuilder.DropTable(
626  name: "Jobs");
627 
628  migrationBuilder.DropTable(
629  name: "RevisionInformations");
630 
631  migrationBuilder.DropTable(
632  name: "Users");
633 
634  migrationBuilder.DropTable(
635  name: "Instances");
636  }
637  }
638 }
ConfigurationRights
Rights for Models.ConfigurationFile
+Go to the documentation of this file.
1 using System;
2 using Microsoft.EntityFrameworkCore.Migrations;
3 using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
4 
5 namespace Tgstation.Server.Host.Database.Migrations
6 {
10  public partial class PGCreate : Migration
11  {
13  protected override void Up(MigrationBuilder migrationBuilder)
14  {
15  if (migrationBuilder == null)
16  throw new ArgumentNullException(nameof(migrationBuilder));
17 
18  migrationBuilder.CreateTable(
19  name: "Instances",
20  columns: table => new
21  {
22  Id = table.Column<long>(nullable: false)
23  .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
24  Name = table.Column<string>(maxLength: 10000, nullable: false),
25  Path = table.Column<string>(nullable: false),
26  Online = table.Column<bool>(nullable: false),
27  ConfigurationType = table.Column<int>(nullable: false),
28  AutoUpdateInterval = table.Column<long>(nullable: false),
29  ChatBotLimit = table.Column<int>(nullable: false)
30  },
31  constraints: table =>
32  {
33  table.PrimaryKey("PK_Instances", x => x.Id);
34  });
35 
36  migrationBuilder.CreateTable(
37  name: "Users",
38  columns: table => new
39  {
40  Id = table.Column<long>(nullable: false)
41  .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
42  Enabled = table.Column<bool>(nullable: false),
43  CreatedAt = table.Column<DateTimeOffset>(nullable: false),
44  SystemIdentifier = table.Column<string>(nullable: true),
45  Name = table.Column<string>(maxLength: 10000, nullable: false),
46  AdministrationRights = table.Column<decimal>(nullable: false),
47  InstanceManagerRights = table.Column<decimal>(nullable: false),
48  PasswordHash = table.Column<string>(nullable: true),
49  CreatedById = table.Column<long>(nullable: true),
50  CanonicalName = table.Column<string>(nullable: false),
51  LastPasswordUpdate = table.Column<DateTimeOffset>(nullable: true)
52  },
53  constraints: table =>
54  {
55  table.PrimaryKey("PK_Users", x => x.Id);
56  table.ForeignKey(
57  name: "FK_Users_Users_CreatedById",
58  column: x => x.CreatedById,
59  principalTable: "Users",
60  principalColumn: "Id",
61  onDelete: ReferentialAction.Restrict);
62  });
63 
64  migrationBuilder.CreateTable(
65  name: "ChatBots",
66  columns: table => new
67  {
68  Id = table.Column<long>(nullable: false)
69  .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
70  Name = table.Column<string>(maxLength: 100, nullable: false),
71  Enabled = table.Column<bool>(nullable: true),
72  ReconnectionInterval = table.Column<long>(nullable: false),
73  ChannelLimit = table.Column<int>(nullable: false),
74  Provider = table.Column<int>(nullable: false),
75  ConnectionString = table.Column<string>(maxLength: 10000, nullable: false),
76  InstanceId = table.Column<long>(nullable: false)
77  },
78  constraints: table =>
79  {
80  table.PrimaryKey("PK_ChatBots", x => x.Id);
81  table.ForeignKey(
82  name: "FK_ChatBots_Instances_InstanceId",
83  column: x => x.InstanceId,
84  principalTable: "Instances",
85  principalColumn: "Id",
86  onDelete: ReferentialAction.Cascade);
87  });
88 
89  migrationBuilder.CreateTable(
90  name: "DreamDaemonSettings",
91  columns: table => new
92  {
93  Id = table.Column<long>(nullable: false)
94  .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
95  AllowWebClient = table.Column<bool>(nullable: false),
96  SecurityLevel = table.Column<int>(nullable: false),
97  PrimaryPort = table.Column<int>(nullable: false),
98  SecondaryPort = table.Column<int>(nullable: false),
99  StartupTimeout = table.Column<long>(nullable: false),
100  HeartbeatSeconds = table.Column<long>(nullable: false),
101  AutoStart = table.Column<bool>(nullable: false),
102  InstanceId = table.Column<long>(nullable: false)
103  },
104  constraints: table =>
105  {
106  table.PrimaryKey("PK_DreamDaemonSettings", x => x.Id);
107  table.ForeignKey(
108  name: "FK_DreamDaemonSettings_Instances_InstanceId",
109  column: x => x.InstanceId,
110  principalTable: "Instances",
111  principalColumn: "Id",
112  onDelete: ReferentialAction.Cascade);
113  });
114 
115  migrationBuilder.CreateTable(
116  name: "DreamMakerSettings",
117  columns: table => new
118  {
119  Id = table.Column<long>(nullable: false)
120  .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
121  ProjectName = table.Column<string>(maxLength: 10000, nullable: true),
122  ApiValidationPort = table.Column<int>(nullable: false),
123  ApiValidationSecurityLevel = table.Column<int>(nullable: false),
124  InstanceId = table.Column<long>(nullable: false)
125  },
126  constraints: table =>
127  {
128  table.PrimaryKey("PK_DreamMakerSettings", x => x.Id);
129  table.ForeignKey(
130  name: "FK_DreamMakerSettings_Instances_InstanceId",
131  column: x => x.InstanceId,
132  principalTable: "Instances",
133  principalColumn: "Id",
134  onDelete: ReferentialAction.Cascade);
135  });
136 
137  migrationBuilder.CreateTable(
138  name: "RepositorySettings",
139  columns: table => new
140  {
141  Id = table.Column<long>(nullable: false)
142  .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
143  CommitterName = table.Column<string>(maxLength: 10000, nullable: false),
144  CommitterEmail = table.Column<string>(maxLength: 10000, nullable: false),
145  AccessUser = table.Column<string>(maxLength: 10000, nullable: true),
146  AccessToken = table.Column<string>(maxLength: 10000, nullable: true),
147  PushTestMergeCommits = table.Column<bool>(nullable: false),
148  ShowTestMergeCommitters = table.Column<bool>(nullable: false),
149  AutoUpdatesKeepTestMerges = table.Column<bool>(nullable: false),
150  AutoUpdatesSynchronize = table.Column<bool>(nullable: false),
151  PostTestMergeComment = table.Column<bool>(nullable: false),
152  InstanceId = table.Column<long>(nullable: false)
153  },
154  constraints: table =>
155  {
156  table.PrimaryKey("PK_RepositorySettings", x => x.Id);
157  table.ForeignKey(
158  name: "FK_RepositorySettings_Instances_InstanceId",
159  column: x => x.InstanceId,
160  principalTable: "Instances",
161  principalColumn: "Id",
162  onDelete: ReferentialAction.Cascade);
163  });
164 
165  migrationBuilder.CreateTable(
166  name: "RevisionInformations",
167  columns: table => new
168  {
169  Id = table.Column<long>(nullable: false)
170  .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
171  CommitSha = table.Column<string>(maxLength: 40, nullable: false),
172  OriginCommitSha = table.Column<string>(maxLength: 40, nullable: false),
173  InstanceId = table.Column<long>(nullable: false)
174  },
175  constraints: table =>
176  {
177  table.PrimaryKey("PK_RevisionInformations", x => x.Id);
178  table.ForeignKey(
179  name: "FK_RevisionInformations_Instances_InstanceId",
180  column: x => x.InstanceId,
181  principalTable: "Instances",
182  principalColumn: "Id",
183  onDelete: ReferentialAction.Cascade);
184  });
185 
186  migrationBuilder.CreateTable(
187  name: "InstanceUsers",
188  columns: table => new
189  {
190  Id = table.Column<long>(nullable: false)
191  .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
192  UserId = table.Column<long>(nullable: false),
193  InstanceUserRights = table.Column<decimal>(nullable: false),
194  ByondRights = table.Column<decimal>(nullable: false),
195  DreamDaemonRights = table.Column<decimal>(nullable: false),
196  DreamMakerRights = table.Column<decimal>(nullable: false),
197  RepositoryRights = table.Column<decimal>(nullable: false),
198  ChatBotRights = table.Column<decimal>(nullable: false),
199  ConfigurationRights = table.Column<decimal>(nullable: false),
200  InstanceId = table.Column<long>(nullable: false)
201  },
202  constraints: table =>
203  {
204  table.PrimaryKey("PK_InstanceUsers", x => x.Id);
205  table.ForeignKey(
206  name: "FK_InstanceUsers_Instances_InstanceId",
207  column: x => x.InstanceId,
208  principalTable: "Instances",
209  principalColumn: "Id",
210  onDelete: ReferentialAction.Cascade);
211  table.ForeignKey(
212  name: "FK_InstanceUsers_Users_UserId",
213  column: x => x.UserId,
214  principalTable: "Users",
215  principalColumn: "Id",
216  onDelete: ReferentialAction.Cascade);
217  });
218 
219  migrationBuilder.CreateTable(
220  name: "Jobs",
221  columns: table => new
222  {
223  Id = table.Column<long>(nullable: false)
224  .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
225  Description = table.Column<string>(nullable: false),
226  ErrorCode = table.Column<long>(nullable: true),
227  ExceptionDetails = table.Column<string>(nullable: true),
228  StartedAt = table.Column<DateTimeOffset>(nullable: false),
229  StoppedAt = table.Column<DateTimeOffset>(nullable: true),
230  Cancelled = table.Column<bool>(nullable: false),
231  CancelRightsType = table.Column<decimal>(nullable: true),
232  CancelRight = table.Column<decimal>(nullable: true),
233  StartedById = table.Column<long>(nullable: false),
234  CancelledById = table.Column<long>(nullable: true),
235  InstanceId = table.Column<long>(nullable: false)
236  },
237  constraints: table =>
238  {
239  table.PrimaryKey("PK_Jobs", x => x.Id);
240  table.ForeignKey(
241  name: "FK_Jobs_Users_CancelledById",
242  column: x => x.CancelledById,
243  principalTable: "Users",
244  principalColumn: "Id",
245  onDelete: ReferentialAction.Restrict);
246  table.ForeignKey(
247  name: "FK_Jobs_Instances_InstanceId",
248  column: x => x.InstanceId,
249  principalTable: "Instances",
250  principalColumn: "Id",
251  onDelete: ReferentialAction.Cascade);
252  table.ForeignKey(
253  name: "FK_Jobs_Users_StartedById",
254  column: x => x.StartedById,
255  principalTable: "Users",
256  principalColumn: "Id",
257  onDelete: ReferentialAction.Cascade);
258  });
259 
260  migrationBuilder.CreateTable(
261  name: "ChatChannels",
262  columns: table => new
263  {
264  Id = table.Column<long>(nullable: false)
265  .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
266  IrcChannel = table.Column<string>(maxLength: 100, nullable: true),
267  DiscordChannelId = table.Column<decimal>(nullable: true),
268  IsAdminChannel = table.Column<bool>(nullable: false),
269  IsWatchdogChannel = table.Column<bool>(nullable: false),
270  IsUpdatesChannel = table.Column<bool>(nullable: false),
271  Tag = table.Column<string>(maxLength: 10000, nullable: true),
272  ChatSettingsId = table.Column<long>(nullable: false)
273  },
274  constraints: table =>
275  {
276  table.PrimaryKey("PK_ChatChannels", x => x.Id);
277  table.ForeignKey(
278  name: "FK_ChatChannels_ChatBots_ChatSettingsId",
279  column: x => x.ChatSettingsId,
280  principalTable: "ChatBots",
281  principalColumn: "Id",
282  onDelete: ReferentialAction.Cascade);
283  });
284 
285  migrationBuilder.CreateTable(
286  name: "TestMerges",
287  columns: table => new
288  {
289  Id = table.Column<long>(nullable: false)
290  .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
291  Number = table.Column<int>(nullable: false),
292  PullRequestRevision = table.Column<string>(maxLength: 40, nullable: false),
293  Comment = table.Column<string>(maxLength: 10000, nullable: true),
294  TitleAtMerge = table.Column<string>(nullable: false),
295  BodyAtMerge = table.Column<string>(nullable: false),
296  Url = table.Column<string>(nullable: false),
297  Author = table.Column<string>(nullable: false),
298  MergedAt = table.Column<DateTimeOffset>(nullable: false),
299  MergedById = table.Column<long>(nullable: false),
300  PrimaryRevisionInformationId = table.Column<long>(nullable: false)
301  },
302  constraints: table =>
303  {
304  table.PrimaryKey("PK_TestMerges", x => x.Id);
305  table.ForeignKey(
306  name: "FK_TestMerges_Users_MergedById",
307  column: x => x.MergedById,
308  principalTable: "Users",
309  principalColumn: "Id",
310  onDelete: ReferentialAction.Restrict);
311  table.ForeignKey(
312  name: "FK_TestMerges_RevisionInformations_PrimaryRevisionInformationId",
313  column: x => x.PrimaryRevisionInformationId,
314  principalTable: "RevisionInformations",
315  principalColumn: "Id",
316  onDelete: ReferentialAction.Cascade);
317  });
318 
319  migrationBuilder.CreateTable(
320  name: "CompileJobs",
321  columns: table => new
322  {
323  Id = table.Column<long>(nullable: false)
324  .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
325  DmeName = table.Column<string>(nullable: false),
326  Output = table.Column<string>(nullable: false),
327  DirectoryName = table.Column<Guid>(nullable: false),
328  MinimumSecurityLevel = table.Column<int>(nullable: false),
329  JobId = table.Column<long>(nullable: false),
330  RevisionInformationId = table.Column<long>(nullable: false),
331  ByondVersion = table.Column<string>(nullable: false),
332  DMApiMajorVersion = table.Column<int>(nullable: true),
333  DMApiMinorVersion = table.Column<int>(nullable: true),
334  DMApiPatchVersion = table.Column<int>(nullable: true)
335  },
336  constraints: table =>
337  {
338  table.PrimaryKey("PK_CompileJobs", x => x.Id);
339  table.ForeignKey(
340  name: "FK_CompileJobs_Jobs_JobId",
341  column: x => x.JobId,
342  principalTable: "Jobs",
343  principalColumn: "Id",
344  onDelete: ReferentialAction.Cascade);
345  table.ForeignKey(
346  name: "FK_CompileJobs_RevisionInformations_RevisionInformationId",
347  column: x => x.RevisionInformationId,
348  principalTable: "RevisionInformations",
349  principalColumn: "Id",
350  onDelete: ReferentialAction.Cascade);
351  });
352 
353  migrationBuilder.CreateTable(
354  name: "RevInfoTestMerges",
355  columns: table => new
356  {
357  Id = table.Column<long>(nullable: false)
358  .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
359  TestMergeId = table.Column<long>(nullable: false),
360  RevisionInformationId = table.Column<long>(nullable: false)
361  },
362  constraints: table =>
363  {
364  table.PrimaryKey("PK_RevInfoTestMerges", x => x.Id);
365  table.ForeignKey(
366  name: "FK_RevInfoTestMerges_RevisionInformations_RevisionInformationId",
367  column: x => x.RevisionInformationId,
368  principalTable: "RevisionInformations",
369  principalColumn: "Id",
370  onDelete: ReferentialAction.Cascade);
371  table.ForeignKey(
372  name: "FK_RevInfoTestMerges_TestMerges_TestMergeId",
373  column: x => x.TestMergeId,
374  principalTable: "TestMerges",
375  principalColumn: "Id");
376  });
377 
378  migrationBuilder.CreateTable(
379  name: "ReattachInformations",
380  columns: table => new
381  {
382  Id = table.Column<long>(nullable: false)
383  .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
384  AccessIdentifier = table.Column<string>(nullable: false),
385  ProcessId = table.Column<int>(nullable: false),
386  IsPrimary = table.Column<bool>(nullable: false),
387  Port = table.Column<int>(nullable: false),
388  RebootState = table.Column<int>(nullable: false),
389  LaunchSecurityLevel = table.Column<int>(nullable: false),
390  CompileJobId = table.Column<long>(nullable: false)
391  },
392  constraints: table =>
393  {
394  table.PrimaryKey("PK_ReattachInformations", x => x.Id);
395  table.ForeignKey(
396  name: "FK_ReattachInformations_CompileJobs_CompileJobId",
397  column: x => x.CompileJobId,
398  principalTable: "CompileJobs",
399  principalColumn: "Id",
400  onDelete: ReferentialAction.Cascade);
401  });
402 
403  migrationBuilder.CreateTable(
404  name: "WatchdogReattachInformations",
405  columns: table => new
406  {
407  Id = table.Column<long>(nullable: false)
408  .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
409  AlphaIsActive = table.Column<bool>(nullable: false),
410  InstanceId = table.Column<long>(nullable: false),
411  AlphaId = table.Column<long>(nullable: true),
412  BravoId = table.Column<long>(nullable: true)
413  },
414  constraints: table =>
415  {
416  table.PrimaryKey("PK_WatchdogReattachInformations", x => x.Id);
417  table.ForeignKey(
418  name: "FK_WatchdogReattachInformations_ReattachInformations_AlphaId",
419  column: x => x.AlphaId,
420  principalTable: "ReattachInformations",
421  principalColumn: "Id",
422  onDelete: ReferentialAction.Restrict);
423  table.ForeignKey(
424  name: "FK_WatchdogReattachInformations_ReattachInformations_BravoId",
425  column: x => x.BravoId,
426  principalTable: "ReattachInformations",
427  principalColumn: "Id",
428  onDelete: ReferentialAction.Restrict);
429  table.ForeignKey(
430  name: "FK_WatchdogReattachInformations_Instances_InstanceId",
431  column: x => x.InstanceId,
432  principalTable: "Instances",
433  principalColumn: "Id",
434  onDelete: ReferentialAction.Cascade);
435  });
436 
437  migrationBuilder.CreateIndex(
438  name: "IX_ChatBots_InstanceId_Name",
439  table: "ChatBots",
440  columns: new[] { "InstanceId", "Name" },
441  unique: true);
442 
443  migrationBuilder.CreateIndex(
444  name: "IX_ChatChannels_ChatSettingsId_DiscordChannelId",
445  table: "ChatChannels",
446  columns: new[] { "ChatSettingsId", "DiscordChannelId" },
447  unique: true);
448 
449  migrationBuilder.CreateIndex(
450  name: "IX_ChatChannels_ChatSettingsId_IrcChannel",
451  table: "ChatChannels",
452  columns: new[] { "ChatSettingsId", "IrcChannel" },
453  unique: true);
454 
455  migrationBuilder.CreateIndex(
456  name: "IX_CompileJobs_DirectoryName",
457  table: "CompileJobs",
458  column: "DirectoryName");
459 
460  migrationBuilder.CreateIndex(
461  name: "IX_CompileJobs_JobId",
462  table: "CompileJobs",
463  column: "JobId",
464  unique: true);
465 
466  migrationBuilder.CreateIndex(
467  name: "IX_CompileJobs_RevisionInformationId",
468  table: "CompileJobs",
469  column: "RevisionInformationId");
470 
471  migrationBuilder.CreateIndex(
472  name: "IX_DreamDaemonSettings_InstanceId",
473  table: "DreamDaemonSettings",
474  column: "InstanceId",
475  unique: true);
476 
477  migrationBuilder.CreateIndex(
478  name: "IX_DreamMakerSettings_InstanceId",
479  table: "DreamMakerSettings",
480  column: "InstanceId",
481  unique: true);
482 
483  migrationBuilder.CreateIndex(
484  name: "IX_Instances_Path",
485  table: "Instances",
486  column: "Path",
487  unique: true);
488 
489  migrationBuilder.CreateIndex(
490  name: "IX_InstanceUsers_InstanceId",
491  table: "InstanceUsers",
492  column: "InstanceId");
493 
494  migrationBuilder.CreateIndex(
495  name: "IX_InstanceUsers_UserId_InstanceId",
496  table: "InstanceUsers",
497  columns: new[] { "UserId", "InstanceId" },
498  unique: true);
499 
500  migrationBuilder.CreateIndex(
501  name: "IX_Jobs_CancelledById",
502  table: "Jobs",
503  column: "CancelledById");
504 
505  migrationBuilder.CreateIndex(
506  name: "IX_Jobs_InstanceId",
507  table: "Jobs",
508  column: "InstanceId");
509 
510  migrationBuilder.CreateIndex(
511  name: "IX_Jobs_StartedById",
512  table: "Jobs",
513  column: "StartedById");
514 
515  migrationBuilder.CreateIndex(
516  name: "IX_ReattachInformations_CompileJobId",
517  table: "ReattachInformations",
518  column: "CompileJobId");
519 
520  migrationBuilder.CreateIndex(
521  name: "IX_RepositorySettings_InstanceId",
522  table: "RepositorySettings",
523  column: "InstanceId",
524  unique: true);
525 
526  migrationBuilder.CreateIndex(
527  name: "IX_RevInfoTestMerges_RevisionInformationId",
528  table: "RevInfoTestMerges",
529  column: "RevisionInformationId");
530 
531  migrationBuilder.CreateIndex(
532  name: "IX_RevInfoTestMerges_TestMergeId",
533  table: "RevInfoTestMerges",
534  column: "TestMergeId");
535 
536  migrationBuilder.CreateIndex(
537  name: "IX_RevisionInformations_InstanceId_CommitSha",
538  table: "RevisionInformations",
539  columns: new[] { "InstanceId", "CommitSha" },
540  unique: true);
541 
542  migrationBuilder.CreateIndex(
543  name: "IX_TestMerges_MergedById",
544  table: "TestMerges",
545  column: "MergedById");
546 
547  migrationBuilder.CreateIndex(
548  name: "IX_TestMerges_PrimaryRevisionInformationId",
549  table: "TestMerges",
550  column: "PrimaryRevisionInformationId",
551  unique: true);
552 
553  migrationBuilder.CreateIndex(
554  name: "IX_Users_CanonicalName",
555  table: "Users",
556  column: "CanonicalName",
557  unique: true);
558 
559  migrationBuilder.CreateIndex(
560  name: "IX_Users_CreatedById",
561  table: "Users",
562  column: "CreatedById");
563 
564  migrationBuilder.CreateIndex(
565  name: "IX_Users_SystemIdentifier",
566  table: "Users",
567  column: "SystemIdentifier",
568  unique: true);
569 
570  migrationBuilder.CreateIndex(
571  name: "IX_WatchdogReattachInformations_AlphaId",
572  table: "WatchdogReattachInformations",
573  column: "AlphaId");
574 
575  migrationBuilder.CreateIndex(
576  name: "IX_WatchdogReattachInformations_BravoId",
577  table: "WatchdogReattachInformations",
578  column: "BravoId");
579 
580  migrationBuilder.CreateIndex(
581  name: "IX_WatchdogReattachInformations_InstanceId",
582  table: "WatchdogReattachInformations",
583  column: "InstanceId",
584  unique: true);
585  }
586 
588  protected override void Down(MigrationBuilder migrationBuilder)
589  {
590  if (migrationBuilder == null)
591  throw new ArgumentNullException(nameof(migrationBuilder));
592 
593  migrationBuilder.DropTable(
594  name: "ChatChannels");
595 
596  migrationBuilder.DropTable(
597  name: "DreamDaemonSettings");
598 
599  migrationBuilder.DropTable(
600  name: "DreamMakerSettings");
601 
602  migrationBuilder.DropTable(
603  name: "InstanceUsers");
604 
605  migrationBuilder.DropTable(
606  name: "RepositorySettings");
607 
608  migrationBuilder.DropTable(
609  name: "RevInfoTestMerges");
610 
611  migrationBuilder.DropTable(
612  name: "WatchdogReattachInformations");
613 
614  migrationBuilder.DropTable(
615  name: "ChatBots");
616 
617  migrationBuilder.DropTable(
618  name: "TestMerges");
619 
620  migrationBuilder.DropTable(
621  name: "ReattachInformations");
622 
623  migrationBuilder.DropTable(
624  name: "CompileJobs");
625 
626  migrationBuilder.DropTable(
627  name: "Jobs");
628 
629  migrationBuilder.DropTable(
630  name: "RevisionInformations");
631 
632  migrationBuilder.DropTable(
633  name: "Users");
634 
635  migrationBuilder.DropTable(
636  name: "Instances");
637  }
638  }
639 }
ConfigurationRights
Rights for Models.ConfigurationFile
ErrorCode
Types of ErrorMessages that the API may return.
Definition: ErrorCode.cs:10
ByondRights
Rights for Models.Byond
Definition: ByondRights.cs:9
@@ -108,7 +108,7 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
DreamDaemonRights
Rights for Models.DreamDaemon
RebootState
Represents the action to take when /world/Reboot() is called
Definition: RebootState.cs:6
InstanceManagerRights
Rights for managing Models.Instances
-
override void Down(MigrationBuilder migrationBuilder)
+
override void Down(MigrationBuilder migrationBuilder)
DreamMakerRights
Rights for Models.DreamMaker
RepositoryRights
Rights for a Models.Repository
diff --git a/20200529171541___m_y_fix_foreign_key_8_designer_8cs.html b/20200529171541___m_y_fix_foreign_key_8_designer_8cs.html new file mode 100644 index 0000000000..e0b525e09d --- /dev/null +++ b/20200529171541___m_y_fix_foreign_key_8_designer_8cs.html @@ -0,0 +1,122 @@ + + + + + + +tgstation-server: src/Tgstation.Server.Host/Database/Migrations/20200529171541_MYFixForeignKey.Designer.cs File Reference + + + + + + + + + + +
+
+ + + + + + + +
+
tgstation-server +  4.3.2 +
+
The /tg/station 13 server suite
+
+
+ + + + + + +
+
+ + +
+ +
+ + +
+
+ +
+
20200529171541_MYFixForeignKey.Designer.cs File Reference
+
+
+ +

Go to the source code of this file.

+ + + + + +

+Classes

class  Tgstation.Server.Host.Database.Migrations.MYFixForeignKey
 Fix the CompileJob/RevisionInformation foreign key for MySQL. More...
 
+ + + +

+Namespaces

namespace  Tgstation.Server.Host.Database.Migrations
 
+
+ + + + diff --git a/20200529171541___m_y_fix_foreign_key_8_designer_8cs_source.html b/20200529171541___m_y_fix_foreign_key_8_designer_8cs_source.html new file mode 100644 index 0000000000..ed6fbc8018 --- /dev/null +++ b/20200529171541___m_y_fix_foreign_key_8_designer_8cs_source.html @@ -0,0 +1,111 @@ + + + + + + +tgstation-server: src/Tgstation.Server.Host/Database/Migrations/20200529171541_MYFixForeignKey.Designer.cs Source File + + + + + + + + + + +
+
+ + + + + + + +
+
tgstation-server +  4.3.2 +
+
The /tg/station 13 server suite
+
+
+ + + + + + +
+
+ + +
+ +
+ + +
+
+
+
20200529171541_MYFixForeignKey.Designer.cs
+
+
+Go to the documentation of this file.
1 // <auto-generated />
2 using System;
3 using Microsoft.EntityFrameworkCore;
4 using Microsoft.EntityFrameworkCore.Infrastructure;
5 using Microsoft.EntityFrameworkCore.Migrations;
6 
7 namespace Tgstation.Server.Host.Database.Migrations
8 {
9  [DbContext(typeof(MySqlDatabaseContext))]
10  [Migration("20200529171541_MYFixForeignKey")]
11  partial class MYFixForeignKey
12  {
14  protected override void BuildTargetModel(ModelBuilder modelBuilder)
15  {
16 #pragma warning disable 612, 618
17  modelBuilder
18  .HasAnnotation("ProductVersion", "3.1.4")
19  .HasAnnotation("Relational:MaxIdentifierLength", 64);
20 
21  modelBuilder.Entity("Tgstation.Server.Host.Models.ChatBot", b =>
22  {
23  b.Property<long>("Id")
24  .ValueGeneratedOnAdd()
25  .HasColumnType("bigint");
26 
27  b.Property<ushort?>("ChannelLimit")
28  .IsRequired()
29  .HasColumnType("smallint unsigned");
30 
31  b.Property<string>("ConnectionString")
32  .IsRequired()
33  .HasColumnType("longtext CHARACTER SET utf8mb4")
34  .HasMaxLength(10000);
35 
36  b.Property<bool?>("Enabled")
37  .HasColumnType("tinyint(1)");
38 
39  b.Property<long>("InstanceId")
40  .HasColumnType("bigint");
41 
42  b.Property<string>("Name")
43  .IsRequired()
44  .HasColumnType("varchar(100) CHARACTER SET utf8mb4")
45  .HasMaxLength(100);
46 
47  b.Property<int>("Provider")
48  .HasColumnType("int");
49 
50  b.Property<uint?>("ReconnectionInterval")
51  .IsRequired()
52  .HasColumnType("int unsigned");
53 
54  b.HasKey("Id");
55 
56  b.HasIndex("InstanceId", "Name")
57  .IsUnique();
58 
59  b.ToTable("ChatBots");
60  });
61 
62  modelBuilder.Entity("Tgstation.Server.Host.Models.ChatChannel", b =>
63  {
64  b.Property<long>("Id")
65  .ValueGeneratedOnAdd()
66  .HasColumnType("bigint");
67 
68  b.Property<long>("ChatSettingsId")
69  .HasColumnType("bigint");
70 
71  b.Property<ulong?>("DiscordChannelId")
72  .HasColumnType("bigint unsigned");
73 
74  b.Property<string>("IrcChannel")
75  .HasColumnType("varchar(100) CHARACTER SET utf8mb4")
76  .HasMaxLength(100);
77 
78  b.Property<bool?>("IsAdminChannel")
79  .IsRequired()
80  .HasColumnType("tinyint(1)");
81 
82  b.Property<bool?>("IsUpdatesChannel")
83  .IsRequired()
84  .HasColumnType("tinyint(1)");
85 
86  b.Property<bool?>("IsWatchdogChannel")
87  .IsRequired()
88  .HasColumnType("tinyint(1)");
89 
90  b.Property<string>("Tag")
91  .HasColumnType("longtext CHARACTER SET utf8mb4")
92  .HasMaxLength(10000);
93 
94  b.HasKey("Id");
95 
96  b.HasIndex("ChatSettingsId", "DiscordChannelId")
97  .IsUnique();
98 
99  b.HasIndex("ChatSettingsId", "IrcChannel")
100  .IsUnique();
101 
102  b.ToTable("ChatChannels");
103  });
104 
105  modelBuilder.Entity("Tgstation.Server.Host.Models.CompileJob", b =>
106  {
107  b.Property<long>("Id")
108  .ValueGeneratedOnAdd()
109  .HasColumnType("bigint");
110 
111  b.Property<string>("ByondVersion")
112  .IsRequired()
113  .HasColumnType("longtext CHARACTER SET utf8mb4");
114 
115  b.Property<int?>("DMApiMajorVersion")
116  .HasColumnType("int");
117 
118  b.Property<int?>("DMApiMinorVersion")
119  .HasColumnType("int");
120 
121  b.Property<int?>("DMApiPatchVersion")
122  .HasColumnType("int");
123 
124  b.Property<Guid?>("DirectoryName")
125  .IsRequired()
126  .HasColumnType("char(36)");
127 
128  b.Property<string>("DmeName")
129  .IsRequired()
130  .HasColumnType("longtext CHARACTER SET utf8mb4");
131 
132  b.Property<long>("JobId")
133  .HasColumnType("bigint");
134 
135  b.Property<int>("MinimumSecurityLevel")
136  .HasColumnType("int");
137 
138  b.Property<string>("Output")
139  .IsRequired()
140  .HasColumnType("longtext CHARACTER SET utf8mb4");
141 
142  b.Property<long>("RevisionInformationId")
143  .HasColumnType("bigint");
144 
145  b.HasKey("Id");
146 
147  b.HasIndex("DirectoryName");
148 
149  b.HasIndex("JobId")
150  .IsUnique();
151 
152  b.HasIndex("RevisionInformationId");
153 
154  b.ToTable("CompileJobs");
155  });
156 
157  modelBuilder.Entity("Tgstation.Server.Host.Models.DreamDaemonSettings", b =>
158  {
159  b.Property<long>("Id")
160  .ValueGeneratedOnAdd()
161  .HasColumnType("bigint");
162 
163  b.Property<bool?>("AllowWebClient")
164  .IsRequired()
165  .HasColumnType("tinyint(1)");
166 
167  b.Property<bool?>("AutoStart")
168  .IsRequired()
169  .HasColumnType("tinyint(1)");
170 
171  b.Property<uint?>("HeartbeatSeconds")
172  .IsRequired()
173  .HasColumnType("int unsigned");
174 
175  b.Property<long>("InstanceId")
176  .HasColumnType("bigint");
177 
178  b.Property<ushort?>("PrimaryPort")
179  .IsRequired()
180  .HasColumnType("smallint unsigned");
181 
182  b.Property<ushort?>("SecondaryPort")
183  .IsRequired()
184  .HasColumnType("smallint unsigned");
185 
186  b.Property<int>("SecurityLevel")
187  .HasColumnType("int");
188 
189  b.Property<uint?>("StartupTimeout")
190  .IsRequired()
191  .HasColumnType("int unsigned");
192 
193  b.HasKey("Id");
194 
195  b.HasIndex("InstanceId")
196  .IsUnique();
197 
198  b.ToTable("DreamDaemonSettings");
199  });
200 
201  modelBuilder.Entity("Tgstation.Server.Host.Models.DreamMakerSettings", b =>
202  {
203  b.Property<long>("Id")
204  .ValueGeneratedOnAdd()
205  .HasColumnType("bigint");
206 
207  b.Property<ushort?>("ApiValidationPort")
208  .IsRequired()
209  .HasColumnType("smallint unsigned");
210 
211  b.Property<int>("ApiValidationSecurityLevel")
212  .HasColumnType("int");
213 
214  b.Property<long>("InstanceId")
215  .HasColumnType("bigint");
216 
217  b.Property<string>("ProjectName")
218  .HasColumnType("longtext CHARACTER SET utf8mb4")
219  .HasMaxLength(10000);
220 
221  b.HasKey("Id");
222 
223  b.HasIndex("InstanceId")
224  .IsUnique();
225 
226  b.ToTable("DreamMakerSettings");
227  });
228 
229  modelBuilder.Entity("Tgstation.Server.Host.Models.DualReattachInformation", b =>
230  {
231  b.Property<long>("Id")
232  .ValueGeneratedOnAdd()
233  .HasColumnType("bigint");
234 
235  b.Property<long?>("AlphaId")
236  .HasColumnType("bigint");
237 
238  b.Property<bool>("AlphaIsActive")
239  .HasColumnType("tinyint(1)");
240 
241  b.Property<long?>("BravoId")
242  .HasColumnType("bigint");
243 
244  b.Property<long>("InstanceId")
245  .HasColumnType("bigint");
246 
247  b.HasKey("Id");
248 
249  b.HasIndex("AlphaId");
250 
251  b.HasIndex("BravoId");
252 
253  b.HasIndex("InstanceId")
254  .IsUnique();
255 
256  b.ToTable("WatchdogReattachInformations");
257  });
258 
259  modelBuilder.Entity("Tgstation.Server.Host.Models.Instance", b =>
260  {
261  b.Property<long>("Id")
262  .ValueGeneratedOnAdd()
263  .HasColumnType("bigint");
264 
265  b.Property<uint?>("AutoUpdateInterval")
266  .IsRequired()
267  .HasColumnType("int unsigned");
268 
269  b.Property<ushort?>("ChatBotLimit")
270  .IsRequired()
271  .HasColumnType("smallint unsigned");
272 
273  b.Property<int>("ConfigurationType")
274  .HasColumnType("int");
275 
276  b.Property<string>("Name")
277  .IsRequired()
278  .HasColumnType("longtext CHARACTER SET utf8mb4")
279  .HasMaxLength(10000);
280 
281  b.Property<bool?>("Online")
282  .IsRequired()
283  .HasColumnType("tinyint(1)");
284 
285  b.Property<string>("Path")
286  .IsRequired()
287  .HasColumnType("varchar(255) CHARACTER SET utf8mb4");
288 
289  b.HasKey("Id");
290 
291  b.HasIndex("Path")
292  .IsUnique();
293 
294  b.ToTable("Instances");
295  });
296 
297  modelBuilder.Entity("Tgstation.Server.Host.Models.InstanceUser", b =>
298  {
299  b.Property<long>("Id")
300  .ValueGeneratedOnAdd()
301  .HasColumnType("bigint");
302 
303  b.Property<ulong>("ByondRights")
304  .HasColumnType("bigint unsigned");
305 
306  b.Property<ulong>("ChatBotRights")
307  .HasColumnType("bigint unsigned");
308 
309  b.Property<ulong>("ConfigurationRights")
310  .HasColumnType("bigint unsigned");
311 
312  b.Property<ulong>("DreamDaemonRights")
313  .HasColumnType("bigint unsigned");
314 
315  b.Property<ulong>("DreamMakerRights")
316  .HasColumnType("bigint unsigned");
317 
318  b.Property<long>("InstanceId")
319  .HasColumnType("bigint");
320 
321  b.Property<ulong>("InstanceUserRights")
322  .HasColumnType("bigint unsigned");
323 
324  b.Property<ulong>("RepositoryRights")
325  .HasColumnType("bigint unsigned");
326 
327  b.Property<long?>("UserId")
328  .IsRequired()
329  .HasColumnType("bigint");
330 
331  b.HasKey("Id");
332 
333  b.HasIndex("InstanceId");
334 
335  b.HasIndex("UserId", "InstanceId")
336  .IsUnique();
337 
338  b.ToTable("InstanceUsers");
339  });
340 
341  modelBuilder.Entity("Tgstation.Server.Host.Models.Job", b =>
342  {
343  b.Property<long>("Id")
344  .ValueGeneratedOnAdd()
345  .HasColumnType("bigint");
346 
347  b.Property<ulong?>("CancelRight")
348  .HasColumnType("bigint unsigned");
349 
350  b.Property<ulong?>("CancelRightsType")
351  .HasColumnType("bigint unsigned");
352 
353  b.Property<bool?>("Cancelled")
354  .IsRequired()
355  .HasColumnType("tinyint(1)");
356 
357  b.Property<long?>("CancelledById")
358  .HasColumnType("bigint");
359 
360  b.Property<string>("Description")
361  .IsRequired()
362  .HasColumnType("longtext CHARACTER SET utf8mb4");
363 
364  b.Property<uint?>("ErrorCode")
365  .HasColumnType("int unsigned");
366 
367  b.Property<string>("ExceptionDetails")
368  .HasColumnType("longtext CHARACTER SET utf8mb4");
369 
370  b.Property<long>("InstanceId")
371  .HasColumnType("bigint");
372 
373  b.Property<DateTimeOffset?>("StartedAt")
374  .IsRequired()
375  .HasColumnType("datetime(6)");
376 
377  b.Property<long>("StartedById")
378  .HasColumnType("bigint");
379 
380  b.Property<DateTimeOffset?>("StoppedAt")
381  .HasColumnType("datetime(6)");
382 
383  b.HasKey("Id");
384 
385  b.HasIndex("CancelledById");
386 
387  b.HasIndex("InstanceId");
388 
389  b.HasIndex("StartedById");
390 
391  b.ToTable("Jobs");
392  });
393 
394  modelBuilder.Entity("Tgstation.Server.Host.Models.ReattachInformation", b =>
395  {
396  b.Property<long>("Id")
397  .ValueGeneratedOnAdd()
398  .HasColumnType("bigint");
399 
400  b.Property<string>("AccessIdentifier")
401  .IsRequired()
402  .HasColumnType("longtext CHARACTER SET utf8mb4");
403 
404  b.Property<long>("CompileJobId")
405  .HasColumnType("bigint");
406 
407  b.Property<bool>("IsPrimary")
408  .HasColumnType("tinyint(1)");
409 
410  b.Property<int>("LaunchSecurityLevel")
411  .HasColumnType("int");
412 
413  b.Property<ushort>("Port")
414  .HasColumnType("smallint unsigned");
415 
416  b.Property<int>("ProcessId")
417  .HasColumnType("int");
418 
419  b.Property<int>("RebootState")
420  .HasColumnType("int");
421 
422  b.HasKey("Id");
423 
424  b.HasIndex("CompileJobId");
425 
426  b.ToTable("ReattachInformations");
427  });
428 
429  modelBuilder.Entity("Tgstation.Server.Host.Models.RepositorySettings", b =>
430  {
431  b.Property<long>("Id")
432  .ValueGeneratedOnAdd()
433  .HasColumnType("bigint");
434 
435  b.Property<string>("AccessToken")
436  .HasColumnType("longtext CHARACTER SET utf8mb4")
437  .HasMaxLength(10000);
438 
439  b.Property<string>("AccessUser")
440  .HasColumnType("longtext CHARACTER SET utf8mb4")
441  .HasMaxLength(10000);
442 
443  b.Property<bool?>("AutoUpdatesKeepTestMerges")
444  .IsRequired()
445  .HasColumnType("tinyint(1)");
446 
447  b.Property<bool?>("AutoUpdatesSynchronize")
448  .IsRequired()
449  .HasColumnType("tinyint(1)");
450 
451  b.Property<string>("CommitterEmail")
452  .IsRequired()
453  .HasColumnType("longtext CHARACTER SET utf8mb4")
454  .HasMaxLength(10000);
455 
456  b.Property<string>("CommitterName")
457  .IsRequired()
458  .HasColumnType("longtext CHARACTER SET utf8mb4")
459  .HasMaxLength(10000);
460 
461  b.Property<long>("InstanceId")
462  .HasColumnType("bigint");
463 
464  b.Property<bool?>("PostTestMergeComment")
465  .IsRequired()
466  .HasColumnType("tinyint(1)");
467 
468  b.Property<bool?>("PushTestMergeCommits")
469  .IsRequired()
470  .HasColumnType("tinyint(1)");
471 
472  b.Property<bool?>("ShowTestMergeCommitters")
473  .IsRequired()
474  .HasColumnType("tinyint(1)");
475 
476  b.HasKey("Id");
477 
478  b.HasIndex("InstanceId")
479  .IsUnique();
480 
481  b.ToTable("RepositorySettings");
482  });
483 
484  modelBuilder.Entity("Tgstation.Server.Host.Models.RevInfoTestMerge", b =>
485  {
486  b.Property<long>("Id")
487  .ValueGeneratedOnAdd()
488  .HasColumnType("bigint");
489 
490  b.Property<long>("RevisionInformationId")
491  .HasColumnType("bigint");
492 
493  b.Property<long>("TestMergeId")
494  .HasColumnType("bigint");
495 
496  b.HasKey("Id");
497 
498  b.HasIndex("RevisionInformationId");
499 
500  b.HasIndex("TestMergeId");
501 
502  b.ToTable("RevInfoTestMerges");
503  });
504 
505  modelBuilder.Entity("Tgstation.Server.Host.Models.RevisionInformation", b =>
506  {
507  b.Property<long>("Id")
508  .ValueGeneratedOnAdd()
509  .HasColumnType("bigint");
510 
511  b.Property<string>("CommitSha")
512  .IsRequired()
513  .HasColumnType("varchar(40) CHARACTER SET utf8mb4")
514  .HasMaxLength(40);
515 
516  b.Property<long>("InstanceId")
517  .HasColumnType("bigint");
518 
519  b.Property<string>("OriginCommitSha")
520  .IsRequired()
521  .HasColumnType("varchar(40) CHARACTER SET utf8mb4")
522  .HasMaxLength(40);
523 
524  b.HasKey("Id");
525 
526  b.HasIndex("InstanceId", "CommitSha")
527  .IsUnique();
528 
529  b.ToTable("RevisionInformations");
530  });
531 
532  modelBuilder.Entity("Tgstation.Server.Host.Models.TestMerge", b =>
533  {
534  b.Property<long>("Id")
535  .ValueGeneratedOnAdd()
536  .HasColumnType("bigint");
537 
538  b.Property<string>("Author")
539  .IsRequired()
540  .HasColumnType("longtext CHARACTER SET utf8mb4");
541 
542  b.Property<string>("BodyAtMerge")
543  .IsRequired()
544  .HasColumnType("longtext CHARACTER SET utf8mb4");
545 
546  b.Property<string>("Comment")
547  .HasColumnType("longtext CHARACTER SET utf8mb4")
548  .HasMaxLength(10000);
549 
550  b.Property<DateTimeOffset>("MergedAt")
551  .HasColumnType("datetime(6)");
552 
553  b.Property<long>("MergedById")
554  .HasColumnType("bigint");
555 
556  b.Property<int>("Number")
557  .HasColumnType("int");
558 
559  b.Property<long?>("PrimaryRevisionInformationId")
560  .IsRequired()
561  .HasColumnType("bigint");
562 
563  b.Property<string>("PullRequestRevision")
564  .IsRequired()
565  .HasColumnType("varchar(40) CHARACTER SET utf8mb4")
566  .HasMaxLength(40);
567 
568  b.Property<string>("TitleAtMerge")
569  .IsRequired()
570  .HasColumnType("longtext CHARACTER SET utf8mb4");
571 
572  b.Property<string>("Url")
573  .IsRequired()
574  .HasColumnType("longtext CHARACTER SET utf8mb4");
575 
576  b.HasKey("Id");
577 
578  b.HasIndex("MergedById");
579 
580  b.HasIndex("PrimaryRevisionInformationId")
581  .IsUnique();
582 
583  b.ToTable("TestMerges");
584  });
585 
586  modelBuilder.Entity("Tgstation.Server.Host.Models.User", b =>
587  {
588  b.Property<long?>("Id")
589  .ValueGeneratedOnAdd()
590  .HasColumnType("bigint");
591 
592  b.Property<ulong>("AdministrationRights")
593  .HasColumnType("bigint unsigned");
594 
595  b.Property<string>("CanonicalName")
596  .IsRequired()
597  .HasColumnType("varchar(255) CHARACTER SET utf8mb4");
598 
599  b.Property<DateTimeOffset?>("CreatedAt")
600  .IsRequired()
601  .HasColumnType("datetime(6)");
602 
603  b.Property<long?>("CreatedById")
604  .HasColumnType("bigint");
605 
606  b.Property<bool?>("Enabled")
607  .IsRequired()
608  .HasColumnType("tinyint(1)");
609 
610  b.Property<ulong>("InstanceManagerRights")
611  .HasColumnType("bigint unsigned");
612 
613  b.Property<DateTimeOffset?>("LastPasswordUpdate")
614  .HasColumnType("datetime(6)");
615 
616  b.Property<string>("Name")
617  .IsRequired()
618  .HasColumnType("longtext CHARACTER SET utf8mb4")
619  .HasMaxLength(10000);
620 
621  b.Property<string>("PasswordHash")
622  .HasColumnType("longtext CHARACTER SET utf8mb4");
623 
624  b.Property<string>("SystemIdentifier")
625  .HasColumnType("varchar(255) CHARACTER SET utf8mb4");
626 
627  b.HasKey("Id");
628 
629  b.HasIndex("CanonicalName")
630  .IsUnique();
631 
632  b.HasIndex("CreatedById");
633 
634  b.HasIndex("SystemIdentifier")
635  .IsUnique();
636 
637  b.ToTable("Users");
638  });
639 
640  modelBuilder.Entity("Tgstation.Server.Host.Models.ChatBot", b =>
641  {
642  b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
643  .WithMany("ChatSettings")
644  .HasForeignKey("InstanceId")
645  .OnDelete(DeleteBehavior.Cascade)
646  .IsRequired();
647  });
648 
649  modelBuilder.Entity("Tgstation.Server.Host.Models.ChatChannel", b =>
650  {
651  b.HasOne("Tgstation.Server.Host.Models.ChatBot", "ChatSettings")
652  .WithMany("Channels")
653  .HasForeignKey("ChatSettingsId")
654  .OnDelete(DeleteBehavior.Cascade)
655  .IsRequired();
656  });
657 
658  modelBuilder.Entity("Tgstation.Server.Host.Models.CompileJob", b =>
659  {
660  b.HasOne("Tgstation.Server.Host.Models.Job", "Job")
661  .WithOne()
662  .HasForeignKey("Tgstation.Server.Host.Models.CompileJob", "JobId")
663  .OnDelete(DeleteBehavior.Cascade)
664  .IsRequired();
665 
666  b.HasOne("Tgstation.Server.Host.Models.RevisionInformation", "RevisionInformation")
667  .WithMany("CompileJobs")
668  .HasForeignKey("RevisionInformationId")
669  .OnDelete(DeleteBehavior.Cascade)
670  .IsRequired();
671  });
672 
673  modelBuilder.Entity("Tgstation.Server.Host.Models.DreamDaemonSettings", b =>
674  {
675  b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
676  .WithOne("DreamDaemonSettings")
677  .HasForeignKey("Tgstation.Server.Host.Models.DreamDaemonSettings", "InstanceId")
678  .OnDelete(DeleteBehavior.Cascade)
679  .IsRequired();
680  });
681 
682  modelBuilder.Entity("Tgstation.Server.Host.Models.DreamMakerSettings", b =>
683  {
684  b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
685  .WithOne("DreamMakerSettings")
686  .HasForeignKey("Tgstation.Server.Host.Models.DreamMakerSettings", "InstanceId")
687  .OnDelete(DeleteBehavior.Cascade)
688  .IsRequired();
689  });
690 
691  modelBuilder.Entity("Tgstation.Server.Host.Models.DualReattachInformation", b =>
692  {
693  b.HasOne("Tgstation.Server.Host.Models.ReattachInformation", "Alpha")
694  .WithMany()
695  .HasForeignKey("AlphaId");
696 
697  b.HasOne("Tgstation.Server.Host.Models.ReattachInformation", "Bravo")
698  .WithMany()
699  .HasForeignKey("BravoId");
700 
701  b.HasOne("Tgstation.Server.Host.Models.Instance", null)
702  .WithOne("WatchdogReattachInformation")
703  .HasForeignKey("Tgstation.Server.Host.Models.DualReattachInformation", "InstanceId")
704  .OnDelete(DeleteBehavior.Cascade)
705  .IsRequired();
706  });
707 
708  modelBuilder.Entity("Tgstation.Server.Host.Models.InstanceUser", b =>
709  {
710  b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
711  .WithMany("InstanceUsers")
712  .HasForeignKey("InstanceId")
713  .OnDelete(DeleteBehavior.Cascade)
714  .IsRequired();
715 
716  b.HasOne("Tgstation.Server.Host.Models.User", null)
717  .WithMany("InstanceUsers")
718  .HasForeignKey("UserId")
719  .OnDelete(DeleteBehavior.Cascade)
720  .IsRequired();
721  });
722 
723  modelBuilder.Entity("Tgstation.Server.Host.Models.Job", b =>
724  {
725  b.HasOne("Tgstation.Server.Host.Models.User", "CancelledBy")
726  .WithMany()
727  .HasForeignKey("CancelledById");
728 
729  b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
730  .WithMany("Jobs")
731  .HasForeignKey("InstanceId")
732  .OnDelete(DeleteBehavior.Cascade)
733  .IsRequired();
734 
735  b.HasOne("Tgstation.Server.Host.Models.User", "StartedBy")
736  .WithMany()
737  .HasForeignKey("StartedById")
738  .OnDelete(DeleteBehavior.Cascade)
739  .IsRequired();
740  });
741 
742  modelBuilder.Entity("Tgstation.Server.Host.Models.ReattachInformation", b =>
743  {
744  b.HasOne("Tgstation.Server.Host.Models.CompileJob", "CompileJob")
745  .WithMany()
746  .HasForeignKey("CompileJobId")
747  .OnDelete(DeleteBehavior.Cascade)
748  .IsRequired();
749  });
750 
751  modelBuilder.Entity("Tgstation.Server.Host.Models.RepositorySettings", b =>
752  {
753  b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
754  .WithOne("RepositorySettings")
755  .HasForeignKey("Tgstation.Server.Host.Models.RepositorySettings", "InstanceId")
756  .OnDelete(DeleteBehavior.Cascade)
757  .IsRequired();
758  });
759 
760  modelBuilder.Entity("Tgstation.Server.Host.Models.RevInfoTestMerge", b =>
761  {
762  b.HasOne("Tgstation.Server.Host.Models.RevisionInformation", "RevisionInformation")
763  .WithMany("ActiveTestMerges")
764  .HasForeignKey("RevisionInformationId")
765  .OnDelete(DeleteBehavior.Cascade)
766  .IsRequired();
767 
768  b.HasOne("Tgstation.Server.Host.Models.TestMerge", "TestMerge")
769  .WithMany("RevisonInformations")
770  .HasForeignKey("TestMergeId")
771  .OnDelete(DeleteBehavior.ClientNoAction)
772  .IsRequired();
773  });
774 
775  modelBuilder.Entity("Tgstation.Server.Host.Models.RevisionInformation", b =>
776  {
777  b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
778  .WithMany("RevisionInformations")
779  .HasForeignKey("InstanceId")
780  .OnDelete(DeleteBehavior.Cascade)
781  .IsRequired();
782  });
783 
784  modelBuilder.Entity("Tgstation.Server.Host.Models.TestMerge", b =>
785  {
786  b.HasOne("Tgstation.Server.Host.Models.User", "MergedBy")
787  .WithMany("TestMerges")
788  .HasForeignKey("MergedById")
789  .OnDelete(DeleteBehavior.Restrict)
790  .IsRequired();
791 
792  b.HasOne("Tgstation.Server.Host.Models.RevisionInformation", "PrimaryRevisionInformation")
793  .WithOne("PrimaryTestMerge")
794  .HasForeignKey("Tgstation.Server.Host.Models.TestMerge", "PrimaryRevisionInformationId")
795  .OnDelete(DeleteBehavior.Cascade)
796  .IsRequired();
797  });
798 
799  modelBuilder.Entity("Tgstation.Server.Host.Models.User", b =>
800  {
801  b.HasOne("Tgstation.Server.Host.Models.User", "CreatedBy")
802  .WithMany("CreatedUsers")
803  .HasForeignKey("CreatedById");
804  });
805 #pragma warning restore 612, 618
806  }
807  }
808 }
+ + + + + +
+ + + + diff --git a/20200529171541___m_y_fix_foreign_key_8cs.html b/20200529171541___m_y_fix_foreign_key_8cs.html new file mode 100644 index 0000000000..5500f68466 --- /dev/null +++ b/20200529171541___m_y_fix_foreign_key_8cs.html @@ -0,0 +1,122 @@ + + + + + + +tgstation-server: src/Tgstation.Server.Host/Database/Migrations/20200529171541_MYFixForeignKey.cs File Reference + + + + + + + + + + +
+
+ + + + + + + +
+
tgstation-server +  4.3.2 +
+
The /tg/station 13 server suite
+
+
+ + + + + + +
+
+ + +
+ +
+ + +
+
+ +
+
20200529171541_MYFixForeignKey.cs File Reference
+
+
+ +

Go to the source code of this file.

+ + + + + +

+Classes

class  Tgstation.Server.Host.Database.Migrations.MYFixForeignKey
 Fix the CompileJob/RevisionInformation foreign key for MySQL. More...
 
+ + + +

+Namespaces

namespace  Tgstation.Server.Host.Database.Migrations
 
+
+ + + + diff --git a/20200529171541___m_y_fix_foreign_key_8cs_source.html b/20200529171541___m_y_fix_foreign_key_8cs_source.html new file mode 100644 index 0000000000..2e91fcdd9d --- /dev/null +++ b/20200529171541___m_y_fix_foreign_key_8cs_source.html @@ -0,0 +1,112 @@ + + + + + + +tgstation-server: src/Tgstation.Server.Host/Database/Migrations/20200529171541_MYFixForeignKey.cs Source File + + + + + + + + + + +
+
+ + + + + + + +
+
tgstation-server +  4.3.2 +
+
The /tg/station 13 server suite
+
+
+ + + + + + +
+
+ + +
+ +
+ + +
+
+
+
20200529171541_MYFixForeignKey.cs
+
+
+Go to the documentation of this file.
1 using Microsoft.EntityFrameworkCore.Migrations;
2 using System;
3 
4 namespace Tgstation.Server.Host.Database.Migrations
5 {
9  public partial class MYFixForeignKey : Migration
10  {
12  protected override void Up(MigrationBuilder migrationBuilder)
13  {
14  if (migrationBuilder == null)
15  throw new ArgumentNullException(nameof(migrationBuilder));
16 
17  migrationBuilder.DropForeignKey(
18  name: "FK_CompileJobs_RevisionInformations_RevisionInformationId",
19  table: "CompileJobs");
20 
21  migrationBuilder.AddForeignKey(
22  name: "FK_CompileJobs_RevisionInformations_RevisionInformationId",
23  table: "CompileJobs",
24  column: "RevisionInformationId",
25  principalTable: "RevisionInformations",
26  principalColumn: "Id",
27  onDelete: ReferentialAction.Cascade);
28  }
29 
31  protected override void Down(MigrationBuilder migrationBuilder)
32  {
33  if (migrationBuilder == null)
34  throw new ArgumentNullException(nameof(migrationBuilder));
35 
36  migrationBuilder.DropForeignKey(
37  name: "FK_CompileJobs_RevisionInformations_RevisionInformationId",
38  table: "CompileJobs");
39 
40  migrationBuilder.AddForeignKey(
41  name: "FK_CompileJobs_RevisionInformations_RevisionInformationId",
42  table: "CompileJobs",
43  column: "RevisionInformationId",
44  principalTable: "RevisionInformations",
45  principalColumn: "Id");
46  }
47  }
48 }
+
Fix the CompileJob/RevisionInformation foreign key for MySQL.
+ + + + + +
+ + + + diff --git a/_application_8cs_source.html b/_application_8cs_source.html index ae9860909c..6f94eb6db1 100644 --- a/_application_8cs_source.html +++ b/_application_8cs_source.html @@ -94,7 +94,7 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
Application.cs
-Go to the documentation of this file.
1 using Byond.TopicSender;
2 using Cyberboss.AspNetCore.AsyncInitializer;
3 using Microsoft.AspNetCore.Authentication.JwtBearer;
4 using Microsoft.AspNetCore.Builder;
5 using Microsoft.AspNetCore.Cors.Infrastructure;
6 using Microsoft.AspNetCore.Hosting;
7 using Microsoft.AspNetCore.Identity;
8 using Microsoft.Extensions.Configuration;
9 using Microsoft.Extensions.DependencyInjection;
10 using Microsoft.Extensions.Hosting;
11 using Microsoft.Extensions.Logging;
12 using Microsoft.Extensions.Options;
13 using Microsoft.Extensions.Primitives;
14 using Newtonsoft.Json;
15 using Serilog;
16 using Serilog.Events;
17 using Serilog.Formatting.Display;
18 using System;
19 using System.Globalization;
20 using System.IdentityModel.Tokens.Jwt;
21 using System.Threading.Tasks;
22 using Tgstation.Server.Api;
35 using Tgstation.Server.Host.IO;
40 
41 namespace Tgstation.Server.Host.Core
42 {
46 #pragma warning disable CA1506
48  {
52  readonly IWebHostEnvironment hostingEnvironment;
53 
58 
64  public Application(
65  IConfiguration configuration,
66  IWebHostEnvironment hostingEnvironment)
67  : base(configuration)
68  {
69  this.hostingEnvironment = hostingEnvironment ?? throw new ArgumentNullException(nameof(hostingEnvironment));
70  }
71 
76  public static IServerFactory CreateDefaultServerFactory()
77  => new ServerFactory(
79  IOManager);
80 
86  public void ConfigureServices(IServiceCollection services, IPostSetupServices postSetupServices)
87  {
88  ConfigureServices(services);
89 
90  if (postSetupServices == null)
91  throw new ArgumentNullException(nameof(postSetupServices));
92 
93  // configure configuration
94  services.UseStandardConfig<UpdatesConfiguration>(Configuration);
95  services.UseStandardConfig<ControlPanelConfiguration>(Configuration);
96 
97  // enable options which give us config reloading
98  services.AddOptions();
99 
100  static LogEventLevel? ConvertSeriLogLevel(LogLevel logLevel) =>
101  logLevel switch
102  {
103  LogLevel.Critical => LogEventLevel.Fatal,
104  LogLevel.Debug => LogEventLevel.Debug,
105  LogLevel.Error => LogEventLevel.Error,
106  LogLevel.Information => LogEventLevel.Information,
107  LogLevel.Trace => LogEventLevel.Verbose,
108  LogLevel.Warning => LogEventLevel.Warning,
109  LogLevel.None => null,
110  _ => throw new InvalidOperationException(String.Format(CultureInfo.InvariantCulture, "Invalid log level {0}", logLevel)),
111  };
112 
113  var microsoftEventLevel = ConvertSeriLogLevel(postSetupServices.FileLoggingConfiguration.MicrosoftLogLevel);
114  services.SetupLogging(
115  config =>
116  {
117  if (microsoftEventLevel.HasValue)
118  config.MinimumLevel.Override("Microsoft", microsoftEventLevel.Value);
119  },
120  sinkConfig =>
121  {
122  if (postSetupServices.FileLoggingConfiguration.Disable)
123  return;
124 
125  // common app data is C:/ProgramData on windows, else /usr/share
126  var logPath = !String.IsNullOrEmpty(postSetupServices.FileLoggingConfiguration.Directory)
127  ? postSetupServices.FileLoggingConfiguration.Directory
128  : IOManager.ConcatPath(
129  Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData),
131  "Logs");
132 
133  var logEventLevel = ConvertSeriLogLevel(postSetupServices.FileLoggingConfiguration.LogLevel);
134 
135  var formatter = new MessageTemplateTextFormatter(
136  "{Timestamp:o} "
138  + ": [{Level:u3}] {SourceContext:l}: {Message} ({EventId:x8}){NewLine}{Exception}",
139  null);
140 
141  logPath = IOManager.ConcatPath(logPath, "tgs-.log");
142  var rollingFileConfig = sinkConfig.File(
143  formatter,
144  logPath,
145  logEventLevel ?? LogEventLevel.Verbose,
146  50 * 1024 * 1024, // 50MB max size
147  flushToDiskInterval: TimeSpan.FromSeconds(2),
148  rollingInterval: RollingInterval.Day,
149  rollOnFileSizeLimit: true);
150  });
151 
152  // configure bearer token validation
153  services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme).AddJwtBearer(jwtBearerOptions =>
154  {
155  // this line isn't actually run until the first request is made
156  // at that point tokenFactory will be populated
157  jwtBearerOptions.TokenValidationParameters = tokenFactory.ValidationParameters;
158  jwtBearerOptions.Events = new JwtBearerEvents
159  {
160  // Application is our composition root so this monstrosity of a line is okay
161  // At least, that's what I tell myself to sleep at night
162  OnTokenValidated = ctx => ctx
163  .HttpContext
164  .RequestServices
165  .GetRequiredService<IClaimsInjector>()
166  .InjectClaimsIntoContext(
167  ctx,
168  ctx.HttpContext.RequestAborted)
169  };
170  });
171 
172  // WARNING: STATIC CODE
173  // fucking prevents converting 'sub' to M$ bs
174  // can't be done in the above lambda, that's too late
175  JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();
176 
177  // add mvc, configure the json serializer settings
178  services
179  .AddMvc(options =>
180  {
181  options.EnableEndpointRouting = false;
182  })
183  .AddNewtonsoftJson(options =>
184  {
185  options.AllowInputFormatterExceptionMessages = true;
186  options.SerializerSettings.NullValueHandling = NullValueHandling.Ignore;
187  options.SerializerSettings.CheckAdditionalContent = true;
188  options.SerializerSettings.MissingMemberHandling = MissingMemberHandling.Error;
189  options.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
190  options.SerializerSettings.Converters = new[] { new VersionConverter() };
191  });
192 
193  if (hostingEnvironment.IsDevelopment())
194  {
195  static string GetDocumentationFilePath(string assemblyLocation) => IOManager.ConcatPath(IOManager.GetDirectoryName(assemblyLocation), String.Concat(IOManager.GetFileNameWithoutExtension(assemblyLocation), ".xml"));
196  var assemblyDocumentationPath = GetDocumentationFilePath(typeof(Application).Assembly.Location);
197  var apiDocumentationPath = GetDocumentationFilePath(typeof(ApiHeaders).Assembly.Location);
198  services.AddSwaggerGen(genOptions => SwaggerConfiguration.Configure(genOptions, assemblyDocumentationPath, apiDocumentationPath));
199  services.AddSwaggerGenNewtonsoftSupport();
200  }
201 
202  // enable browser detection
203  services.AddDetectionCore().AddBrowser();
204 
205  // CORS conditionally enabled later
206  services.AddCors();
207 
208  void AddTypedContext<TContext>() where TContext : DatabaseContext
209  {
210  services.AddDbContext<TContext>(builder =>
211  {
212  if (hostingEnvironment.IsDevelopment())
213  builder.EnableSensitiveDataLogging();
214  });
215  services.AddScoped<IDatabaseContext>(x => x.GetRequiredService<TContext>());
216  }
217 
218  // add the correct database context type
219  var dbType = postSetupServices.DatabaseConfiguration.DatabaseType;
220  switch (dbType)
221  {
222  case DatabaseType.MySql:
223  case DatabaseType.MariaDB:
224  AddTypedContext<MySqlDatabaseContext>();
225  break;
226  case DatabaseType.SqlServer:
227  AddTypedContext<SqlServerDatabaseContext>();
228  break;
229  case DatabaseType.Sqlite:
230  AddTypedContext<SqliteDatabaseContext>();
231  break;
232  case DatabaseType.PostgresSql:
233  AddTypedContext<PostgresSqlDatabaseContext>();
234  break;
235  default:
236  throw new InvalidOperationException(String.Format(CultureInfo.InvariantCulture, "Invalid {0}: {1}!", nameof(DatabaseType), dbType));
237  }
238 
239  // configure other database services
240  services.AddSingleton<IDatabaseContextFactory, DatabaseContextFactory>();
241  services.AddSingleton<IDatabaseSeeder, DatabaseSeeder>();
242 
243  // configure security services
245  services.AddScoped<IClaimsInjector, ClaimsInjector>();
246  services.AddSingleton<IIdentityCache, IdentityCache>();
247  services.AddSingleton<ICryptographySuite, CryptographySuite>();
248  services.AddSingleton<ITokenFactory, TokenFactory>();
249  services.AddSingleton<IPasswordHasher<Models.User>, PasswordHasher<Models.User>>();
250 
251  // configure platform specific services
252  if (postSetupServices.PlatformIdentifier.IsWindows)
253  {
254  if (postSetupServices.GeneralConfiguration.UseBasicWatchdogOnWindows)
255  services.AddSingleton<IWatchdogFactory, WatchdogFactory>();
256  else
257  services.AddSingleton<IWatchdogFactory, WindowsWatchdogFactory>();
258 
259  services.AddSingleton<ISystemIdentityFactory, WindowsSystemIdentityFactory>();
260  services.AddSingleton<ISymlinkFactory, WindowsSymlinkFactory>();
261  services.AddSingleton<IByondInstaller, WindowsByondInstaller>();
262  services.AddSingleton<IPostWriteHandler, WindowsPostWriteHandler>();
263  services.AddSingleton<IProcessFeatures, WindowsProcessFeatures>();
264 
265  services.AddSingleton<WindowsNetworkPromptReaper>();
266  services.AddSingleton<INetworkPromptReaper>(x => x.GetRequiredService<WindowsNetworkPromptReaper>());
267  services.AddSingleton<IHostedService>(x => x.GetRequiredService<WindowsNetworkPromptReaper>());
268  }
269  else
270  {
271  services.AddSingleton<IWatchdogFactory, WatchdogFactory>();
272  services.AddSingleton<ISystemIdentityFactory, PosixSystemIdentityFactory>();
273  services.AddSingleton<ISymlinkFactory, PosixSymlinkFactory>();
274  services.AddSingleton<IByondInstaller, PosixByondInstaller>();
275  services.AddSingleton<IPostWriteHandler, PosixPostWriteHandler>();
276  services.AddSingleton<IProcessFeatures, PosixProcessFeatures>();
277  services.AddSingleton<INetworkPromptReaper, PosixNetworkPromptReaper>();
278  }
279 
280  // configure misc services
281  services.AddSingleton<ISynchronousIOManager, SynchronousIOManager>();
282  services.AddSingleton<IGitHubClientFactory, GitHubClientFactory>();
283  services.AddSingleton<IProcessExecutor, ProcessExecutor>();
284  services.AddSingleton<IServerPortProvider, ServerPortProivder>();
285  services.AddSingleton<ITopicClient, TopicClient>();
286  services.AddSingleton(new SocketParameters
287  {
288  ReceiveTimeout = TimeSpan.FromMilliseconds(postSetupServices.GeneralConfiguration.ByondTopicTimeout),
289  SendTimeout = TimeSpan.FromMilliseconds(postSetupServices.GeneralConfiguration.ByondTopicTimeout),
290  ConnectTimeout = TimeSpan.FromMilliseconds(postSetupServices.GeneralConfiguration.ByondTopicTimeout),
291  DisconnectTimeout = TimeSpan.FromMilliseconds(postSetupServices.GeneralConfiguration.ByondTopicTimeout)
292  });
293 
294  // configure component services
295  services.AddSingleton<ILibGit2RepositoryFactory, LibGit2RepositoryFactory>();
296  services.AddSingleton<ILibGit2Commands, LibGit2Commands>();
297  services.AddSingleton<IProviderFactory, ProviderFactory>();
298  services.AddSingleton<IChatManagerFactory, ChatManagerFactory>();
299  services.AddSingleton<IInstanceFactory, InstanceFactory>();
300 
301  // configure root services
302  services.AddSingleton<IJobManager, JobManager>();
303 
304  services.AddSingleton<InstanceManager>();
305  services.AddSingleton<IBridgeDispatcher>(x => x.GetRequiredService<InstanceManager>());
306  services.AddSingleton<IInstanceManager>(x => x.GetRequiredService<InstanceManager>());
307  }
308 
310  protected override void ConfigureHostedService(IServiceCollection services)
311  => services.AddSingleton<IHostedService>(x => x.GetRequiredService<InstanceManager>());
312 
323  public void Configure(
324  IApplicationBuilder applicationBuilder,
325  IServerControl serverControl,
326  ITokenFactory tokenFactory,
327  IInstanceManager instanceManager,
328  IOptions<ControlPanelConfiguration> controlPanelConfigurationOptions,
329  IOptions<GeneralConfiguration> generalConfigurationOptions,
330  ILogger<Application> logger)
331  {
332  if (applicationBuilder == null)
333  throw new ArgumentNullException(nameof(applicationBuilder));
334  if (serverControl == null)
335  throw new ArgumentNullException(nameof(serverControl));
336 
337  this.tokenFactory = tokenFactory ?? throw new ArgumentNullException(nameof(tokenFactory));
338 
339  var controlPanelConfiguration = controlPanelConfigurationOptions?.Value ?? throw new ArgumentNullException(nameof(controlPanelConfigurationOptions));
340  var generalConfiguration = generalConfigurationOptions?.Value ?? throw new ArgumentNullException(nameof(generalConfigurationOptions));
341 
342  if (logger == null)
343  throw new ArgumentNullException(nameof(logger));
344 
345  logger.LogDebug("Content Root: {0}", hostingEnvironment.ContentRootPath);
346  logger.LogTrace("Web Root: {0}", hostingEnvironment.WebRootPath);
347 
348  if (generalConfiguration.MinimumPasswordLength > Limits.MaximumStringLength)
349  {
350  logger.LogCritical("Configured minimum password length ({0}) is greater than the maximum database string length ({1})!");
351  serverControl.Die(new InvalidOperationException("Minimum password length greater than database limit!"));
352  return;
353  }
354 
355  // attempt to restart the server if the configuration changes
356  if (serverControl.WatchdogPresent)
357  ChangeToken.OnChange(Configuration.GetReloadToken, () => serverControl.Restart());
358 
359  // setup the HTTP request pipeline
360  // Final point where we wrap exceptions in a 500 (ErrorMessage) response
361  applicationBuilder.UseServerErrorHandling();
362 
363  // 503 requests made while the application is starting
364  applicationBuilder.UseAsyncInitialization(async (cancellationToken) =>
365  {
366  var tcs = new TaskCompletionSource<object>();
367  using (cancellationToken.Register(() => tcs.SetCanceled()))
368  await Task.WhenAny(tcs.Task, instanceManager.Ready).ConfigureAwait(false);
369  });
370 
371  // suppress OperationCancelledExceptions, they are just aborted HTTP requests
372  applicationBuilder.UseCancelledRequestSuppression();
373 
374  if (hostingEnvironment.IsDevelopment())
375  {
376  applicationBuilder.UseSwagger();
377  applicationBuilder.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "TGS API V4"));
378  logger.LogTrace("Swagger API generation enabled");
379  }
380 
381  // Set up CORS based on configuration if necessary
382  Action<CorsPolicyBuilder> corsBuilder = null;
383  if (controlPanelConfiguration.AllowAnyOrigin)
384  {
385  logger.LogTrace("Access-Control-Allow-Origin: *");
386  corsBuilder = builder => builder.AllowAnyOrigin();
387  }
388  else if (controlPanelConfiguration.AllowedOrigins?.Count > 0)
389  {
390  logger.LogTrace("Access-Control-Allow-Origin: ", String.Join(',', controlPanelConfiguration.AllowedOrigins));
391  corsBuilder = builder => builder.WithOrigins(controlPanelConfiguration.AllowedOrigins.ToArray());
392  }
393 
394  var originalBuilder = corsBuilder;
395  corsBuilder = builder =>
396  {
397  builder.AllowAnyHeader().AllowAnyMethod();
398  originalBuilder?.Invoke(builder);
399  };
400  applicationBuilder.UseCors(corsBuilder);
401 
402  // spa loading if necessary
403  if (controlPanelConfiguration.Enable)
404  {
405  logger.LogWarning("Web control panel enabled. This is a highly WIP feature!");
406  applicationBuilder.UseStaticFiles();
407  }
408  else
409  logger.LogDebug("Web control panel disabled!");
410 
411  // authenticate JWT tokens using our security pipeline if present, returns 401 if bad
412  applicationBuilder.UseAuthentication();
413 
414  // suppress and log database exceptions
415  applicationBuilder.UseDbConflictHandling();
416 
417  // majority of handling is done in the controllers
418  applicationBuilder.UseMvc();
419 
420  // 404 anything that gets this far
421  }
422  }
423 }
LogLevel MicrosoftLogLevel
The minimum Microsoft.Extensions.Logging.LogLevel to display in logs for Microsoft library sources ...
+Go to the documentation of this file.
1 using Byond.TopicSender;
2 using Cyberboss.AspNetCore.AsyncInitializer;
3 using Microsoft.AspNetCore.Authentication.JwtBearer;
4 using Microsoft.AspNetCore.Builder;
5 using Microsoft.AspNetCore.Cors.Infrastructure;
6 using Microsoft.AspNetCore.Hosting;
7 using Microsoft.AspNetCore.Identity;
8 using Microsoft.Extensions.Configuration;
9 using Microsoft.Extensions.DependencyInjection;
10 using Microsoft.Extensions.Hosting;
11 using Microsoft.Extensions.Logging;
12 using Microsoft.Extensions.Options;
13 using Microsoft.Extensions.Primitives;
14 using Newtonsoft.Json;
15 using Serilog;
16 using Serilog.Events;
17 using Serilog.Formatting.Display;
18 using System;
19 using System.Globalization;
20 using System.IdentityModel.Tokens.Jwt;
21 using System.Threading.Tasks;
22 using Tgstation.Server.Api;
35 using Tgstation.Server.Host.IO;
40 
41 namespace Tgstation.Server.Host.Core
42 {
46 #pragma warning disable CA1506
48  {
52  readonly IWebHostEnvironment hostingEnvironment;
53 
58 
64  public Application(
65  IConfiguration configuration,
66  IWebHostEnvironment hostingEnvironment)
67  : base(configuration)
68  {
69  this.hostingEnvironment = hostingEnvironment ?? throw new ArgumentNullException(nameof(hostingEnvironment));
70  }
71 
76  public static IServerFactory CreateDefaultServerFactory()
77  => new ServerFactory(
79  IOManager);
80 
86  public void ConfigureServices(IServiceCollection services, IPostSetupServices postSetupServices)
87  {
88  ConfigureServices(services);
89 
90  if (postSetupServices == null)
91  throw new ArgumentNullException(nameof(postSetupServices));
92 
93  // configure configuration
94  services.UseStandardConfig<UpdatesConfiguration>(Configuration);
95  services.UseStandardConfig<ControlPanelConfiguration>(Configuration);
96 
97  // enable options which give us config reloading
98  services.AddOptions();
99 
100  static LogEventLevel? ConvertSeriLogLevel(LogLevel logLevel) =>
101  logLevel switch
102  {
103  LogLevel.Critical => LogEventLevel.Fatal,
104  LogLevel.Debug => LogEventLevel.Debug,
105  LogLevel.Error => LogEventLevel.Error,
106  LogLevel.Information => LogEventLevel.Information,
107  LogLevel.Trace => LogEventLevel.Verbose,
108  LogLevel.Warning => LogEventLevel.Warning,
109  LogLevel.None => null,
110  _ => throw new InvalidOperationException(String.Format(CultureInfo.InvariantCulture, "Invalid log level {0}", logLevel)),
111  };
112 
113  var microsoftEventLevel = ConvertSeriLogLevel(postSetupServices.FileLoggingConfiguration.MicrosoftLogLevel);
114  services.SetupLogging(
115  config =>
116  {
117  if (microsoftEventLevel.HasValue)
118  config.MinimumLevel.Override("Microsoft", microsoftEventLevel.Value);
119  },
120  sinkConfig =>
121  {
122  if (postSetupServices.FileLoggingConfiguration.Disable)
123  return;
124 
125  // common app data is C:/ProgramData on windows, else /usr/share
126  var logPath = !String.IsNullOrEmpty(postSetupServices.FileLoggingConfiguration.Directory)
127  ? postSetupServices.FileLoggingConfiguration.Directory
128  : IOManager.ConcatPath(
129  Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData),
131  "Logs");
132 
133  var logEventLevel = ConvertSeriLogLevel(postSetupServices.FileLoggingConfiguration.LogLevel);
134 
135  var formatter = new MessageTemplateTextFormatter(
136  "{Timestamp:o} "
138  + ": [{Level:u3}] {SourceContext:l}: {Message} ({EventId:x8}){NewLine}{Exception}",
139  null);
140 
141  logPath = IOManager.ConcatPath(logPath, "tgs-.log");
142  var rollingFileConfig = sinkConfig.File(
143  formatter,
144  logPath,
145  logEventLevel ?? LogEventLevel.Verbose,
146  50 * 1024 * 1024, // 50MB max size
147  flushToDiskInterval: TimeSpan.FromSeconds(2),
148  rollingInterval: RollingInterval.Day,
149  rollOnFileSizeLimit: true);
150  });
151 
152  // configure bearer token validation
153  services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme).AddJwtBearer(jwtBearerOptions =>
154  {
155  // this line isn't actually run until the first request is made
156  // at that point tokenFactory will be populated
157  jwtBearerOptions.TokenValidationParameters = tokenFactory.ValidationParameters;
158  jwtBearerOptions.Events = new JwtBearerEvents
159  {
160  // Application is our composition root so this monstrosity of a line is okay
161  // At least, that's what I tell myself to sleep at night
162  OnTokenValidated = ctx => ctx
163  .HttpContext
164  .RequestServices
165  .GetRequiredService<IClaimsInjector>()
166  .InjectClaimsIntoContext(
167  ctx,
168  ctx.HttpContext.RequestAborted)
169  };
170  });
171 
172  // WARNING: STATIC CODE
173  // fucking prevents converting 'sub' to M$ bs
174  // can't be done in the above lambda, that's too late
175  JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();
176 
177  // add mvc, configure the json serializer settings
178  services
179  .AddMvc(options =>
180  {
181  options.EnableEndpointRouting = false;
182  })
183  .AddNewtonsoftJson(options =>
184  {
185  options.AllowInputFormatterExceptionMessages = true;
186  options.SerializerSettings.NullValueHandling = NullValueHandling.Ignore;
187  options.SerializerSettings.CheckAdditionalContent = true;
188  options.SerializerSettings.MissingMemberHandling = MissingMemberHandling.Error;
189  options.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
190  options.SerializerSettings.Converters = new[] { new VersionConverter() };
191  });
192 
193  if (hostingEnvironment.IsDevelopment())
194  {
195  static string GetDocumentationFilePath(string assemblyLocation) => IOManager.ConcatPath(IOManager.GetDirectoryName(assemblyLocation), String.Concat(IOManager.GetFileNameWithoutExtension(assemblyLocation), ".xml"));
196  var assemblyDocumentationPath = GetDocumentationFilePath(typeof(Application).Assembly.Location);
197  var apiDocumentationPath = GetDocumentationFilePath(typeof(ApiHeaders).Assembly.Location);
198  services.AddSwaggerGen(genOptions => SwaggerConfiguration.Configure(genOptions, assemblyDocumentationPath, apiDocumentationPath));
199  services.AddSwaggerGenNewtonsoftSupport();
200  }
201 
202  // enable browser detection
203  services.AddDetectionCore().AddBrowser();
204 
205  // CORS conditionally enabled later
206  services.AddCors();
207 
208  void AddTypedContext<TContext>() where TContext : DatabaseContext
209  {
210  services.AddDbContext<TContext>(builder =>
211  {
212  if (hostingEnvironment.IsDevelopment())
213  builder.EnableSensitiveDataLogging();
214  });
215  services.AddScoped<IDatabaseContext>(x => x.GetRequiredService<TContext>());
216  }
217 
218  // add the correct database context type
219  var dbType = postSetupServices.DatabaseConfiguration.DatabaseType;
220  switch (dbType)
221  {
222  case DatabaseType.MySql:
223  case DatabaseType.MariaDB:
224  AddTypedContext<MySqlDatabaseContext>();
225  break;
226  case DatabaseType.SqlServer:
227  AddTypedContext<SqlServerDatabaseContext>();
228  break;
229  case DatabaseType.Sqlite:
230  AddTypedContext<SqliteDatabaseContext>();
231  break;
232  case DatabaseType.PostgresSql:
233  AddTypedContext<PostgresSqlDatabaseContext>();
234  break;
235  default:
236  throw new InvalidOperationException(String.Format(CultureInfo.InvariantCulture, "Invalid {0}: {1}!", nameof(DatabaseType), dbType));
237  }
238 
239  // configure other database services
240  services.AddSingleton<IDatabaseContextFactory, DatabaseContextFactory>();
241  services.AddSingleton<IDatabaseSeeder, DatabaseSeeder>();
242 
243  // configure security services
245  services.AddScoped<IClaimsInjector, ClaimsInjector>();
246  services.AddSingleton<IIdentityCache, IdentityCache>();
247  services.AddSingleton<ICryptographySuite, CryptographySuite>();
248  services.AddSingleton<ITokenFactory, TokenFactory>();
249  services.AddSingleton<IPasswordHasher<Models.User>, PasswordHasher<Models.User>>();
250 
251  // configure platform specific services
252  if (postSetupServices.PlatformIdentifier.IsWindows)
253  {
254  if (postSetupServices.GeneralConfiguration.UseBasicWatchdogOnWindows)
255  services.AddSingleton<IWatchdogFactory, WatchdogFactory>();
256  else
257  services.AddSingleton<IWatchdogFactory, WindowsWatchdogFactory>();
258 
259  services.AddSingleton<ISystemIdentityFactory, WindowsSystemIdentityFactory>();
260  services.AddSingleton<ISymlinkFactory, WindowsSymlinkFactory>();
261  services.AddSingleton<IByondInstaller, WindowsByondInstaller>();
262  services.AddSingleton<IPostWriteHandler, WindowsPostWriteHandler>();
263  services.AddSingleton<IProcessFeatures, WindowsProcessFeatures>();
264 
265  services.AddSingleton<WindowsNetworkPromptReaper>();
266  services.AddSingleton<INetworkPromptReaper>(x => x.GetRequiredService<WindowsNetworkPromptReaper>());
267  services.AddSingleton<IHostedService>(x => x.GetRequiredService<WindowsNetworkPromptReaper>());
268  }
269  else
270  {
271  services.AddSingleton<IWatchdogFactory, WatchdogFactory>();
272  services.AddSingleton<ISystemIdentityFactory, PosixSystemIdentityFactory>();
273  services.AddSingleton<ISymlinkFactory, PosixSymlinkFactory>();
274  services.AddSingleton<IByondInstaller, PosixByondInstaller>();
275  services.AddSingleton<IPostWriteHandler, PosixPostWriteHandler>();
276  services.AddSingleton<IProcessFeatures, PosixProcessFeatures>();
277  services.AddSingleton<INetworkPromptReaper, PosixNetworkPromptReaper>();
278  }
279 
280  // configure misc services
281  services.AddSingleton<ISynchronousIOManager, SynchronousIOManager>();
282  services.AddSingleton<IGitHubClientFactory, GitHubClientFactory>();
283  services.AddSingleton<IProcessExecutor, ProcessExecutor>();
284  services.AddSingleton<IServerPortProvider, ServerPortProivder>();
285  services.AddSingleton<ITopicClient, TopicClient>();
286  services.AddSingleton(new SocketParameters
287  {
288  ReceiveTimeout = TimeSpan.FromMilliseconds(postSetupServices.GeneralConfiguration.ByondTopicTimeout),
289  SendTimeout = TimeSpan.FromMilliseconds(postSetupServices.GeneralConfiguration.ByondTopicTimeout),
290  ConnectTimeout = TimeSpan.FromMilliseconds(postSetupServices.GeneralConfiguration.ByondTopicTimeout),
291  DisconnectTimeout = TimeSpan.FromMilliseconds(postSetupServices.GeneralConfiguration.ByondTopicTimeout)
292  });
293 
294  // configure component services
295  services.AddSingleton<ILibGit2RepositoryFactory, LibGit2RepositoryFactory>();
296  services.AddSingleton<ILibGit2Commands, LibGit2Commands>();
297  services.AddSingleton<IProviderFactory, ProviderFactory>();
298  services.AddSingleton<IChatManagerFactory, ChatManagerFactory>();
299  services.AddSingleton<IInstanceFactory, InstanceFactory>();
300 
301  // configure root services
302  services.AddSingleton<IJobManager, JobManager>();
303 
304  services.AddSingleton<InstanceManager>();
305  services.AddSingleton<IBridgeDispatcher>(x => x.GetRequiredService<InstanceManager>());
306  services.AddSingleton<IInstanceManager>(x => x.GetRequiredService<InstanceManager>());
307  }
308 
310  protected override void ConfigureHostedService(IServiceCollection services)
311  => services.AddSingleton<IHostedService>(x => x.GetRequiredService<InstanceManager>());
312 
323  public void Configure(
324  IApplicationBuilder applicationBuilder,
325  IServerControl serverControl,
326  ITokenFactory tokenFactory,
327  IInstanceManager instanceManager,
328  IOptions<ControlPanelConfiguration> controlPanelConfigurationOptions,
329  IOptions<GeneralConfiguration> generalConfigurationOptions,
330  ILogger<Application> logger)
331  {
332  if (applicationBuilder == null)
333  throw new ArgumentNullException(nameof(applicationBuilder));
334  if (serverControl == null)
335  throw new ArgumentNullException(nameof(serverControl));
336 
337  this.tokenFactory = tokenFactory ?? throw new ArgumentNullException(nameof(tokenFactory));
338 
339  var controlPanelConfiguration = controlPanelConfigurationOptions?.Value ?? throw new ArgumentNullException(nameof(controlPanelConfigurationOptions));
340  var generalConfiguration = generalConfigurationOptions?.Value ?? throw new ArgumentNullException(nameof(generalConfigurationOptions));
341 
342  if (logger == null)
343  throw new ArgumentNullException(nameof(logger));
344 
345  logger.LogDebug("Content Root: {0}", hostingEnvironment.ContentRootPath);
346  logger.LogTrace("Web Root: {0}", hostingEnvironment.WebRootPath);
347 
348  if (generalConfiguration.MinimumPasswordLength > Limits.MaximumStringLength)
349  {
350  logger.LogCritical("Configured minimum password length ({0}) is greater than the maximum database string length ({1})!");
351  serverControl.Die(new InvalidOperationException("Minimum password length greater than database limit!"));
352  return;
353  }
354 
355  // attempt to restart the server if the configuration changes
356  if (serverControl.WatchdogPresent)
357  ChangeToken.OnChange(Configuration.GetReloadToken, () =>
358  {
359  logger.LogInformation("Configuration change detected");
360  serverControl.Restart();
361  });
362 
363  // setup the HTTP request pipeline
364  // Final point where we wrap exceptions in a 500 (ErrorMessage) response
365  applicationBuilder.UseServerErrorHandling();
366 
367  // 503 requests made while the application is starting
368  applicationBuilder.UseAsyncInitialization(async (cancellationToken) =>
369  {
370  var tcs = new TaskCompletionSource<object>();
371  using (cancellationToken.Register(() => tcs.SetCanceled()))
372  await Task.WhenAny(tcs.Task, instanceManager.Ready).ConfigureAwait(false);
373  });
374 
375  // suppress OperationCancelledExceptions, they are just aborted HTTP requests
376  applicationBuilder.UseCancelledRequestSuppression();
377 
378  if (hostingEnvironment.IsDevelopment())
379  {
380  applicationBuilder.UseSwagger();
381  applicationBuilder.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "TGS API V4"));
382  logger.LogTrace("Swagger API generation enabled");
383  }
384 
385  // Set up CORS based on configuration if necessary
386  Action<CorsPolicyBuilder> corsBuilder = null;
387  if (controlPanelConfiguration.AllowAnyOrigin)
388  {
389  logger.LogTrace("Access-Control-Allow-Origin: *");
390  corsBuilder = builder => builder.AllowAnyOrigin();
391  }
392  else if (controlPanelConfiguration.AllowedOrigins?.Count > 0)
393  {
394  logger.LogTrace("Access-Control-Allow-Origin: ", String.Join(',', controlPanelConfiguration.AllowedOrigins));
395  corsBuilder = builder => builder.WithOrigins(controlPanelConfiguration.AllowedOrigins.ToArray());
396  }
397 
398  var originalBuilder = corsBuilder;
399  corsBuilder = builder =>
400  {
401  builder.AllowAnyHeader().AllowAnyMethod();
402  originalBuilder?.Invoke(builder);
403  };
404  applicationBuilder.UseCors(corsBuilder);
405 
406  // spa loading if necessary
407  if (controlPanelConfiguration.Enable)
408  {
409  logger.LogWarning("Web control panel enabled. This is a highly WIP feature!");
410  applicationBuilder.UseStaticFiles();
411  }
412  else
413  logger.LogDebug("Web control panel disabled!");
414 
415  // authenticate JWT tokens using our security pipeline if present, returns 401 if bad
416  applicationBuilder.UseAuthentication();
417 
418  // suppress and log database exceptions
419  applicationBuilder.UseDbConflictHandling();
420 
421  // majority of handling is done in the controllers
422  applicationBuilder.UseMvc();
423 
424  // 404 anything that gets this far
425  }
426  }
427 }
LogLevel MicrosoftLogLevel
The minimum Microsoft.Extensions.Logging.LogLevel to display in logs for Microsoft library sources ...
@@ -145,7 +145,6 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
Application(IConfiguration configuration, IWebHostEnvironment hostingEnvironment)
Construct an Application
Definition: Application.cs:64
Represents the header that must be present for every server request
Definition: ApiHeaders.cs:17
-
readonly IWebHostEnvironment hostingEnvironment
The IWebHostEnvironment for the Application.
Definition: Application.cs:52
DatabaseConfiguration DatabaseConfiguration
The Configuration.DatabaseConfiguration.
diff --git a/_database_configuration_8cs_source.html b/_database_configuration_8cs_source.html index eec78c7563..9f6be2d73d 100644 --- a/_database_configuration_8cs_source.html +++ b/_database_configuration_8cs_source.html @@ -94,7 +94,7 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
DatabaseConfiguration.cs
-Go to the documentation of this file.
1 using Newtonsoft.Json;
2 using Newtonsoft.Json.Converters;
3 
4 namespace Tgstation.Server.Host.Configuration
5 {
9  sealed class DatabaseConfiguration
10  {
14  public const string Section = "Database";
15 
19  [JsonConverter(typeof(StringEnumConverter))]
20  public DatabaseType DatabaseType { get; set; }
21 
25  public bool ResetAdminPassword { get; set; }
26 
30  public string ConnectionString { get; set; }
31 
35  public bool DropDatabase { get; set; }
36 
40  public bool DesignTime { get; set; }
41 
45  public string MySqlServerVersion { get; set; }
46  }
47 }
+Go to the documentation of this file.
1 using Newtonsoft.Json;
2 using Newtonsoft.Json.Converters;
3 
4 namespace Tgstation.Server.Host.Configuration
5 {
9  sealed class DatabaseConfiguration
10  {
14  public const string Section = "Database";
15 
19  [JsonConverter(typeof(StringEnumConverter))]
20  public DatabaseType DatabaseType { get; set; }
21 
25  public bool ResetAdminPassword { get; set; }
26 
30  public string ConnectionString { get; set; }
31 
35  public bool DropDatabase { get; set; }
36 
40  public bool DesignTime { get; set; }
41 
45  public string ServerVersion { get; set; }
46  }
47 }
Configuration options for the Database.DatabaseContext
diff --git a/_database_context_8cs_source.html b/_database_context_8cs_source.html index 9bf2316466..5122e74e38 100644 --- a/_database_context_8cs_source.html +++ b/_database_context_8cs_source.html @@ -94,18 +94,18 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
DatabaseContext.cs
-Go to the documentation of this file.
1 using Microsoft.EntityFrameworkCore;
2 using Microsoft.EntityFrameworkCore.Infrastructure;
3 using Microsoft.EntityFrameworkCore.Migrations;
4 using Microsoft.Extensions.DependencyInjection;
5 using Microsoft.Extensions.Logging;
6 using Microsoft.Extensions.Options;
7 using System;
8 using System.Globalization;
9 using System.Linq;
10 using System.Threading;
11 using System.Threading.Tasks;
15 
16 namespace Tgstation.Server.Host.Database
17 {
21 #pragma warning disable CA1506 // TODO: Decomplexify
23  {
26 
30  public DbSet<User> Users { get; set; }
31 
35  public DbSet<Instance> Instances { get; set; }
36 
40  public DbSet<CompileJob> CompileJobs { get; set; }
41 
45  public DbSet<RevisionInformation> RevisionInformations { get; set; }
46 
50  public DbSet<DreamMakerSettings> DreamMakerSettings { get; set; }
51 
55  public DbSet<ChatBot> ChatBots { get; set; }
56 
60  public DbSet<DreamDaemonSettings> DreamDaemonSettings { get; set; }
61 
65  public DbSet<RepositorySettings> RepositorySettings { get; set; }
66 
70  public DbSet<InstanceUser> InstanceUsers { get; set; }
71 
75  public DbSet<ChatChannel> ChatChannels { get; set; }
76 
80  public DbSet<Job> Jobs { get; set; }
81 
85  public DbSet<ReattachInformation> ReattachInformations { get; set; }
86 
90  public DbSet<DualReattachInformation> WatchdogReattachInformations { get; set; }
91 
95  public DbSet<TestMerge> TestMerges { get; set; }
96 
100  public DbSet<RevInfoTestMerge> RevInfoTestMerges { get; set; }
101 
105  protected ILogger Logger { get; }
106 
111 
114 
117 
120 
123 
126 
129 
132 
135 
138 
141 
144 
147 
150 
155 
160 
165 
170 
175 
180 
185 
190 
195 
200 
205 
210 
215 
220 
228  public DatabaseContext(DbContextOptions dbContextOptions, IOptions<DatabaseConfiguration> databaseConfigurationOptions, IDatabaseSeeder databaseSeeder, ILogger logger) : base(dbContextOptions)
229  {
230  DatabaseConfiguration = databaseConfigurationOptions?.Value ?? throw new ArgumentNullException(nameof(databaseConfigurationOptions));
231  this.databaseSeeder = databaseSeeder ?? throw new ArgumentNullException(nameof(databaseSeeder));
232  Logger = logger ?? throw new ArgumentNullException(nameof(logger));
233 
234  usersCollection = new DatabaseCollection<User>(Users);
235  instancesCollection = new DatabaseCollection<Instance>(Instances);
236  instanceUsersCollection = new DatabaseCollection<InstanceUser>(InstanceUsers);
237  compileJobsCollection = new DatabaseCollection<CompileJob>(CompileJobs);
238  repositorySettingsCollection = new DatabaseCollection<RepositorySettings>(RepositorySettings);
239  dreamMakerSettingsCollection = new DatabaseCollection<DreamMakerSettings>(DreamMakerSettings);
240  dreamDaemonSettingsCollection = new DatabaseCollection<DreamDaemonSettings>(DreamDaemonSettings);
241  chatBotsCollection = new DatabaseCollection<ChatBot>(ChatBots);
242  chatChannelsCollection = new DatabaseCollection<ChatChannel>(ChatChannels);
243  revisionInformationsCollection = new DatabaseCollection<RevisionInformation>(RevisionInformations);
244  jobsCollection = new DatabaseCollection<Job>(Jobs);
245  reattachInformationsCollection = new DatabaseCollection<ReattachInformation>(ReattachInformations);
246  watchdogReattachInformationsCollection = new DatabaseCollection<DualReattachInformation>(WatchdogReattachInformations);
247  }
248 
250  protected override void OnModelCreating(ModelBuilder modelBuilder)
251  {
252  // Setup our more complex database relations
253  Logger.LogTrace("Building entity framework context...");
254  base.OnModelCreating(modelBuilder);
255 
256  var userModel = modelBuilder.Entity<User>();
257  userModel.HasIndex(x => x.CanonicalName).IsUnique();
258  userModel.HasIndex(x => x.SystemIdentifier).IsUnique();
259  userModel.HasMany(x => x.TestMerges).WithOne(x => x.MergedBy).OnDelete(DeleteBehavior.Restrict);
260 
261  modelBuilder.Entity<InstanceUser>().HasIndex(x => new { x.UserId, x.InstanceId }).IsUnique();
262 
263  var revInfo = modelBuilder.Entity<RevisionInformation>();
264  revInfo.HasMany(x => x.ActiveTestMerges).WithOne(x => x.RevisionInformation).OnDelete(DeleteBehavior.Cascade);
265  revInfo.HasOne(x => x.PrimaryTestMerge).WithOne(x => x.PrimaryRevisionInformation).OnDelete(DeleteBehavior.Cascade);
266  revInfo.HasIndex(x => new { x.InstanceId, x.CommitSha }).IsUnique();
267 
268  // IMPORTANT: When an instance is deleted (detached) it cascades into the maze of revinfo/testmerge/ritm/compilejob/job/ri relations
269  // This maze starts at revInfo and jobs
270  // jobs takes care of deleting compile jobs and ris
271  // rev info takes care of the rest
272  // Break the link here so the db doesn't shit itself complaining about cascading deletes
273  // EF will handle making the right query to destroy everything
274  revInfo.HasMany(x => x.CompileJobs).WithOne(x => x.RevisionInformation).OnDelete(DeleteBehavior.ClientNoAction);
275 
276  // Also break the link between ritm and testmerge so it doesn't cycle in a triangle with rev info
277  modelBuilder.Entity<TestMerge>().HasMany(x => x.RevisonInformations).WithOne(x => x.TestMerge).OnDelete(DeleteBehavior.ClientNoAction);
278 
279  var compileJob = modelBuilder.Entity<CompileJob>();
280  compileJob.HasIndex(x => x.DirectoryName);
281  compileJob.HasOne(x => x.Job).WithOne().OnDelete(DeleteBehavior.Cascade);
282 
283  var chatChannel = modelBuilder.Entity<ChatChannel>();
284  chatChannel.HasIndex(x => new { x.ChatSettingsId, x.IrcChannel }).IsUnique();
285  chatChannel.HasIndex(x => new { x.ChatSettingsId, x.DiscordChannelId }).IsUnique();
286  chatChannel.HasOne(x => x.ChatSettings).WithMany(x => x.Channels).HasForeignKey(x => x.ChatSettingsId).OnDelete(DeleteBehavior.Cascade);
287 
288  modelBuilder.Entity<ChatBot>().HasIndex(x => new { x.InstanceId, x.Name }).IsUnique();
289 
290  var instanceModel = modelBuilder.Entity<Instance>();
291  instanceModel.HasIndex(x => x.Path).IsUnique();
292  instanceModel.HasMany(x => x.ChatSettings).WithOne(x => x.Instance).OnDelete(DeleteBehavior.Cascade);
293  instanceModel.HasOne(x => x.DreamDaemonSettings).WithOne(x => x.Instance).OnDelete(DeleteBehavior.Cascade);
294  instanceModel.HasOne(x => x.DreamMakerSettings).WithOne(x => x.Instance).OnDelete(DeleteBehavior.Cascade);
295  instanceModel.HasOne(x => x.RepositorySettings).WithOne(x => x.Instance).OnDelete(DeleteBehavior.Cascade);
296  instanceModel.HasMany(x => x.RevisionInformations).WithOne(x => x.Instance).OnDelete(DeleteBehavior.Cascade);
297  instanceModel.HasMany(x => x.InstanceUsers).WithOne(x => x.Instance).OnDelete(DeleteBehavior.Cascade);
298  instanceModel.HasMany(x => x.Jobs).WithOne(x => x.Instance).OnDelete(DeleteBehavior.Cascade);
299  instanceModel.HasOne(x => x.WatchdogReattachInformation).WithOne().OnDelete(DeleteBehavior.Cascade);
300  }
301 
303  public async Task Initialize(CancellationToken cancellationToken)
304  {
305  ValidateDatabaseType();
306 
308  {
309  Logger.LogCritical("DropDatabase configuration option set! Dropping any existing database...");
310  await Database.EnsureDeletedAsync(cancellationToken).ConfigureAwait(false);
311  }
312 
313  var migrations = await Database.GetAppliedMigrationsAsync(cancellationToken).ConfigureAwait(false);
314  var wasEmpty = !migrations.Any();
315 
316  if (wasEmpty || (await Database.GetPendingMigrationsAsync(cancellationToken).ConfigureAwait(false)).Any())
317  {
318  Logger.LogInformation("Migrating database...");
319  await Database.MigrateAsync(cancellationToken).ConfigureAwait(false);
320  }
321  else
322  Logger.LogDebug("No migrations to apply.");
323 
324  wasEmpty |= (await Users.AsQueryable().CountAsync(cancellationToken).ConfigureAwait(false)) == 0;
325 
326  if (wasEmpty)
327  {
328  Logger.LogInformation("Seeding database...");
329  await databaseSeeder.SeedDatabase(this, cancellationToken).ConfigureAwait(false);
330  }
331  else
332  {
334  {
335  Logger.LogWarning("Enabling and resetting admin password due to configuration!");
336  await databaseSeeder.ResetAdminPassword(this, cancellationToken).ConfigureAwait(false);
337  }
338 
339  await databaseSeeder.SanitizeDatabase(this, cancellationToken).ConfigureAwait(false);
340  }
341  }
342 
344  public Task Save(CancellationToken cancellationToken) => SaveChangesAsync(cancellationToken);
345 
347  public async Task SchemaDowngradeForServerVersion(Version version, CancellationToken cancellationToken)
348  {
349  if (version == null)
350  throw new ArgumentNullException(nameof(version));
351  if (version < new Version(4, 0))
352  throw new ArgumentOutOfRangeException(nameof(version), version, "Not a valid V4 version!");
353 
354  // Update this with new migrations as they are made
355  string targetMigration = null;
356 
357  if (DatabaseType == DatabaseType.PostgresSql && version < new Version(4, 3, 0))
358  throw new NotSupportedException("Cannot migrate below version 4.3.0 with PostgresSql!");
359 
360  if (version < new Version(4, 1, 0))
361  throw new NotSupportedException("Cannot migrate below version 4.1.0!");
362 
363  if (version < new Version(4, 2, 0))
364  targetMigration = DatabaseType == DatabaseType.Sqlite ? nameof(SLRebuild) : nameof(MSFixCascadingDelete);
365 
366  if (targetMigration == null)
367  {
368  Logger.LogDebug("No down migration required.");
369  return;
370  }
371 
372  string migrationSubstitution;
373  switch (DatabaseType)
374  {
375  case DatabaseType.SqlServer:
376  // already setup
377  migrationSubstitution = null;
378  break;
379  case DatabaseType.MySql:
380  case DatabaseType.MariaDB:
381  migrationSubstitution = "MY{0}";
382  break;
383  case DatabaseType.Sqlite:
384  migrationSubstitution = "SL{0}";
385  break;
386  case DatabaseType.PostgresSql:
387  migrationSubstitution = "PG{0}";
388  break;
389  default:
390  throw new InvalidOperationException($"Invalid DatabaseType: {DatabaseType}");
391  }
392 
393  if (migrationSubstitution != null)
394  targetMigration = String.Format(CultureInfo.InvariantCulture, migrationSubstitution, targetMigration.Substring(2));
395 
396  // even though it clearly implements it in the DatabaseFacade definition this won't work without casting (╯ಠ益ಠ)╯︵ ┻━┻
397  var dbServiceProvider = ((IInfrastructure<IServiceProvider>)Database).Instance;
398  var migrator = dbServiceProvider.GetRequiredService<IMigrator>();
399 
400  Logger.LogInformation("Migrating down to version {0}. Target: {1}", version, targetMigration);
401  try
402  {
403  await migrator.MigrateAsync(targetMigration, cancellationToken).ConfigureAwait(false);
404  }
405  catch (Exception e)
406  {
407  Logger.LogCritical("Failed to migrate! Exception: {0}", e);
408  }
409  }
410 
414  protected abstract void ValidateDatabaseType();
415  }
416 }
+Go to the documentation of this file.
1 using Microsoft.EntityFrameworkCore;
2 using Microsoft.EntityFrameworkCore.Infrastructure;
3 using Microsoft.EntityFrameworkCore.Migrations;
4 using Microsoft.Extensions.DependencyInjection;
5 using Microsoft.Extensions.Logging;
6 using Microsoft.Extensions.Options;
7 using System;
8 using System.Globalization;
9 using System.Linq;
10 using System.Threading;
11 using System.Threading.Tasks;
15 
16 namespace Tgstation.Server.Host.Database
17 {
21 #pragma warning disable CA1506 // TODO: Decomplexify
23  {
26 
30  public DbSet<User> Users { get; set; }
31 
35  public DbSet<Instance> Instances { get; set; }
36 
40  public DbSet<CompileJob> CompileJobs { get; set; }
41 
45  public DbSet<RevisionInformation> RevisionInformations { get; set; }
46 
50  public DbSet<DreamMakerSettings> DreamMakerSettings { get; set; }
51 
55  public DbSet<ChatBot> ChatBots { get; set; }
56 
60  public DbSet<DreamDaemonSettings> DreamDaemonSettings { get; set; }
61 
65  public DbSet<RepositorySettings> RepositorySettings { get; set; }
66 
70  public DbSet<InstanceUser> InstanceUsers { get; set; }
71 
75  public DbSet<ChatChannel> ChatChannels { get; set; }
76 
80  public DbSet<Job> Jobs { get; set; }
81 
85  public DbSet<ReattachInformation> ReattachInformations { get; set; }
86 
90  public DbSet<DualReattachInformation> WatchdogReattachInformations { get; set; }
91 
95  public DbSet<TestMerge> TestMerges { get; set; }
96 
100  public DbSet<RevInfoTestMerge> RevInfoTestMerges { get; set; }
101 
105  protected ILogger Logger { get; }
106 
111 
115  protected virtual DeleteBehavior RevInfoCompileJobDeleteBehavior => DeleteBehavior.ClientNoAction;
116 
119 
122 
125 
128 
131 
134 
137 
140 
143 
146 
149 
152 
155 
160 
165 
170 
175 
180 
185 
190 
195 
200 
205 
210 
215 
220 
225 
233  public DatabaseContext(DbContextOptions dbContextOptions, IOptions<DatabaseConfiguration> databaseConfigurationOptions, IDatabaseSeeder databaseSeeder, ILogger logger) : base(dbContextOptions)
234  {
235  DatabaseConfiguration = databaseConfigurationOptions?.Value ?? throw new ArgumentNullException(nameof(databaseConfigurationOptions));
236  this.databaseSeeder = databaseSeeder ?? throw new ArgumentNullException(nameof(databaseSeeder));
237  Logger = logger ?? throw new ArgumentNullException(nameof(logger));
238 
239  usersCollection = new DatabaseCollection<User>(Users);
240  instancesCollection = new DatabaseCollection<Instance>(Instances);
241  instanceUsersCollection = new DatabaseCollection<InstanceUser>(InstanceUsers);
242  compileJobsCollection = new DatabaseCollection<CompileJob>(CompileJobs);
243  repositorySettingsCollection = new DatabaseCollection<RepositorySettings>(RepositorySettings);
244  dreamMakerSettingsCollection = new DatabaseCollection<DreamMakerSettings>(DreamMakerSettings);
245  dreamDaemonSettingsCollection = new DatabaseCollection<DreamDaemonSettings>(DreamDaemonSettings);
246  chatBotsCollection = new DatabaseCollection<ChatBot>(ChatBots);
247  chatChannelsCollection = new DatabaseCollection<ChatChannel>(ChatChannels);
248  revisionInformationsCollection = new DatabaseCollection<RevisionInformation>(RevisionInformations);
249  jobsCollection = new DatabaseCollection<Job>(Jobs);
250  reattachInformationsCollection = new DatabaseCollection<ReattachInformation>(ReattachInformations);
251  watchdogReattachInformationsCollection = new DatabaseCollection<DualReattachInformation>(WatchdogReattachInformations);
252  }
253 
255  protected override void OnModelCreating(ModelBuilder modelBuilder)
256  {
257  // Setup our more complex database relations
258  Logger.LogTrace("Building entity framework context...");
259  base.OnModelCreating(modelBuilder);
260 
261  var userModel = modelBuilder.Entity<User>();
262  userModel.HasIndex(x => x.CanonicalName).IsUnique();
263  userModel.HasIndex(x => x.SystemIdentifier).IsUnique();
264  userModel.HasMany(x => x.TestMerges).WithOne(x => x.MergedBy).OnDelete(DeleteBehavior.Restrict);
265 
266  modelBuilder.Entity<InstanceUser>().HasIndex(x => new { x.UserId, x.InstanceId }).IsUnique();
267 
268  var revInfo = modelBuilder.Entity<RevisionInformation>();
269  revInfo.HasMany(x => x.ActiveTestMerges).WithOne(x => x.RevisionInformation).OnDelete(DeleteBehavior.Cascade);
270  revInfo.HasOne(x => x.PrimaryTestMerge).WithOne(x => x.PrimaryRevisionInformation).OnDelete(DeleteBehavior.Cascade);
271  revInfo.HasIndex(x => new { x.InstanceId, x.CommitSha }).IsUnique();
272 
273  // IMPORTANT: When an instance is deleted (detached) it cascades into the maze of revinfo/testmerge/ritm/compilejob/job/ri relations
274  // This maze starts at revInfo and jobs
275  // jobs takes care of deleting compile jobs and ris
276  // rev info takes care of the rest
277  // Break the link here so the db doesn't shit itself complaining about cascading deletes
278  // EF will handle making the right query to destroy everything
279  // UPDATE: I fuck with this constantly in hopes of eliminating FK issues on instance detack
280  revInfo.HasMany(x => x.CompileJobs).WithOne(x => x.RevisionInformation).OnDelete(RevInfoCompileJobDeleteBehavior);
281 
282  // Also break the link between ritm and testmerge so it doesn't cycle in a triangle with rev info
283  modelBuilder.Entity<TestMerge>().HasMany(x => x.RevisonInformations).WithOne(x => x.TestMerge).OnDelete(DeleteBehavior.ClientNoAction);
284 
285  var compileJob = modelBuilder.Entity<CompileJob>();
286  compileJob.HasIndex(x => x.DirectoryName);
287  compileJob.HasOne(x => x.Job).WithOne().OnDelete(DeleteBehavior.Cascade);
288 
289  var chatChannel = modelBuilder.Entity<ChatChannel>();
290  chatChannel.HasIndex(x => new { x.ChatSettingsId, x.IrcChannel }).IsUnique();
291  chatChannel.HasIndex(x => new { x.ChatSettingsId, x.DiscordChannelId }).IsUnique();
292  chatChannel.HasOne(x => x.ChatSettings).WithMany(x => x.Channels).HasForeignKey(x => x.ChatSettingsId).OnDelete(DeleteBehavior.Cascade);
293 
294  modelBuilder.Entity<ChatBot>().HasIndex(x => new { x.InstanceId, x.Name }).IsUnique();
295 
296  var instanceModel = modelBuilder.Entity<Instance>();
297  instanceModel.HasIndex(x => x.Path).IsUnique();
298  instanceModel.HasMany(x => x.ChatSettings).WithOne(x => x.Instance).OnDelete(DeleteBehavior.Cascade);
299  instanceModel.HasOne(x => x.DreamDaemonSettings).WithOne(x => x.Instance).OnDelete(DeleteBehavior.Cascade);
300  instanceModel.HasOne(x => x.DreamMakerSettings).WithOne(x => x.Instance).OnDelete(DeleteBehavior.Cascade);
301  instanceModel.HasOne(x => x.RepositorySettings).WithOne(x => x.Instance).OnDelete(DeleteBehavior.Cascade);
302  instanceModel.HasMany(x => x.RevisionInformations).WithOne(x => x.Instance).OnDelete(DeleteBehavior.Cascade);
303  instanceModel.HasMany(x => x.InstanceUsers).WithOne(x => x.Instance).OnDelete(DeleteBehavior.Cascade);
304  instanceModel.HasMany(x => x.Jobs).WithOne(x => x.Instance).OnDelete(DeleteBehavior.Cascade);
305  instanceModel.HasOne(x => x.WatchdogReattachInformation).WithOne().OnDelete(DeleteBehavior.Cascade);
306  }
307 
309  public async Task Initialize(CancellationToken cancellationToken)
310  {
311  ValidateDatabaseType();
312 
314  {
315  Logger.LogCritical("DropDatabase configuration option set! Dropping any existing database...");
316  await Database.EnsureDeletedAsync(cancellationToken).ConfigureAwait(false);
317  }
318 
319  var migrations = await Database.GetAppliedMigrationsAsync(cancellationToken).ConfigureAwait(false);
320  var wasEmpty = !migrations.Any();
321 
322  if (wasEmpty || (await Database.GetPendingMigrationsAsync(cancellationToken).ConfigureAwait(false)).Any())
323  {
324  Logger.LogInformation("Migrating database...");
325  await Database.MigrateAsync(cancellationToken).ConfigureAwait(false);
326  }
327  else
328  Logger.LogDebug("No migrations to apply.");
329 
330  wasEmpty |= (await Users.AsQueryable().CountAsync(cancellationToken).ConfigureAwait(false)) == 0;
331 
332  if (wasEmpty)
333  {
334  Logger.LogInformation("Seeding database...");
335  await databaseSeeder.SeedDatabase(this, cancellationToken).ConfigureAwait(false);
336  }
337  else
338  {
340  {
341  Logger.LogWarning("Enabling and resetting admin password due to configuration!");
342  await databaseSeeder.ResetAdminPassword(this, cancellationToken).ConfigureAwait(false);
343  }
344 
345  await databaseSeeder.SanitizeDatabase(this, cancellationToken).ConfigureAwait(false);
346  }
347  }
348 
350  public Task Save(CancellationToken cancellationToken) => SaveChangesAsync(cancellationToken);
351 
353  public async Task SchemaDowngradeForServerVersion(Version version, CancellationToken cancellationToken)
354  {
355  if (version == null)
356  throw new ArgumentNullException(nameof(version));
357  if (version < new Version(4, 0))
358  throw new ArgumentOutOfRangeException(nameof(version), version, "Not a valid V4 version!");
359 
360  // Update this with new migrations as they are made
361  string targetMigration = null;
362 
363  if (DatabaseType == DatabaseType.PostgresSql && version < new Version(4, 3, 0))
364  throw new NotSupportedException("Cannot migrate below version 4.3.0 with PostgresSql!");
365 
366  if (version < new Version(4, 1, 0))
367  throw new NotSupportedException("Cannot migrate below version 4.1.0!");
368 
369  if (version < new Version(4, 2, 0))
370  targetMigration = DatabaseType == DatabaseType.Sqlite ? nameof(SLRebuild) : nameof(MSFixCascadingDelete);
371 
372  if (targetMigration == null)
373  {
374  Logger.LogDebug("No down migration required.");
375  return;
376  }
377 
378  string migrationSubstitution;
379  switch (DatabaseType)
380  {
381  case DatabaseType.SqlServer:
382  // already setup
383  migrationSubstitution = null;
384  break;
385  case DatabaseType.MySql:
386  case DatabaseType.MariaDB:
387  migrationSubstitution = "MY{0}";
388  break;
389  case DatabaseType.Sqlite:
390  migrationSubstitution = "SL{0}";
391  break;
392  case DatabaseType.PostgresSql:
393  migrationSubstitution = "PG{0}";
394  break;
395  default:
396  throw new InvalidOperationException($"Invalid DatabaseType: {DatabaseType}");
397  }
398 
399  if (migrationSubstitution != null)
400  targetMigration = String.Format(CultureInfo.InvariantCulture, migrationSubstitution, targetMigration.Substring(2));
401 
402  // even though it clearly implements it in the DatabaseFacade definition this won't work without casting (╯ಠ益ಠ)╯︵ ┻━┻
403  var dbServiceProvider = ((IInfrastructure<IServiceProvider>)Database).Instance;
404  var migrator = dbServiceProvider.GetRequiredService<IMigrator>();
405 
406  Logger.LogInformation("Migrating down to version {0}. Target: {1}", version, targetMigration);
407  try
408  {
409  await migrator.MigrateAsync(targetMigration, cancellationToken).ConfigureAwait(false);
410  }
411  catch (Exception e)
412  {
413  Logger.LogCritical("Failed to migrate! Exception: {0}", e);
414  }
415  }
416 
420  protected abstract void ValidateDatabaseType();
421  }
422 }
IDatabaseCollection< RevisionInformation > RevisionInformations
The RevisionInformations in the IDatabaseContext
IDatabaseCollection< InstanceUser > InstanceUsers
The InstanceUsers in the IDatabaseContext
IDatabaseCollection< ChatChannel > ChatChannels
The ChatChannel in the IDatabaseContext
-
readonly IDatabaseCollection< DualReattachInformation > watchdogReattachInformationsCollection
Backing field for IDatabaseContext.WatchdogReattachInformations.
+
readonly IDatabaseCollection< DualReattachInformation > watchdogReattachInformationsCollection
Backing field for IDatabaseContext.WatchdogReattachInformations.
-
readonly IDatabaseCollection< ChatChannel > chatChannelsCollection
Backing field for IDatabaseContext.ChatChannels.
+
readonly IDatabaseCollection< ChatChannel > chatChannelsCollection
Backing field for IDatabaseContext.ChatChannels.
For initially seeding a database
Fix cascading data deletes for Models.Instances on MSSQL.
IDatabaseCollection< CompileJob > CompileJobs
The CompileJobs in the IDatabaseContext
@@ -119,38 +119,38 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
DatabaseType DatabaseType
The Configuration.DatabaseType to create
IDatabaseCollection< RepositorySettings > RepositorySettings
The Models.RepositorySettings in the IDatabaseContext
-
readonly IDatabaseCollection< Instance > instancesCollection
Backing field for IDatabaseContext.Instances.
+
readonly IDatabaseCollection< Instance > instancesCollection
Backing field for IDatabaseContext.Instances.
-
readonly IDatabaseSeeder databaseSeeder
The IDatabaseSeeder for the DatabaseContext
-
readonly IDatabaseCollection< DreamMakerSettings > dreamMakerSettingsCollection
Backing field for IDatabaseContext.DreamMakerSettings.
+
readonly IDatabaseSeeder databaseSeeder
The IDatabaseSeeder for the DatabaseContext
+
readonly IDatabaseCollection< DreamMakerSettings > dreamMakerSettingsCollection
Backing field for IDatabaseContext.DreamMakerSettings.
bool ResetAdminPassword
If the admin user should be enabled and have it&#39;s password reset
IDatabaseCollection< DreamMakerSettings > DreamMakerSettings
The Models.DreamMakerSettings in the IDatabaseContext
-
readonly IDatabaseCollection< User > usersCollection
Backing field for IDatabaseContext.Users.
-
readonly IDatabaseCollection< RevisionInformation > revisionInformationsCollection
Backing field for IDatabaseContext.RevisionInformations.
+
readonly IDatabaseCollection< User > usersCollection
Backing field for IDatabaseContext.Users.
+
readonly IDatabaseCollection< RevisionInformation > revisionInformationsCollection
Backing field for IDatabaseContext.RevisionInformations.
Represents an Api.Models.Instance in the database
Definition: Instance.cs:8
-
async Task SchemaDowngradeForServerVersion(Version version, CancellationToken cancellationToken)
Attempt to downgrade the schema to the migration used for a given server version ...
+
async Task SchemaDowngradeForServerVersion(Version version, CancellationToken cancellationToken)
Attempt to downgrade the schema to the migration used for a given server version ...
-
DatabaseContext(DbContextOptions dbContextOptions, IOptions< DatabaseConfiguration > databaseConfigurationOptions, IDatabaseSeeder databaseSeeder, ILogger logger)
Construct a DatabaseContext
-
readonly IDatabaseCollection< CompileJob > compileJobsCollection
Backing field for IDatabaseContext.CompileJobs.
+
DatabaseContext(DbContextOptions dbContextOptions, IOptions< DatabaseConfiguration > databaseConfigurationOptions, IDatabaseSeeder databaseSeeder, ILogger logger)
Construct a DatabaseContext
+
readonly IDatabaseCollection< CompileJob > compileJobsCollection
Backing field for IDatabaseContext.CompileJobs.
IDatabaseCollection< DualReattachInformation > WatchdogReattachInformations
The DbSet<TEntity> for DualReattachInformations
Configuration options for the Database.DatabaseContext
DatabaseType
Type of database to user
Definition: DatabaseType.cs:6
-
readonly IDatabaseCollection< InstanceUser > instanceUsersCollection
Backing field for IDatabaseContext.InstanceUsers.
+
readonly IDatabaseCollection< InstanceUser > instanceUsersCollection
Backing field for IDatabaseContext.InstanceUsers.
bool DropDatabase
If the database should be deleted on application startup. Should not be used in production! ...
-
override void OnModelCreating(ModelBuilder modelBuilder)
-
readonly IDatabaseCollection< DreamDaemonSettings > dreamDaemonSettingsCollection
Backing field for IDatabaseContext.DreamDaemonSettings.
-
readonly IDatabaseCollection< ChatBot > chatBotsCollection
Backing field for IDatabaseContext.ChatBots.
-
readonly IDatabaseCollection< RepositorySettings > repositorySettingsCollection
Backing field for IDatabaseContext.RepositorySettings.
+
override void OnModelCreating(ModelBuilder modelBuilder)
+
readonly IDatabaseCollection< DreamDaemonSettings > dreamDaemonSettingsCollection
Backing field for IDatabaseContext.DreamDaemonSettings.
+
readonly IDatabaseCollection< ChatBot > chatBotsCollection
Backing field for IDatabaseContext.ChatBots.
+
readonly IDatabaseCollection< RepositorySettings > repositorySettingsCollection
Backing field for IDatabaseContext.RepositorySettings.
IDatabaseCollection< DreamDaemonSettings > DreamDaemonSettings
The Models.DreamDaemonSettings in the IDatabaseContext
IDatabaseCollection< ReattachInformation > ReattachInformations
The DbSet<TEntity> for ReattachInformations
IDatabaseCollection< Job > Jobs
The Jobs in the IDatabaseContext
-
async Task Initialize(CancellationToken cancellationToken)
Creates and migrates the IDatabaseContext
-
readonly IDatabaseCollection< ReattachInformation > reattachInformationsCollection
Backing field for IDatabaseContext.ReattachInformations.
+
async Task Initialize(CancellationToken cancellationToken)
Creates and migrates the IDatabaseContext
+
readonly IDatabaseCollection< ReattachInformation > reattachInformationsCollection
Backing field for IDatabaseContext.ReattachInformations.
IDatabaseCollection< User > Users
The Users in the IDatabaseContext
-
readonly IDatabaseCollection< Job > jobsCollection
Backing field for IDatabaseContext.Jobs.
+
readonly IDatabaseCollection< Job > jobsCollection
Backing field for IDatabaseContext.Jobs.
diff --git a/_error_code_8cs_source.html b/_error_code_8cs_source.html index 6b6663fc01..eb79eb3ab3 100644 --- a/_error_code_8cs_source.html +++ b/_error_code_8cs_source.html @@ -94,7 +94,7 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
ErrorCode.cs
-Go to the documentation of this file.
1 using System;
2 using System.ComponentModel;
3 
4 namespace Tgstation.Server.Api.Models
5 {
10  public enum ErrorCode : uint
11  {
15  [Description("An internal server error occurred!")]
17 
21  [Description("API Mismatch but no current API version provided!")]
23 
27  [Description("The request body was malformed!")]
29 
33  [Description("An IO error occurred during the operation!")]
34  IOError,
35 
39  [Description("A header validation error occurred!")]
40  BadHeaders,
41 
45  [Description("Cannot generate a bearer token using a bearer token!")]
47 
51  [Description("The operation could not be performed as it would violate database integrity. Please retry the request, making sure to not duplicate field names with existing entities.")]
53 
57  [Description("Functionality is unavailable due to a missing host watchdog component! This should NOT be seen in a production environment!")]
59 
63  [Description("Cannot update to a different tgstation-server suite version.")]
65 
69  [Description("A required GitHub request returned an API error!")]
71 
75  [Description("A server update was requested while another was in progress")]
77 
81  [Description("Can only change the capitalization of a user's name!")]
83 
87  [Description("Cannot change a user's systemIdentifier!")]
89 
93  [Description("A user cannot have both a name and systemIdentifier!")]
95 
99  [Description("A user cannot have both a password and systemIdentifier!")]
101 
105  [Description("The given password is less than the server's configured minimum password length!")]
107 
111  [Description("User names cannot contain the ':' character!")]
113 
117  [Description("User's name is missing or invalid whitespace!")]
119 
123  [Description("Cannot relocate an instance while it is online!")]
125 
129  [Description("The instance cannot be placed at the given path because it is not empty!")]
131 
135  [Description("Cannot detach an instance while it is online!")]
137 
141  [Description("The instance cannot be placed at the given path because it is a child of an existing instance or the server installation directory!")]
143 
147  [Description("The instance cannot be created because the configured limit has been reached!")]
149 
153  [Description("Instance names cannot be whitespace!")]
155 
159  [Description("The request path requires the 'Instance' header to be set!")]
161 
165  [Description("This operation requires POSIX system identities to be implemented. See https://github.com/tgstation/tgstation-server/issues/709 for details.")]
167 
171  [Description("This existing file hash does not match, the file has beeen updated!")]
173 
177  [Description("The directory cannot be deleted because it is not empty!")]
179 
183  [Description("Cannot clone repository with missing origin field!")]
185 
189  [Description("Either both accessUser and accessToken must be set or neither!")]
191 
195  [Description("The repository is busy being cloned!")]
196  RepoCloning,
197 
201  [Description("The repository is busy with another operation!")]
202  RepoBusy,
203 
207  [Description("The repository already exists!")]
208  RepoExists,
209 
213  [Description("The repository does not exist and must be cloned!")]
214  RepoMissing,
215 
219  [Description("Cannot checkoutSha and set reference at the same time!")]
221 
225  [Description("Cannot checkoutSha and updateFromOrigin at the same time!")]
227 
231  [Description("Cannot change the origin of an existing repository, delete and recreate it instead!")]
233 
237  [Description("The same pull request was present more than once in the test merge requests or is already merged!")]
239 
243  [Description("committerName cannot be whitespace!")]
245 
249  [Description("committerEmail cannot be whitespace!")]
251 
255  [Description("Primary and secondary ports cannot be the same!")]
257 
261  [Description("Deprecated error code.")]
262  [Obsolete("With DMAPI-5.0.0, ultrasafe security is now supported.", true)]
264 
268  [Description("One or more of the channels for one or more of the provided chat bots do not have the correct channel data for their provider!")]
270 
274  [Description("A chat bot's connection string cannot be whitespace!")]
276 
280  [Description("A chat bot's name cannot be whitespace!")]
282 
286  [Description("Missing chat bot provider!")]
288 
292  [Description("Missing user ID!")]
294 
298  [Description("Performing this operation would violate the instance's configured chatBotLimit!")]
299  ChatBotMax,
300 
304  [Description("Set amount of chatChannels exceeds the configured channelLimit!")]
306 
310  [Description("Unable to start DirectX installer process! Is the server running with admin privileges?")]
312 
316  [Description("Error downloading specified BYOND version!")]
318 
322  [Description("Could not acquire lock on BYOND installation as none exist!")]
324 
328  [Description("DreamDaemon exited without validating the DMAPI@")]
330 
334  [Description("The DMAPI sent an invalid validation request!")]
336 
340  [Description("The DreamDaemon startup timeout was hit before the DMAPI validated!")]
342 
346  [Description("No .dme configured and could not automatically detect one!")]
348 
352  [Description("Could not load configured .dme!")]
354 
358  [Description("DreamMaker exited with a non-zero exit code!")]
360 
364  [Description("There is already a deployment operation in progress!")]
366 
370  [Description("Could not retrieve DreamDaemon settings from the database!")]
372 
376  [Description("Could not retrieve DreamMaker settings from the database!")]
378 
382  [Description("Could not retrieve Repository settings from the database!")]
384 
388  [Description("Performing this automatic update as a merge would result in conficts. Aborting!")]
390 
394  [Description("Git credentials are required for this operation!")]
396 
400  [Description("The remote is requesting authentication, but is not allowing credentials to be received!")]
402 
406  [Description("This git operation requires the repository HEAD to currently be on a tracked reference!")]
408 
412  [Description("The watchdog is already running!")]
414 
418  [Description("Cannot launch active compile job as it is missing or corrupted!")]
420 
424  [Description("DreamDaemon failed to start!")]
426 
430  [Description("DreamDaemon failed to start within the configured timeout!")]
432 
436  [Description("Test merging with the current remote is not supported!")]
438 
442  [Description("The value set for checkoutSha or reference should be in the other field!")]
444 
448  [Description("A merge conflict occurred while performing the operation!")]
450 
454  [Description("The repository's current reference is unsuitable for this operation as it does not track a remote reference!")]
456 
460  [Description("Encountered merge conflicts while test merging one or more pull requests!")]
462 
466  [Description("The new instance's path is not under a white-listed path.")]
468 
472  [Description("Cannot set both softShutdown and softReboot at once!")]
474 
478  [Description("Cannot start DreamDaemon headless with the BYOND pager running!")]
480 
484  [Description("Could not bind to requested DreamDaemon port! Is there another service running on that port?")]
486 
490  [Description("The deployment succeeded but one or more notification events failed!")]
492 
496  [Description("Cannot restart the watchdog as it is not running!")]
498  }
499 }
Either Repository.CheckoutSha or Repository.Reference was in one when it should have been the other...
+Go to the documentation of this file.
1 using System;
2 using System.ComponentModel;
3 
4 namespace Tgstation.Server.Api.Models
5 {
10  public enum ErrorCode : uint
11  {
15  [Description("An internal server error occurred!")]
17 
21  [Description("API Mismatch but no current API version provided!")]
23 
27  [Description("The request body was malformed!")]
29 
33  [Description("An IO error occurred during the operation!")]
34  IOError,
35 
39  [Description("A header validation error occurred!")]
40  BadHeaders,
41 
45  [Description("Cannot generate a bearer token using a bearer token!")]
47 
51  [Description("The operation could not be performed as it would violate database integrity. Please retry the request, making sure to not duplicate field names with existing entities.")]
53 
57  [Description("Functionality is unavailable due to a missing host watchdog component! This should NOT be seen in a production environment!")]
59 
63  [Description("Cannot update to a different tgstation-server suite version.")]
65 
69  [Description("A required GitHub request returned an API error!")]
71 
75  [Description("A server update was requested while another was in progress")]
77 
81  [Description("Can only change the capitalization of a user's name!")]
83 
87  [Description("Cannot change a user's systemIdentifier!")]
89 
93  [Description("A user cannot have both a name and systemIdentifier!")]
95 
99  [Description("A user cannot have both a password and systemIdentifier!")]
101 
105  [Description("The given password is less than the server's configured minimum password length!")]
107 
111  [Description("User names cannot contain the ':' character!")]
113 
117  [Description("User's name is missing or invalid whitespace!")]
119 
123  [Description("Cannot relocate an instance while it is online!")]
125 
129  [Description("The instance cannot be placed at the given path because it is not empty!")]
131 
135  [Description("Cannot detach an instance while it is online!")]
137 
141  [Description("The instance cannot be placed at the given path because it is a child of an existing instance or the server installation directory!")]
143 
147  [Description("The instance cannot be created because the configured limit has been reached!")]
149 
153  [Description("Instance names cannot be whitespace!")]
155 
159  [Description("The request path requires the 'Instance' header to be set!")]
161 
165  [Description("This operation requires POSIX system identities to be implemented. See https://github.com/tgstation/tgstation-server/issues/709 for details.")]
167 
171  [Description("This existing file hash does not match, the file has beeen updated!")]
173 
177  [Description("The directory cannot be deleted because it is not empty!")]
179 
183  [Description("Cannot clone repository with missing origin field!")]
185 
189  [Description("Either both accessUser and accessToken must be set or neither!")]
191 
195  [Description("The repository is busy being cloned!")]
196  RepoCloning,
197 
201  [Description("The repository is busy with another operation!")]
202  RepoBusy,
203 
207  [Description("The repository already exists!")]
208  RepoExists,
209 
213  [Description("The repository does not exist and must be cloned!")]
214  RepoMissing,
215 
219  [Description("Cannot checkoutSha and set reference at the same time!")]
221 
225  [Description("Cannot checkoutSha and updateFromOrigin at the same time!")]
227 
231  [Description("Cannot change the origin of an existing repository, delete and recreate it instead!")]
233 
237  [Description("The same pull request was present more than once in the test merge requests or is already merged!")]
239 
243  [Description("committerName cannot be whitespace!")]
245 
249  [Description("committerEmail cannot be whitespace!")]
251 
255  [Description("Primary and secondary ports cannot be the same!")]
257 
261  [Description("Deprecated error code.")]
262  [Obsolete("With DMAPI-5.0.0, ultrasafe security is now supported.", true)]
264 
268  [Description("One or more of the channels for one or more of the provided chat bots do not have the correct channel data for their provider!")]
270 
274  [Description("A chat bot's connection string cannot be whitespace!")]
276 
280  [Description("A chat bot's name cannot be whitespace!")]
282 
286  [Description("Missing chat bot provider!")]
288 
292  [Description("Missing user ID!")]
294 
298  [Description("Performing this operation would violate the instance's configured chatBotLimit!")]
299  ChatBotMax,
300 
304  [Description("Set amount of chatChannels exceeds the configured channelLimit!")]
306 
310  [Description("Unable to start DirectX installer process! Is the server running with admin privileges?")]
312 
316  [Description("Error downloading specified BYOND version!")]
318 
322  [Description("Could not acquire lock on BYOND installation as none exist!")]
324 
328  [Description("DreamDaemon exited without validating the DMAPI!")]
330 
334  [Description("The DMAPI sent an invalid validation request!")]
336 
340  [Description("The DreamDaemon startup timeout was hit before the DMAPI validated!")]
342 
346  [Description("No .dme configured and could not automatically detect one!")]
348 
352  [Description("Could not load configured .dme!")]
354 
358  [Description("DreamMaker exited with a non-zero exit code!")]
360 
364  [Description("There is already a deployment operation in progress!")]
366 
370  [Description("Could not retrieve DreamDaemon settings from the database!")]
372 
376  [Description("Could not retrieve DreamMaker settings from the database!")]
378 
382  [Description("Could not retrieve Repository settings from the database!")]
384 
388  [Description("Performing this automatic update as a merge would result in conficts. Aborting!")]
390 
394  [Description("Git credentials are required for this operation!")]
396 
400  [Description("The remote is requesting authentication, but is not allowing credentials to be received!")]
402 
406  [Description("This git operation requires the repository HEAD to currently be on a tracked reference!")]
408 
412  [Description("The watchdog is already running!")]
414 
418  [Description("Cannot launch active compile job as it is missing or corrupted!")]
420 
424  [Description("DreamDaemon failed to start!")]
426 
430  [Description("DreamDaemon failed to start within the configured timeout!")]
432 
436  [Description("Test merging with the current remote is not supported!")]
438 
442  [Description("The value set for checkoutSha or reference should be in the other field!")]
444 
448  [Description("A merge conflict occurred while performing the operation!")]
450 
454  [Description("The repository's current reference is unsuitable for this operation as it does not track a remote reference!")]
456 
460  [Description("Encountered merge conflicts while test merging one or more pull requests!")]
462 
466  [Description("The new instance's path is not under a white-listed path.")]
468 
472  [Description("Cannot set both softShutdown and softReboot at once!")]
474 
478  [Description("Cannot start DreamDaemon headless with the BYOND pager running!")]
480 
484  [Description("Could not bind to requested DreamDaemon port! Is there another service running on that port?")]
486 
490  [Description("The deployment succeeded but one or more notification events failed!")]
492 
496  [Description("Cannot restart the watchdog as it is not running!")]
498  }
499 }
Either Repository.CheckoutSha or Repository.Reference was in one when it should have been the other...
The remote returned an invalid authentication request.
Attempted to create a User with a or whitespace Internal.User.Name.
Attempted to create a User with a &#39;:&#39; in the Internal.User.Name.
diff --git a/_my_sql_database_context_8cs_source.html b/_my_sql_database_context_8cs_source.html index d6aa023881..839efb3b89 100644 --- a/_my_sql_database_context_8cs_source.html +++ b/_my_sql_database_context_8cs_source.html @@ -94,12 +94,12 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
MySqlDatabaseContext.cs
-Go to the documentation of this file.
1 using Microsoft.EntityFrameworkCore;
2 using Microsoft.Extensions.Logging;
3 using Microsoft.Extensions.Options;
4 using MySql.Data.MySqlClient;
5 using Pomelo.EntityFrameworkCore.MySql.Infrastructure;
6 using System;
8 
9 namespace Tgstation.Server.Host.Database
10 {
15  {
23  public MySqlDatabaseContext(DbContextOptions<MySqlDatabaseContext> dbContextOptions, IOptions<DatabaseConfiguration> databaseConfiguration, IDatabaseSeeder databaseSeeder, ILogger<MySqlDatabaseContext> logger) : base(dbContextOptions, databaseConfiguration, databaseSeeder, logger)
24  { }
25 
27  protected override void OnConfiguring(DbContextOptionsBuilder options)
28  {
29  base.OnConfiguring(options);
30  var stringDeconstructor = new MySqlConnectionStringBuilder
31  {
32  ConnectionString = DatabaseConfiguration.ConnectionString
33  };
34  if (stringDeconstructor.Server == "localhost")
35  Logger.LogWarning("MariaDB/MySQL server address is set to 'localhost'! If there are connection issues, try setting it to '127.0.0.1'!");
36  options.UseMySql(
38  mySqlOptions =>
39  {
40  mySqlOptions.EnableRetryOnFailure();
41 
42  if (!String.IsNullOrEmpty(DatabaseConfiguration.MySqlServerVersion))
43  mySqlOptions.ServerVersion(
44  Version.Parse(DatabaseConfiguration.MySqlServerVersion),
45  DatabaseConfiguration.DatabaseType == DatabaseType.MariaDB
46  ? ServerType.MariaDb
47  : ServerType.MySql);
48  });
49  }
50 
52  protected override void ValidateDatabaseType()
53  {
54  if (DatabaseType != DatabaseType.MariaDB && DatabaseType != DatabaseType.MySql)
55  throw new InvalidOperationException("Invalid DatabaseType for MySqlDatabaseContext!");
56  }
57  }
58 }
string ConnectionString
The connection string for the database
-
override void ValidateDatabaseType()
Ensure the DatabaseType is correct for the DatabaseContext.
-
MySqlDatabaseContext(DbContextOptions< MySqlDatabaseContext > dbContextOptions, IOptions< DatabaseConfiguration > databaseConfiguration, IDatabaseSeeder databaseSeeder, ILogger< MySqlDatabaseContext > logger)
Construct a MySqlDatabaseContext
+Go to the documentation of this file.
1 using Microsoft.EntityFrameworkCore;
2 using Microsoft.Extensions.Logging;
3 using Microsoft.Extensions.Options;
4 using MySql.Data.MySqlClient;
5 using Pomelo.EntityFrameworkCore.MySql.Infrastructure;
6 using System;
8 
9 namespace Tgstation.Server.Host.Database
10 {
15  {
17  protected override DeleteBehavior RevInfoCompileJobDeleteBehavior => DeleteBehavior.Cascade;
18 
26  public MySqlDatabaseContext(DbContextOptions<MySqlDatabaseContext> dbContextOptions, IOptions<DatabaseConfiguration> databaseConfiguration, IDatabaseSeeder databaseSeeder, ILogger<MySqlDatabaseContext> logger) : base(dbContextOptions, databaseConfiguration, databaseSeeder, logger)
27  { }
28 
30  protected override void OnConfiguring(DbContextOptionsBuilder options)
31  {
32  base.OnConfiguring(options);
33  var stringDeconstructor = new MySqlConnectionStringBuilder
34  {
35  ConnectionString = DatabaseConfiguration.ConnectionString
36  };
37  if (stringDeconstructor.Server == "localhost")
38  Logger.LogWarning("MariaDB/MySQL server address is set to 'localhost'! If there are connection issues, try setting it to '127.0.0.1'!");
39  options.UseMySql(
41  mySqlOptions =>
42  {
43  mySqlOptions.EnableRetryOnFailure();
44 
45  if (!String.IsNullOrEmpty(DatabaseConfiguration.ServerVersion))
46  mySqlOptions.ServerVersion(
47  Version.Parse(DatabaseConfiguration.ServerVersion),
48  DatabaseConfiguration.DatabaseType == DatabaseType.MariaDB
49  ? ServerType.MariaDb
50  : ServerType.MySql);
51  });
52  }
53 
55  protected override void ValidateDatabaseType()
56  {
57  if (DatabaseType != DatabaseType.MariaDB && DatabaseType != DatabaseType.MySql)
58  throw new InvalidOperationException("Invalid DatabaseType for MySqlDatabaseContext!");
59  }
60  }
61 }
string ConnectionString
The connection string for the database
+
override void ValidateDatabaseType()
Ensure the DatabaseType is correct for the DatabaseContext.
+
MySqlDatabaseContext(DbContextOptions< MySqlDatabaseContext > dbContextOptions, IOptions< DatabaseConfiguration > databaseConfiguration, IDatabaseSeeder databaseSeeder, ILogger< MySqlDatabaseContext > logger)
Construct a MySqlDatabaseContext
-
override void OnConfiguring(DbContextOptionsBuilder options)
+
override void OnConfiguring(DbContextOptionsBuilder options)
For initially seeding a database
Backend abstract implementation of IDatabaseContext
diff --git a/_my_sql_database_context_model_snapshot_8cs_source.html b/_my_sql_database_context_model_snapshot_8cs_source.html index c37a49761d..1703e6917f 100644 --- a/_my_sql_database_context_model_snapshot_8cs_source.html +++ b/_my_sql_database_context_model_snapshot_8cs_source.html @@ -94,7 +94,7 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
MySqlDatabaseContextModelSnapshot.cs
-Go to the documentation of this file.
1 // <auto-generated />
2 using System;
3 using Microsoft.EntityFrameworkCore;
4 using Microsoft.EntityFrameworkCore.Infrastructure;
5 
6 namespace Tgstation.Server.Host.Database.Migrations
7 {
8  [DbContext(typeof(MySqlDatabaseContext))]
10  {
11  protected override void BuildModel(ModelBuilder modelBuilder)
12  {
13 #pragma warning disable 612, 618
14  modelBuilder
15  .HasAnnotation("ProductVersion", "3.1.3")
16  .HasAnnotation("Relational:MaxIdentifierLength", 64);
17 
18  modelBuilder.Entity("Tgstation.Server.Host.Models.ChatBot", b =>
19  {
20  b.Property<long>("Id")
21  .ValueGeneratedOnAdd()
22  .HasColumnType("bigint");
23 
24  b.Property<ushort?>("ChannelLimit")
25  .IsRequired()
26  .HasColumnType("smallint unsigned");
27 
28  b.Property<string>("ConnectionString")
29  .IsRequired()
30  .HasColumnType("longtext CHARACTER SET utf8mb4")
31  .HasMaxLength(10000);
32 
33  b.Property<bool?>("Enabled")
34  .HasColumnType("tinyint(1)");
35 
36  b.Property<long>("InstanceId")
37  .HasColumnType("bigint");
38 
39  b.Property<string>("Name")
40  .IsRequired()
41  .HasColumnType("varchar(100) CHARACTER SET utf8mb4")
42  .HasMaxLength(100);
43 
44  b.Property<int>("Provider")
45  .HasColumnType("int");
46 
47  b.Property<uint?>("ReconnectionInterval")
48  .IsRequired()
49  .HasColumnType("int unsigned");
50 
51  b.HasKey("Id");
52 
53  b.HasIndex("InstanceId", "Name")
54  .IsUnique();
55 
56  b.ToTable("ChatBots");
57  });
58 
59  modelBuilder.Entity("Tgstation.Server.Host.Models.ChatChannel", b =>
60  {
61  b.Property<long>("Id")
62  .ValueGeneratedOnAdd()
63  .HasColumnType("bigint");
64 
65  b.Property<long>("ChatSettingsId")
66  .HasColumnType("bigint");
67 
68  b.Property<ulong?>("DiscordChannelId")
69  .HasColumnType("bigint unsigned");
70 
71  b.Property<string>("IrcChannel")
72  .HasColumnType("varchar(100) CHARACTER SET utf8mb4")
73  .HasMaxLength(100);
74 
75  b.Property<bool?>("IsAdminChannel")
76  .IsRequired()
77  .HasColumnType("tinyint(1)");
78 
79  b.Property<bool?>("IsUpdatesChannel")
80  .IsRequired()
81  .HasColumnType("tinyint(1)");
82 
83  b.Property<bool?>("IsWatchdogChannel")
84  .IsRequired()
85  .HasColumnType("tinyint(1)");
86 
87  b.Property<string>("Tag")
88  .HasColumnType("longtext CHARACTER SET utf8mb4")
89  .HasMaxLength(10000);
90 
91  b.HasKey("Id");
92 
93  b.HasIndex("ChatSettingsId", "DiscordChannelId")
94  .IsUnique();
95 
96  b.HasIndex("ChatSettingsId", "IrcChannel")
97  .IsUnique();
98 
99  b.ToTable("ChatChannels");
100  });
101 
102  modelBuilder.Entity("Tgstation.Server.Host.Models.CompileJob", b =>
103  {
104  b.Property<long>("Id")
105  .ValueGeneratedOnAdd()
106  .HasColumnType("bigint");
107 
108  b.Property<string>("ByondVersion")
109  .IsRequired()
110  .HasColumnType("longtext CHARACTER SET utf8mb4");
111 
112  b.Property<int?>("DMApiMajorVersion")
113  .HasColumnType("int");
114 
115  b.Property<int?>("DMApiMinorVersion")
116  .HasColumnType("int");
117 
118  b.Property<int?>("DMApiPatchVersion")
119  .HasColumnType("int");
120 
121  b.Property<Guid?>("DirectoryName")
122  .IsRequired()
123  .HasColumnType("char(36)");
124 
125  b.Property<string>("DmeName")
126  .IsRequired()
127  .HasColumnType("longtext CHARACTER SET utf8mb4");
128 
129  b.Property<long>("JobId")
130  .HasColumnType("bigint");
131 
132  b.Property<int>("MinimumSecurityLevel")
133  .HasColumnType("int");
134 
135  b.Property<string>("Output")
136  .IsRequired()
137  .HasColumnType("longtext CHARACTER SET utf8mb4");
138 
139  b.Property<long>("RevisionInformationId")
140  .HasColumnType("bigint");
141 
142  b.HasKey("Id");
143 
144  b.HasIndex("DirectoryName");
145 
146  b.HasIndex("JobId")
147  .IsUnique();
148 
149  b.HasIndex("RevisionInformationId");
150 
151  b.ToTable("CompileJobs");
152  });
153 
154  modelBuilder.Entity("Tgstation.Server.Host.Models.DreamDaemonSettings", b =>
155  {
156  b.Property<long>("Id")
157  .ValueGeneratedOnAdd()
158  .HasColumnType("bigint");
159 
160  b.Property<bool?>("AllowWebClient")
161  .IsRequired()
162  .HasColumnType("tinyint(1)");
163 
164  b.Property<bool?>("AutoStart")
165  .IsRequired()
166  .HasColumnType("tinyint(1)");
167 
168  b.Property<uint?>("HeartbeatSeconds")
169  .IsRequired()
170  .HasColumnType("int unsigned");
171 
172  b.Property<long>("InstanceId")
173  .HasColumnType("bigint");
174 
175  b.Property<ushort?>("PrimaryPort")
176  .IsRequired()
177  .HasColumnType("smallint unsigned");
178 
179  b.Property<ushort?>("SecondaryPort")
180  .IsRequired()
181  .HasColumnType("smallint unsigned");
182 
183  b.Property<int>("SecurityLevel")
184  .HasColumnType("int");
185 
186  b.Property<uint?>("StartupTimeout")
187  .IsRequired()
188  .HasColumnType("int unsigned");
189 
190  b.HasKey("Id");
191 
192  b.HasIndex("InstanceId")
193  .IsUnique();
194 
195  b.ToTable("DreamDaemonSettings");
196  });
197 
198  modelBuilder.Entity("Tgstation.Server.Host.Models.DreamMakerSettings", b =>
199  {
200  b.Property<long>("Id")
201  .ValueGeneratedOnAdd()
202  .HasColumnType("bigint");
203 
204  b.Property<ushort?>("ApiValidationPort")
205  .IsRequired()
206  .HasColumnType("smallint unsigned");
207 
208  b.Property<int>("ApiValidationSecurityLevel")
209  .HasColumnType("int");
210 
211  b.Property<long>("InstanceId")
212  .HasColumnType("bigint");
213 
214  b.Property<string>("ProjectName")
215  .HasColumnType("longtext CHARACTER SET utf8mb4")
216  .HasMaxLength(10000);
217 
218  b.HasKey("Id");
219 
220  b.HasIndex("InstanceId")
221  .IsUnique();
222 
223  b.ToTable("DreamMakerSettings");
224  });
225 
226  modelBuilder.Entity("Tgstation.Server.Host.Models.Instance", b =>
227  {
228  b.Property<long>("Id")
229  .ValueGeneratedOnAdd()
230  .HasColumnType("bigint");
231 
232  b.Property<uint?>("AutoUpdateInterval")
233  .IsRequired()
234  .HasColumnType("int unsigned");
235 
236  b.Property<ushort?>("ChatBotLimit")
237  .IsRequired()
238  .HasColumnType("smallint unsigned");
239 
240  b.Property<int>("ConfigurationType")
241  .HasColumnType("int");
242 
243  b.Property<string>("Name")
244  .IsRequired()
245  .HasColumnType("longtext CHARACTER SET utf8mb4")
246  .HasMaxLength(10000);
247 
248  b.Property<bool?>("Online")
249  .IsRequired()
250  .HasColumnType("tinyint(1)");
251 
252  b.Property<string>("Path")
253  .IsRequired()
254  .HasColumnType("varchar(255) CHARACTER SET utf8mb4");
255 
256  b.HasKey("Id");
257 
258  b.HasIndex("Path")
259  .IsUnique();
260 
261  b.ToTable("Instances");
262  });
263 
264  modelBuilder.Entity("Tgstation.Server.Host.Models.InstanceUser", b =>
265  {
266  b.Property<long>("Id")
267  .ValueGeneratedOnAdd()
268  .HasColumnType("bigint");
269 
270  b.Property<ulong>("ByondRights")
271  .HasColumnType("bigint unsigned");
272 
273  b.Property<ulong>("ChatBotRights")
274  .HasColumnType("bigint unsigned");
275 
276  b.Property<ulong>("ConfigurationRights")
277  .HasColumnType("bigint unsigned");
278 
279  b.Property<ulong>("DreamDaemonRights")
280  .HasColumnType("bigint unsigned");
281 
282  b.Property<ulong>("DreamMakerRights")
283  .HasColumnType("bigint unsigned");
284 
285  b.Property<long>("InstanceId")
286  .HasColumnType("bigint");
287 
288  b.Property<ulong>("InstanceUserRights")
289  .HasColumnType("bigint unsigned");
290 
291  b.Property<ulong>("RepositoryRights")
292  .HasColumnType("bigint unsigned");
293 
294  b.Property<long?>("UserId")
295  .IsRequired()
296  .HasColumnType("bigint");
297 
298  b.HasKey("Id");
299 
300  b.HasIndex("InstanceId");
301 
302  b.HasIndex("UserId", "InstanceId")
303  .IsUnique();
304 
305  b.ToTable("InstanceUsers");
306  });
307 
308  modelBuilder.Entity("Tgstation.Server.Host.Models.Job", b =>
309  {
310  b.Property<long>("Id")
311  .ValueGeneratedOnAdd()
312  .HasColumnType("bigint");
313 
314  b.Property<ulong?>("CancelRight")
315  .HasColumnType("bigint unsigned");
316 
317  b.Property<ulong?>("CancelRightsType")
318  .HasColumnType("bigint unsigned");
319 
320  b.Property<bool?>("Cancelled")
321  .IsRequired()
322  .HasColumnType("tinyint(1)");
323 
324  b.Property<long?>("CancelledById")
325  .HasColumnType("bigint");
326 
327  b.Property<string>("Description")
328  .IsRequired()
329  .HasColumnType("longtext CHARACTER SET utf8mb4");
330 
331  b.Property<uint?>("ErrorCode")
332  .HasColumnType("int unsigned");
333 
334  b.Property<string>("ExceptionDetails")
335  .HasColumnType("longtext CHARACTER SET utf8mb4");
336 
337  b.Property<long>("InstanceId")
338  .HasColumnType("bigint");
339 
340  b.Property<DateTimeOffset?>("StartedAt")
341  .IsRequired()
342  .HasColumnType("datetime(6)");
343 
344  b.Property<long>("StartedById")
345  .HasColumnType("bigint");
346 
347  b.Property<DateTimeOffset?>("StoppedAt")
348  .HasColumnType("datetime(6)");
349 
350  b.HasKey("Id");
351 
352  b.HasIndex("CancelledById");
353 
354  b.HasIndex("InstanceId");
355 
356  b.HasIndex("StartedById");
357 
358  b.ToTable("Jobs");
359  });
360 
361  modelBuilder.Entity("Tgstation.Server.Host.Models.ReattachInformation", b =>
362  {
363  b.Property<long>("Id")
364  .ValueGeneratedOnAdd()
365  .HasColumnType("bigint");
366 
367  b.Property<string>("AccessIdentifier")
368  .IsRequired()
369  .HasColumnType("longtext CHARACTER SET utf8mb4");
370 
371  b.Property<long>("CompileJobId")
372  .HasColumnType("bigint");
373 
374  b.Property<bool>("IsPrimary")
375  .HasColumnType("tinyint(1)");
376 
377  b.Property<int>("LaunchSecurityLevel")
378  .HasColumnType("int");
379 
380  b.Property<ushort>("Port")
381  .HasColumnType("smallint unsigned");
382 
383  b.Property<int>("ProcessId")
384  .HasColumnType("int");
385 
386  b.Property<int>("RebootState")
387  .HasColumnType("int");
388 
389  b.HasKey("Id");
390 
391  b.HasIndex("CompileJobId");
392 
393  b.ToTable("ReattachInformations");
394  });
395 
396  modelBuilder.Entity("Tgstation.Server.Host.Models.RepositorySettings", b =>
397  {
398  b.Property<long>("Id")
399  .ValueGeneratedOnAdd()
400  .HasColumnType("bigint");
401 
402  b.Property<string>("AccessToken")
403  .HasColumnType("longtext CHARACTER SET utf8mb4")
404  .HasMaxLength(10000);
405 
406  b.Property<string>("AccessUser")
407  .HasColumnType("longtext CHARACTER SET utf8mb4")
408  .HasMaxLength(10000);
409 
410  b.Property<bool?>("AutoUpdatesKeepTestMerges")
411  .IsRequired()
412  .HasColumnType("tinyint(1)");
413 
414  b.Property<bool?>("AutoUpdatesSynchronize")
415  .IsRequired()
416  .HasColumnType("tinyint(1)");
417 
418  b.Property<string>("CommitterEmail")
419  .IsRequired()
420  .HasColumnType("longtext CHARACTER SET utf8mb4")
421  .HasMaxLength(10000);
422 
423  b.Property<string>("CommitterName")
424  .IsRequired()
425  .HasColumnType("longtext CHARACTER SET utf8mb4")
426  .HasMaxLength(10000);
427 
428  b.Property<long>("InstanceId")
429  .HasColumnType("bigint");
430 
431  b.Property<bool?>("PostTestMergeComment")
432  .IsRequired()
433  .HasColumnType("tinyint(1)");
434 
435  b.Property<bool?>("PushTestMergeCommits")
436  .IsRequired()
437  .HasColumnType("tinyint(1)");
438 
439  b.Property<bool?>("ShowTestMergeCommitters")
440  .IsRequired()
441  .HasColumnType("tinyint(1)");
442 
443  b.HasKey("Id");
444 
445  b.HasIndex("InstanceId")
446  .IsUnique();
447 
448  b.ToTable("RepositorySettings");
449  });
450 
451  modelBuilder.Entity("Tgstation.Server.Host.Models.RevInfoTestMerge", b =>
452  {
453  b.Property<long>("Id")
454  .ValueGeneratedOnAdd()
455  .HasColumnType("bigint");
456 
457  b.Property<long>("RevisionInformationId")
458  .HasColumnType("bigint");
459 
460  b.Property<long>("TestMergeId")
461  .HasColumnType("bigint");
462 
463  b.HasKey("Id");
464 
465  b.HasIndex("RevisionInformationId");
466 
467  b.HasIndex("TestMergeId");
468 
469  b.ToTable("RevInfoTestMerges");
470  });
471 
472  modelBuilder.Entity("Tgstation.Server.Host.Models.RevisionInformation", b =>
473  {
474  b.Property<long>("Id")
475  .ValueGeneratedOnAdd()
476  .HasColumnType("bigint");
477 
478  b.Property<string>("CommitSha")
479  .IsRequired()
480  .HasColumnType("varchar(40) CHARACTER SET utf8mb4")
481  .HasMaxLength(40);
482 
483  b.Property<long>("InstanceId")
484  .HasColumnType("bigint");
485 
486  b.Property<string>("OriginCommitSha")
487  .IsRequired()
488  .HasColumnType("varchar(40) CHARACTER SET utf8mb4")
489  .HasMaxLength(40);
490 
491  b.HasKey("Id");
492 
493  b.HasIndex("InstanceId", "CommitSha")
494  .IsUnique();
495 
496  b.ToTable("RevisionInformations");
497  });
498 
499  modelBuilder.Entity("Tgstation.Server.Host.Models.TestMerge", b =>
500  {
501  b.Property<long>("Id")
502  .ValueGeneratedOnAdd()
503  .HasColumnType("bigint");
504 
505  b.Property<string>("Author")
506  .IsRequired()
507  .HasColumnType("longtext CHARACTER SET utf8mb4");
508 
509  b.Property<string>("BodyAtMerge")
510  .IsRequired()
511  .HasColumnType("longtext CHARACTER SET utf8mb4");
512 
513  b.Property<string>("Comment")
514  .HasColumnType("longtext CHARACTER SET utf8mb4")
515  .HasMaxLength(10000);
516 
517  b.Property<DateTimeOffset>("MergedAt")
518  .HasColumnType("datetime(6)");
519 
520  b.Property<long>("MergedById")
521  .HasColumnType("bigint");
522 
523  b.Property<int>("Number")
524  .HasColumnType("int");
525 
526  b.Property<long?>("PrimaryRevisionInformationId")
527  .IsRequired()
528  .HasColumnType("bigint");
529 
530  b.Property<string>("PullRequestRevision")
531  .IsRequired()
532  .HasColumnType("varchar(40) CHARACTER SET utf8mb4")
533  .HasMaxLength(40);
534 
535  b.Property<string>("TitleAtMerge")
536  .IsRequired()
537  .HasColumnType("longtext CHARACTER SET utf8mb4");
538 
539  b.Property<string>("Url")
540  .IsRequired()
541  .HasColumnType("longtext CHARACTER SET utf8mb4");
542 
543  b.HasKey("Id");
544 
545  b.HasIndex("MergedById");
546 
547  b.HasIndex("PrimaryRevisionInformationId")
548  .IsUnique();
549 
550  b.ToTable("TestMerges");
551  });
552 
553  modelBuilder.Entity("Tgstation.Server.Host.Models.User", b =>
554  {
555  b.Property<long?>("Id")
556  .ValueGeneratedOnAdd()
557  .HasColumnType("bigint");
558 
559  b.Property<ulong>("AdministrationRights")
560  .HasColumnType("bigint unsigned");
561 
562  b.Property<string>("CanonicalName")
563  .IsRequired()
564  .HasColumnType("varchar(255) CHARACTER SET utf8mb4");
565 
566  b.Property<DateTimeOffset?>("CreatedAt")
567  .IsRequired()
568  .HasColumnType("datetime(6)");
569 
570  b.Property<long?>("CreatedById")
571  .HasColumnType("bigint");
572 
573  b.Property<bool?>("Enabled")
574  .IsRequired()
575  .HasColumnType("tinyint(1)");
576 
577  b.Property<ulong>("InstanceManagerRights")
578  .HasColumnType("bigint unsigned");
579 
580  b.Property<DateTimeOffset?>("LastPasswordUpdate")
581  .HasColumnType("datetime(6)");
582 
583  b.Property<string>("Name")
584  .IsRequired()
585  .HasColumnType("longtext CHARACTER SET utf8mb4")
586  .HasMaxLength(10000);
587 
588  b.Property<string>("PasswordHash")
589  .HasColumnType("longtext CHARACTER SET utf8mb4");
590 
591  b.Property<string>("SystemIdentifier")
592  .HasColumnType("varchar(255) CHARACTER SET utf8mb4");
593 
594  b.HasKey("Id");
595 
596  b.HasIndex("CanonicalName")
597  .IsUnique();
598 
599  b.HasIndex("CreatedById");
600 
601  b.HasIndex("SystemIdentifier")
602  .IsUnique();
603 
604  b.ToTable("Users");
605  });
606 
607  modelBuilder.Entity("Tgstation.Server.Host.Models.WatchdogReattachInformation", b =>
608  {
609  b.Property<long>("Id")
610  .ValueGeneratedOnAdd()
611  .HasColumnType("bigint");
612 
613  b.Property<long?>("AlphaId")
614  .HasColumnType("bigint");
615 
616  b.Property<bool>("AlphaIsActive")
617  .HasColumnType("tinyint(1)");
618 
619  b.Property<long?>("BravoId")
620  .HasColumnType("bigint");
621 
622  b.Property<long>("InstanceId")
623  .HasColumnType("bigint");
624 
625  b.HasKey("Id");
626 
627  b.HasIndex("AlphaId");
628 
629  b.HasIndex("BravoId");
630 
631  b.HasIndex("InstanceId")
632  .IsUnique();
633 
634  b.ToTable("WatchdogReattachInformations");
635  });
636 
637  modelBuilder.Entity("Tgstation.Server.Host.Models.ChatBot", b =>
638  {
639  b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
640  .WithMany("ChatSettings")
641  .HasForeignKey("InstanceId")
642  .OnDelete(DeleteBehavior.Cascade)
643  .IsRequired();
644  });
645 
646  modelBuilder.Entity("Tgstation.Server.Host.Models.ChatChannel", b =>
647  {
648  b.HasOne("Tgstation.Server.Host.Models.ChatBot", "ChatSettings")
649  .WithMany("Channels")
650  .HasForeignKey("ChatSettingsId")
651  .OnDelete(DeleteBehavior.Cascade)
652  .IsRequired();
653  });
654 
655  modelBuilder.Entity("Tgstation.Server.Host.Models.CompileJob", b =>
656  {
657  b.HasOne("Tgstation.Server.Host.Models.Job", "Job")
658  .WithOne()
659  .HasForeignKey("Tgstation.Server.Host.Models.CompileJob", "JobId")
660  .OnDelete(DeleteBehavior.Cascade)
661  .IsRequired();
662 
663  b.HasOne("Tgstation.Server.Host.Models.RevisionInformation", "RevisionInformation")
664  .WithMany("CompileJobs")
665  .HasForeignKey("RevisionInformationId")
666  .OnDelete(DeleteBehavior.ClientNoAction)
667  .IsRequired();
668  });
669 
670  modelBuilder.Entity("Tgstation.Server.Host.Models.DreamDaemonSettings", b =>
671  {
672  b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
673  .WithOne("DreamDaemonSettings")
674  .HasForeignKey("Tgstation.Server.Host.Models.DreamDaemonSettings", "InstanceId")
675  .OnDelete(DeleteBehavior.Cascade)
676  .IsRequired();
677  });
678 
679  modelBuilder.Entity("Tgstation.Server.Host.Models.DreamMakerSettings", b =>
680  {
681  b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
682  .WithOne("DreamMakerSettings")
683  .HasForeignKey("Tgstation.Server.Host.Models.DreamMakerSettings", "InstanceId")
684  .OnDelete(DeleteBehavior.Cascade)
685  .IsRequired();
686  });
687 
688  modelBuilder.Entity("Tgstation.Server.Host.Models.InstanceUser", b =>
689  {
690  b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
691  .WithMany("InstanceUsers")
692  .HasForeignKey("InstanceId")
693  .OnDelete(DeleteBehavior.Cascade)
694  .IsRequired();
695 
696  b.HasOne("Tgstation.Server.Host.Models.User", null)
697  .WithMany("InstanceUsers")
698  .HasForeignKey("UserId")
699  .OnDelete(DeleteBehavior.Cascade)
700  .IsRequired();
701  });
702 
703  modelBuilder.Entity("Tgstation.Server.Host.Models.Job", b =>
704  {
705  b.HasOne("Tgstation.Server.Host.Models.User", "CancelledBy")
706  .WithMany()
707  .HasForeignKey("CancelledById");
708 
709  b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
710  .WithMany("Jobs")
711  .HasForeignKey("InstanceId")
712  .OnDelete(DeleteBehavior.Cascade)
713  .IsRequired();
714 
715  b.HasOne("Tgstation.Server.Host.Models.User", "StartedBy")
716  .WithMany()
717  .HasForeignKey("StartedById")
718  .OnDelete(DeleteBehavior.Cascade)
719  .IsRequired();
720  });
721 
722  modelBuilder.Entity("Tgstation.Server.Host.Models.ReattachInformation", b =>
723  {
724  b.HasOne("Tgstation.Server.Host.Models.CompileJob", "CompileJob")
725  .WithMany()
726  .HasForeignKey("CompileJobId")
727  .OnDelete(DeleteBehavior.Cascade)
728  .IsRequired();
729  });
730 
731  modelBuilder.Entity("Tgstation.Server.Host.Models.RepositorySettings", b =>
732  {
733  b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
734  .WithOne("RepositorySettings")
735  .HasForeignKey("Tgstation.Server.Host.Models.RepositorySettings", "InstanceId")
736  .OnDelete(DeleteBehavior.Cascade)
737  .IsRequired();
738  });
739 
740  modelBuilder.Entity("Tgstation.Server.Host.Models.RevInfoTestMerge", b =>
741  {
742  b.HasOne("Tgstation.Server.Host.Models.RevisionInformation", "RevisionInformation")
743  .WithMany("ActiveTestMerges")
744  .HasForeignKey("RevisionInformationId")
745  .OnDelete(DeleteBehavior.Cascade)
746  .IsRequired();
747 
748  b.HasOne("Tgstation.Server.Host.Models.TestMerge", "TestMerge")
749  .WithMany("RevisonInformations")
750  .HasForeignKey("TestMergeId")
751  .OnDelete(DeleteBehavior.ClientNoAction)
752  .IsRequired();
753  });
754 
755  modelBuilder.Entity("Tgstation.Server.Host.Models.RevisionInformation", b =>
756  {
757  b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
758  .WithMany("RevisionInformations")
759  .HasForeignKey("InstanceId")
760  .OnDelete(DeleteBehavior.Cascade)
761  .IsRequired();
762  });
763 
764  modelBuilder.Entity("Tgstation.Server.Host.Models.TestMerge", b =>
765  {
766  b.HasOne("Tgstation.Server.Host.Models.User", "MergedBy")
767  .WithMany("TestMerges")
768  .HasForeignKey("MergedById")
769  .OnDelete(DeleteBehavior.Restrict)
770  .IsRequired();
771 
772  b.HasOne("Tgstation.Server.Host.Models.RevisionInformation", "PrimaryRevisionInformation")
773  .WithOne("PrimaryTestMerge")
774  .HasForeignKey("Tgstation.Server.Host.Models.TestMerge", "PrimaryRevisionInformationId")
775  .OnDelete(DeleteBehavior.Cascade)
776  .IsRequired();
777  });
778 
779  modelBuilder.Entity("Tgstation.Server.Host.Models.User", b =>
780  {
781  b.HasOne("Tgstation.Server.Host.Models.User", "CreatedBy")
782  .WithMany("CreatedUsers")
783  .HasForeignKey("CreatedById");
784  });
785 
786  modelBuilder.Entity("Tgstation.Server.Host.Models.WatchdogReattachInformation", b =>
787  {
788  b.HasOne("Tgstation.Server.Host.Models.ReattachInformation", "Alpha")
789  .WithMany()
790  .HasForeignKey("AlphaId");
791 
792  b.HasOne("Tgstation.Server.Host.Models.ReattachInformation", "Bravo")
793  .WithMany()
794  .HasForeignKey("BravoId");
795 
796  b.HasOne("Tgstation.Server.Host.Models.Instance", null)
797  .WithOne("WatchdogReattachInformation")
798  .HasForeignKey("Tgstation.Server.Host.Models.WatchdogReattachInformation", "InstanceId")
799  .OnDelete(DeleteBehavior.Cascade)
800  .IsRequired();
801  });
802 #pragma warning restore 612, 618
803  }
804  }
805 }
+Go to the documentation of this file.
1 // <auto-generated />
2 using System;
3 using Microsoft.EntityFrameworkCore;
4 using Microsoft.EntityFrameworkCore.Infrastructure;
5 
6 namespace Tgstation.Server.Host.Database.Migrations
7 {
8  [DbContext(typeof(MySqlDatabaseContext))]
10  {
11  protected override void BuildModel(ModelBuilder modelBuilder)
12  {
13 #pragma warning disable 612, 618
14  modelBuilder
15  .HasAnnotation("ProductVersion", "3.1.4")
16  .HasAnnotation("Relational:MaxIdentifierLength", 64);
17 
18  modelBuilder.Entity("Tgstation.Server.Host.Models.ChatBot", b =>
19  {
20  b.Property<long>("Id")
21  .ValueGeneratedOnAdd()
22  .HasColumnType("bigint");
23 
24  b.Property<ushort?>("ChannelLimit")
25  .IsRequired()
26  .HasColumnType("smallint unsigned");
27 
28  b.Property<string>("ConnectionString")
29  .IsRequired()
30  .HasColumnType("longtext CHARACTER SET utf8mb4")
31  .HasMaxLength(10000);
32 
33  b.Property<bool?>("Enabled")
34  .HasColumnType("tinyint(1)");
35 
36  b.Property<long>("InstanceId")
37  .HasColumnType("bigint");
38 
39  b.Property<string>("Name")
40  .IsRequired()
41  .HasColumnType("varchar(100) CHARACTER SET utf8mb4")
42  .HasMaxLength(100);
43 
44  b.Property<int>("Provider")
45  .HasColumnType("int");
46 
47  b.Property<uint?>("ReconnectionInterval")
48  .IsRequired()
49  .HasColumnType("int unsigned");
50 
51  b.HasKey("Id");
52 
53  b.HasIndex("InstanceId", "Name")
54  .IsUnique();
55 
56  b.ToTable("ChatBots");
57  });
58 
59  modelBuilder.Entity("Tgstation.Server.Host.Models.ChatChannel", b =>
60  {
61  b.Property<long>("Id")
62  .ValueGeneratedOnAdd()
63  .HasColumnType("bigint");
64 
65  b.Property<long>("ChatSettingsId")
66  .HasColumnType("bigint");
67 
68  b.Property<ulong?>("DiscordChannelId")
69  .HasColumnType("bigint unsigned");
70 
71  b.Property<string>("IrcChannel")
72  .HasColumnType("varchar(100) CHARACTER SET utf8mb4")
73  .HasMaxLength(100);
74 
75  b.Property<bool?>("IsAdminChannel")
76  .IsRequired()
77  .HasColumnType("tinyint(1)");
78 
79  b.Property<bool?>("IsUpdatesChannel")
80  .IsRequired()
81  .HasColumnType("tinyint(1)");
82 
83  b.Property<bool?>("IsWatchdogChannel")
84  .IsRequired()
85  .HasColumnType("tinyint(1)");
86 
87  b.Property<string>("Tag")
88  .HasColumnType("longtext CHARACTER SET utf8mb4")
89  .HasMaxLength(10000);
90 
91  b.HasKey("Id");
92 
93  b.HasIndex("ChatSettingsId", "DiscordChannelId")
94  .IsUnique();
95 
96  b.HasIndex("ChatSettingsId", "IrcChannel")
97  .IsUnique();
98 
99  b.ToTable("ChatChannels");
100  });
101 
102  modelBuilder.Entity("Tgstation.Server.Host.Models.CompileJob", b =>
103  {
104  b.Property<long>("Id")
105  .ValueGeneratedOnAdd()
106  .HasColumnType("bigint");
107 
108  b.Property<string>("ByondVersion")
109  .IsRequired()
110  .HasColumnType("longtext CHARACTER SET utf8mb4");
111 
112  b.Property<int?>("DMApiMajorVersion")
113  .HasColumnType("int");
114 
115  b.Property<int?>("DMApiMinorVersion")
116  .HasColumnType("int");
117 
118  b.Property<int?>("DMApiPatchVersion")
119  .HasColumnType("int");
120 
121  b.Property<Guid?>("DirectoryName")
122  .IsRequired()
123  .HasColumnType("char(36)");
124 
125  b.Property<string>("DmeName")
126  .IsRequired()
127  .HasColumnType("longtext CHARACTER SET utf8mb4");
128 
129  b.Property<long>("JobId")
130  .HasColumnType("bigint");
131 
132  b.Property<int>("MinimumSecurityLevel")
133  .HasColumnType("int");
134 
135  b.Property<string>("Output")
136  .IsRequired()
137  .HasColumnType("longtext CHARACTER SET utf8mb4");
138 
139  b.Property<long>("RevisionInformationId")
140  .HasColumnType("bigint");
141 
142  b.HasKey("Id");
143 
144  b.HasIndex("DirectoryName");
145 
146  b.HasIndex("JobId")
147  .IsUnique();
148 
149  b.HasIndex("RevisionInformationId");
150 
151  b.ToTable("CompileJobs");
152  });
153 
154  modelBuilder.Entity("Tgstation.Server.Host.Models.DreamDaemonSettings", b =>
155  {
156  b.Property<long>("Id")
157  .ValueGeneratedOnAdd()
158  .HasColumnType("bigint");
159 
160  b.Property<bool?>("AllowWebClient")
161  .IsRequired()
162  .HasColumnType("tinyint(1)");
163 
164  b.Property<bool?>("AutoStart")
165  .IsRequired()
166  .HasColumnType("tinyint(1)");
167 
168  b.Property<uint?>("HeartbeatSeconds")
169  .IsRequired()
170  .HasColumnType("int unsigned");
171 
172  b.Property<long>("InstanceId")
173  .HasColumnType("bigint");
174 
175  b.Property<ushort?>("PrimaryPort")
176  .IsRequired()
177  .HasColumnType("smallint unsigned");
178 
179  b.Property<ushort?>("SecondaryPort")
180  .IsRequired()
181  .HasColumnType("smallint unsigned");
182 
183  b.Property<int>("SecurityLevel")
184  .HasColumnType("int");
185 
186  b.Property<uint?>("StartupTimeout")
187  .IsRequired()
188  .HasColumnType("int unsigned");
189 
190  b.HasKey("Id");
191 
192  b.HasIndex("InstanceId")
193  .IsUnique();
194 
195  b.ToTable("DreamDaemonSettings");
196  });
197 
198  modelBuilder.Entity("Tgstation.Server.Host.Models.DreamMakerSettings", b =>
199  {
200  b.Property<long>("Id")
201  .ValueGeneratedOnAdd()
202  .HasColumnType("bigint");
203 
204  b.Property<ushort?>("ApiValidationPort")
205  .IsRequired()
206  .HasColumnType("smallint unsigned");
207 
208  b.Property<int>("ApiValidationSecurityLevel")
209  .HasColumnType("int");
210 
211  b.Property<long>("InstanceId")
212  .HasColumnType("bigint");
213 
214  b.Property<string>("ProjectName")
215  .HasColumnType("longtext CHARACTER SET utf8mb4")
216  .HasMaxLength(10000);
217 
218  b.HasKey("Id");
219 
220  b.HasIndex("InstanceId")
221  .IsUnique();
222 
223  b.ToTable("DreamMakerSettings");
224  });
225 
226  modelBuilder.Entity("Tgstation.Server.Host.Models.DualReattachInformation", b =>
227  {
228  b.Property<long>("Id")
229  .ValueGeneratedOnAdd()
230  .HasColumnType("bigint");
231 
232  b.Property<long?>("AlphaId")
233  .HasColumnType("bigint");
234 
235  b.Property<bool>("AlphaIsActive")
236  .HasColumnType("tinyint(1)");
237 
238  b.Property<long?>("BravoId")
239  .HasColumnType("bigint");
240 
241  b.Property<long>("InstanceId")
242  .HasColumnType("bigint");
243 
244  b.HasKey("Id");
245 
246  b.HasIndex("AlphaId");
247 
248  b.HasIndex("BravoId");
249 
250  b.HasIndex("InstanceId")
251  .IsUnique();
252 
253  b.ToTable("WatchdogReattachInformations");
254  });
255 
256  modelBuilder.Entity("Tgstation.Server.Host.Models.Instance", b =>
257  {
258  b.Property<long>("Id")
259  .ValueGeneratedOnAdd()
260  .HasColumnType("bigint");
261 
262  b.Property<uint?>("AutoUpdateInterval")
263  .IsRequired()
264  .HasColumnType("int unsigned");
265 
266  b.Property<ushort?>("ChatBotLimit")
267  .IsRequired()
268  .HasColumnType("smallint unsigned");
269 
270  b.Property<int>("ConfigurationType")
271  .HasColumnType("int");
272 
273  b.Property<string>("Name")
274  .IsRequired()
275  .HasColumnType("longtext CHARACTER SET utf8mb4")
276  .HasMaxLength(10000);
277 
278  b.Property<bool?>("Online")
279  .IsRequired()
280  .HasColumnType("tinyint(1)");
281 
282  b.Property<string>("Path")
283  .IsRequired()
284  .HasColumnType("varchar(255) CHARACTER SET utf8mb4");
285 
286  b.HasKey("Id");
287 
288  b.HasIndex("Path")
289  .IsUnique();
290 
291  b.ToTable("Instances");
292  });
293 
294  modelBuilder.Entity("Tgstation.Server.Host.Models.InstanceUser", b =>
295  {
296  b.Property<long>("Id")
297  .ValueGeneratedOnAdd()
298  .HasColumnType("bigint");
299 
300  b.Property<ulong>("ByondRights")
301  .HasColumnType("bigint unsigned");
302 
303  b.Property<ulong>("ChatBotRights")
304  .HasColumnType("bigint unsigned");
305 
306  b.Property<ulong>("ConfigurationRights")
307  .HasColumnType("bigint unsigned");
308 
309  b.Property<ulong>("DreamDaemonRights")
310  .HasColumnType("bigint unsigned");
311 
312  b.Property<ulong>("DreamMakerRights")
313  .HasColumnType("bigint unsigned");
314 
315  b.Property<long>("InstanceId")
316  .HasColumnType("bigint");
317 
318  b.Property<ulong>("InstanceUserRights")
319  .HasColumnType("bigint unsigned");
320 
321  b.Property<ulong>("RepositoryRights")
322  .HasColumnType("bigint unsigned");
323 
324  b.Property<long?>("UserId")
325  .IsRequired()
326  .HasColumnType("bigint");
327 
328  b.HasKey("Id");
329 
330  b.HasIndex("InstanceId");
331 
332  b.HasIndex("UserId", "InstanceId")
333  .IsUnique();
334 
335  b.ToTable("InstanceUsers");
336  });
337 
338  modelBuilder.Entity("Tgstation.Server.Host.Models.Job", b =>
339  {
340  b.Property<long>("Id")
341  .ValueGeneratedOnAdd()
342  .HasColumnType("bigint");
343 
344  b.Property<ulong?>("CancelRight")
345  .HasColumnType("bigint unsigned");
346 
347  b.Property<ulong?>("CancelRightsType")
348  .HasColumnType("bigint unsigned");
349 
350  b.Property<bool?>("Cancelled")
351  .IsRequired()
352  .HasColumnType("tinyint(1)");
353 
354  b.Property<long?>("CancelledById")
355  .HasColumnType("bigint");
356 
357  b.Property<string>("Description")
358  .IsRequired()
359  .HasColumnType("longtext CHARACTER SET utf8mb4");
360 
361  b.Property<uint?>("ErrorCode")
362  .HasColumnType("int unsigned");
363 
364  b.Property<string>("ExceptionDetails")
365  .HasColumnType("longtext CHARACTER SET utf8mb4");
366 
367  b.Property<long>("InstanceId")
368  .HasColumnType("bigint");
369 
370  b.Property<DateTimeOffset?>("StartedAt")
371  .IsRequired()
372  .HasColumnType("datetime(6)");
373 
374  b.Property<long>("StartedById")
375  .HasColumnType("bigint");
376 
377  b.Property<DateTimeOffset?>("StoppedAt")
378  .HasColumnType("datetime(6)");
379 
380  b.HasKey("Id");
381 
382  b.HasIndex("CancelledById");
383 
384  b.HasIndex("InstanceId");
385 
386  b.HasIndex("StartedById");
387 
388  b.ToTable("Jobs");
389  });
390 
391  modelBuilder.Entity("Tgstation.Server.Host.Models.ReattachInformation", b =>
392  {
393  b.Property<long>("Id")
394  .ValueGeneratedOnAdd()
395  .HasColumnType("bigint");
396 
397  b.Property<string>("AccessIdentifier")
398  .IsRequired()
399  .HasColumnType("longtext CHARACTER SET utf8mb4");
400 
401  b.Property<long>("CompileJobId")
402  .HasColumnType("bigint");
403 
404  b.Property<bool>("IsPrimary")
405  .HasColumnType("tinyint(1)");
406 
407  b.Property<int>("LaunchSecurityLevel")
408  .HasColumnType("int");
409 
410  b.Property<ushort>("Port")
411  .HasColumnType("smallint unsigned");
412 
413  b.Property<int>("ProcessId")
414  .HasColumnType("int");
415 
416  b.Property<int>("RebootState")
417  .HasColumnType("int");
418 
419  b.HasKey("Id");
420 
421  b.HasIndex("CompileJobId");
422 
423  b.ToTable("ReattachInformations");
424  });
425 
426  modelBuilder.Entity("Tgstation.Server.Host.Models.RepositorySettings", b =>
427  {
428  b.Property<long>("Id")
429  .ValueGeneratedOnAdd()
430  .HasColumnType("bigint");
431 
432  b.Property<string>("AccessToken")
433  .HasColumnType("longtext CHARACTER SET utf8mb4")
434  .HasMaxLength(10000);
435 
436  b.Property<string>("AccessUser")
437  .HasColumnType("longtext CHARACTER SET utf8mb4")
438  .HasMaxLength(10000);
439 
440  b.Property<bool?>("AutoUpdatesKeepTestMerges")
441  .IsRequired()
442  .HasColumnType("tinyint(1)");
443 
444  b.Property<bool?>("AutoUpdatesSynchronize")
445  .IsRequired()
446  .HasColumnType("tinyint(1)");
447 
448  b.Property<string>("CommitterEmail")
449  .IsRequired()
450  .HasColumnType("longtext CHARACTER SET utf8mb4")
451  .HasMaxLength(10000);
452 
453  b.Property<string>("CommitterName")
454  .IsRequired()
455  .HasColumnType("longtext CHARACTER SET utf8mb4")
456  .HasMaxLength(10000);
457 
458  b.Property<long>("InstanceId")
459  .HasColumnType("bigint");
460 
461  b.Property<bool?>("PostTestMergeComment")
462  .IsRequired()
463  .HasColumnType("tinyint(1)");
464 
465  b.Property<bool?>("PushTestMergeCommits")
466  .IsRequired()
467  .HasColumnType("tinyint(1)");
468 
469  b.Property<bool?>("ShowTestMergeCommitters")
470  .IsRequired()
471  .HasColumnType("tinyint(1)");
472 
473  b.HasKey("Id");
474 
475  b.HasIndex("InstanceId")
476  .IsUnique();
477 
478  b.ToTable("RepositorySettings");
479  });
480 
481  modelBuilder.Entity("Tgstation.Server.Host.Models.RevInfoTestMerge", b =>
482  {
483  b.Property<long>("Id")
484  .ValueGeneratedOnAdd()
485  .HasColumnType("bigint");
486 
487  b.Property<long>("RevisionInformationId")
488  .HasColumnType("bigint");
489 
490  b.Property<long>("TestMergeId")
491  .HasColumnType("bigint");
492 
493  b.HasKey("Id");
494 
495  b.HasIndex("RevisionInformationId");
496 
497  b.HasIndex("TestMergeId");
498 
499  b.ToTable("RevInfoTestMerges");
500  });
501 
502  modelBuilder.Entity("Tgstation.Server.Host.Models.RevisionInformation", b =>
503  {
504  b.Property<long>("Id")
505  .ValueGeneratedOnAdd()
506  .HasColumnType("bigint");
507 
508  b.Property<string>("CommitSha")
509  .IsRequired()
510  .HasColumnType("varchar(40) CHARACTER SET utf8mb4")
511  .HasMaxLength(40);
512 
513  b.Property<long>("InstanceId")
514  .HasColumnType("bigint");
515 
516  b.Property<string>("OriginCommitSha")
517  .IsRequired()
518  .HasColumnType("varchar(40) CHARACTER SET utf8mb4")
519  .HasMaxLength(40);
520 
521  b.HasKey("Id");
522 
523  b.HasIndex("InstanceId", "CommitSha")
524  .IsUnique();
525 
526  b.ToTable("RevisionInformations");
527  });
528 
529  modelBuilder.Entity("Tgstation.Server.Host.Models.TestMerge", b =>
530  {
531  b.Property<long>("Id")
532  .ValueGeneratedOnAdd()
533  .HasColumnType("bigint");
534 
535  b.Property<string>("Author")
536  .IsRequired()
537  .HasColumnType("longtext CHARACTER SET utf8mb4");
538 
539  b.Property<string>("BodyAtMerge")
540  .IsRequired()
541  .HasColumnType("longtext CHARACTER SET utf8mb4");
542 
543  b.Property<string>("Comment")
544  .HasColumnType("longtext CHARACTER SET utf8mb4")
545  .HasMaxLength(10000);
546 
547  b.Property<DateTimeOffset>("MergedAt")
548  .HasColumnType("datetime(6)");
549 
550  b.Property<long>("MergedById")
551  .HasColumnType("bigint");
552 
553  b.Property<int>("Number")
554  .HasColumnType("int");
555 
556  b.Property<long?>("PrimaryRevisionInformationId")
557  .IsRequired()
558  .HasColumnType("bigint");
559 
560  b.Property<string>("PullRequestRevision")
561  .IsRequired()
562  .HasColumnType("varchar(40) CHARACTER SET utf8mb4")
563  .HasMaxLength(40);
564 
565  b.Property<string>("TitleAtMerge")
566  .IsRequired()
567  .HasColumnType("longtext CHARACTER SET utf8mb4");
568 
569  b.Property<string>("Url")
570  .IsRequired()
571  .HasColumnType("longtext CHARACTER SET utf8mb4");
572 
573  b.HasKey("Id");
574 
575  b.HasIndex("MergedById");
576 
577  b.HasIndex("PrimaryRevisionInformationId")
578  .IsUnique();
579 
580  b.ToTable("TestMerges");
581  });
582 
583  modelBuilder.Entity("Tgstation.Server.Host.Models.User", b =>
584  {
585  b.Property<long?>("Id")
586  .ValueGeneratedOnAdd()
587  .HasColumnType("bigint");
588 
589  b.Property<ulong>("AdministrationRights")
590  .HasColumnType("bigint unsigned");
591 
592  b.Property<string>("CanonicalName")
593  .IsRequired()
594  .HasColumnType("varchar(255) CHARACTER SET utf8mb4");
595 
596  b.Property<DateTimeOffset?>("CreatedAt")
597  .IsRequired()
598  .HasColumnType("datetime(6)");
599 
600  b.Property<long?>("CreatedById")
601  .HasColumnType("bigint");
602 
603  b.Property<bool?>("Enabled")
604  .IsRequired()
605  .HasColumnType("tinyint(1)");
606 
607  b.Property<ulong>("InstanceManagerRights")
608  .HasColumnType("bigint unsigned");
609 
610  b.Property<DateTimeOffset?>("LastPasswordUpdate")
611  .HasColumnType("datetime(6)");
612 
613  b.Property<string>("Name")
614  .IsRequired()
615  .HasColumnType("longtext CHARACTER SET utf8mb4")
616  .HasMaxLength(10000);
617 
618  b.Property<string>("PasswordHash")
619  .HasColumnType("longtext CHARACTER SET utf8mb4");
620 
621  b.Property<string>("SystemIdentifier")
622  .HasColumnType("varchar(255) CHARACTER SET utf8mb4");
623 
624  b.HasKey("Id");
625 
626  b.HasIndex("CanonicalName")
627  .IsUnique();
628 
629  b.HasIndex("CreatedById");
630 
631  b.HasIndex("SystemIdentifier")
632  .IsUnique();
633 
634  b.ToTable("Users");
635  });
636 
637  modelBuilder.Entity("Tgstation.Server.Host.Models.ChatBot", b =>
638  {
639  b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
640  .WithMany("ChatSettings")
641  .HasForeignKey("InstanceId")
642  .OnDelete(DeleteBehavior.Cascade)
643  .IsRequired();
644  });
645 
646  modelBuilder.Entity("Tgstation.Server.Host.Models.ChatChannel", b =>
647  {
648  b.HasOne("Tgstation.Server.Host.Models.ChatBot", "ChatSettings")
649  .WithMany("Channels")
650  .HasForeignKey("ChatSettingsId")
651  .OnDelete(DeleteBehavior.Cascade)
652  .IsRequired();
653  });
654 
655  modelBuilder.Entity("Tgstation.Server.Host.Models.CompileJob", b =>
656  {
657  b.HasOne("Tgstation.Server.Host.Models.Job", "Job")
658  .WithOne()
659  .HasForeignKey("Tgstation.Server.Host.Models.CompileJob", "JobId")
660  .OnDelete(DeleteBehavior.Cascade)
661  .IsRequired();
662 
663  b.HasOne("Tgstation.Server.Host.Models.RevisionInformation", "RevisionInformation")
664  .WithMany("CompileJobs")
665  .HasForeignKey("RevisionInformationId")
666  .OnDelete(DeleteBehavior.Cascade)
667  .IsRequired();
668  });
669 
670  modelBuilder.Entity("Tgstation.Server.Host.Models.DreamDaemonSettings", b =>
671  {
672  b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
673  .WithOne("DreamDaemonSettings")
674  .HasForeignKey("Tgstation.Server.Host.Models.DreamDaemonSettings", "InstanceId")
675  .OnDelete(DeleteBehavior.Cascade)
676  .IsRequired();
677  });
678 
679  modelBuilder.Entity("Tgstation.Server.Host.Models.DreamMakerSettings", b =>
680  {
681  b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
682  .WithOne("DreamMakerSettings")
683  .HasForeignKey("Tgstation.Server.Host.Models.DreamMakerSettings", "InstanceId")
684  .OnDelete(DeleteBehavior.Cascade)
685  .IsRequired();
686  });
687 
688  modelBuilder.Entity("Tgstation.Server.Host.Models.DualReattachInformation", b =>
689  {
690  b.HasOne("Tgstation.Server.Host.Models.ReattachInformation", "Alpha")
691  .WithMany()
692  .HasForeignKey("AlphaId");
693 
694  b.HasOne("Tgstation.Server.Host.Models.ReattachInformation", "Bravo")
695  .WithMany()
696  .HasForeignKey("BravoId");
697 
698  b.HasOne("Tgstation.Server.Host.Models.Instance", null)
699  .WithOne("WatchdogReattachInformation")
700  .HasForeignKey("Tgstation.Server.Host.Models.DualReattachInformation", "InstanceId")
701  .OnDelete(DeleteBehavior.Cascade)
702  .IsRequired();
703  });
704 
705  modelBuilder.Entity("Tgstation.Server.Host.Models.InstanceUser", b =>
706  {
707  b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
708  .WithMany("InstanceUsers")
709  .HasForeignKey("InstanceId")
710  .OnDelete(DeleteBehavior.Cascade)
711  .IsRequired();
712 
713  b.HasOne("Tgstation.Server.Host.Models.User", null)
714  .WithMany("InstanceUsers")
715  .HasForeignKey("UserId")
716  .OnDelete(DeleteBehavior.Cascade)
717  .IsRequired();
718  });
719 
720  modelBuilder.Entity("Tgstation.Server.Host.Models.Job", b =>
721  {
722  b.HasOne("Tgstation.Server.Host.Models.User", "CancelledBy")
723  .WithMany()
724  .HasForeignKey("CancelledById");
725 
726  b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
727  .WithMany("Jobs")
728  .HasForeignKey("InstanceId")
729  .OnDelete(DeleteBehavior.Cascade)
730  .IsRequired();
731 
732  b.HasOne("Tgstation.Server.Host.Models.User", "StartedBy")
733  .WithMany()
734  .HasForeignKey("StartedById")
735  .OnDelete(DeleteBehavior.Cascade)
736  .IsRequired();
737  });
738 
739  modelBuilder.Entity("Tgstation.Server.Host.Models.ReattachInformation", b =>
740  {
741  b.HasOne("Tgstation.Server.Host.Models.CompileJob", "CompileJob")
742  .WithMany()
743  .HasForeignKey("CompileJobId")
744  .OnDelete(DeleteBehavior.Cascade)
745  .IsRequired();
746  });
747 
748  modelBuilder.Entity("Tgstation.Server.Host.Models.RepositorySettings", b =>
749  {
750  b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
751  .WithOne("RepositorySettings")
752  .HasForeignKey("Tgstation.Server.Host.Models.RepositorySettings", "InstanceId")
753  .OnDelete(DeleteBehavior.Cascade)
754  .IsRequired();
755  });
756 
757  modelBuilder.Entity("Tgstation.Server.Host.Models.RevInfoTestMerge", b =>
758  {
759  b.HasOne("Tgstation.Server.Host.Models.RevisionInformation", "RevisionInformation")
760  .WithMany("ActiveTestMerges")
761  .HasForeignKey("RevisionInformationId")
762  .OnDelete(DeleteBehavior.Cascade)
763  .IsRequired();
764 
765  b.HasOne("Tgstation.Server.Host.Models.TestMerge", "TestMerge")
766  .WithMany("RevisonInformations")
767  .HasForeignKey("TestMergeId")
768  .OnDelete(DeleteBehavior.ClientNoAction)
769  .IsRequired();
770  });
771 
772  modelBuilder.Entity("Tgstation.Server.Host.Models.RevisionInformation", b =>
773  {
774  b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
775  .WithMany("RevisionInformations")
776  .HasForeignKey("InstanceId")
777  .OnDelete(DeleteBehavior.Cascade)
778  .IsRequired();
779  });
780 
781  modelBuilder.Entity("Tgstation.Server.Host.Models.TestMerge", b =>
782  {
783  b.HasOne("Tgstation.Server.Host.Models.User", "MergedBy")
784  .WithMany("TestMerges")
785  .HasForeignKey("MergedById")
786  .OnDelete(DeleteBehavior.Restrict)
787  .IsRequired();
788 
789  b.HasOne("Tgstation.Server.Host.Models.RevisionInformation", "PrimaryRevisionInformation")
790  .WithOne("PrimaryTestMerge")
791  .HasForeignKey("Tgstation.Server.Host.Models.TestMerge", "PrimaryRevisionInformationId")
792  .OnDelete(DeleteBehavior.Cascade)
793  .IsRequired();
794  });
795 
796  modelBuilder.Entity("Tgstation.Server.Host.Models.User", b =>
797  {
798  b.HasOne("Tgstation.Server.Host.Models.User", "CreatedBy")
799  .WithMany("CreatedUsers")
800  .HasForeignKey("CreatedById");
801  });
802 #pragma warning restore 612, 618
803  }
804  }
805 }
diff --git a/_postgres_sql_database_context_8cs_source.html b/_postgres_sql_database_context_8cs_source.html index b15d9e2751..967a6b355b 100644 --- a/_postgres_sql_database_context_8cs_source.html +++ b/_postgres_sql_database_context_8cs_source.html @@ -94,16 +94,16 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
PostgresSqlDatabaseContext.cs
-Go to the documentation of this file.
1 using Microsoft.EntityFrameworkCore;
2 using Microsoft.Extensions.Logging;
3 using Microsoft.Extensions.Options;
4 using System;
5 using System.Diagnostics;
7 
8 namespace Tgstation.Server.Host.Database
9 {
14  {
23  DbContextOptions<PostgresSqlDatabaseContext> dbContextOptions,
24  IOptions<DatabaseConfiguration> databaseConfiguration,
25  IDatabaseSeeder databaseSeeder,
26  ILogger<PostgresSqlDatabaseContext> logger)
27  : base(dbContextOptions, databaseConfiguration, databaseSeeder, logger)
28  { }
29 
31  protected override void OnConfiguring(DbContextOptionsBuilder options)
32  {
33  base.OnConfiguring(options);
34  options.UseNpgsql(DatabaseConfiguration.ConnectionString, x => x.EnableRetryOnFailure());
35  }
36 
38  protected override void ValidateDatabaseType()
39  {
40  if (!Debugger.IsAttached)
41  throw new NotImplementedException("PostgresSQL implementation is not complete yet!");
42 
43  if (DatabaseType != DatabaseType.PostgresSql)
44  throw new InvalidOperationException("Invalid DatabaseType for PostgresSqlDatabaseContext!");
45  }
46  }
47 }
string ConnectionString
The connection string for the database
+Go to the documentation of this file.
1 using Microsoft.EntityFrameworkCore;
2 using Microsoft.Extensions.Logging;
3 using Microsoft.Extensions.Options;
4 using System;
6 
7 namespace Tgstation.Server.Host.Database
8 {
13  {
15  protected override DeleteBehavior RevInfoCompileJobDeleteBehavior => DeleteBehavior.Cascade;
16 
25  DbContextOptions<PostgresSqlDatabaseContext> dbContextOptions,
26  IOptions<DatabaseConfiguration> databaseConfiguration,
27  IDatabaseSeeder databaseSeeder,
28  ILogger<PostgresSqlDatabaseContext> logger)
29  : base(dbContextOptions, databaseConfiguration, databaseSeeder, logger)
30  { }
31 
33  protected override void OnConfiguring(DbContextOptionsBuilder options)
34  {
35  base.OnConfiguring(options);
36  options.UseNpgsql(DatabaseConfiguration.ConnectionString, options =>
37  {
38  options.EnableRetryOnFailure();
39 
40  if (!String.IsNullOrEmpty(DatabaseConfiguration.ServerVersion))
41  options.SetPostgresVersion(
42  Version.Parse(DatabaseConfiguration.ServerVersion));
43  });
44  }
45 
47  protected override void ValidateDatabaseType()
48  {
49  if (DatabaseType != DatabaseType.PostgresSql)
50  throw new InvalidOperationException("Invalid DatabaseType for PostgresSqlDatabaseContext!");
51  }
52  }
53 }
string ConnectionString
The connection string for the database
For initially seeding a database
Backend abstract implementation of IDatabaseContext
- -
override void OnConfiguring(DbContextOptionsBuilder options)
-
override void ValidateDatabaseType()
Ensure the DatabaseType is correct for the DatabaseContext.
-
PostgresSqlDatabaseContext(DbContextOptions< PostgresSqlDatabaseContext > dbContextOptions, IOptions< DatabaseConfiguration > databaseConfiguration, IDatabaseSeeder databaseSeeder, ILogger< PostgresSqlDatabaseContext > logger)
Construct a SqlServerDatabaseContext
+ +
override void OnConfiguring(DbContextOptionsBuilder options)
+
override void ValidateDatabaseType()
Ensure the DatabaseType is correct for the DatabaseContext.
+
PostgresSqlDatabaseContext(DbContextOptions< PostgresSqlDatabaseContext > dbContextOptions, IOptions< DatabaseConfiguration > databaseConfiguration, IDatabaseSeeder databaseSeeder, ILogger< PostgresSqlDatabaseContext > logger)
Construct a SqlServerDatabaseContext
Configuration options for the Database.DatabaseContext
DatabaseType
Type of database to user
Definition: DatabaseType.cs:6
diff --git a/_postgres_sql_database_context_model_snapshot_8cs_source.html b/_postgres_sql_database_context_model_snapshot_8cs_source.html index 925c2f0eae..d8e9a263c0 100644 --- a/_postgres_sql_database_context_model_snapshot_8cs_source.html +++ b/_postgres_sql_database_context_model_snapshot_8cs_source.html @@ -94,12 +94,12 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
PostgresSqlDatabaseContextModelSnapshot.cs
-Go to the documentation of this file.
1 // <auto-generated />
2 using System;
3 using Microsoft.EntityFrameworkCore;
4 using Microsoft.EntityFrameworkCore.Infrastructure;
5 using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
7 
8 namespace Tgstation.Server.Host.Migrations
9 {
12  {
13  protected override void BuildModel(ModelBuilder modelBuilder)
14  {
15 #pragma warning disable 612, 618
16  modelBuilder
17  .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn)
18  .HasAnnotation("ProductVersion", "3.1.4")
19  .HasAnnotation("Relational:MaxIdentifierLength", 63);
20 
21  modelBuilder.Entity("Tgstation.Server.Host.Models.ChatBot", b =>
22  {
23  b.Property<long>("Id")
24  .ValueGeneratedOnAdd()
25  .HasColumnType("bigint")
26  .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
27 
28  b.Property<int>("ChannelLimit")
29  .HasColumnType("integer");
30 
31  b.Property<string>("ConnectionString")
32  .IsRequired()
33  .HasColumnType("character varying(10000)")
34  .HasMaxLength(10000);
35 
36  b.Property<bool?>("Enabled")
37  .HasColumnType("boolean");
38 
39  b.Property<long>("InstanceId")
40  .HasColumnType("bigint");
41 
42  b.Property<string>("Name")
43  .IsRequired()
44  .HasColumnType("character varying(100)")
45  .HasMaxLength(100);
46 
47  b.Property<int>("Provider")
48  .HasColumnType("integer");
49 
50  b.Property<long>("ReconnectionInterval")
51  .HasColumnType("bigint");
52 
53  b.HasKey("Id");
54 
55  b.HasIndex("InstanceId", "Name")
56  .IsUnique();
57 
58  b.ToTable("ChatBots");
59  });
60 
61  modelBuilder.Entity("Tgstation.Server.Host.Models.ChatChannel", b =>
62  {
63  b.Property<long>("Id")
64  .ValueGeneratedOnAdd()
65  .HasColumnType("bigint")
66  .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
67 
68  b.Property<long>("ChatSettingsId")
69  .HasColumnType("bigint");
70 
71  b.Property<decimal?>("DiscordChannelId")
72  .HasColumnType("numeric(20,0)");
73 
74  b.Property<string>("IrcChannel")
75  .HasColumnType("character varying(100)")
76  .HasMaxLength(100);
77 
78  b.Property<bool?>("IsAdminChannel")
79  .IsRequired()
80  .HasColumnType("boolean");
81 
82  b.Property<bool?>("IsUpdatesChannel")
83  .IsRequired()
84  .HasColumnType("boolean");
85 
86  b.Property<bool?>("IsWatchdogChannel")
87  .IsRequired()
88  .HasColumnType("boolean");
89 
90  b.Property<string>("Tag")
91  .HasColumnType("character varying(10000)")
92  .HasMaxLength(10000);
93 
94  b.HasKey("Id");
95 
96  b.HasIndex("ChatSettingsId", "DiscordChannelId")
97  .IsUnique();
98 
99  b.HasIndex("ChatSettingsId", "IrcChannel")
100  .IsUnique();
101 
102  b.ToTable("ChatChannels");
103  });
104 
105  modelBuilder.Entity("Tgstation.Server.Host.Models.CompileJob", b =>
106  {
107  b.Property<long>("Id")
108  .ValueGeneratedOnAdd()
109  .HasColumnType("bigint")
110  .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
111 
112  b.Property<string>("ByondVersion")
113  .IsRequired()
114  .HasColumnType("text");
115 
116  b.Property<int?>("DMApiMajorVersion")
117  .HasColumnType("integer");
118 
119  b.Property<int?>("DMApiMinorVersion")
120  .HasColumnType("integer");
121 
122  b.Property<int?>("DMApiPatchVersion")
123  .HasColumnType("integer");
124 
125  b.Property<Guid?>("DirectoryName")
126  .IsRequired()
127  .HasColumnType("uuid");
128 
129  b.Property<string>("DmeName")
130  .IsRequired()
131  .HasColumnType("text");
132 
133  b.Property<long>("JobId")
134  .HasColumnType("bigint");
135 
136  b.Property<int>("MinimumSecurityLevel")
137  .HasColumnType("integer");
138 
139  b.Property<string>("Output")
140  .IsRequired()
141  .HasColumnType("text");
142 
143  b.Property<long>("RevisionInformationId")
144  .HasColumnType("bigint");
145 
146  b.HasKey("Id");
147 
148  b.HasIndex("DirectoryName");
149 
150  b.HasIndex("JobId")
151  .IsUnique();
152 
153  b.HasIndex("RevisionInformationId");
154 
155  b.ToTable("CompileJobs");
156  });
157 
158  modelBuilder.Entity("Tgstation.Server.Host.Models.DreamDaemonSettings", b =>
159  {
160  b.Property<long>("Id")
161  .ValueGeneratedOnAdd()
162  .HasColumnType("bigint")
163  .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
164 
165  b.Property<bool?>("AllowWebClient")
166  .IsRequired()
167  .HasColumnType("boolean");
168 
169  b.Property<bool?>("AutoStart")
170  .IsRequired()
171  .HasColumnType("boolean");
172 
173  b.Property<long>("HeartbeatSeconds")
174  .HasColumnType("bigint");
175 
176  b.Property<long>("InstanceId")
177  .HasColumnType("bigint");
178 
179  b.Property<int>("PrimaryPort")
180  .HasColumnType("integer");
181 
182  b.Property<int>("SecondaryPort")
183  .HasColumnType("integer");
184 
185  b.Property<int>("SecurityLevel")
186  .HasColumnType("integer");
187 
188  b.Property<long>("StartupTimeout")
189  .HasColumnType("bigint");
190 
191  b.HasKey("Id");
192 
193  b.HasIndex("InstanceId")
194  .IsUnique();
195 
196  b.ToTable("DreamDaemonSettings");
197  });
198 
199  modelBuilder.Entity("Tgstation.Server.Host.Models.DreamMakerSettings", b =>
200  {
201  b.Property<long>("Id")
202  .ValueGeneratedOnAdd()
203  .HasColumnType("bigint")
204  .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
205 
206  b.Property<int>("ApiValidationPort")
207  .HasColumnType("integer");
208 
209  b.Property<int>("ApiValidationSecurityLevel")
210  .HasColumnType("integer");
211 
212  b.Property<long>("InstanceId")
213  .HasColumnType("bigint");
214 
215  b.Property<string>("ProjectName")
216  .HasColumnType("character varying(10000)")
217  .HasMaxLength(10000);
218 
219  b.HasKey("Id");
220 
221  b.HasIndex("InstanceId")
222  .IsUnique();
223 
224  b.ToTable("DreamMakerSettings");
225  });
226 
227  modelBuilder.Entity("Tgstation.Server.Host.Models.DualReattachInformation", b =>
228  {
229  b.Property<long>("Id")
230  .ValueGeneratedOnAdd()
231  .HasColumnType("bigint")
232  .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
233 
234  b.Property<long?>("AlphaId")
235  .HasColumnType("bigint");
236 
237  b.Property<bool>("AlphaIsActive")
238  .HasColumnType("boolean");
239 
240  b.Property<long?>("BravoId")
241  .HasColumnType("bigint");
242 
243  b.Property<long>("InstanceId")
244  .HasColumnType("bigint");
245 
246  b.HasKey("Id");
247 
248  b.HasIndex("AlphaId");
249 
250  b.HasIndex("BravoId");
251 
252  b.HasIndex("InstanceId")
253  .IsUnique();
254 
255  b.ToTable("WatchdogReattachInformations");
256  });
257 
258  modelBuilder.Entity("Tgstation.Server.Host.Models.Instance", b =>
259  {
260  b.Property<long>("Id")
261  .ValueGeneratedOnAdd()
262  .HasColumnType("bigint")
263  .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
264 
265  b.Property<long>("AutoUpdateInterval")
266  .HasColumnType("bigint");
267 
268  b.Property<int>("ChatBotLimit")
269  .HasColumnType("integer");
270 
271  b.Property<int>("ConfigurationType")
272  .HasColumnType("integer");
273 
274  b.Property<string>("Name")
275  .IsRequired()
276  .HasColumnType("character varying(10000)")
277  .HasMaxLength(10000);
278 
279  b.Property<bool?>("Online")
280  .IsRequired()
281  .HasColumnType("boolean");
282 
283  b.Property<string>("Path")
284  .IsRequired()
285  .HasColumnType("text");
286 
287  b.HasKey("Id");
288 
289  b.HasIndex("Path")
290  .IsUnique();
291 
292  b.ToTable("Instances");
293  });
294 
295  modelBuilder.Entity("Tgstation.Server.Host.Models.InstanceUser", b =>
296  {
297  b.Property<long>("Id")
298  .ValueGeneratedOnAdd()
299  .HasColumnType("bigint")
300  .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
301 
302  b.Property<decimal>("ByondRights")
303  .HasColumnType("numeric(20,0)");
304 
305  b.Property<decimal>("ChatBotRights")
306  .HasColumnType("numeric(20,0)");
307 
308  b.Property<decimal>("ConfigurationRights")
309  .HasColumnType("numeric(20,0)");
310 
311  b.Property<decimal>("DreamDaemonRights")
312  .HasColumnType("numeric(20,0)");
313 
314  b.Property<decimal>("DreamMakerRights")
315  .HasColumnType("numeric(20,0)");
316 
317  b.Property<long>("InstanceId")
318  .HasColumnType("bigint");
319 
320  b.Property<decimal>("InstanceUserRights")
321  .HasColumnType("numeric(20,0)");
322 
323  b.Property<decimal>("RepositoryRights")
324  .HasColumnType("numeric(20,0)");
325 
326  b.Property<long?>("UserId")
327  .IsRequired()
328  .HasColumnType("bigint");
329 
330  b.HasKey("Id");
331 
332  b.HasIndex("InstanceId");
333 
334  b.HasIndex("UserId", "InstanceId")
335  .IsUnique();
336 
337  b.ToTable("InstanceUsers");
338  });
339 
340  modelBuilder.Entity("Tgstation.Server.Host.Models.Job", b =>
341  {
342  b.Property<long>("Id")
343  .ValueGeneratedOnAdd()
344  .HasColumnType("bigint")
345  .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
346 
347  b.Property<decimal?>("CancelRight")
348  .HasColumnType("numeric(20,0)");
349 
350  b.Property<decimal?>("CancelRightsType")
351  .HasColumnType("numeric(20,0)");
352 
353  b.Property<bool?>("Cancelled")
354  .IsRequired()
355  .HasColumnType("boolean");
356 
357  b.Property<long?>("CancelledById")
358  .HasColumnType("bigint");
359 
360  b.Property<string>("Description")
361  .IsRequired()
362  .HasColumnType("text");
363 
364  b.Property<long?>("ErrorCode")
365  .HasColumnType("bigint");
366 
367  b.Property<string>("ExceptionDetails")
368  .HasColumnType("text");
369 
370  b.Property<long>("InstanceId")
371  .HasColumnType("bigint");
372 
373  b.Property<DateTimeOffset?>("StartedAt")
374  .IsRequired()
375  .HasColumnType("timestamp with time zone");
376 
377  b.Property<long>("StartedById")
378  .HasColumnType("bigint");
379 
380  b.Property<DateTimeOffset?>("StoppedAt")
381  .HasColumnType("timestamp with time zone");
382 
383  b.HasKey("Id");
384 
385  b.HasIndex("CancelledById");
386 
387  b.HasIndex("InstanceId");
388 
389  b.HasIndex("StartedById");
390 
391  b.ToTable("Jobs");
392  });
393 
394  modelBuilder.Entity("Tgstation.Server.Host.Models.ReattachInformation", b =>
395  {
396  b.Property<long>("Id")
397  .ValueGeneratedOnAdd()
398  .HasColumnType("bigint")
399  .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
400 
401  b.Property<string>("AccessIdentifier")
402  .IsRequired()
403  .HasColumnType("text");
404 
405  b.Property<long>("CompileJobId")
406  .HasColumnType("bigint");
407 
408  b.Property<bool>("IsPrimary")
409  .HasColumnType("boolean");
410 
411  b.Property<int>("LaunchSecurityLevel")
412  .HasColumnType("integer");
413 
414  b.Property<int>("Port")
415  .HasColumnType("integer");
416 
417  b.Property<int>("ProcessId")
418  .HasColumnType("integer");
419 
420  b.Property<int>("RebootState")
421  .HasColumnType("integer");
422 
423  b.HasKey("Id");
424 
425  b.HasIndex("CompileJobId");
426 
427  b.ToTable("ReattachInformations");
428  });
429 
430  modelBuilder.Entity("Tgstation.Server.Host.Models.RepositorySettings", b =>
431  {
432  b.Property<long>("Id")
433  .ValueGeneratedOnAdd()
434  .HasColumnType("bigint")
435  .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
436 
437  b.Property<string>("AccessToken")
438  .HasColumnType("character varying(10000)")
439  .HasMaxLength(10000);
440 
441  b.Property<string>("AccessUser")
442  .HasColumnType("character varying(10000)")
443  .HasMaxLength(10000);
444 
445  b.Property<bool?>("AutoUpdatesKeepTestMerges")
446  .IsRequired()
447  .HasColumnType("boolean");
448 
449  b.Property<bool?>("AutoUpdatesSynchronize")
450  .IsRequired()
451  .HasColumnType("boolean");
452 
453  b.Property<string>("CommitterEmail")
454  .IsRequired()
455  .HasColumnType("character varying(10000)")
456  .HasMaxLength(10000);
457 
458  b.Property<string>("CommitterName")
459  .IsRequired()
460  .HasColumnType("character varying(10000)")
461  .HasMaxLength(10000);
462 
463  b.Property<long>("InstanceId")
464  .HasColumnType("bigint");
465 
466  b.Property<bool?>("PostTestMergeComment")
467  .IsRequired()
468  .HasColumnType("boolean");
469 
470  b.Property<bool?>("PushTestMergeCommits")
471  .IsRequired()
472  .HasColumnType("boolean");
473 
474  b.Property<bool?>("ShowTestMergeCommitters")
475  .IsRequired()
476  .HasColumnType("boolean");
477 
478  b.HasKey("Id");
479 
480  b.HasIndex("InstanceId")
481  .IsUnique();
482 
483  b.ToTable("RepositorySettings");
484  });
485 
486  modelBuilder.Entity("Tgstation.Server.Host.Models.RevInfoTestMerge", b =>
487  {
488  b.Property<long>("Id")
489  .ValueGeneratedOnAdd()
490  .HasColumnType("bigint")
491  .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
492 
493  b.Property<long>("RevisionInformationId")
494  .HasColumnType("bigint");
495 
496  b.Property<long>("TestMergeId")
497  .HasColumnType("bigint");
498 
499  b.HasKey("Id");
500 
501  b.HasIndex("RevisionInformationId");
502 
503  b.HasIndex("TestMergeId");
504 
505  b.ToTable("RevInfoTestMerges");
506  });
507 
508  modelBuilder.Entity("Tgstation.Server.Host.Models.RevisionInformation", b =>
509  {
510  b.Property<long>("Id")
511  .ValueGeneratedOnAdd()
512  .HasColumnType("bigint")
513  .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
514 
515  b.Property<string>("CommitSha")
516  .IsRequired()
517  .HasColumnType("character varying(40)")
518  .HasMaxLength(40);
519 
520  b.Property<long>("InstanceId")
521  .HasColumnType("bigint");
522 
523  b.Property<string>("OriginCommitSha")
524  .IsRequired()
525  .HasColumnType("character varying(40)")
526  .HasMaxLength(40);
527 
528  b.HasKey("Id");
529 
530  b.HasIndex("InstanceId", "CommitSha")
531  .IsUnique();
532 
533  b.ToTable("RevisionInformations");
534  });
535 
536  modelBuilder.Entity("Tgstation.Server.Host.Models.TestMerge", b =>
537  {
538  b.Property<long>("Id")
539  .ValueGeneratedOnAdd()
540  .HasColumnType("bigint")
541  .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
542 
543  b.Property<string>("Author")
544  .IsRequired()
545  .HasColumnType("text");
546 
547  b.Property<string>("BodyAtMerge")
548  .IsRequired()
549  .HasColumnType("text");
550 
551  b.Property<string>("Comment")
552  .HasColumnType("character varying(10000)")
553  .HasMaxLength(10000);
554 
555  b.Property<DateTimeOffset>("MergedAt")
556  .HasColumnType("timestamp with time zone");
557 
558  b.Property<long>("MergedById")
559  .HasColumnType("bigint");
560 
561  b.Property<int>("Number")
562  .HasColumnType("integer");
563 
564  b.Property<long?>("PrimaryRevisionInformationId")
565  .IsRequired()
566  .HasColumnType("bigint");
567 
568  b.Property<string>("PullRequestRevision")
569  .IsRequired()
570  .HasColumnType("character varying(40)")
571  .HasMaxLength(40);
572 
573  b.Property<string>("TitleAtMerge")
574  .IsRequired()
575  .HasColumnType("text");
576 
577  b.Property<string>("Url")
578  .IsRequired()
579  .HasColumnType("text");
580 
581  b.HasKey("Id");
582 
583  b.HasIndex("MergedById");
584 
585  b.HasIndex("PrimaryRevisionInformationId")
586  .IsUnique();
587 
588  b.ToTable("TestMerges");
589  });
590 
591  modelBuilder.Entity("Tgstation.Server.Host.Models.User", b =>
592  {
593  b.Property<long?>("Id")
594  .ValueGeneratedOnAdd()
595  .HasColumnType("bigint")
596  .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
597 
598  b.Property<decimal>("AdministrationRights")
599  .HasColumnType("numeric(20,0)");
600 
601  b.Property<string>("CanonicalName")
602  .IsRequired()
603  .HasColumnType("text");
604 
605  b.Property<DateTimeOffset?>("CreatedAt")
606  .IsRequired()
607  .HasColumnType("timestamp with time zone");
608 
609  b.Property<long?>("CreatedById")
610  .HasColumnType("bigint");
611 
612  b.Property<bool?>("Enabled")
613  .IsRequired()
614  .HasColumnType("boolean");
615 
616  b.Property<decimal>("InstanceManagerRights")
617  .HasColumnType("numeric(20,0)");
618 
619  b.Property<DateTimeOffset?>("LastPasswordUpdate")
620  .HasColumnType("timestamp with time zone");
621 
622  b.Property<string>("Name")
623  .IsRequired()
624  .HasColumnType("character varying(10000)")
625  .HasMaxLength(10000);
626 
627  b.Property<string>("PasswordHash")
628  .HasColumnType("text");
629 
630  b.Property<string>("SystemIdentifier")
631  .HasColumnType("text");
632 
633  b.HasKey("Id");
634 
635  b.HasIndex("CanonicalName")
636  .IsUnique();
637 
638  b.HasIndex("CreatedById");
639 
640  b.HasIndex("SystemIdentifier")
641  .IsUnique();
642 
643  b.ToTable("Users");
644  });
645 
646  modelBuilder.Entity("Tgstation.Server.Host.Models.ChatBot", b =>
647  {
648  b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
649  .WithMany("ChatSettings")
650  .HasForeignKey("InstanceId")
651  .OnDelete(DeleteBehavior.Cascade)
652  .IsRequired();
653  });
654 
655  modelBuilder.Entity("Tgstation.Server.Host.Models.ChatChannel", b =>
656  {
657  b.HasOne("Tgstation.Server.Host.Models.ChatBot", "ChatSettings")
658  .WithMany("Channels")
659  .HasForeignKey("ChatSettingsId")
660  .OnDelete(DeleteBehavior.Cascade)
661  .IsRequired();
662  });
663 
664  modelBuilder.Entity("Tgstation.Server.Host.Models.CompileJob", b =>
665  {
666  b.HasOne("Tgstation.Server.Host.Models.Job", "Job")
667  .WithOne()
668  .HasForeignKey("Tgstation.Server.Host.Models.CompileJob", "JobId")
669  .OnDelete(DeleteBehavior.Cascade)
670  .IsRequired();
671 
672  b.HasOne("Tgstation.Server.Host.Models.RevisionInformation", "RevisionInformation")
673  .WithMany("CompileJobs")
674  .HasForeignKey("RevisionInformationId")
675  .OnDelete(DeleteBehavior.ClientNoAction)
676  .IsRequired();
677  });
678 
679  modelBuilder.Entity("Tgstation.Server.Host.Models.DreamDaemonSettings", b =>
680  {
681  b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
682  .WithOne("DreamDaemonSettings")
683  .HasForeignKey("Tgstation.Server.Host.Models.DreamDaemonSettings", "InstanceId")
684  .OnDelete(DeleteBehavior.Cascade)
685  .IsRequired();
686  });
687 
688  modelBuilder.Entity("Tgstation.Server.Host.Models.DreamMakerSettings", b =>
689  {
690  b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
691  .WithOne("DreamMakerSettings")
692  .HasForeignKey("Tgstation.Server.Host.Models.DreamMakerSettings", "InstanceId")
693  .OnDelete(DeleteBehavior.Cascade)
694  .IsRequired();
695  });
696 
697  modelBuilder.Entity("Tgstation.Server.Host.Models.DualReattachInformation", b =>
698  {
699  b.HasOne("Tgstation.Server.Host.Models.ReattachInformation", "Alpha")
700  .WithMany()
701  .HasForeignKey("AlphaId");
702 
703  b.HasOne("Tgstation.Server.Host.Models.ReattachInformation", "Bravo")
704  .WithMany()
705  .HasForeignKey("BravoId");
706 
707  b.HasOne("Tgstation.Server.Host.Models.Instance", null)
708  .WithOne("WatchdogReattachInformation")
709  .HasForeignKey("Tgstation.Server.Host.Models.DualReattachInformation", "InstanceId")
710  .OnDelete(DeleteBehavior.Cascade)
711  .IsRequired();
712  });
713 
714  modelBuilder.Entity("Tgstation.Server.Host.Models.InstanceUser", b =>
715  {
716  b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
717  .WithMany("InstanceUsers")
718  .HasForeignKey("InstanceId")
719  .OnDelete(DeleteBehavior.Cascade)
720  .IsRequired();
721 
722  b.HasOne("Tgstation.Server.Host.Models.User", null)
723  .WithMany("InstanceUsers")
724  .HasForeignKey("UserId")
725  .OnDelete(DeleteBehavior.Cascade)
726  .IsRequired();
727  });
728 
729  modelBuilder.Entity("Tgstation.Server.Host.Models.Job", b =>
730  {
731  b.HasOne("Tgstation.Server.Host.Models.User", "CancelledBy")
732  .WithMany()
733  .HasForeignKey("CancelledById");
734 
735  b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
736  .WithMany("Jobs")
737  .HasForeignKey("InstanceId")
738  .OnDelete(DeleteBehavior.Cascade)
739  .IsRequired();
740 
741  b.HasOne("Tgstation.Server.Host.Models.User", "StartedBy")
742  .WithMany()
743  .HasForeignKey("StartedById")
744  .OnDelete(DeleteBehavior.Cascade)
745  .IsRequired();
746  });
747 
748  modelBuilder.Entity("Tgstation.Server.Host.Models.ReattachInformation", b =>
749  {
750  b.HasOne("Tgstation.Server.Host.Models.CompileJob", "CompileJob")
751  .WithMany()
752  .HasForeignKey("CompileJobId")
753  .OnDelete(DeleteBehavior.Cascade)
754  .IsRequired();
755  });
756 
757  modelBuilder.Entity("Tgstation.Server.Host.Models.RepositorySettings", b =>
758  {
759  b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
760  .WithOne("RepositorySettings")
761  .HasForeignKey("Tgstation.Server.Host.Models.RepositorySettings", "InstanceId")
762  .OnDelete(DeleteBehavior.Cascade)
763  .IsRequired();
764  });
765 
766  modelBuilder.Entity("Tgstation.Server.Host.Models.RevInfoTestMerge", b =>
767  {
768  b.HasOne("Tgstation.Server.Host.Models.RevisionInformation", "RevisionInformation")
769  .WithMany("ActiveTestMerges")
770  .HasForeignKey("RevisionInformationId")
771  .OnDelete(DeleteBehavior.Cascade)
772  .IsRequired();
773 
774  b.HasOne("Tgstation.Server.Host.Models.TestMerge", "TestMerge")
775  .WithMany("RevisonInformations")
776  .HasForeignKey("TestMergeId")
777  .OnDelete(DeleteBehavior.ClientNoAction)
778  .IsRequired();
779  });
780 
781  modelBuilder.Entity("Tgstation.Server.Host.Models.RevisionInformation", b =>
782  {
783  b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
784  .WithMany("RevisionInformations")
785  .HasForeignKey("InstanceId")
786  .OnDelete(DeleteBehavior.Cascade)
787  .IsRequired();
788  });
789 
790  modelBuilder.Entity("Tgstation.Server.Host.Models.TestMerge", b =>
791  {
792  b.HasOne("Tgstation.Server.Host.Models.User", "MergedBy")
793  .WithMany("TestMerges")
794  .HasForeignKey("MergedById")
795  .OnDelete(DeleteBehavior.Restrict)
796  .IsRequired();
797 
798  b.HasOne("Tgstation.Server.Host.Models.RevisionInformation", "PrimaryRevisionInformation")
799  .WithOne("PrimaryTestMerge")
800  .HasForeignKey("Tgstation.Server.Host.Models.TestMerge", "PrimaryRevisionInformationId")
801  .OnDelete(DeleteBehavior.Cascade)
802  .IsRequired();
803  });
804 
805  modelBuilder.Entity("Tgstation.Server.Host.Models.User", b =>
806  {
807  b.HasOne("Tgstation.Server.Host.Models.User", "CreatedBy")
808  .WithMany("CreatedUsers")
809  .HasForeignKey("CreatedById");
810  });
811 #pragma warning restore 612, 618
812  }
813  }
814 }
+Go to the documentation of this file.
1 // <auto-generated />
2 using System;
3 using Microsoft.EntityFrameworkCore;
4 using Microsoft.EntityFrameworkCore.Infrastructure;
5 using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
7 
8 namespace Tgstation.Server.Host.Migrations
9 {
12  {
13  protected override void BuildModel(ModelBuilder modelBuilder)
14  {
15 #pragma warning disable 612, 618
16  modelBuilder
17  .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn)
18  .HasAnnotation("ProductVersion", "3.1.4")
19  .HasAnnotation("Relational:MaxIdentifierLength", 63);
20 
21  modelBuilder.Entity("Tgstation.Server.Host.Models.ChatBot", b =>
22  {
23  b.Property<long>("Id")
24  .ValueGeneratedOnAdd()
25  .HasColumnType("bigint")
26  .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
27 
28  b.Property<int>("ChannelLimit")
29  .HasColumnType("integer");
30 
31  b.Property<string>("ConnectionString")
32  .IsRequired()
33  .HasColumnType("character varying(10000)")
34  .HasMaxLength(10000);
35 
36  b.Property<bool?>("Enabled")
37  .HasColumnType("boolean");
38 
39  b.Property<long>("InstanceId")
40  .HasColumnType("bigint");
41 
42  b.Property<string>("Name")
43  .IsRequired()
44  .HasColumnType("character varying(100)")
45  .HasMaxLength(100);
46 
47  b.Property<int>("Provider")
48  .HasColumnType("integer");
49 
50  b.Property<long>("ReconnectionInterval")
51  .HasColumnType("bigint");
52 
53  b.HasKey("Id");
54 
55  b.HasIndex("InstanceId", "Name")
56  .IsUnique();
57 
58  b.ToTable("ChatBots");
59  });
60 
61  modelBuilder.Entity("Tgstation.Server.Host.Models.ChatChannel", b =>
62  {
63  b.Property<long>("Id")
64  .ValueGeneratedOnAdd()
65  .HasColumnType("bigint")
66  .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
67 
68  b.Property<long>("ChatSettingsId")
69  .HasColumnType("bigint");
70 
71  b.Property<decimal?>("DiscordChannelId")
72  .HasColumnType("numeric(20,0)");
73 
74  b.Property<string>("IrcChannel")
75  .HasColumnType("character varying(100)")
76  .HasMaxLength(100);
77 
78  b.Property<bool?>("IsAdminChannel")
79  .IsRequired()
80  .HasColumnType("boolean");
81 
82  b.Property<bool?>("IsUpdatesChannel")
83  .IsRequired()
84  .HasColumnType("boolean");
85 
86  b.Property<bool?>("IsWatchdogChannel")
87  .IsRequired()
88  .HasColumnType("boolean");
89 
90  b.Property<string>("Tag")
91  .HasColumnType("character varying(10000)")
92  .HasMaxLength(10000);
93 
94  b.HasKey("Id");
95 
96  b.HasIndex("ChatSettingsId", "DiscordChannelId")
97  .IsUnique();
98 
99  b.HasIndex("ChatSettingsId", "IrcChannel")
100  .IsUnique();
101 
102  b.ToTable("ChatChannels");
103  });
104 
105  modelBuilder.Entity("Tgstation.Server.Host.Models.CompileJob", b =>
106  {
107  b.Property<long>("Id")
108  .ValueGeneratedOnAdd()
109  .HasColumnType("bigint")
110  .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
111 
112  b.Property<string>("ByondVersion")
113  .IsRequired()
114  .HasColumnType("text");
115 
116  b.Property<int?>("DMApiMajorVersion")
117  .HasColumnType("integer");
118 
119  b.Property<int?>("DMApiMinorVersion")
120  .HasColumnType("integer");
121 
122  b.Property<int?>("DMApiPatchVersion")
123  .HasColumnType("integer");
124 
125  b.Property<Guid?>("DirectoryName")
126  .IsRequired()
127  .HasColumnType("uuid");
128 
129  b.Property<string>("DmeName")
130  .IsRequired()
131  .HasColumnType("text");
132 
133  b.Property<long>("JobId")
134  .HasColumnType("bigint");
135 
136  b.Property<int>("MinimumSecurityLevel")
137  .HasColumnType("integer");
138 
139  b.Property<string>("Output")
140  .IsRequired()
141  .HasColumnType("text");
142 
143  b.Property<long>("RevisionInformationId")
144  .HasColumnType("bigint");
145 
146  b.HasKey("Id");
147 
148  b.HasIndex("DirectoryName");
149 
150  b.HasIndex("JobId")
151  .IsUnique();
152 
153  b.HasIndex("RevisionInformationId");
154 
155  b.ToTable("CompileJobs");
156  });
157 
158  modelBuilder.Entity("Tgstation.Server.Host.Models.DreamDaemonSettings", b =>
159  {
160  b.Property<long>("Id")
161  .ValueGeneratedOnAdd()
162  .HasColumnType("bigint")
163  .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
164 
165  b.Property<bool?>("AllowWebClient")
166  .IsRequired()
167  .HasColumnType("boolean");
168 
169  b.Property<bool?>("AutoStart")
170  .IsRequired()
171  .HasColumnType("boolean");
172 
173  b.Property<long>("HeartbeatSeconds")
174  .HasColumnType("bigint");
175 
176  b.Property<long>("InstanceId")
177  .HasColumnType("bigint");
178 
179  b.Property<int>("PrimaryPort")
180  .HasColumnType("integer");
181 
182  b.Property<int>("SecondaryPort")
183  .HasColumnType("integer");
184 
185  b.Property<int>("SecurityLevel")
186  .HasColumnType("integer");
187 
188  b.Property<long>("StartupTimeout")
189  .HasColumnType("bigint");
190 
191  b.HasKey("Id");
192 
193  b.HasIndex("InstanceId")
194  .IsUnique();
195 
196  b.ToTable("DreamDaemonSettings");
197  });
198 
199  modelBuilder.Entity("Tgstation.Server.Host.Models.DreamMakerSettings", b =>
200  {
201  b.Property<long>("Id")
202  .ValueGeneratedOnAdd()
203  .HasColumnType("bigint")
204  .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
205 
206  b.Property<int>("ApiValidationPort")
207  .HasColumnType("integer");
208 
209  b.Property<int>("ApiValidationSecurityLevel")
210  .HasColumnType("integer");
211 
212  b.Property<long>("InstanceId")
213  .HasColumnType("bigint");
214 
215  b.Property<string>("ProjectName")
216  .HasColumnType("character varying(10000)")
217  .HasMaxLength(10000);
218 
219  b.HasKey("Id");
220 
221  b.HasIndex("InstanceId")
222  .IsUnique();
223 
224  b.ToTable("DreamMakerSettings");
225  });
226 
227  modelBuilder.Entity("Tgstation.Server.Host.Models.DualReattachInformation", b =>
228  {
229  b.Property<long>("Id")
230  .ValueGeneratedOnAdd()
231  .HasColumnType("bigint")
232  .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
233 
234  b.Property<long?>("AlphaId")
235  .HasColumnType("bigint");
236 
237  b.Property<bool>("AlphaIsActive")
238  .HasColumnType("boolean");
239 
240  b.Property<long?>("BravoId")
241  .HasColumnType("bigint");
242 
243  b.Property<long>("InstanceId")
244  .HasColumnType("bigint");
245 
246  b.HasKey("Id");
247 
248  b.HasIndex("AlphaId");
249 
250  b.HasIndex("BravoId");
251 
252  b.HasIndex("InstanceId")
253  .IsUnique();
254 
255  b.ToTable("WatchdogReattachInformations");
256  });
257 
258  modelBuilder.Entity("Tgstation.Server.Host.Models.Instance", b =>
259  {
260  b.Property<long>("Id")
261  .ValueGeneratedOnAdd()
262  .HasColumnType("bigint")
263  .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
264 
265  b.Property<long>("AutoUpdateInterval")
266  .HasColumnType("bigint");
267 
268  b.Property<int>("ChatBotLimit")
269  .HasColumnType("integer");
270 
271  b.Property<int>("ConfigurationType")
272  .HasColumnType("integer");
273 
274  b.Property<string>("Name")
275  .IsRequired()
276  .HasColumnType("character varying(10000)")
277  .HasMaxLength(10000);
278 
279  b.Property<bool?>("Online")
280  .IsRequired()
281  .HasColumnType("boolean");
282 
283  b.Property<string>("Path")
284  .IsRequired()
285  .HasColumnType("text");
286 
287  b.HasKey("Id");
288 
289  b.HasIndex("Path")
290  .IsUnique();
291 
292  b.ToTable("Instances");
293  });
294 
295  modelBuilder.Entity("Tgstation.Server.Host.Models.InstanceUser", b =>
296  {
297  b.Property<long>("Id")
298  .ValueGeneratedOnAdd()
299  .HasColumnType("bigint")
300  .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
301 
302  b.Property<decimal>("ByondRights")
303  .HasColumnType("numeric(20,0)");
304 
305  b.Property<decimal>("ChatBotRights")
306  .HasColumnType("numeric(20,0)");
307 
308  b.Property<decimal>("ConfigurationRights")
309  .HasColumnType("numeric(20,0)");
310 
311  b.Property<decimal>("DreamDaemonRights")
312  .HasColumnType("numeric(20,0)");
313 
314  b.Property<decimal>("DreamMakerRights")
315  .HasColumnType("numeric(20,0)");
316 
317  b.Property<long>("InstanceId")
318  .HasColumnType("bigint");
319 
320  b.Property<decimal>("InstanceUserRights")
321  .HasColumnType("numeric(20,0)");
322 
323  b.Property<decimal>("RepositoryRights")
324  .HasColumnType("numeric(20,0)");
325 
326  b.Property<long?>("UserId")
327  .IsRequired()
328  .HasColumnType("bigint");
329 
330  b.HasKey("Id");
331 
332  b.HasIndex("InstanceId");
333 
334  b.HasIndex("UserId", "InstanceId")
335  .IsUnique();
336 
337  b.ToTable("InstanceUsers");
338  });
339 
340  modelBuilder.Entity("Tgstation.Server.Host.Models.Job", b =>
341  {
342  b.Property<long>("Id")
343  .ValueGeneratedOnAdd()
344  .HasColumnType("bigint")
345  .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
346 
347  b.Property<decimal?>("CancelRight")
348  .HasColumnType("numeric(20,0)");
349 
350  b.Property<decimal?>("CancelRightsType")
351  .HasColumnType("numeric(20,0)");
352 
353  b.Property<bool?>("Cancelled")
354  .IsRequired()
355  .HasColumnType("boolean");
356 
357  b.Property<long?>("CancelledById")
358  .HasColumnType("bigint");
359 
360  b.Property<string>("Description")
361  .IsRequired()
362  .HasColumnType("text");
363 
364  b.Property<long?>("ErrorCode")
365  .HasColumnType("bigint");
366 
367  b.Property<string>("ExceptionDetails")
368  .HasColumnType("text");
369 
370  b.Property<long>("InstanceId")
371  .HasColumnType("bigint");
372 
373  b.Property<DateTimeOffset?>("StartedAt")
374  .IsRequired()
375  .HasColumnType("timestamp with time zone");
376 
377  b.Property<long>("StartedById")
378  .HasColumnType("bigint");
379 
380  b.Property<DateTimeOffset?>("StoppedAt")
381  .HasColumnType("timestamp with time zone");
382 
383  b.HasKey("Id");
384 
385  b.HasIndex("CancelledById");
386 
387  b.HasIndex("InstanceId");
388 
389  b.HasIndex("StartedById");
390 
391  b.ToTable("Jobs");
392  });
393 
394  modelBuilder.Entity("Tgstation.Server.Host.Models.ReattachInformation", b =>
395  {
396  b.Property<long>("Id")
397  .ValueGeneratedOnAdd()
398  .HasColumnType("bigint")
399  .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
400 
401  b.Property<string>("AccessIdentifier")
402  .IsRequired()
403  .HasColumnType("text");
404 
405  b.Property<long>("CompileJobId")
406  .HasColumnType("bigint");
407 
408  b.Property<bool>("IsPrimary")
409  .HasColumnType("boolean");
410 
411  b.Property<int>("LaunchSecurityLevel")
412  .HasColumnType("integer");
413 
414  b.Property<int>("Port")
415  .HasColumnType("integer");
416 
417  b.Property<int>("ProcessId")
418  .HasColumnType("integer");
419 
420  b.Property<int>("RebootState")
421  .HasColumnType("integer");
422 
423  b.HasKey("Id");
424 
425  b.HasIndex("CompileJobId");
426 
427  b.ToTable("ReattachInformations");
428  });
429 
430  modelBuilder.Entity("Tgstation.Server.Host.Models.RepositorySettings", b =>
431  {
432  b.Property<long>("Id")
433  .ValueGeneratedOnAdd()
434  .HasColumnType("bigint")
435  .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
436 
437  b.Property<string>("AccessToken")
438  .HasColumnType("character varying(10000)")
439  .HasMaxLength(10000);
440 
441  b.Property<string>("AccessUser")
442  .HasColumnType("character varying(10000)")
443  .HasMaxLength(10000);
444 
445  b.Property<bool?>("AutoUpdatesKeepTestMerges")
446  .IsRequired()
447  .HasColumnType("boolean");
448 
449  b.Property<bool?>("AutoUpdatesSynchronize")
450  .IsRequired()
451  .HasColumnType("boolean");
452 
453  b.Property<string>("CommitterEmail")
454  .IsRequired()
455  .HasColumnType("character varying(10000)")
456  .HasMaxLength(10000);
457 
458  b.Property<string>("CommitterName")
459  .IsRequired()
460  .HasColumnType("character varying(10000)")
461  .HasMaxLength(10000);
462 
463  b.Property<long>("InstanceId")
464  .HasColumnType("bigint");
465 
466  b.Property<bool?>("PostTestMergeComment")
467  .IsRequired()
468  .HasColumnType("boolean");
469 
470  b.Property<bool?>("PushTestMergeCommits")
471  .IsRequired()
472  .HasColumnType("boolean");
473 
474  b.Property<bool?>("ShowTestMergeCommitters")
475  .IsRequired()
476  .HasColumnType("boolean");
477 
478  b.HasKey("Id");
479 
480  b.HasIndex("InstanceId")
481  .IsUnique();
482 
483  b.ToTable("RepositorySettings");
484  });
485 
486  modelBuilder.Entity("Tgstation.Server.Host.Models.RevInfoTestMerge", b =>
487  {
488  b.Property<long>("Id")
489  .ValueGeneratedOnAdd()
490  .HasColumnType("bigint")
491  .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
492 
493  b.Property<long>("RevisionInformationId")
494  .HasColumnType("bigint");
495 
496  b.Property<long>("TestMergeId")
497  .HasColumnType("bigint");
498 
499  b.HasKey("Id");
500 
501  b.HasIndex("RevisionInformationId");
502 
503  b.HasIndex("TestMergeId");
504 
505  b.ToTable("RevInfoTestMerges");
506  });
507 
508  modelBuilder.Entity("Tgstation.Server.Host.Models.RevisionInformation", b =>
509  {
510  b.Property<long>("Id")
511  .ValueGeneratedOnAdd()
512  .HasColumnType("bigint")
513  .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
514 
515  b.Property<string>("CommitSha")
516  .IsRequired()
517  .HasColumnType("character varying(40)")
518  .HasMaxLength(40);
519 
520  b.Property<long>("InstanceId")
521  .HasColumnType("bigint");
522 
523  b.Property<string>("OriginCommitSha")
524  .IsRequired()
525  .HasColumnType("character varying(40)")
526  .HasMaxLength(40);
527 
528  b.HasKey("Id");
529 
530  b.HasIndex("InstanceId", "CommitSha")
531  .IsUnique();
532 
533  b.ToTable("RevisionInformations");
534  });
535 
536  modelBuilder.Entity("Tgstation.Server.Host.Models.TestMerge", b =>
537  {
538  b.Property<long>("Id")
539  .ValueGeneratedOnAdd()
540  .HasColumnType("bigint")
541  .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
542 
543  b.Property<string>("Author")
544  .IsRequired()
545  .HasColumnType("text");
546 
547  b.Property<string>("BodyAtMerge")
548  .IsRequired()
549  .HasColumnType("text");
550 
551  b.Property<string>("Comment")
552  .HasColumnType("character varying(10000)")
553  .HasMaxLength(10000);
554 
555  b.Property<DateTimeOffset>("MergedAt")
556  .HasColumnType("timestamp with time zone");
557 
558  b.Property<long>("MergedById")
559  .HasColumnType("bigint");
560 
561  b.Property<int>("Number")
562  .HasColumnType("integer");
563 
564  b.Property<long?>("PrimaryRevisionInformationId")
565  .IsRequired()
566  .HasColumnType("bigint");
567 
568  b.Property<string>("PullRequestRevision")
569  .IsRequired()
570  .HasColumnType("character varying(40)")
571  .HasMaxLength(40);
572 
573  b.Property<string>("TitleAtMerge")
574  .IsRequired()
575  .HasColumnType("text");
576 
577  b.Property<string>("Url")
578  .IsRequired()
579  .HasColumnType("text");
580 
581  b.HasKey("Id");
582 
583  b.HasIndex("MergedById");
584 
585  b.HasIndex("PrimaryRevisionInformationId")
586  .IsUnique();
587 
588  b.ToTable("TestMerges");
589  });
590 
591  modelBuilder.Entity("Tgstation.Server.Host.Models.User", b =>
592  {
593  b.Property<long?>("Id")
594  .ValueGeneratedOnAdd()
595  .HasColumnType("bigint")
596  .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
597 
598  b.Property<decimal>("AdministrationRights")
599  .HasColumnType("numeric(20,0)");
600 
601  b.Property<string>("CanonicalName")
602  .IsRequired()
603  .HasColumnType("text");
604 
605  b.Property<DateTimeOffset?>("CreatedAt")
606  .IsRequired()
607  .HasColumnType("timestamp with time zone");
608 
609  b.Property<long?>("CreatedById")
610  .HasColumnType("bigint");
611 
612  b.Property<bool?>("Enabled")
613  .IsRequired()
614  .HasColumnType("boolean");
615 
616  b.Property<decimal>("InstanceManagerRights")
617  .HasColumnType("numeric(20,0)");
618 
619  b.Property<DateTimeOffset?>("LastPasswordUpdate")
620  .HasColumnType("timestamp with time zone");
621 
622  b.Property<string>("Name")
623  .IsRequired()
624  .HasColumnType("character varying(10000)")
625  .HasMaxLength(10000);
626 
627  b.Property<string>("PasswordHash")
628  .HasColumnType("text");
629 
630  b.Property<string>("SystemIdentifier")
631  .HasColumnType("text");
632 
633  b.HasKey("Id");
634 
635  b.HasIndex("CanonicalName")
636  .IsUnique();
637 
638  b.HasIndex("CreatedById");
639 
640  b.HasIndex("SystemIdentifier")
641  .IsUnique();
642 
643  b.ToTable("Users");
644  });
645 
646  modelBuilder.Entity("Tgstation.Server.Host.Models.ChatBot", b =>
647  {
648  b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
649  .WithMany("ChatSettings")
650  .HasForeignKey("InstanceId")
651  .OnDelete(DeleteBehavior.Cascade)
652  .IsRequired();
653  });
654 
655  modelBuilder.Entity("Tgstation.Server.Host.Models.ChatChannel", b =>
656  {
657  b.HasOne("Tgstation.Server.Host.Models.ChatBot", "ChatSettings")
658  .WithMany("Channels")
659  .HasForeignKey("ChatSettingsId")
660  .OnDelete(DeleteBehavior.Cascade)
661  .IsRequired();
662  });
663 
664  modelBuilder.Entity("Tgstation.Server.Host.Models.CompileJob", b =>
665  {
666  b.HasOne("Tgstation.Server.Host.Models.Job", "Job")
667  .WithOne()
668  .HasForeignKey("Tgstation.Server.Host.Models.CompileJob", "JobId")
669  .OnDelete(DeleteBehavior.Cascade)
670  .IsRequired();
671 
672  b.HasOne("Tgstation.Server.Host.Models.RevisionInformation", "RevisionInformation")
673  .WithMany("CompileJobs")
674  .HasForeignKey("RevisionInformationId")
675  .OnDelete(DeleteBehavior.Cascade)
676  .IsRequired();
677  });
678 
679  modelBuilder.Entity("Tgstation.Server.Host.Models.DreamDaemonSettings", b =>
680  {
681  b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
682  .WithOne("DreamDaemonSettings")
683  .HasForeignKey("Tgstation.Server.Host.Models.DreamDaemonSettings", "InstanceId")
684  .OnDelete(DeleteBehavior.Cascade)
685  .IsRequired();
686  });
687 
688  modelBuilder.Entity("Tgstation.Server.Host.Models.DreamMakerSettings", b =>
689  {
690  b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
691  .WithOne("DreamMakerSettings")
692  .HasForeignKey("Tgstation.Server.Host.Models.DreamMakerSettings", "InstanceId")
693  .OnDelete(DeleteBehavior.Cascade)
694  .IsRequired();
695  });
696 
697  modelBuilder.Entity("Tgstation.Server.Host.Models.DualReattachInformation", b =>
698  {
699  b.HasOne("Tgstation.Server.Host.Models.ReattachInformation", "Alpha")
700  .WithMany()
701  .HasForeignKey("AlphaId");
702 
703  b.HasOne("Tgstation.Server.Host.Models.ReattachInformation", "Bravo")
704  .WithMany()
705  .HasForeignKey("BravoId");
706 
707  b.HasOne("Tgstation.Server.Host.Models.Instance", null)
708  .WithOne("WatchdogReattachInformation")
709  .HasForeignKey("Tgstation.Server.Host.Models.DualReattachInformation", "InstanceId")
710  .OnDelete(DeleteBehavior.Cascade)
711  .IsRequired();
712  });
713 
714  modelBuilder.Entity("Tgstation.Server.Host.Models.InstanceUser", b =>
715  {
716  b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
717  .WithMany("InstanceUsers")
718  .HasForeignKey("InstanceId")
719  .OnDelete(DeleteBehavior.Cascade)
720  .IsRequired();
721 
722  b.HasOne("Tgstation.Server.Host.Models.User", null)
723  .WithMany("InstanceUsers")
724  .HasForeignKey("UserId")
725  .OnDelete(DeleteBehavior.Cascade)
726  .IsRequired();
727  });
728 
729  modelBuilder.Entity("Tgstation.Server.Host.Models.Job", b =>
730  {
731  b.HasOne("Tgstation.Server.Host.Models.User", "CancelledBy")
732  .WithMany()
733  .HasForeignKey("CancelledById");
734 
735  b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
736  .WithMany("Jobs")
737  .HasForeignKey("InstanceId")
738  .OnDelete(DeleteBehavior.Cascade)
739  .IsRequired();
740 
741  b.HasOne("Tgstation.Server.Host.Models.User", "StartedBy")
742  .WithMany()
743  .HasForeignKey("StartedById")
744  .OnDelete(DeleteBehavior.Cascade)
745  .IsRequired();
746  });
747 
748  modelBuilder.Entity("Tgstation.Server.Host.Models.ReattachInformation", b =>
749  {
750  b.HasOne("Tgstation.Server.Host.Models.CompileJob", "CompileJob")
751  .WithMany()
752  .HasForeignKey("CompileJobId")
753  .OnDelete(DeleteBehavior.Cascade)
754  .IsRequired();
755  });
756 
757  modelBuilder.Entity("Tgstation.Server.Host.Models.RepositorySettings", b =>
758  {
759  b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
760  .WithOne("RepositorySettings")
761  .HasForeignKey("Tgstation.Server.Host.Models.RepositorySettings", "InstanceId")
762  .OnDelete(DeleteBehavior.Cascade)
763  .IsRequired();
764  });
765 
766  modelBuilder.Entity("Tgstation.Server.Host.Models.RevInfoTestMerge", b =>
767  {
768  b.HasOne("Tgstation.Server.Host.Models.RevisionInformation", "RevisionInformation")
769  .WithMany("ActiveTestMerges")
770  .HasForeignKey("RevisionInformationId")
771  .OnDelete(DeleteBehavior.Cascade)
772  .IsRequired();
773 
774  b.HasOne("Tgstation.Server.Host.Models.TestMerge", "TestMerge")
775  .WithMany("RevisonInformations")
776  .HasForeignKey("TestMergeId")
777  .OnDelete(DeleteBehavior.ClientNoAction)
778  .IsRequired();
779  });
780 
781  modelBuilder.Entity("Tgstation.Server.Host.Models.RevisionInformation", b =>
782  {
783  b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
784  .WithMany("RevisionInformations")
785  .HasForeignKey("InstanceId")
786  .OnDelete(DeleteBehavior.Cascade)
787  .IsRequired();
788  });
789 
790  modelBuilder.Entity("Tgstation.Server.Host.Models.TestMerge", b =>
791  {
792  b.HasOne("Tgstation.Server.Host.Models.User", "MergedBy")
793  .WithMany("TestMerges")
794  .HasForeignKey("MergedById")
795  .OnDelete(DeleteBehavior.Restrict)
796  .IsRequired();
797 
798  b.HasOne("Tgstation.Server.Host.Models.RevisionInformation", "PrimaryRevisionInformation")
799  .WithOne("PrimaryTestMerge")
800  .HasForeignKey("Tgstation.Server.Host.Models.TestMerge", "PrimaryRevisionInformationId")
801  .OnDelete(DeleteBehavior.Cascade)
802  .IsRequired();
803  });
804 
805  modelBuilder.Entity("Tgstation.Server.Host.Models.User", b =>
806  {
807  b.HasOne("Tgstation.Server.Host.Models.User", "CreatedBy")
808  .WithMany("CreatedUsers")
809  .HasForeignKey("CreatedById");
810  });
811 #pragma warning restore 612, 618
812  }
813  }
814 }
- + diff --git a/_postgres_sql_design_time_db_context_factory_8cs_source.html b/_postgres_sql_design_time_db_context_factory_8cs_source.html index 2fdc9d470d..e7d7be8258 100644 --- a/_postgres_sql_design_time_db_context_factory_8cs_source.html +++ b/_postgres_sql_design_time_db_context_factory_8cs_source.html @@ -102,7 +102,7 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search'); - +
DatabaseType
Type of database to user
Definition: DatabaseType.cs:6
diff --git a/_r_e_a_d_m_e_8md_source.html b/_r_e_a_d_m_e_8md_source.html index 4e7298ca5d..ea63b48e47 100644 --- a/_r_e_a_d_m_e_8md_source.html +++ b/_r_e_a_d_m_e_8md_source.html @@ -90,7 +90,7 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
README.md
-Go to the documentation of this file.
1 # tgstation-server v4:
2 
3 [![Build status](https://ci.appveyor.com/api/projects/status/7t1h7bvuha0p9j5f/branch/master?svg=true)](https://ci.appveyor.com/project/Cyberboss/tgstation-server-tools/branch/master) [![Build Status](https://travis-ci.org/tgstation/tgstation-server.svg?branch=master)](https://travis-ci.org/tgstation/tgstation-server) [![codecov](https://codecov.io/gh/tgstation/tgstation-server/branch/master/graph/badge.svg)](https://codecov.io/gh/tgstation/tgstation-server)
4 
5 [![GitHub license](https://img.shields.io/github/license/tgstation/tgstation-server.svg)](LICENSE) [![Average time to resolve an issue](http://isitmaintained.com/badge/resolution/tgstation/tgstation-server.svg)](http://isitmaintained.com/project/tgstation/tgstation-server "Average time to resolve an issue") [![NuGet version](https://img.shields.io/nuget/v/Tgstation.Server.Api.svg)](https://www.nuget.org/packages/Tgstation.Server.Api) [![NuGet version](https://img.shields.io/nuget/v/Tgstation.Server.Client.svg)](https://www.nuget.org/packages/Tgstation.Server.Client)
6 
7 [![forthebadge](http://forthebadge.com/images/badges/made-with-c-sharp.svg)](http://forthebadge.com) [![forinfinityandbyond](https://user-images.githubusercontent.com/5211576/29499758-4efff304-85e6-11e7-8267-62919c3688a9.gif)](https://www.reddit.com/r/SS13/comments/5oplxp/what_is_the_main_problem_with_byond_as_an_engine/dclbu1a)
8 
9 [![forthebadge](http://forthebadge.com/images/badges/built-with-love.svg)](http://forthebadge.com) [![forthebadge](http://forthebadge.com/images/badges/60-percent-of-the-time-works-every-time.svg)](http://forthebadge.com)
10 
11 This is a toolset to manage production BYOND servers. It includes the ability to update the server without having to stop or shutdown the server (the update will take effect on a "reboot" of the server) the ability start the server and restart it if it crashes, as well as systems for managing code and game files, and merging GitHub Pull Requests for test deployments.
12 
13 ### Legacy Servers
14 
15 Older server versions can be found in the V# branches of this repository. Note that V4 is nearly fully incompatible with existing installations. Only some static files may be copied over: https://github.com/tgstation/tgstation-server#static-files
16 
17 ## Setup
18 
19 ### Pre-Requisites
20 
21 - [ASP .NET Core Runtime (>= v3.1)](https://dotnet.microsoft.com/download/dotnet-core/current/runtime) (Choose the option to `Run Server Apps` for your system) If you plan to install tgstation-server as a Windows service, you should also ensure that your .NET Framework runtime version is >= v4.7.2 (Download can be found on same page). Ensure that the `dotnet` executable file is in your system's `PATH` variable (or that of the user's that will be running the server).
22 - A [MariaDB](https://downloads.mariadb.org/), MySQL, or [Microsoft SQL Server](https://www.microsoft.com/en-us/download/details.aspx?id=55994) database engine is required
23 
24 ### Installation
25 
26 1. [Download the latest V4 release .zip](https://github.com/tgstation/tgstation-server/releases/latest). The `ServerService` package will only work on Windows. Choose `ServerConsole` if that is not your target OS or you prefer not to use the Windows service.
27 2. Extract the .zip file to where you want the server to run from. Note the account running the server must have write and delete access to the `lib` subdirectory.
28 
29 #### Windows
30 
31 If you wish to install the TGS as a service, run `Tgstation.Server.Host.Service.exe`. It should prompt you to install it. Click `Yes` and accept a potential UAC elevation prompt and the setup wizard should run.
32 
33 #### Linux (Native)
34 
35 We recommend using Docker for Linux installations, see below. The content of this parent section may be skipped if you choose to do so.
36 
37 The following dependencies are required to run tgstation-server on Linux alongside the .NET Core runtime
38 
39 - libc6-i386
40 - libstdc++6:i386
41 - libssl1.0.0
42 - gcc-multilib (Only on 64-bit systems)
43 
44 Note that tgstation-server has only ever been tested on Linux via it's [docker environment](build/Dockerfile#L22). If you are having trouble with something in a native installation, or figure out a required workaround, please contact project maintainers so this documentation may be better updated.
45 
46 #### Docker (Linux)
47 
48 tgstation-server supports running in a docker container and is the recommended deployment method for Linux systems. The official image repository is located at https://hub.docker.com/r/tgstation/server. It can also be built locally by running `docker build . -f build/Dockerfile -t <your tag name>` in the repository root.
49 
50 To create a container run
51 ```sh
52 docker run \
53  -ti \ #start interactive for manual configuration
54  --restart=always \ #if you want maximum uptime
55  --network="host" \ #if your sql server is on the same machine
56  --name="tgs" \ #or whatever else you wanna call it
57  --cap-add=sys_nice \ #allows tgs to schedule DreamDaemon as a higher priority process
58  --init \ #reaps potential zombie processes
59  -p <tgs port>:80 \
60  -p 0.0.0.0:<public game port>:<public game port> \
61  -v /path/to/your/configfile/directory:/config_data \ #only if you want to use manual configuration
62  -v /path/to/store/instances:/tgs4_instances \
63  -v /path/to/your/log/folder:/tgs_logs \
64  tgstation/server:<release version> #replace this with <your tag name> if you built the image locally
65 ```
66 with any additional options you desire (i.e. You'll have to expose more game ports in order to host more than one instance).
67 
68 - Important note about port exposure: The internal port used by DreamDaemon _**MUST**_ match the port you want users to connect on. If it doesn't, you'll still be able to have them connect HOWEVER links from the BYOND hub will point at what DreamDaemon thinks the port is.
69 
70 Note although `/app/lib` is specified as a volume mount point in the `Dockerfile`, unless you REALLY know what you're doing. Do not mount any volumes over this for fear of breaking your container.
71 
72 The configuration option `General:ValidInstancePaths` will be preconfigured to point to `/tgs4_instances`. It is recommended you don't change this.
73 
74 Note that this container is meant to be long running. Updates are handled internally as opposed to at the container level.
75 
76 If using manual configuration, before starting your container make sure the aforementioned `appsettings.Production.json` is setup properly. See below
77 
78 ### Configuring
79 
80 The first time you run TGS4 you should be prompted with a configuration wizard which will guide you through setting up your appsettings.Production.json
81 
82 This wizard will, generally, run whenever the server is launched without detecting the config json. Follow the instructions below to perform this process manually.
83 
84 #### Manual Configuration
85 
86 Create an `appsettings.Production.json` file next to `appsettings.json`. This will override the default settings in appsettings.json with your production settings. There are a few keys meant to be changed by hosts. Modifying any config files while the server is running will trigger a safe restart (Keeps DreamDaemon's running). Note these are all case-sensitive:
87 
88 - `General:MinimumPasswordLength`: Minimum password length requirement for database users
89 
90 - `General:ValidInstancePaths`: Array meant to limit the directories in which instances may be created.
91 
92 - `General:UserLimit`: Maximum number of users that may be created
93 
94 - `General:InstanceLimit`: Maximum number of instances that may be created
95 
96 - `General:GitHubAccessToken`: Specify a GitHub personal access token with no scopes here to highly mitigate the possiblity of 429 response codes from GitHub requests
97 
98 - `FileLogging:Directory`: Override the default directory where server logs are stored. Default is C:/ProgramData/tgstation-server/logs on Windows, /usr/share/tgstation-server/logs otherwise
99 
100 - `FileLogging:LogLevel`: Can be one of `Trace`, `Debug`, `Information`, `Warning`, `Error`, or `Critical`. Restricts what is put into the log files. Currently `Debug` is reccommended for help with error reporting.
101 
102 - `Kestrel:Endpoints:Http:Url`: The URL (i.e. interface and ports) your application should listen on. General use case should be `http://localhost:<port>` for restricted local connections. See the Remote Access section for configuring public access to the World Wide Web. This doesn't need to be changed using the docker setup and should be mapped with the `-p` option instead
103 
104 - `Database:DatabaseType`: Can be one of `SqlServer`, `MariaDB`, `MySql`, or `Sqlite`.
105 
106 - `Database:MySqlServerVersion`: The version of MySql/MariaDB the database resides on, can be left as null for attempted auto detection. Used by the MySQL/MariaDB provider for selection of [certain features](https://github.com/PomeloFoundation/Pomelo.EntityFrameworkCore.MySql/blob/2.2.6/src/EFCore.MySql/Storage/Internal/ServerVersion.cs) ignore at your own risk. A string in the form `<major>.<minor>.<patch>`
107 
108 - `Database:ConnectionString`: Connection string for your database. Click [here](https://www.developerfusion.com/tools/sql-connection-string/) for an SQL Server generator or see [here](https://www.connectionstrings.com/mysql/) for a MySQL guide ([You should probably use '127.0.0.1' instead of 'localhost'](https://stackoverflow.com/questions/19712307/mysql-localhost-127-0-0-1)).
109 
110 - `ControlPanel:Enable`: Enable the javascript based control panel to be served from the server via /index.html
111 
112 - `ControlPanel:AllowAnyOrigin`: Set the Access-Control-Allow-Origin header to * for all responses (also enables all headers and methods)
113 
114 - `ControlPanel:AllowedOrigins`: Set the Access-Control-Allow-Origin headers to this list of origins for all responses (also enables all headers and methods). This is overridden by `ControlPanel:AllowAnyOrigin`
115 
116 ### Database Configuration
117 
118 If using a MariaDB/MySQL server, our client library [recommends you set 'utf8mb4' as your default charset](https://github.com/PomeloFoundation/Pomelo.EntityFrameworkCore.MySql#1-recommended-server-charset) disregard at your own risk.
119 
120 The user created for the application will need the privilege to create databases on the first run, do not create the database for it. Once the initial set of migrations is run, the create right may be revoked. The user should maintain DDL rights though for applying future migrations
121 
122 Note that the ratio of application installations to databases is 1:1. Do not attempt to share a database amongst multiple TGS installations.
123 
124 ### Starting
125 
126 For the Windows service version start the `tgstation-server-4` service. If it fails to start, check the Windows event log under Windows/Application for entries from tgstation-server-4 for errors.
127 
128 For the console version run `dotnet Tgstation.Server.Host.Console.dll` in the installation directory. The `tgs.bat` and `tgs.sh` shell scripts are shortcuts for this. If on Windows, you must do this as admin to give the server permission to install the required DirectX dependency for certain 512 BYOND versions as well as create symlinks.
129 
130 For the docker version run `docker start <your container name>`
131 
132 Test your server is running by visiting the local port in your browser. You should receive a 401 Unauthorized response (You may need to view the developer console). Otherwise an error page will be present.
133 
134 ### Stopping
135 
136 Note that the live detach for DreamDaemon servers is only supported for updates or restarts via the API at this time. Stopping tgstation-server will TERMINATE ALL CHILD DREAMDAEMON SERVERS.
137 
138 For the Windows service version stop the `tgstation-server-4` service
139 
140 For the console version press `Ctrl+C` or send a SIGQUIT to the ORIGINAL dotnet process
141 
142 For the docker version run `docker stop <your container name>`
143 
144 ## Integrating
145 
146 tgstation-server 4 currently REQUIRES the DMAPI to be integrated into any BYOND codebase which plans on being used by it. The integration process is a fairly simple set of code changes.
147 
148 1. Copy the [latest release of the DMAPI](https://github.com/tgstation/tgstation-server/releases) anywhere in your code base. `tgs.dm` can be seperated from the `tgs` folder, but do not modify or move the contents of the `tgs` folder
149 2. Modify your `.dme`(s) to include the `tgs.dm` and `tgs/includes.dm` files (ORDER OF APPEARANCE IS MANDATORY)
150 3. Follow the instructions in `tgs.dm` to integrate the API with your codebase.
151 
152 The DMAPI is fully backwards compatible and should function with any tgstation-server version to date. Updates can be performed in the same manner. Using the `TGS_EXTERNAL_CONFIGURATION` is recommended in order to make the process as easy as replacing `tgs.dm` and the `tgs` folder with a newer version
153 
154 ### Example
155 
156 Here is a bare minimum example project that implements the essential code changes for integrating the DMAPI
157 
158 Before `tgs.dm`:
159 ```dm
160 //Remember, every codebase is different, you probably have better methods for these defines than the ones given here
161 #define TGS_EXTERNAL_CONFIGURATION
162 #define TGS_DEFINE_AND_SET_GLOBAL(Name, Value) var/global/##Name = ##Value
163 #define TGS_READ_GLOBAL(Name) global.##Name
164 #define TGS_WRITE_GLOBAL(Name, Value) global.##Name = ##Value
165 #define TGS_WORLD_ANNOUNCE(message) world << ##message
166 #define TGS_INFO_LOG(message) world.log << "TGS Info: [##message]"
167 #define TGS_ERROR_LOG(message) world.log << "TGS Error: [##message]"
168 #define TGS_NOTIFY_ADMINS(event) world.log << "TGS Admin Message: [##event]"
169 #define TGS_CLIENT_COUNT global.client_cout
170 #define TGS_PROTECT_DATUM(Path) // Leave blank if your codebase doesn't give administrators code reflection capabilities
171 ```
172 
173 Anywhere else:
174 ```dm
175 var/global/client_count = 0
176 
177 /world/New()
178  ..()
179  TgsNew()
180  TgsInitializationsComplete()
181 
182 /world/Reboot()
183  TgsReboot()
184  ..()
185 
186 /world/Topic()
187  TGS_TOPIC
188  ..()
189 
190 /client/New()
191  ..()
192  ++global.client_count
193 
194 /client/Del()
195  ..()
196  --global.client_count
197 ```
198 
199 ## Remote Access
200 
201 tgstation-server is an [ASP.Net Core](https://docs.microsoft.com/en-us/aspnet/core/) app based on the Kestrel web server. This section is meant to serve as a general use case overview, but the entire Kestrel configuration can be modified to your liking with the configuration JSON. See [the official documentation](https://docs.microsoft.com/en-us/aspnet/core/fundamentals/servers/kestrel) for details.
202 
203 Exposing the builtin Kestrel server to the internet directly over HTTP is highly not reccommended due to the lack of security. The recommended way to expose tgstation-server to the internet is to host it through a reverse proxy with HTTPS support. Here are some step by step examples to achieve this for major web servers.
204 
205 System administrators will most likely have their own configuration plans, but here are some basic guides for beginners.
206 
207 Once complete, test that your configuration worked by visiting your proxy site from a browser on a different computer. You should recieve a 401 Unauthorized response.
208 
209 ### IIS (Reccommended for Windows)
210 
211 1. Acquire an HTTPS certificate. The easiet free way for Windows is [win-acme](https://github.com/PKISharp/win-acme) (requires you to set up the website first)
212 2. Install the [Web Platform Installer](https://www.microsoft.com/web/downloads/platform.aspx)
213 3. Open the web platform installer in the IIS Manager and install the Application Request Routing 3.0 module
214 4. Create a new website, bind it to HTTPS only with your chosen certificate and exposed port. The physical path won't matter since it won't be used. Use `Require Server Name Indication` if you want to limit requests to a specific URL prefix.
215 5. Close and reopen the IIS Manager
216 5. Open the site and navigate to the `URL Rewrite` module
217 6. In the `Actions` Pane on the right click `Add Rule(s)...`
218 7. For the rule template, select `Reverse Proxy` under `Inbound and Outbound Rules` and click `OK`
219 8. You may get a prompt about enabling proxy functionality. Click `OK`
220 9. In the window that appears set the `Inbound Rules` textbox to the URL of your tgstation-server i.e. `http://localhost:5000`. Ensure `Enable SSL Offloading` is checked, then click `OK`
221 
222 ### Caddy (Reccommended for Linux, or those unfamilar with configuring NGINX or Apache)
223 
224 1. Setup a basic website configuration. Instructions on how to do so are out of scope.
225 2. In your Caddyfile, under a server entry, add the following (replace 8080 with the port TGS is hosted on):
226 ```
227 proxy /tgs localhost:8080 {
228  transparent
229 }
230 ```
231 
232 See https://caddyserver.com/docs/proxy
233 
234 ### NGINX (Reccommended for Linux)
235 
236 1. Setup a basic website configuration. Instructions on how to do so are out of scope.
237 2. Acquire an HTTPS certificate, likely via Let's Encrypt, and configure NGINX to use it.
238 3. Setup a path under a server like the following (replace 8080 with the port TGS is hosted on):
239 ```
240 location /tgs {
241  proxy_pass http://127.0.0.1:8080;
242  break;
243 }
244 ```
245 
246 See https://docs.nginx.com/nginx/admin-guide/web-server/reverse-proxy/
247 
248 ### Apache
249 
250 1. Ensure the `mod_proxy` extension is installed.
251 2. Setup a basic website configuration. Instructions on how to do so are out of scope.
252 3. Acquire an HTTPS certificate, likely via Let's Encrypt, and configure Apache to use it.
253 4. Under a VirtualHost entry, setup the following (replace 8080 with the port TGS is hosted on):
254 ```
255 ProxyPass / http://127.0.0.1:8080
256 ProxyPassReverse / http://127.0.0.1:8080
257 ```
258 
259 See https://httpd.apache.org/docs/2.4/howto/reverse_proxy.html
260 
261 Example VirtualHost Entry
262 ```
263 <IfModule mod_ssl.c>
264 <VirtualHost *:443>
265  ServerName tgs_subdomain.example.com
266 
267  SSLEngine on
268  SSLCertificateFile /etc/letsencrypt/live/example.com/fullchain.pem
269  SSLCertificateKeyFile /etc/letsencrypt/live/example.com/privkey.pem
270 
271  ProxyPass / http://127.0.0.1:8080/
272  ProxyPassReverse / http://127.0.0.1:8080/
273 </VirtualHost>
274 </IfModule>
275 ```
276 
277 ## Usage
278 
279 tgstation-server v4 is controlled via a RESTful HTTP json API. Documentation on this API can be found [here](https://tgstation.github.io/tgstation-server/api.html). This section serves to document the concepts of the server. The API is versioned separately from the release version. A specification for it can be found in the api-vX.X.X git releases/tags.
280 
281 ### Users
282 
283 All actions apart from logging in must be taken by a user. TGS installs with one default user whose credentials can be found [here](src/Tgstation.Server.Api/Models/User.cs). It is recommended to disable this user ASAP as it is used to create Jobs that are started by the server itself. If access to all users is lost, the default user can be reset using the `Database:ResetAdminPassword` configuration setting.
284 
285 Users can be enabled/disabled and have a very granular set of rights associated to them that determine the actions they are allowed to take (i.e. Modify the user list or create instances). Users can be _database based_ or _system based_. Database users are your standard web users with a username and password. System users, on the otherhand, are authenticated with the host OS. These users cannot have their password or names changed by TGS as they are managed by the system (and in reverse, login tokens don't expire when their password changes). The benefit to having these users is it allows the use of system ACLs for static file control. More on that later.
286 
287 ### Instances
288 
289 A TGS deployment is made up with a set of instances, which each represent a production BYOND server. As many instances as desired can be created. Be aware, however, due to the nature of BYOND, this will quickly result in system resource exhaustion.
290 
291 An instance is stored in a single folder anywhere on a system and is made up of several components: The source code git repository, the BYOND installations, the compiler, the watchdog, chat bots, and static file management systems.
292 
293 ##### Instance Users
294 
295 All users with access to an instance have an InstanceUser object associated with the two that defines more rights specific to that instance (i.e. Deploy code, modify bots, edit other InstanceUsers).
296 
297 #### Repository
298 
299 The `Repository` folder is a git repository containing the code of the game you wish to host. It can be cloned from any public or private remote repository and has capabilities to affect changes back to it. All the standard benefits of git are utilized (i.e. check out any revision or reference).
300 
301 Additional features become available if the remote repository is hosted on https://github.com/. Namely the Test Merge feature, which allows you to take a pull request opened on the repository and compile it into a game deployment for testing. Information about test merges is available in game via the DMAPI and via the main API as well.
302 
303 Manual operations on the repository while an instance is running may lead to git data corruption. Thankfully, it's simple enough to delete and reclone the repository via the API.
304 
305 #### Byond
306 
307 The `Byond` folder contains installations of [BYOND](https://secure.byond.com/) versions. The version which is used by your game code can be changed on a whim (Note that only versions >= 511.1385 have been thouroughly tested. Lower versions should work but if one doesn't function, please open an issue report) and the server will take care of installing it.
308 
309 #### Compiler
310 
311 The compiler deploys code from the `Repository` folder to the `Game` folder and compiles it either by autodetecting the `.dme` or having it set by configuration. Several other step are also run such as validating the DMAPI version and creating symlinks for static files are done at this point. The compiler also applies server side code modifications and duplicates compiled code for the watchdog as well (See following section).
312 
313 #### Watchdog
314 
315 The watchdog is responsible for starting and keeping your server running. It functions by launching two servers which are hot-swapped on `/world/Reboot`s and during crashes to prevent downtime. This hot swapping feature is also what allows TGS to deploy updates to live servers without bringing them down.
316 
317 DreamDaemon can be finicky and will crash with several high load games or bad DM code. The watchdog has several failure prevention methods to keep at least one server running while these issues are sorted out.
318 
319 #### Chat Bots
320 
321 TGS supports creating infinite chat bots for notifying staff or players of things like code deployments and uptime in. Currently the following providers are supported
322 
323 - Internet Relay Chat (IRC)
324 - Discord
325 
326 More can be added by providing a new implementation of the [IProvider](src/Tgstation.Server.Host/Components/Chat/Providers/IProvider.cs) interface
327 
328 Bots have a set of built-in commands that can be triggered via `!tgs`, mentioning, or private messaging them. Along with these, custom commands can be defined using the DMAPI by creating a subtype of the `/datum/tgs_chat_command` type (See `tgs.dm` for details). Invocation for custom commands can be restricted to certain channels.
329 
330 #### Static Files
331 
332 All files in game code deployments are considered transient by default, meaning when new code is deployed, changes will be lost. Static files allow you to specify which files and folders stick around throughout all deployments.
333 
334 The `StaticFiles` folder contains 3 root folders which cannot be deleted and operate under special rules
335  - `CodeModifications`
336  - `EventScripts`
337  - `GameStaticFiles`
338 
339 These files can be modified either in host mode or system user mode. In host mode, TGS itself is responsible for reading and writing the files. In system user mode read and write actions are performed using the system account of the logged on User, enabling the use of ACLs to control access to files. Database users will not be able to use the static file system if this mode is configured for an instance.
340 
341 This folder may be freely modified manually just beware this may cause in-progress deployments to error if done on Windows systems.
342 
343 #### CodeModifications
344 
345 When a deployment is made by the compiler, all the contents of this folder are copied over the repository contents. Then one of two code change modes are selected based on the prescense of certain files.
346 
347 If `<target dme>.dm` is present, that .dme will be used instead of the repository's `.dme`
348 
349 Otherwise the files `HeadInclude.dm` and `TailInclude.dm` are searched for and added as include lines to the top and bottom of the target `.dme` repsectively if they exist. These files can contain any valid DreamMaker code (Including `#include`ing other `.dm` files!) allowing you to modify the a repository's code on a per instance basis
350 
351 #### EventScripts
352 
353 This folder can contain anything. But, when certain events occur in the instance, TGS will look here for `.bat` or `.sh` files with the same name and run those with corresponding arguments. List of supported events can be found [here](src/Tgstation.Server.Host/Components/StaticFiles/Configuration.cs#L28) (subject to expansion) list of event parameters can be found [here](src/Tgstation.Server.Host/Components/EventType.cs)
354 
355 #### GameStaticFiles
356 
357 Any files and folders contained in this root level of this folder will be symbolically linked to all deployments at the time they are created. This allows persistent game data (BYOND `.sav`s or code configuration files for example) to persist across all deployments. This folder contains a .tgsignore file which can be used to prevent symlinks from being generated by entering the names of files and folders (1 per line)
358 
359 ### Updating
360 
361 TGS 4 can self update without stopping your DreamDaemon servers. Any V4 release made to this repository is bound by a contract that allows changes of the runtime assemblies without stopping your servers. Database migrations are automatically applied as well. Because of this REVERTING TO LOWER VERSIONS IS NOT OFFICIALLY SUPPORTED, do so at your own risk (check changes made to `/src/Tgstation.Server.Host/Models/Migrations`).
362 
363 ### Clients
364 
365 Here are tools for interacting with the TGS 4 web API
366 
367 - [tgstation-server-control-panel]: Official client and included with the server. A react web app for using tgstation-server.
368 - [Tgstation.Server.ControlPanel](https://github.com/tgstation/Tgstation.Server.ControlPanel): Official client. A cross platform GUI for using tgstation-server
369 - [Tgstation.Server.Client](https://www.nuget.org/packages/Tgstation.Server.Client): A nuget .NET Standard 2.0 TAP based library for communicating with tgstation-server
370 - [Tgstation.Server.Api](https://www.nuget.org/packages/Tgstation.Server.Api): A nuget .NET Standard 2.0 library containing API definitions for tgstation-server
371 - [Postman](https://www.getpostman.com/): This repository contains [TGS.postman_collection.json](tools/TGS.postman_collection.json) which is used during development for testing. Contains example requests for all endpoints but takes some knowledge to use (Note that the pre-request script is configured to login the default admin user for every request)
372 
373 Contact project maintainers to get your client added to this list
374 
375 ## Backup/Restore
376 
377 Note that tgstation-server is NOT a backup solution, the onus is on the server runners.
378 
379 The `Repository` folder should be backed up on the remote server, do not rely on the instance copy to store changes.
380 
381 The `BYOND` and `Game` folders should never be backed up due to being intertwined with instance data.
382 
383 The `Configuration` folder should be fully backed up.
384 
385 The database should be fully backed up.
386 
387 To restore an installation from backups, first restore the instance `Configuration` folder in its new home. Then restore the database, modifying the `Path` column in the `Instances` table where necessary to point to the new instances. Then start the server pointed at the new database.
388 
389 Should you end up with a lost database for some reason or want to reattach a detached instance you can reattach an existing folder by creating an empty file named `TGS4_ALLOW_INSTANCE_ATTACH` inside it (This is automatically created when detaching instances). Then create a new instance with that path, this will bypass the empty folder check. Note that this will not restore things such as user permissions, server config options, or deployment metadata. Those must be reconfigured manually.
390 
391 ## Troubleshooting
392 
393 Feel free to ask for help at the coderbus discord in \#hosting-questions: https://discord.gg/Vh8TJp9. Cyberboss#8246 can answer most questions.
394 
395 ## Contributing
396 
397 * See [CONTRIBUTING.md](.github/CONTRIBUTING.md)
398 
399 ## Licensing
400 
401 * The DMAPI for the project is licensed under the MIT license.
402 * The /tg/station 13 icon is licensed under [Creative Commons 3.0 BY-SA](http://creativecommons.org/licenses/by-sa/3.0/).
403 * The remainder of the project is licensed under [GNU AGPL v3](http://www.gnu.org/licenses/agpl-3.0.html)
404 
405 See the files in the `/src/DMAPI` tree for the MIT license
+Go to the documentation of this file.
1 # tgstation-server v4:
2 
3 [![Build status](https://ci.appveyor.com/api/projects/status/7t1h7bvuha0p9j5f/branch/master?svg=true)](https://ci.appveyor.com/project/Cyberboss/tgstation-server-tools/branch/master) [![Build Status](https://travis-ci.org/tgstation/tgstation-server.svg?branch=master)](https://travis-ci.org/tgstation/tgstation-server) [![codecov](https://codecov.io/gh/tgstation/tgstation-server/branch/master/graph/badge.svg)](https://codecov.io/gh/tgstation/tgstation-server)
4 
5 [![GitHub license](https://img.shields.io/github/license/tgstation/tgstation-server.svg)](LICENSE) [![Average time to resolve an issue](http://isitmaintained.com/badge/resolution/tgstation/tgstation-server.svg)](http://isitmaintained.com/project/tgstation/tgstation-server "Average time to resolve an issue") [![NuGet version](https://img.shields.io/nuget/v/Tgstation.Server.Api.svg)](https://www.nuget.org/packages/Tgstation.Server.Api) [![NuGet version](https://img.shields.io/nuget/v/Tgstation.Server.Client.svg)](https://www.nuget.org/packages/Tgstation.Server.Client)
6 
7 [![forthebadge](http://forthebadge.com/images/badges/made-with-c-sharp.svg)](http://forthebadge.com) [![forinfinityandbyond](https://user-images.githubusercontent.com/5211576/29499758-4efff304-85e6-11e7-8267-62919c3688a9.gif)](https://www.reddit.com/r/SS13/comments/5oplxp/what_is_the_main_problem_with_byond_as_an_engine/dclbu1a)
8 
9 [![forthebadge](http://forthebadge.com/images/badges/built-with-love.svg)](http://forthebadge.com) [![forthebadge](http://forthebadge.com/images/badges/60-percent-of-the-time-works-every-time.svg)](http://forthebadge.com)
10 
11 This is a toolset to manage production BYOND servers. It includes the ability to update the server without having to stop or shutdown the server (the update will take effect on a "reboot" of the server) the ability start the server and restart it if it crashes, as well as systems for managing code and game files, and merging GitHub Pull Requests for test deployments.
12 
13 ### Legacy Servers
14 
15 Older server versions can be found in the V# branches of this repository. Note that V4 is nearly fully incompatible with existing installations. Only some static files may be copied over: https://github.com/tgstation/tgstation-server#static-files
16 
17 ## Setup
18 
19 ### Pre-Requisites
20 
21 - [ASP .NET Core Runtime (>= v3.1)](https://dotnet.microsoft.com/download/dotnet-core/current/runtime) (Choose the option to `Run Server Apps` for your system) If you plan to install tgstation-server as a Windows service, you should also ensure that your .NET Framework runtime version is >= v4.7.2 (Download can be found on same page). Ensure that the `dotnet` executable file is in your system's `PATH` variable (or that of the user's that will be running the server).
22 - A [MariaDB](https://downloads.mariadb.org/), MySQL, or [Microsoft SQL Server](https://www.microsoft.com/en-us/download/details.aspx?id=55994) database engine is required
23 
24 ### Installation
25 
26 1. [Download the latest V4 release .zip](https://github.com/tgstation/tgstation-server/releases/latest). The `ServerService` package will only work on Windows. Choose `ServerConsole` if that is not your target OS or you prefer not to use the Windows service.
27 2. Extract the .zip file to where you want the server to run from. Note the account running the server must have write and delete access to the `lib` subdirectory.
28 
29 #### Windows
30 
31 If you wish to install the TGS as a service, run `Tgstation.Server.Host.Service.exe`. It should prompt you to install it. Click `Yes` and accept a potential UAC elevation prompt and the setup wizard should run.
32 
33 #### Linux (Native)
34 
35 We recommend using Docker for Linux installations, see below. The content of this parent section may be skipped if you choose to do so.
36 
37 The following dependencies are required to run tgstation-server on Linux alongside the .NET Core runtime
38 
39 - libc6-i386
40 - libstdc++6:i386
41 - libssl1.0.0
42 - gcc-multilib (Only on 64-bit systems)
43 
44 Note that tgstation-server has only ever been tested on Linux via it's [docker environment](build/Dockerfile#L22). If you are having trouble with something in a native installation, or figure out a required workaround, please contact project maintainers so this documentation may be better updated.
45 
46 #### Docker (Linux)
47 
48 tgstation-server supports running in a docker container and is the recommended deployment method for Linux systems. The official image repository is located at https://hub.docker.com/r/tgstation/server. It can also be built locally by running `docker build . -f build/Dockerfile -t <your tag name>` in the repository root.
49 
50 To create a container run
51 ```sh
52 docker run \
53  -ti \ #start interactive for manual configuration
54  --restart=always \ #if you want maximum uptime
55  --network="host" \ #if your sql server is on the same machine
56  --name="tgs" \ #or whatever else you wanna call it
57  --cap-add=sys_nice \ #allows tgs to schedule DreamDaemon as a higher priority process
58  --init \ #reaps potential zombie processes
59  -p <tgs port>:80 \
60  -p 0.0.0.0:<public game port>:<public game port> \
61  -v /path/to/your/configfile/directory:/config_data \ #only if you want to use manual configuration
62  -v /path/to/store/instances:/tgs4_instances \
63  -v /path/to/your/log/folder:/tgs_logs \
64  tgstation/server:<release version> #replace this with <your tag name> if you built the image locally
65 ```
66 with any additional options you desire (i.e. You'll have to expose more game ports in order to host more than one instance).
67 
68 - Important note about port exposure: The internal port used by DreamDaemon _**MUST**_ match the port you want users to connect on. If it doesn't, you'll still be able to have them connect HOWEVER links from the BYOND hub will point at what DreamDaemon thinks the port is.
69 
70 Note although `/app/lib` is specified as a volume mount point in the `Dockerfile`, unless you REALLY know what you're doing. Do not mount any volumes over this for fear of breaking your container.
71 
72 The configuration option `General:ValidInstancePaths` will be preconfigured to point to `/tgs4_instances`. It is recommended you don't change this.
73 
74 Note that this container is meant to be long running. Updates are handled internally as opposed to at the container level.
75 
76 If using manual configuration, before starting your container make sure the aforementioned `appsettings.Production.json` is setup properly. See below
77 
78 ### Configuring
79 
80 The first time you run TGS4 you should be prompted with a configuration wizard which will guide you through setting up your appsettings.Production.json
81 
82 This wizard will, generally, run whenever the server is launched without detecting the config json. Follow the instructions below to perform this process manually.
83 
84 #### Manual Configuration
85 
86 Create an `appsettings.Production.json` file next to `appsettings.json`. This will override the default settings in appsettings.json with your production settings. There are a few keys meant to be changed by hosts. Modifying any config files while the server is running will trigger a safe restart (Keeps DreamDaemon's running). Note these are all case-sensitive:
87 
88 - `General:MinimumPasswordLength`: Minimum password length requirement for database users
89 
90 - `General:ValidInstancePaths`: Array meant to limit the directories in which instances may be created.
91 
92 - `General:UserLimit`: Maximum number of users that may be created
93 
94 - `General:InstanceLimit`: Maximum number of instances that may be created
95 
96 - `General:GitHubAccessToken`: Specify a GitHub personal access token with no scopes here to highly mitigate the possiblity of 429 response codes from GitHub requests
97 
98 - `FileLogging:Directory`: Override the default directory where server logs are stored. Default is C:/ProgramData/tgstation-server/logs on Windows, /usr/share/tgstation-server/logs otherwise
99 
100 - `FileLogging:LogLevel`: Can be one of `Trace`, `Debug`, `Information`, `Warning`, `Error`, or `Critical`. Restricts what is put into the log files. Currently `Debug` is reccommended for help with error reporting.
101 
102 - `Kestrel:Endpoints:Http:Url`: The URL (i.e. interface and ports) your application should listen on. General use case should be `http://localhost:<port>` for restricted local connections. See the Remote Access section for configuring public access to the World Wide Web. This doesn't need to be changed using the docker setup and should be mapped with the `-p` option instead
103 
104 - `Database:DatabaseType`: Can be one of `SqlServer`, `MariaDB`, `MySql`, or `Sqlite`.
105 
106 - `Database:ServerVersion`: The version of the database server. Used by the MySQL/MariaDB and Postgres providers for selection of certain features, ignore at your own risk. A string in the form `<major>.<minor>.<patch>` for MySQL/MariaDB or `<major>.<minor>` for Postgres.
107 
108 - `Database:ConnectionString`: Connection string for your database. Click [here](https://www.developerfusion.com/tools/sql-connection-string/) for an SQL Server generator or see [here](https://www.connectionstrings.com/mysql/) for a MySQL guide ([You should probably use '127.0.0.1' instead of 'localhost'](https://stackoverflow.com/questions/19712307/mysql-localhost-127-0-0-1)).
109 
110 - `ControlPanel:Enable`: Enable the javascript based control panel to be served from the server via /index.html
111 
112 - `ControlPanel:AllowAnyOrigin`: Set the Access-Control-Allow-Origin header to * for all responses (also enables all headers and methods)
113 
114 - `ControlPanel:AllowedOrigins`: Set the Access-Control-Allow-Origin headers to this list of origins for all responses (also enables all headers and methods). This is overridden by `ControlPanel:AllowAnyOrigin`
115 
116 ### Database Configuration
117 
118 If using a MariaDB/MySQL server, our client library [recommends you set 'utf8mb4' as your default charset](https://github.com/PomeloFoundation/Pomelo.EntityFrameworkCore.MySql#1-recommended-server-charset) disregard at your own risk.
119 
120 The user created for the application will need the privilege to create databases on the first run, do not create the database for it. Once the initial set of migrations is run, the create right may be revoked. The user should maintain DDL rights though for applying future migrations
121 
122 Note that the ratio of application installations to databases is 1:1. Do not attempt to share a database amongst multiple TGS installations.
123 
124 ### Starting
125 
126 For the Windows service version start the `tgstation-server-4` service. If it fails to start, check the Windows event log under Windows/Application for entries from tgstation-server-4 for errors.
127 
128 For the console version run `dotnet Tgstation.Server.Host.Console.dll` in the installation directory. The `tgs.bat` and `tgs.sh` shell scripts are shortcuts for this. If on Windows, you must do this as admin to give the server permission to install the required DirectX dependency for certain 512 BYOND versions as well as create symlinks.
129 
130 For the docker version run `docker start <your container name>`
131 
132 Test your server is running by visiting the local port in your browser. You should receive a 401 Unauthorized response (You may need to view the developer console). Otherwise an error page will be present.
133 
134 ### Stopping
135 
136 Note that the live detach for DreamDaemon servers is only supported for updates or restarts via the API at this time. Stopping tgstation-server will TERMINATE ALL CHILD DREAMDAEMON SERVERS.
137 
138 For the Windows service version stop the `tgstation-server-4` service
139 
140 For the console version press `Ctrl+C` or send a SIGQUIT to the ORIGINAL dotnet process
141 
142 For the docker version run `docker stop <your container name>`
143 
144 ## Integrating
145 
146 tgstation-server 4 currently REQUIRES the DMAPI to be integrated into any BYOND codebase which plans on being used by it. The integration process is a fairly simple set of code changes.
147 
148 1. Copy the [latest release of the DMAPI](https://github.com/tgstation/tgstation-server/releases) anywhere in your code base. `tgs.dm` can be seperated from the `tgs` folder, but do not modify or move the contents of the `tgs` folder
149 2. Modify your `.dme`(s) to include the `tgs.dm` and `tgs/includes.dm` files (ORDER OF APPEARANCE IS MANDATORY)
150 3. Follow the instructions in `tgs.dm` to integrate the API with your codebase.
151 
152 The DMAPI is fully backwards compatible and should function with any tgstation-server version to date. Updates can be performed in the same manner. Using the `TGS_EXTERNAL_CONFIGURATION` is recommended in order to make the process as easy as replacing `tgs.dm` and the `tgs` folder with a newer version
153 
154 ### Example
155 
156 Here is a bare minimum example project that implements the essential code changes for integrating the DMAPI
157 
158 Before `tgs.dm`:
159 ```dm
160 //Remember, every codebase is different, you probably have better methods for these defines than the ones given here
161 #define TGS_EXTERNAL_CONFIGURATION
162 #define TGS_DEFINE_AND_SET_GLOBAL(Name, Value) var/global/##Name = ##Value
163 #define TGS_READ_GLOBAL(Name) global.##Name
164 #define TGS_WRITE_GLOBAL(Name, Value) global.##Name = ##Value
165 #define TGS_WORLD_ANNOUNCE(message) world << ##message
166 #define TGS_INFO_LOG(message) world.log << "TGS Info: [##message]"
167 #define TGS_ERROR_LOG(message) world.log << "TGS Error: [##message]"
168 #define TGS_NOTIFY_ADMINS(event) world.log << "TGS Admin Message: [##event]"
169 #define TGS_CLIENT_COUNT global.client_cout
170 #define TGS_PROTECT_DATUM(Path) // Leave blank if your codebase doesn't give administrators code reflection capabilities
171 ```
172 
173 Anywhere else:
174 ```dm
175 var/global/client_count = 0
176 
177 /world/New()
178  ..()
179  TgsNew()
180  TgsInitializationsComplete()
181 
182 /world/Reboot()
183  TgsReboot()
184  ..()
185 
186 /world/Topic()
187  TGS_TOPIC
188  ..()
189 
190 /client/New()
191  ..()
192  ++global.client_count
193 
194 /client/Del()
195  ..()
196  --global.client_count
197 ```
198 
199 ## Remote Access
200 
201 tgstation-server is an [ASP.Net Core](https://docs.microsoft.com/en-us/aspnet/core/) app based on the Kestrel web server. This section is meant to serve as a general use case overview, but the entire Kestrel configuration can be modified to your liking with the configuration JSON. See [the official documentation](https://docs.microsoft.com/en-us/aspnet/core/fundamentals/servers/kestrel) for details.
202 
203 Exposing the builtin Kestrel server to the internet directly over HTTP is highly not reccommended due to the lack of security. The recommended way to expose tgstation-server to the internet is to host it through a reverse proxy with HTTPS support. Here are some step by step examples to achieve this for major web servers.
204 
205 System administrators will most likely have their own configuration plans, but here are some basic guides for beginners.
206 
207 Once complete, test that your configuration worked by visiting your proxy site from a browser on a different computer. You should recieve a 401 Unauthorized response.
208 
209 ### IIS (Reccommended for Windows)
210 
211 1. Acquire an HTTPS certificate. The easiet free way for Windows is [win-acme](https://github.com/PKISharp/win-acme) (requires you to set up the website first)
212 2. Install the [Web Platform Installer](https://www.microsoft.com/web/downloads/platform.aspx)
213 3. Open the web platform installer in the IIS Manager and install the Application Request Routing 3.0 module
214 4. Create a new website, bind it to HTTPS only with your chosen certificate and exposed port. The physical path won't matter since it won't be used. Use `Require Server Name Indication` if you want to limit requests to a specific URL prefix.
215 5. Close and reopen the IIS Manager
216 5. Open the site and navigate to the `URL Rewrite` module
217 6. In the `Actions` Pane on the right click `Add Rule(s)...`
218 7. For the rule template, select `Reverse Proxy` under `Inbound and Outbound Rules` and click `OK`
219 8. You may get a prompt about enabling proxy functionality. Click `OK`
220 9. In the window that appears set the `Inbound Rules` textbox to the URL of your tgstation-server i.e. `http://localhost:5000`. Ensure `Enable SSL Offloading` is checked, then click `OK`
221 
222 ### Caddy (Reccommended for Linux, or those unfamilar with configuring NGINX or Apache)
223 
224 1. Setup a basic website configuration. Instructions on how to do so are out of scope.
225 2. In your Caddyfile, under a server entry, add the following (replace 8080 with the port TGS is hosted on):
226 ```
227 proxy /tgs localhost:8080 {
228  transparent
229 }
230 ```
231 
232 See https://caddyserver.com/docs/proxy
233 
234 ### NGINX (Reccommended for Linux)
235 
236 1. Setup a basic website configuration. Instructions on how to do so are out of scope.
237 2. Acquire an HTTPS certificate, likely via Let's Encrypt, and configure NGINX to use it.
238 3. Setup a path under a server like the following (replace 8080 with the port TGS is hosted on):
239 ```
240 location /tgs {
241  proxy_pass http://127.0.0.1:8080;
242  break;
243 }
244 ```
245 
246 See https://docs.nginx.com/nginx/admin-guide/web-server/reverse-proxy/
247 
248 ### Apache
249 
250 1. Ensure the `mod_proxy` extension is installed.
251 2. Setup a basic website configuration. Instructions on how to do so are out of scope.
252 3. Acquire an HTTPS certificate, likely via Let's Encrypt, and configure Apache to use it.
253 4. Under a VirtualHost entry, setup the following (replace 8080 with the port TGS is hosted on):
254 ```
255 ProxyPass / http://127.0.0.1:8080
256 ProxyPassReverse / http://127.0.0.1:8080
257 ```
258 
259 See https://httpd.apache.org/docs/2.4/howto/reverse_proxy.html
260 
261 Example VirtualHost Entry
262 ```
263 <IfModule mod_ssl.c>
264 <VirtualHost *:443>
265  ServerName tgs_subdomain.example.com
266 
267  SSLEngine on
268  SSLCertificateFile /etc/letsencrypt/live/example.com/fullchain.pem
269  SSLCertificateKeyFile /etc/letsencrypt/live/example.com/privkey.pem
270 
271  ProxyPass / http://127.0.0.1:8080/
272  ProxyPassReverse / http://127.0.0.1:8080/
273 </VirtualHost>
274 </IfModule>
275 ```
276 
277 ## Usage
278 
279 tgstation-server v4 is controlled via a RESTful HTTP json API. Documentation on this API can be found [here](https://tgstation.github.io/tgstation-server/api.html). This section serves to document the concepts of the server. The API is versioned separately from the release version. A specification for it can be found in the api-vX.X.X git releases/tags.
280 
281 ### Users
282 
283 All actions apart from logging in must be taken by a user. TGS installs with one default user whose credentials can be found [here](src/Tgstation.Server.Api/Models/User.cs). It is recommended to disable this user ASAP as it is used to create Jobs that are started by the server itself. If access to all users is lost, the default user can be reset using the `Database:ResetAdminPassword` configuration setting.
284 
285 Users can be enabled/disabled and have a very granular set of rights associated to them that determine the actions they are allowed to take (i.e. Modify the user list or create instances). Users can be _database based_ or _system based_. Database users are your standard web users with a username and password. System users, on the otherhand, are authenticated with the host OS. These users cannot have their password or names changed by TGS as they are managed by the system (and in reverse, login tokens don't expire when their password changes). The benefit to having these users is it allows the use of system ACLs for static file control. More on that later.
286 
287 ### Instances
288 
289 A TGS deployment is made up with a set of instances, which each represent a production BYOND server. As many instances as desired can be created. Be aware, however, due to the nature of BYOND, this will quickly result in system resource exhaustion.
290 
291 An instance is stored in a single folder anywhere on a system and is made up of several components: The source code git repository, the BYOND installations, the compiler, the watchdog, chat bots, and static file management systems.
292 
293 ##### Instance Users
294 
295 All users with access to an instance have an InstanceUser object associated with the two that defines more rights specific to that instance (i.e. Deploy code, modify bots, edit other InstanceUsers).
296 
297 #### Repository
298 
299 The `Repository` folder is a git repository containing the code of the game you wish to host. It can be cloned from any public or private remote repository and has capabilities to affect changes back to it. All the standard benefits of git are utilized (i.e. check out any revision or reference).
300 
301 Additional features become available if the remote repository is hosted on https://github.com/. Namely the Test Merge feature, which allows you to take a pull request opened on the repository and compile it into a game deployment for testing. Information about test merges is available in game via the DMAPI and via the main API as well.
302 
303 Manual operations on the repository while an instance is running may lead to git data corruption. Thankfully, it's simple enough to delete and reclone the repository via the API.
304 
305 #### Byond
306 
307 The `Byond` folder contains installations of [BYOND](https://secure.byond.com/) versions. The version which is used by your game code can be changed on a whim (Note that only versions >= 511.1385 have been thouroughly tested. Lower versions should work but if one doesn't function, please open an issue report) and the server will take care of installing it.
308 
309 #### Compiler
310 
311 The compiler deploys code from the `Repository` folder to the `Game` folder and compiles it either by autodetecting the `.dme` or having it set by configuration. Several other step are also run such as validating the DMAPI version and creating symlinks for static files are done at this point. The compiler also applies server side code modifications and duplicates compiled code for the watchdog as well (See following section).
312 
313 #### Watchdog
314 
315 The watchdog is responsible for starting and keeping your server running. It functions by launching two servers which are hot-swapped on `/world/Reboot`s and during crashes to prevent downtime. This hot swapping feature is also what allows TGS to deploy updates to live servers without bringing them down.
316 
317 DreamDaemon can be finicky and will crash with several high load games or bad DM code. The watchdog has several failure prevention methods to keep at least one server running while these issues are sorted out.
318 
319 #### Chat Bots
320 
321 TGS supports creating infinite chat bots for notifying staff or players of things like code deployments and uptime in. Currently the following providers are supported
322 
323 - Internet Relay Chat (IRC)
324 - Discord
325 
326 More can be added by providing a new implementation of the [IProvider](src/Tgstation.Server.Host/Components/Chat/Providers/IProvider.cs) interface
327 
328 Bots have a set of built-in commands that can be triggered via `!tgs`, mentioning, or private messaging them. Along with these, custom commands can be defined using the DMAPI by creating a subtype of the `/datum/tgs_chat_command` type (See `tgs.dm` for details). Invocation for custom commands can be restricted to certain channels.
329 
330 #### Static Files
331 
332 All files in game code deployments are considered transient by default, meaning when new code is deployed, changes will be lost. Static files allow you to specify which files and folders stick around throughout all deployments.
333 
334 The `StaticFiles` folder contains 3 root folders which cannot be deleted and operate under special rules
335  - `CodeModifications`
336  - `EventScripts`
337  - `GameStaticFiles`
338 
339 These files can be modified either in host mode or system user mode. In host mode, TGS itself is responsible for reading and writing the files. In system user mode read and write actions are performed using the system account of the logged on User, enabling the use of ACLs to control access to files. Database users will not be able to use the static file system if this mode is configured for an instance.
340 
341 This folder may be freely modified manually just beware this may cause in-progress deployments to error if done on Windows systems.
342 
343 #### CodeModifications
344 
345 When a deployment is made by the compiler, all the contents of this folder are copied over the repository contents. Then one of two code change modes are selected based on the prescense of certain files.
346 
347 If `<target dme>.dm` is present, that .dme will be used instead of the repository's `.dme`
348 
349 Otherwise the files `HeadInclude.dm` and `TailInclude.dm` are searched for and added as include lines to the top and bottom of the target `.dme` repsectively if they exist. These files can contain any valid DreamMaker code (Including `#include`ing other `.dm` files!) allowing you to modify the a repository's code on a per instance basis
350 
351 #### EventScripts
352 
353 This folder can contain anything. But, when certain events occur in the instance, TGS will look here for `.bat` or `.sh` files with the same name and run those with corresponding arguments. List of supported events can be found [here](src/Tgstation.Server.Host/Components/StaticFiles/Configuration.cs#L28) (subject to expansion) list of event parameters can be found [here](src/Tgstation.Server.Host/Components/EventType.cs)
354 
355 #### GameStaticFiles
356 
357 Any files and folders contained in this root level of this folder will be symbolically linked to all deployments at the time they are created. This allows persistent game data (BYOND `.sav`s or code configuration files for example) to persist across all deployments. This folder contains a .tgsignore file which can be used to prevent symlinks from being generated by entering the names of files and folders (1 per line)
358 
359 ### Updating
360 
361 TGS 4 can self update without stopping your DreamDaemon servers. Any V4 release made to this repository is bound by a contract that allows changes of the runtime assemblies without stopping your servers. Database migrations are automatically applied as well. Because of this REVERTING TO LOWER VERSIONS IS NOT OFFICIALLY SUPPORTED, do so at your own risk (check changes made to `/src/Tgstation.Server.Host/Models/Migrations`).
362 
363 ### Clients
364 
365 Here are tools for interacting with the TGS 4 web API
366 
367 - [tgstation-server-control-panel]: Official client and included with the server. A react web app for using tgstation-server.
368 - [Tgstation.Server.ControlPanel](https://github.com/tgstation/Tgstation.Server.ControlPanel): Official client. A cross platform GUI for using tgstation-server
369 - [Tgstation.Server.Client](https://www.nuget.org/packages/Tgstation.Server.Client): A nuget .NET Standard 2.0 TAP based library for communicating with tgstation-server
370 - [Tgstation.Server.Api](https://www.nuget.org/packages/Tgstation.Server.Api): A nuget .NET Standard 2.0 library containing API definitions for tgstation-server
371 - [Postman](https://www.getpostman.com/): This repository contains [TGS.postman_collection.json](tools/TGS.postman_collection.json) which is used during development for testing. Contains example requests for all endpoints but takes some knowledge to use (Note that the pre-request script is configured to login the default admin user for every request)
372 
373 Contact project maintainers to get your client added to this list
374 
375 ## Backup/Restore
376 
377 Note that tgstation-server is NOT a backup solution, the onus is on the server runners.
378 
379 The `Repository` folder should be backed up on the remote server, do not rely on the instance copy to store changes.
380 
381 The `BYOND` and `Game` folders should never be backed up due to being intertwined with instance data.
382 
383 The `Configuration` folder should be fully backed up.
384 
385 The database should be fully backed up.
386 
387 To restore an installation from backups, first restore the instance `Configuration` folder in its new home. Then restore the database, modifying the `Path` column in the `Instances` table where necessary to point to the new instances. Then start the server pointed at the new database.
388 
389 Should you end up with a lost database for some reason or want to reattach a detached instance you can reattach an existing folder by creating an empty file named `TGS4_ALLOW_INSTANCE_ATTACH` inside it (This is automatically created when detaching instances). Then create a new instance with that path, this will bypass the empty folder check. Note that this will not restore things such as user permissions, server config options, or deployment metadata. Those must be reconfigured manually.
390 
391 ## Troubleshooting
392 
393 Feel free to ask for help at the coderbus discord in \#hosting-questions: https://discord.gg/Vh8TJp9. Cyberboss#8246 can answer most questions.
394 
395 ## Contributing
396 
397 * See [CONTRIBUTING.md](.github/CONTRIBUTING.md)
398 
399 ## Licensing
400 
401 * The DMAPI for the project is licensed under the MIT license.
402 * The /tg/station 13 icon is licensed under [Creative Commons 3.0 BY-SA](http://creativecommons.org/licenses/by-sa/3.0/).
403 * The remainder of the project is licensed under [GNU AGPL v3](http://www.gnu.org/licenses/agpl-3.0.html)
404 
405 See the files in the `/src/DMAPI` tree for the MIT license
-Go to the documentation of this file.
1 using Microsoft.EntityFrameworkCore;
2 using Microsoft.Extensions.Logging;
3 using System;
4 using System.Linq;
5 using System.Threading;
6 using System.Threading.Tasks;
9 using Z.EntityFramework.Plus;
10 
11 namespace Tgstation.Server.Host.Components.Session
12 {
15  {
20 
25 
29  readonly ILogger<ReattachInfoHandler> logger;
30 
34  readonly Api.Models.Instance metadata;
35 
43  public ReattachInfoHandler(IDatabaseContextFactory databaseContextFactory, IDmbFactory dmbFactory, ILogger<ReattachInfoHandler> logger, Api.Models.Instance metadata)
44  {
45  this.databaseContextFactory = databaseContextFactory ?? throw new ArgumentNullException(nameof(databaseContextFactory));
46  this.dmbFactory = dmbFactory ?? throw new ArgumentNullException(nameof(dmbFactory));
47  this.logger = logger ?? throw new ArgumentNullException(nameof(logger));
48  this.metadata = metadata ?? throw new ArgumentNullException(nameof(metadata));
49  }
50 
52  public Task Save(DualReattachInformation reattachInformation, CancellationToken cancellationToken) => databaseContextFactory.UseContext(async (db) =>
53  {
54  if (reattachInformation == null)
55  throw new ArgumentNullException(nameof(reattachInformation));
56 
57  logger.LogDebug("Saving reattach information: {0}...", reattachInformation);
58 
59  var deleteTask = db
60  .WatchdogReattachInformations
61  .AsQueryable()
62  .Where(x => x.InstanceId == metadata.Id)
63  .DeleteAsync(cancellationToken);
64 
65  Models.ReattachInformation ConvertReattachInfo(ReattachInformation wdInfo)
66  {
67  if (wdInfo == null)
68  return null;
69  db.CompileJobs.Attach(wdInfo.Dmb.CompileJob);
70  return new Models.ReattachInformation
71  {
72  AccessIdentifier = wdInfo.AccessIdentifier,
73  CompileJob = wdInfo.Dmb.CompileJob,
74  IsPrimary = wdInfo.IsPrimary,
75  Port = wdInfo.Port,
76  ProcessId = wdInfo.ProcessId,
77  RebootState = wdInfo.RebootState,
78  LaunchSecurityLevel = wdInfo.LaunchSecurityLevel
79  };
80  }
81 
82  await deleteTask.ConfigureAwait(false);
83 
84  db.WatchdogReattachInformations.Add(new Models.DualReattachInformation
85  {
86  Alpha = ConvertReattachInfo(reattachInformation.Alpha),
87  Bravo = ConvertReattachInfo(reattachInformation.Bravo),
88  AlphaIsActive = reattachInformation.AlphaIsActive,
89  InstanceId = metadata.Id
90  });
91  await db.Save(cancellationToken).ConfigureAwait(false);
92  });
93 
95  public async Task<DualReattachInformation> Load(CancellationToken cancellationToken)
96  {
97  Models.DualReattachInformation result = null;
98  await databaseContextFactory.UseContext(async (db) =>
99  {
100  var instance = await db.Instances
101  .AsQueryable()
102  .Where(x => x.Id == metadata.Id)
103  .Include(x => x.WatchdogReattachInformation).ThenInclude(x => x.Alpha).ThenInclude(x => x.CompileJob)
104  .Include(x => x.WatchdogReattachInformation).ThenInclude(x => x.Bravo).ThenInclude(x => x.CompileJob)
105  .FirstOrDefaultAsync(cancellationToken).ConfigureAwait(false);
106  result = instance.WatchdogReattachInformation;
107  if (result == default)
108  return;
109  instance.WatchdogReattachInformation = null;
110  db.WatchdogReattachInformations.Remove(result);
111  await db.Save(cancellationToken).ConfigureAwait(false);
112  }).ConfigureAwait(false);
113 
114  if (result == default)
115  {
116  logger.LogDebug("Reattach information not found!");
117  return null;
118  }
119 
120  Task<IDmbProvider> GetDmbForReattachInfo(Models.ReattachInformation reattachInformation) => reattachInformation != null
121  ? dmbFactory.FromCompileJob(reattachInformation.CompileJob, cancellationToken)
122  : Task.FromResult<IDmbProvider>(null);
123 
124  var bravoDmbTask = GetDmbForReattachInfo(result.Bravo);
125  var info = new DualReattachInformation(result, await GetDmbForReattachInfo(result.Alpha).ConfigureAwait(false), await bravoDmbTask.ConfigureAwait(false));
126  logger.LogDebug("Reattach information loaded: {0}", info);
127  return info;
128  }
129  }
130 }
CompileJob CompileJob
The CompileJob of the .dmb
Definition: IDmbProvider.cs:29
+Go to the documentation of this file.
1 using Microsoft.EntityFrameworkCore;
2 using Microsoft.Extensions.Logging;
3 using System;
4 using System.Linq;
5 using System.Threading;
6 using System.Threading.Tasks;
9 using Z.EntityFramework.Plus;
10 
11 namespace Tgstation.Server.Host.Components.Session
12 {
15  {
20 
25 
29  readonly ILogger<ReattachInfoHandler> logger;
30 
34  readonly Api.Models.Instance metadata;
35 
43  public ReattachInfoHandler(IDatabaseContextFactory databaseContextFactory, IDmbFactory dmbFactory, ILogger<ReattachInfoHandler> logger, Api.Models.Instance metadata)
44  {
45  this.databaseContextFactory = databaseContextFactory ?? throw new ArgumentNullException(nameof(databaseContextFactory));
46  this.dmbFactory = dmbFactory ?? throw new ArgumentNullException(nameof(dmbFactory));
47  this.logger = logger ?? throw new ArgumentNullException(nameof(logger));
48  this.metadata = metadata ?? throw new ArgumentNullException(nameof(metadata));
49  }
50 
52  public Task Save(DualReattachInformation reattachInformation, CancellationToken cancellationToken) => databaseContextFactory.UseContext(async (db) =>
53  {
54  if (reattachInformation == null)
55  throw new ArgumentNullException(nameof(reattachInformation));
56 
57  logger.LogDebug("Saving reattach information: {0}...", reattachInformation);
58 
59  await db
60  .WatchdogReattachInformations
61  .AsQueryable()
62  .Where(x => x.InstanceId == metadata.Id)
63  .DeleteAsync(cancellationToken)
64  .ConfigureAwait(false);
65 
66  Models.ReattachInformation ConvertReattachInfo(ReattachInformation wdInfo)
67  {
68  if (wdInfo == null)
69  return null;
70  db.CompileJobs.Attach(wdInfo.Dmb.CompileJob);
71  return new Models.ReattachInformation
72  {
73  AccessIdentifier = wdInfo.AccessIdentifier,
74  CompileJob = wdInfo.Dmb.CompileJob,
75  IsPrimary = wdInfo.IsPrimary,
76  Port = wdInfo.Port,
77  ProcessId = wdInfo.ProcessId,
78  RebootState = wdInfo.RebootState,
79  LaunchSecurityLevel = wdInfo.LaunchSecurityLevel
80  };
81  }
82 
83  db.WatchdogReattachInformations.Add(new Models.DualReattachInformation
84  {
85  Alpha = ConvertReattachInfo(reattachInformation.Alpha),
86  Bravo = ConvertReattachInfo(reattachInformation.Bravo),
87  AlphaIsActive = reattachInformation.AlphaIsActive,
88  InstanceId = metadata.Id
89  });
90  await db.Save(cancellationToken).ConfigureAwait(false);
91  });
92 
94  public async Task<DualReattachInformation> Load(CancellationToken cancellationToken)
95  {
96  Models.DualReattachInformation result = null;
97  await databaseContextFactory.UseContext(async (db) =>
98  {
99  var instance = await db.Instances
100  .AsQueryable()
101  .Where(x => x.Id == metadata.Id)
102  .Include(x => x.WatchdogReattachInformation).ThenInclude(x => x.Alpha).ThenInclude(x => x.CompileJob)
103  .Include(x => x.WatchdogReattachInformation).ThenInclude(x => x.Bravo).ThenInclude(x => x.CompileJob)
104  .FirstOrDefaultAsync(cancellationToken).ConfigureAwait(false);
105  result = instance.WatchdogReattachInformation;
106  if (result == default)
107  return;
108  instance.WatchdogReattachInformation = null;
109  db.WatchdogReattachInformations.Remove(result);
110  await db.Save(cancellationToken).ConfigureAwait(false);
111  }).ConfigureAwait(false);
112 
113  if (result == default)
114  {
115  logger.LogDebug("Reattach information not found!");
116  return null;
117  }
118 
119  Task<IDmbProvider> GetDmbForReattachInfo(Models.ReattachInformation reattachInformation) => reattachInformation != null
120  ? dmbFactory.FromCompileJob(reattachInformation.CompileJob, cancellationToken)
121  : Task.FromResult<IDmbProvider>(null);
122 
123  var bravoDmbTask = GetDmbForReattachInfo(result.Bravo);
124  var info = new DualReattachInformation(result, await GetDmbForReattachInfo(result.Alpha).ConfigureAwait(false), await bravoDmbTask.ConfigureAwait(false));
125  logger.LogDebug("Reattach information loaded: {0}", info);
126  return info;
127  }
128  }
129 }
CompileJob CompileJob
The CompileJob of the .dmb
Definition: IDmbProvider.cs:29
Factory for scoping usage of IDatabaseContexts. Meant for use by Components
@@ -121,7 +121,7 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
readonly IDmbFactory dmbFactory
The IDmbFactory for the ReattachInfoHandler
-
async Task< DualReattachInformation > Load(CancellationToken cancellationToken)
Load a saved DualReattachInformation
+
async Task< DualReattachInformation > Load(CancellationToken cancellationToken)
Load a saved DualReattachInformation
diff --git a/_server_factory_8cs_source.html b/_server_factory_8cs_source.html index d1ffe8fe32..87ec4ce814 100644 --- a/_server_factory_8cs_source.html +++ b/_server_factory_8cs_source.html @@ -94,7 +94,7 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
ServerFactory.cs
-Go to the documentation of this file.
1 using Microsoft.AspNetCore.Hosting;
2 using Microsoft.AspNetCore.Server.Kestrel.Core;
3 using Microsoft.Extensions.Configuration;
4 using Microsoft.Extensions.DependencyInjection;
5 using Microsoft.Extensions.Hosting;
6 using System;
7 using System.Threading;
8 using System.Threading.Tasks;
11 using Tgstation.Server.Host.IO;
14 
15 namespace Tgstation.Server.Host
16 {
21  {
26 
28  public IIOManager IOManager { get; }
29 
35  internal ServerFactory(IAssemblyInformationProvider assemblyInformationProvider, IIOManager ioManager)
36  {
37  this.assemblyInformationProvider = assemblyInformationProvider ?? throw new ArgumentNullException(nameof(assemblyInformationProvider));
38  IOManager = ioManager ?? throw new ArgumentNullException(nameof(ioManager));
39  }
40 
42  public async Task<IServer> CreateServer(string[] args, string updatePath, CancellationToken cancellationToken)
43  {
44  if (args == null)
45  throw new ArgumentNullException(nameof(args));
46 
47  IHostBuilder CreateDefaultBuilder() => Microsoft.Extensions.Hosting.Host.CreateDefaultBuilder(args)
48  .ConfigureAppConfiguration((context, configuration) => configuration
49  .SetBasePath(
50  IOManager.ResolvePath()));
51 
52  var setupWizardHostBuilder = CreateDefaultBuilder()
53  .UseSetupApplication();
54 
55  IPostSetupServices postSetupServices;
56  using (var setupHost = setupWizardHostBuilder.Build())
57  {
58  postSetupServices = setupHost.Services.GetRequiredService<IPostSetupServices>();
59  await setupHost.RunAsync(cancellationToken).ConfigureAwait(false);
60  }
61 
62  var hostBuilder = CreateDefaultBuilder()
63  .ConfigureWebHost(webHostBuilder =>
64  webHostBuilder
65  .UseKestrel(kestrelOptions =>
66  {
67  var serverPortProvider = kestrelOptions.ApplicationServices.GetRequiredService<IServerPortProvider>();
68  kestrelOptions.ListenAnyIP(
69  serverPortProvider.HttpApiPort,
70  listenOptions => listenOptions.Protocols = HttpProtocols.Http1AndHttp2);
71  })
72  .UseIIS()
73  .UseIISIntegration()
74  .UseApplication(postSetupServices)
75  .SuppressStatusMessages(true)
76  .UseShutdownTimeout(TimeSpan.FromMinutes(1)));
77 
78  if (updatePath != null)
79  hostBuilder.UseContentRoot(
80  IOManager.ResolvePath(
81  IOManager.GetDirectoryName(assemblyInformationProvider.Path)));
82 
83  return new Server(hostBuilder, updatePath);
84  }
85  }
86 }
+Go to the documentation of this file.
1 using Microsoft.AspNetCore.Hosting;
2 using Microsoft.AspNetCore.Server.Kestrel.Core;
3 using Microsoft.Extensions.Configuration;
4 using Microsoft.Extensions.DependencyInjection;
5 using Microsoft.Extensions.Hosting;
6 using System;
7 using System.Threading;
8 using System.Threading.Tasks;
11 using Tgstation.Server.Host.IO;
14 
15 namespace Tgstation.Server.Host
16 {
21  {
26 
28  public IIOManager IOManager { get; }
29 
35  internal ServerFactory(IAssemblyInformationProvider assemblyInformationProvider, IIOManager ioManager)
36  {
37  this.assemblyInformationProvider = assemblyInformationProvider ?? throw new ArgumentNullException(nameof(assemblyInformationProvider));
38  IOManager = ioManager ?? throw new ArgumentNullException(nameof(ioManager));
39  }
40 
42  public async Task<IServer> CreateServer(string[] args, string updatePath, CancellationToken cancellationToken)
43  {
44  if (args == null)
45  throw new ArgumentNullException(nameof(args));
46 
47  IHostBuilder CreateDefaultBuilder() => Microsoft.Extensions.Hosting.Host.CreateDefaultBuilder(args)
48  .ConfigureAppConfiguration((context, configuration) => configuration
49  .SetBasePath(
50  IOManager.ResolvePath()));
51 
52  var setupWizardHostBuilder = CreateDefaultBuilder()
53  .UseSetupApplication();
54 
55  IPostSetupServices postSetupServices;
56  using (var setupHost = setupWizardHostBuilder.Build())
57  {
58  postSetupServices = setupHost.Services.GetRequiredService<IPostSetupServices>();
59  await setupHost.RunAsync(cancellationToken).ConfigureAwait(false);
60  }
61 
62  var hostBuilder = CreateDefaultBuilder()
63  .ConfigureWebHost(webHostBuilder =>
64  webHostBuilder
65  .UseKestrel(kestrelOptions =>
66  {
67  var serverPortProvider = kestrelOptions.ApplicationServices.GetRequiredService<IServerPortProvider>();
68  kestrelOptions.ListenAnyIP(
69  serverPortProvider.HttpApiPort,
70  listenOptions => listenOptions.Protocols = HttpProtocols.Http1AndHttp2);
71  })
72  .UseIIS()
73  .UseIISIntegration()
74  .UseApplication(postSetupServices)
75  .SuppressStatusMessages(true)
76  .UseShutdownTimeout(TimeSpan.FromMilliseconds(postSetupServices.GeneralConfiguration.RestartTimeout)));
77 
78  if (updatePath != null)
79  hostBuilder.UseContentRoot(
80  IOManager.ResolvePath(
81  IOManager.GetDirectoryName(assemblyInformationProvider.Path)));
82 
83  return new Server(hostBuilder, updatePath);
84  }
85  }
86 }
async Task< IServer > CreateServer(string[] args, string updatePath, CancellationToken cancellationToken)
Create a IServer
@@ -107,6 +107,7 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
Set of objects needed to configure an Core.Application.
+
GeneralConfiguration GeneralConfiguration
The Configuration.GeneralConfiguration.
Interface for using filesystems
Definition: IIOManager.cs:11
diff --git a/_setup_wizard_8cs_source.html b/_setup_wizard_8cs_source.html index f62eb55da7..1577fe7165 100644 --- a/_setup_wizard_8cs_source.html +++ b/_setup_wizard_8cs_source.html @@ -94,7 +94,7 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
SetupWizard.cs
-Go to the documentation of this file.
1 using Microsoft.Data.Sqlite;
2 using Microsoft.Extensions.Hosting;
3 using Microsoft.Extensions.Logging;
4 using Microsoft.Extensions.Options;
5 using MySql.Data.MySqlClient;
6 using Newtonsoft.Json;
7 using Npgsql;
8 using System;
9 using System.Collections.Generic;
10 using System.Data.Common;
11 using System.Data.SqlClient;
12 using System.Globalization;
13 using System.IO;
14 using System.Linq;
15 using System.Text;
16 using System.Text.RegularExpressions;
17 using System.Threading;
18 using System.Threading.Tasks;
22 using Tgstation.Server.Host.IO;
24 
25 namespace Tgstation.Server.Host.Setup
26 {
28  sealed class SetupWizard : IHostedService
29  {
34 
38  readonly IConsole console;
39 
43  readonly IHostEnvironment hostingEnvironment;
44 
49 
54 
59 
64 
68  readonly IHostApplicationLifetime applicationLifetime;
69 
74 
87  public SetupWizard(
88  IIOManager ioManager,
89  IConsole console,
90  IHostEnvironment hostingEnvironment,
91  IAssemblyInformationProvider assemblyInformationProvider,
92  IDatabaseConnectionFactory dbConnectionFactory,
93  IPlatformIdentifier platformIdentifier,
94  IAsyncDelayer asyncDelayer,
95  IHostApplicationLifetime applicationLifetime,
96  IOptions<GeneralConfiguration> generalConfigurationOptions)
97  {
98  this.ioManager = ioManager ?? throw new ArgumentNullException(nameof(ioManager));
99  this.console = console ?? throw new ArgumentNullException(nameof(console));
100  this.hostingEnvironment = hostingEnvironment ?? throw new ArgumentNullException(nameof(hostingEnvironment));
101  this.assemblyInformationProvider = assemblyInformationProvider ?? throw new ArgumentNullException(nameof(assemblyInformationProvider));
102  this.dbConnectionFactory = dbConnectionFactory ?? throw new ArgumentNullException(nameof(dbConnectionFactory));
103  this.platformIdentifier = platformIdentifier ?? throw new ArgumentNullException(nameof(platformIdentifier));
104  this.asyncDelayer = asyncDelayer ?? throw new ArgumentNullException(nameof(asyncDelayer));
105  this.applicationLifetime = applicationLifetime ?? throw new ArgumentNullException(nameof(applicationLifetime));
106  generalConfiguration = generalConfigurationOptions?.Value ?? throw new ArgumentNullException(nameof(generalConfigurationOptions));
107  }
108 
115  async Task<bool> PromptYesNo(string question, CancellationToken cancellationToken)
116  {
117  do
118  {
119  await console.WriteAsync(question, false, cancellationToken).ConfigureAwait(false);
120  var responseString = await console.ReadLineAsync(false, cancellationToken).ConfigureAwait(false);
121  var upperResponse = responseString.ToUpperInvariant();
122  if (upperResponse == "Y" || upperResponse == "YES")
123  return true;
124  else if (upperResponse == "N" || upperResponse == "NO")
125  return false;
126  await console.WriteAsync("Invalid response!", true, cancellationToken).ConfigureAwait(false);
127  }
128  while (true);
129  }
130 
136  async Task<ushort?> PromptForHostingPort(CancellationToken cancellationToken)
137  {
138  await console.WriteAsync(null, true, cancellationToken).ConfigureAwait(false);
139  await console.WriteAsync("What port would you like to connect to TGS on?", true, cancellationToken).ConfigureAwait(false);
140  await console.WriteAsync("Note: If this is a docker container with the default port already mapped, use the default.", true, cancellationToken).ConfigureAwait(false);
141 
142  do
143  {
144  await console.WriteAsync(
145  $"API Port (leave blank for default of {GeneralConfiguration.DefaultApiPort}): ",
146  false,
147  cancellationToken)
148  .ConfigureAwait(false);
149  var portString = await console.ReadLineAsync(false, cancellationToken).ConfigureAwait(false);
150  if (String.IsNullOrWhiteSpace(portString))
151  return null;
152  if (UInt16.TryParse(portString, out var port) && port != 0)
153  return port;
154  await console.WriteAsync("Invalid port! Please enter a value between 1 and 65535", true, cancellationToken).ConfigureAwait(false);
155  }
156  while (true);
157  }
158 
169  DbConnection testConnection,
170  DatabaseConfiguration databaseConfiguration,
171  string databaseName,
172  bool dbExists,
173  CancellationToken cancellationToken)
174  {
175  bool isSqliteDB = databaseConfiguration.DatabaseType == DatabaseType.Sqlite;
176  using (testConnection)
177  {
178  await console.WriteAsync("Testing connection...", true, cancellationToken).ConfigureAwait(false);
179  await testConnection.OpenAsync(cancellationToken).ConfigureAwait(false);
180  await console.WriteAsync("Connection successful!", true, cancellationToken).ConfigureAwait(false);
181 
182  if (databaseConfiguration.DatabaseType == DatabaseType.MariaDB
183  || databaseConfiguration.DatabaseType == DatabaseType.MySql)
184  {
185  await console.WriteAsync("Checking MySQL/MariaDB version...", true, cancellationToken).ConfigureAwait(false);
186  using var command = testConnection.CreateCommand();
187  command.CommandText = "SELECT VERSION()";
188  var fullVersion = (string)await command.ExecuteScalarAsync(cancellationToken).ConfigureAwait(false);
189  await console.WriteAsync(String.Format(CultureInfo.InvariantCulture, "Found {0}", fullVersion), true, cancellationToken).ConfigureAwait(false);
190  var splits = fullVersion.Split('-');
191  databaseConfiguration.MySqlServerVersion = splits.First();
192  }
193 
194  if (!isSqliteDB && !dbExists)
195  {
196  await console.WriteAsync("Testing create DB permission...", true, cancellationToken).ConfigureAwait(false);
197  using (var command = testConnection.CreateCommand())
198  {
199  // I really don't care about user sanitization here, they want to fuck their own DB? so be it
200 #pragma warning disable CA2100 // Review SQL queries for security vulnerabilities
201  command.CommandText = $"CREATE DATABASE {databaseName}";
202 #pragma warning restore CA2100 // Review SQL queries for security vulnerabilities
203  await command.ExecuteNonQueryAsync(cancellationToken).ConfigureAwait(false);
204  }
205 
206  await console.WriteAsync("Success!", true, cancellationToken).ConfigureAwait(false);
207  await console.WriteAsync("Dropping test database...", true, cancellationToken).ConfigureAwait(false);
208  using (var command = testConnection.CreateCommand())
209  {
210 #pragma warning disable CA2100 // Review SQL queries for security vulnerabilities
211  command.CommandText = $"DROP DATABASE {databaseName}";
212 #pragma warning restore CA2100 // Review SQL queries for security vulnerabilities
213  try
214  {
215  await command.ExecuteNonQueryAsync(cancellationToken).ConfigureAwait(false);
216  }
217  catch (OperationCanceledException)
218  {
219  throw;
220  }
221  catch (Exception e)
222  {
223  await console.WriteAsync(e.Message, true, cancellationToken).ConfigureAwait(false);
224  await console.WriteAsync(null, true, cancellationToken).ConfigureAwait(false);
225  await console.WriteAsync("This should be okay, but you may want to manually drop the database before continuing!", true, cancellationToken).ConfigureAwait(false);
226  await console.WriteAsync("Press any key to continue...", true, cancellationToken).ConfigureAwait(false);
227  await console.PressAnyKeyAsync(cancellationToken).ConfigureAwait(false);
228  }
229  }
230  }
231  }
232 
233  if (isSqliteDB && !dbExists)
234  await Task.WhenAll(
235  console.WriteAsync("Deleting test database file...", true, cancellationToken),
236  ioManager.DeleteFile(databaseName, cancellationToken)).ConfigureAwait(false);
237  }
238 
239  async Task<string> ValidateNonExistantSqliteDBName(string databaseName, CancellationToken cancellationToken)
240  {
241  var resolvedPath = ioManager.ResolvePath(databaseName);
242  try
243  {
244  var directoryName = ioManager.GetDirectoryName(resolvedPath);
245  bool directoryExisted = await ioManager.DirectoryExists(directoryName, cancellationToken).ConfigureAwait(false);
246  await ioManager.CreateDirectory(directoryName, cancellationToken).ConfigureAwait(false);
247  try
248  {
249  await ioManager.WriteAllBytes(resolvedPath, Array.Empty<byte>(), cancellationToken).ConfigureAwait(false);
250  }
251  catch
252  {
253  if (!directoryExisted)
254  await ioManager.DeleteDirectory(directoryName, cancellationToken).ConfigureAwait(false);
255  throw;
256  }
257  }
258  catch (IOException)
259  {
260  return null;
261  }
262 
263  if (!Path.IsPathRooted(databaseName))
264  {
265  await console.WriteAsync("Note, this relative path (currently) resolves to the following:", true, cancellationToken).ConfigureAwait(false);
266  await console.WriteAsync(resolvedPath, true, cancellationToken).ConfigureAwait(false);
267  bool writeResolved = await PromptYesNo(
268  "Would you like to save the relative path in the configuration? If not, the full path will be saved. (y/n): ",
269  cancellationToken)
270  .ConfigureAwait(false);
271 
272  if (writeResolved)
273  databaseName = resolvedPath;
274  }
275 
276  await ioManager.DeleteFile(databaseName, cancellationToken).ConfigureAwait(false);
277  return databaseName;
278  }
279 
286  async Task<DatabaseType> PromptDatabaseType(bool firstTime, CancellationToken cancellationToken)
287  {
288  if (firstTime)
289  {
290  await console.WriteAsync(String.Empty, true, cancellationToken).ConfigureAwait(false);
291  await console.WriteAsync(
292  "NOTE: It is HIGHLY reccommended that TGS runs on a complete relational database, specfically *NOT* Sqlite.",
293  true,
294  cancellationToken)
295  .ConfigureAwait(false);
296  await console.WriteAsync(
297  "Sqlite, by nature cannot perform several DDL operations. Because of this future compatiblility cannot be guaranteed.",
298  true,
299  cancellationToken)
300  .ConfigureAwait(false);
301  await console.WriteAsync(
302  "This means that you may not be able to update to the next minor version of TGS4 without a clean re-installation!",
303  true,
304  cancellationToken)
305  .ConfigureAwait(false);
306  await console.WriteAsync(
307  "Please consider taking the time to set up a relational database if this is meant to be a long-standing server.",
308  true,
309  cancellationToken)
310  .ConfigureAwait(false);
311  await console.WriteAsync(String.Empty, true, cancellationToken).ConfigureAwait(false);
312 
313  await asyncDelayer.Delay(TimeSpan.FromSeconds(3), cancellationToken).ConfigureAwait(false);
314  }
315 
316  await console.WriteAsync("What SQL database type will you be using?", true, cancellationToken).ConfigureAwait(false);
317  do
318  {
319  await console.WriteAsync(
320  String.Format(
321  CultureInfo.InvariantCulture,
322  "Please enter one of {0}, {1}, {2}, or {3}: ",
323  DatabaseType.MariaDB,
324  DatabaseType.MySql,
325 #pragma warning disable SA1515 // Single-line comment should be preceded by blank line
326  // DatabaseType.PostgresSql,
327  DatabaseType.SqlServer,
328 #pragma warning restore SA1515 // Single-line comment should be preceded by blank line
329  DatabaseType.Sqlite),
330  false,
331  cancellationToken)
332  .ConfigureAwait(false);
333  var databaseTypeString = await console.ReadLineAsync(false, cancellationToken).ConfigureAwait(false);
334  if (Enum.TryParse<DatabaseType>(databaseTypeString, out var databaseType))
335  return databaseType;
336 
337  await console.WriteAsync("Invalid database type!", true, cancellationToken).ConfigureAwait(false);
338  }
339  while (true);
340  }
341 
347  #pragma warning disable CA1502 // TODO: Decomplexify
348  async Task<DatabaseConfiguration> ConfigureDatabase(CancellationToken cancellationToken)
349  {
350  bool firstTime = true;
351  do
352  {
353  await console.WriteAsync(null, true, cancellationToken).ConfigureAwait(false);
354 
355  var databaseConfiguration = new DatabaseConfiguration
356  {
357  DatabaseType = await PromptDatabaseType(firstTime, cancellationToken).ConfigureAwait(false)
358  };
359  firstTime = false;
360 
361  string serverAddress = null;
362  ushort? serverPort = null;
363 
364  bool isSqliteDB = databaseConfiguration.DatabaseType == DatabaseType.Sqlite;
365  if (!isSqliteDB)
366  do
367  {
368  await console.WriteAsync(null, true, cancellationToken).ConfigureAwait(false);
369  await console.WriteAsync("Enter the server's address and port [<server>:<port> or <server>] (blank for local): ", false, cancellationToken).ConfigureAwait(false);
370  serverAddress = await console.ReadLineAsync(false, cancellationToken).ConfigureAwait(false);
371  if (String.IsNullOrWhiteSpace(serverAddress))
372  serverAddress = null;
373  else if (databaseConfiguration.DatabaseType == DatabaseType.SqlServer)
374  {
375  var match = Regex.Match(serverAddress, @"^(?<server>.+):(?<port>.+)$");
376  if (match.Success)
377  {
378  serverAddress = match.Groups["server"].Value;
379  var portString = match.Groups["port"].Value;
380  if (UInt16.TryParse(portString, out var port))
381  serverPort = port;
382  else
383  {
384  await console.WriteAsync($"Failed to parse port \"{portString}\", please try again.", true, cancellationToken).ConfigureAwait(false);
385  continue;
386  }
387  }
388  }
389 
390  break;
391  }
392  while (true);
393 
394  await console.WriteAsync(null, true, cancellationToken).ConfigureAwait(false);
395  await console.WriteAsync($"Enter the database {(isSqliteDB ? "file path" : "name")} (Can be from previous installation. Otherwise, should not exist): ", false, cancellationToken).ConfigureAwait(false);
396 
397  string databaseName;
398  bool dbExists = false;
399  do
400  {
401  databaseName = await console.ReadLineAsync(false, cancellationToken).ConfigureAwait(false);
402  if (!String.IsNullOrWhiteSpace(databaseName))
403  {
404  if (isSqliteDB)
405  {
406  dbExists = await ioManager.FileExists(databaseName, cancellationToken).ConfigureAwait(false);
407  if (!dbExists)
408  databaseName = await ValidateNonExistantSqliteDBName(databaseName, cancellationToken).ConfigureAwait(false);
409  }
410  else
411  dbExists = await PromptYesNo("Does this database already exist? If not, we will attempt to CREATE it. (y/n): ", cancellationToken).ConfigureAwait(false);
412  }
413 
414  if (String.IsNullOrWhiteSpace(databaseName))
415  await console.WriteAsync("Invalid database name!", true, cancellationToken).ConfigureAwait(false);
416  else
417  break;
418  }
419  while (true);
420 
421  bool useWinAuth;
422  if (databaseConfiguration.DatabaseType == DatabaseType.SqlServer && platformIdentifier.IsWindows)
423  useWinAuth = await PromptYesNo("Use Windows Authentication? (y/n): ", cancellationToken).ConfigureAwait(false);
424  else
425  useWinAuth = false;
426 
427  await console.WriteAsync(null, true, cancellationToken).ConfigureAwait(false);
428 
429  string username = null;
430  string password = null;
431  if (!isSqliteDB)
432  if (!useWinAuth)
433  {
434  await console.WriteAsync("Enter username: ", false, cancellationToken).ConfigureAwait(false);
435  username = await console.ReadLineAsync(false, cancellationToken).ConfigureAwait(false);
436  await console.WriteAsync("Enter password: ", false, cancellationToken).ConfigureAwait(false);
437  password = await console.ReadLineAsync(true, cancellationToken).ConfigureAwait(false);
438  }
439  else
440  {
441  await console.WriteAsync("IMPORTANT: If using the service runner, ensure this computer's LocalSystem account has CREATE DATABASE permissions on the target server!", true, cancellationToken).ConfigureAwait(false);
442  await console.WriteAsync("The account it uses in MSSQL is usually \"NT AUTHORITY\\SYSTEM\" and the role it needs is usually \"dbcreator\".", true, cancellationToken).ConfigureAwait(false);
443  await console.WriteAsync("We'll run a sanity test here, but it won't be indicative of the service's permissions if that is the case", true, cancellationToken).ConfigureAwait(false);
444  }
445 
446  await console.WriteAsync(null, true, cancellationToken).ConfigureAwait(false);
447 
448  DbConnection testConnection;
449  void CreateTestConnection(string connectionString) =>
450  testConnection = dbConnectionFactory.CreateConnection(
451  connectionString,
452  databaseConfiguration.DatabaseType);
453 
454  switch (databaseConfiguration.DatabaseType)
455  {
456  case DatabaseType.SqlServer:
457  {
458  var csb = new SqlConnectionStringBuilder
459  {
460  ApplicationName = assemblyInformationProvider.VersionPrefix,
461  DataSource = serverAddress ?? "(local)"
462  };
463 
464  if (useWinAuth)
465  csb.IntegratedSecurity = true;
466  else
467  {
468  csb.UserID = username;
469  csb.Password = password;
470  }
471 
472  CreateTestConnection(csb.ConnectionString);
473  csb.InitialCatalog = databaseName;
474  databaseConfiguration.ConnectionString = csb.ConnectionString;
475  }
476 
477  break;
478  case DatabaseType.MariaDB:
479  case DatabaseType.MySql:
480  {
481  // MySQL/MariaDB
482  var csb = new MySqlConnectionStringBuilder
483  {
484  Server = serverAddress ?? "127.0.0.1",
485  UserID = username,
486  Password = password
487  };
488 
489  if (serverPort.HasValue)
490  csb.Port = serverPort.Value;
491 
492  CreateTestConnection(csb.ConnectionString);
493  csb.Database = databaseName;
494  databaseConfiguration.ConnectionString = csb.ConnectionString;
495  }
496 
497  break;
498  case DatabaseType.Sqlite:
499  {
500  var csb = new SqliteConnectionStringBuilder
501  {
502  DataSource = databaseName,
503  Mode = dbExists ? SqliteOpenMode.ReadOnly : SqliteOpenMode.ReadWriteCreate
504  };
505 
506  CreateTestConnection(csb.ConnectionString);
507  databaseConfiguration.ConnectionString = csb.ConnectionString;
508  }
509 
510  break;
511  case DatabaseType.PostgresSql:
512  {
513  var csb = new NpgsqlConnectionStringBuilder
514  {
515  ApplicationName = assemblyInformationProvider.VersionPrefix,
516  Host = serverAddress ?? "127.0.0.1",
517  Password = password,
518  Username = username
519  };
520 
521  if (serverPort.HasValue)
522  csb.Port = serverPort.Value;
523 
524  CreateTestConnection(csb.ConnectionString);
525  csb.Database = databaseName;
526  databaseConfiguration.ConnectionString = csb.ConnectionString;
527  }
528 
529  break;
530  default:
531  throw new InvalidOperationException("Invalid DatabaseType!");
532  }
533 
534  try
535  {
536  await TestDatabaseConnection(testConnection, databaseConfiguration, databaseName, dbExists, cancellationToken).ConfigureAwait(false);
537 
538  return databaseConfiguration;
539  }
540  catch (OperationCanceledException)
541  {
542  throw;
543  }
544  catch (Exception e)
545  {
546  await console.WriteAsync(e.Message, true, cancellationToken).ConfigureAwait(false);
547  await console.WriteAsync(null, true, cancellationToken).ConfigureAwait(false);
548  await console.WriteAsync("Retrying database configuration...", true, cancellationToken).ConfigureAwait(false);
549  }
550  }
551  while (true);
552  }
553  #pragma warning restore CA1502
554 
560  async Task<GeneralConfiguration> ConfigureGeneral(CancellationToken cancellationToken)
561  {
562  var newGeneralConfiguration = new GeneralConfiguration
563  {
565  };
566 
567  do
568  {
569  await console.WriteAsync(null, true, cancellationToken).ConfigureAwait(false);
570  await console.WriteAsync(String.Format(CultureInfo.InvariantCulture, "Minimum database user password length (leave blank for default of {0}): ", newGeneralConfiguration.MinimumPasswordLength), false, cancellationToken).ConfigureAwait(false);
571  var passwordLengthString = await console.ReadLineAsync(false, cancellationToken).ConfigureAwait(false);
572  if (String.IsNullOrWhiteSpace(passwordLengthString))
573  break;
574  if (UInt32.TryParse(passwordLengthString, out var passwordLength) && passwordLength >= 0)
575  {
576  newGeneralConfiguration.MinimumPasswordLength = passwordLength;
577  break;
578  }
579 
580  await console.WriteAsync("Please enter a positive integer!", true, cancellationToken).ConfigureAwait(false);
581  }
582  while (true);
583 
584  do
585  {
586  await console.WriteAsync(null, true, cancellationToken).ConfigureAwait(false);
587  await console.WriteAsync(String.Format(CultureInfo.InvariantCulture, "Timeout for sending and receiving BYOND topics (ms, 0 for infinite, leave blank for default of {0}): ", newGeneralConfiguration.ByondTopicTimeout), false, cancellationToken).ConfigureAwait(false);
588  var topicTimeoutString = await console.ReadLineAsync(false, cancellationToken).ConfigureAwait(false);
589  if (String.IsNullOrWhiteSpace(topicTimeoutString))
590  break;
591  if (Int32.TryParse(topicTimeoutString, out var topicTimeout) && topicTimeout >= 0)
592  {
593  newGeneralConfiguration.ByondTopicTimeout = topicTimeout;
594  break;
595  }
596 
597  await console.WriteAsync("Please enter a positive integer!", true, cancellationToken).ConfigureAwait(false);
598  }
599  while (true);
600 
601  await console.WriteAsync(null, true, cancellationToken).ConfigureAwait(false);
602  await console.WriteAsync("Enter a GitHub personal access token to bypass some rate limits (this is optional and does not require any scopes)", true, cancellationToken).ConfigureAwait(false);
603  await console.WriteAsync("GitHub personal access token: ", false, cancellationToken).ConfigureAwait(false);
604  newGeneralConfiguration.GitHubAccessToken = await console.ReadLineAsync(true, cancellationToken).ConfigureAwait(false);
605  if (String.IsNullOrWhiteSpace(newGeneralConfiguration.GitHubAccessToken))
606  newGeneralConfiguration.GitHubAccessToken = null;
607 
608  // newGeneralConfiguration.UseExperimentalWatchdog = await PromptYesNo("Use the experimental watchdog (NOT RECOMMENDED)? (y/n): ", cancellationToken).ConfigureAwait(false);
609  return newGeneralConfiguration;
610  }
611 
617  async Task<FileLoggingConfiguration> ConfigureLogging(CancellationToken cancellationToken)
618  {
619  var fileLoggingConfiguration = new FileLoggingConfiguration();
620  await console.WriteAsync(null, true, cancellationToken).ConfigureAwait(false);
621  fileLoggingConfiguration.Disable = !await PromptYesNo("Enable file logging? (y/n): ", cancellationToken).ConfigureAwait(false);
622 
623  if (!fileLoggingConfiguration.Disable)
624  {
625  do
626  {
627  await console.WriteAsync("Log file directory path (leave blank for default): ", false, cancellationToken).ConfigureAwait(false);
628  fileLoggingConfiguration.Directory = await console.ReadLineAsync(false, cancellationToken).ConfigureAwait(false);
629  if (String.IsNullOrWhiteSpace(fileLoggingConfiguration.Directory))
630  {
631  fileLoggingConfiguration.Directory = null;
632  break;
633  }
634 
635  // test a write of it
636  await console.WriteAsync(null, true, cancellationToken).ConfigureAwait(false);
637  await console.WriteAsync("Testing directory access...", true, cancellationToken).ConfigureAwait(false);
638  try
639  {
640  await ioManager.CreateDirectory(fileLoggingConfiguration.Directory, cancellationToken).ConfigureAwait(false);
641  var testFile = ioManager.ConcatPath(fileLoggingConfiguration.Directory, String.Format(CultureInfo.InvariantCulture, "WizardAccesTest.{0}.deleteme", Guid.NewGuid()));
642  await ioManager.WriteAllBytes(testFile, Array.Empty<byte>(), cancellationToken).ConfigureAwait(false);
643  try
644  {
645  await ioManager.DeleteFile(testFile, cancellationToken).ConfigureAwait(false);
646  }
647  catch (OperationCanceledException)
648  {
649  throw;
650  }
651  catch (Exception e)
652  {
653  await console.WriteAsync(String.Format(CultureInfo.InvariantCulture, "Error deleting test log file: {0}", testFile), true, cancellationToken).ConfigureAwait(false);
654  await console.WriteAsync(e.Message, true, cancellationToken).ConfigureAwait(false);
655  await console.WriteAsync(null, true, cancellationToken).ConfigureAwait(false);
656  }
657 
658  break;
659  }
660  catch (OperationCanceledException)
661  {
662  throw;
663  }
664  catch (Exception e)
665  {
666  await console.WriteAsync(e.Message, true, cancellationToken).ConfigureAwait(false);
667  await console.WriteAsync(null, true, cancellationToken).ConfigureAwait(false);
668  await console.WriteAsync("Please verify the path is valid and you have access to it!", true, cancellationToken).ConfigureAwait(false);
669  }
670  }
671  while (true);
672 
673  async Task<LogLevel?> PromptLogLevel(string question)
674  {
675  do
676  {
677  await console.WriteAsync(null, true, cancellationToken).ConfigureAwait(false);
678  await console.WriteAsync(question, true, cancellationToken).ConfigureAwait(false);
679  await console.WriteAsync(String.Format(CultureInfo.InvariantCulture, "Enter one of {0}/{1}/{2}/{3}/{4}/{5} (leave blank for default): ", nameof(LogLevel.Trace), nameof(LogLevel.Debug), nameof(LogLevel.Information), nameof(LogLevel.Warning), nameof(LogLevel.Error), nameof(LogLevel.Critical)), false, cancellationToken).ConfigureAwait(false);
680  var responseString = await console.ReadLineAsync(false, cancellationToken).ConfigureAwait(false);
681  if (String.IsNullOrWhiteSpace(responseString))
682  return null;
683  if (Enum.TryParse<LogLevel>(responseString, out var logLevel) && logLevel != LogLevel.None)
684  return logLevel;
685  await console.WriteAsync("Invalid log level!", true, cancellationToken).ConfigureAwait(false);
686  }
687  while (true);
688  }
689 
690  fileLoggingConfiguration.LogLevel = await PromptLogLevel(String.Format(CultureInfo.InvariantCulture, "Enter the level limit for normal logs (default {0}).", fileLoggingConfiguration.LogLevel)).ConfigureAwait(false) ?? fileLoggingConfiguration.LogLevel;
691  fileLoggingConfiguration.MicrosoftLogLevel = await PromptLogLevel(String.Format(CultureInfo.InvariantCulture, "Enter the level limit for Microsoft logs (VERY verbose, default {0}).", fileLoggingConfiguration.MicrosoftLogLevel)).ConfigureAwait(false) ?? fileLoggingConfiguration.MicrosoftLogLevel;
692  }
693 
694  return fileLoggingConfiguration;
695  }
696 
702  async Task<ControlPanelConfiguration> ConfigureControlPanel(CancellationToken cancellationToken)
703  {
704  var config = new ControlPanelConfiguration
705  {
706  Enable = await PromptYesNo("Enable the web control panel (Incomplete)? (y/n): ", cancellationToken).ConfigureAwait(false),
707  AllowAnyOrigin = await PromptYesNo("Allow web control panels hosted elsewhere to access the server? (Access-Control-Allow-Origin: *) (y/n): ", cancellationToken).ConfigureAwait(false)
708  };
709 
710  if (!config.AllowAnyOrigin)
711  {
712  await console.WriteAsync("Enter a comma seperated list of CORS allowed origins (optional): ", false, cancellationToken).ConfigureAwait(false);
713  var commaSeperatedOrigins = await console.ReadLineAsync(false, cancellationToken).ConfigureAwait(false);
714  if (!String.IsNullOrWhiteSpace(commaSeperatedOrigins))
715  {
716  var splits = commaSeperatedOrigins.Split(',');
717  config.AllowedOrigins = new List<string>(splits.Select(x => x.Trim()));
718  }
719  }
720 
721  return config;
722  }
723 
735  async Task SaveConfiguration(string userConfigFileName, ushort? hostingPort, DatabaseConfiguration databaseConfiguration, GeneralConfiguration newGeneralConfiguration, FileLoggingConfiguration fileLoggingConfiguration, ControlPanelConfiguration controlPanelConfiguration, CancellationToken cancellationToken)
736  {
737  await console.WriteAsync(String.Format(CultureInfo.InvariantCulture, "Configuration complete! Saving to {0}", userConfigFileName), true, cancellationToken).ConfigureAwait(false);
738 
739  newGeneralConfiguration.ApiPort = hostingPort ?? GeneralConfiguration.DefaultApiPort;
740  var map = new Dictionary<string, object>()
741  {
742  { DatabaseConfiguration.Section, databaseConfiguration },
743  { GeneralConfiguration.Section, newGeneralConfiguration },
744  { FileLoggingConfiguration.Section, fileLoggingConfiguration },
745  { ControlPanelConfiguration.Section, controlPanelConfiguration }
746  };
747 
748  var json = JsonConvert.SerializeObject(map, Formatting.Indented);
749  var configBytes = Encoding.UTF8.GetBytes(json);
750 
751  try
752  {
753  await ioManager.WriteAllBytes(userConfigFileName, configBytes, cancellationToken).ConfigureAwait(false);
754  }
755  catch (OperationCanceledException)
756  {
757  throw;
758  }
759  catch (Exception e)
760  {
761  await console.WriteAsync(e.Message, true, cancellationToken).ConfigureAwait(false);
762  await console.WriteAsync(null, true, cancellationToken).ConfigureAwait(false);
763  await console.WriteAsync("For your convienence, here's the json we tried to write out:", true, cancellationToken).ConfigureAwait(false);
764  await console.WriteAsync(null, true, cancellationToken).ConfigureAwait(false);
765  await console.WriteAsync(json, true, cancellationToken).ConfigureAwait(false);
766  await console.WriteAsync(null, true, cancellationToken).ConfigureAwait(false);
767  await console.WriteAsync("Press any key to exit...", true, cancellationToken).ConfigureAwait(false);
768  await console.PressAnyKeyAsync(cancellationToken).ConfigureAwait(false);
769  throw new OperationCanceledException();
770  }
771  }
772 
779  async Task RunWizard(string userConfigFileName, CancellationToken cancellationToken)
780  {
781  // welcome message
782  await console.WriteAsync("Welcome to tgstation-server 4!", true, cancellationToken).ConfigureAwait(false);
783  await console.WriteAsync("This wizard will help you configure your server.", true, cancellationToken).ConfigureAwait(false);
784 
785  var hostingPort = await PromptForHostingPort(cancellationToken).ConfigureAwait(false);
786 
787  var databaseConfiguration = await ConfigureDatabase(cancellationToken).ConfigureAwait(false);
788 
789  var newGeneralConfiguration = await ConfigureGeneral(cancellationToken).ConfigureAwait(false);
790 
791  var fileLoggingConfiguration = await ConfigureLogging(cancellationToken).ConfigureAwait(false);
792 
793  var controlPanelConfiguration = await ConfigureControlPanel(cancellationToken).ConfigureAwait(false);
794 
795  await console.WriteAsync(null, true, cancellationToken).ConfigureAwait(false);
796 
797  await SaveConfiguration(userConfigFileName, hostingPort, databaseConfiguration, newGeneralConfiguration, fileLoggingConfiguration, controlPanelConfiguration, cancellationToken).ConfigureAwait(false);
798  }
799 
805  async Task CheckRunWizard(CancellationToken cancellationToken)
806  {
807  var setupWizardMode = generalConfiguration.SetupWizardMode;
808  if (setupWizardMode == SetupWizardMode.Never)
809  return;
810 
811  var forceRun = setupWizardMode == SetupWizardMode.Force || setupWizardMode == SetupWizardMode.Only;
812  if (!console.Available)
813  {
814  if (forceRun)
815  throw new InvalidOperationException("Asked to run setup wizard with no console avaliable!");
816  return;
817  }
818 
819  var userConfigFileName = String.Format(CultureInfo.InvariantCulture, "appsettings.{0}.json", hostingEnvironment.EnvironmentName);
820 
821  async Task HandleSetupCancel()
822  {
823  await console.WriteAsync(String.Empty, true, default).ConfigureAwait(false);
824  await console.WriteAsync("Aborting setup!", true, default).ConfigureAwait(false);
825  }
826 
827  // Link passed cancellationToken with cancel key press
828  Task finalTask = Task.CompletedTask;
829  using (var cts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, console.CancelKeyPress))
830  using ((cancellationToken = cts.Token).Register(() => finalTask = HandleSetupCancel()))
831  try
832  {
833  var exists = await ioManager.FileExists(userConfigFileName, cancellationToken).ConfigureAwait(false);
834 
835  bool shouldRunBasedOnAutodetect;
836  if (exists)
837  {
838  var bytes = await ioManager.ReadAllBytes(userConfigFileName, cancellationToken).ConfigureAwait(false);
839  var contents = Encoding.UTF8.GetString(bytes);
840  var existingConfigIsEmpty = String.IsNullOrWhiteSpace(contents) || contents.Trim() == "{}";
841  shouldRunBasedOnAutodetect = existingConfigIsEmpty;
842  }
843  else
844  shouldRunBasedOnAutodetect = true;
845 
846  if (!shouldRunBasedOnAutodetect)
847  {
848  if (forceRun)
849  {
850  await console.WriteAsync(String.Format(CultureInfo.InvariantCulture, "The configuration settings are requesting the setup wizard be run, but you already appear to have a configuration file ({0})!", userConfigFileName), true, cancellationToken).ConfigureAwait(false);
851 
852  forceRun = await PromptYesNo("Continue running setup wizard? (y/n): ", cancellationToken).ConfigureAwait(false);
853  }
854 
855  if (!forceRun)
856  return;
857  }
858 
859  // flush the logs to prevent console conflicts
860  await asyncDelayer.Delay(TimeSpan.FromSeconds(1), cancellationToken).ConfigureAwait(false);
861 
862  await RunWizard(userConfigFileName, cancellationToken).ConfigureAwait(false);
863  }
864  finally
865  {
866  await finalTask.ConfigureAwait(false);
867  }
868  }
869 
871  public async Task StartAsync(CancellationToken cancellationToken)
872  {
873  await CheckRunWizard(cancellationToken).ConfigureAwait(false);
874  applicationLifetime.StopApplication();
875  }
876 
878  public Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask;
879  }
880 }
+Go to the documentation of this file.
1 using Microsoft.Data.Sqlite;
2 using Microsoft.Extensions.Hosting;
3 using Microsoft.Extensions.Logging;
4 using Microsoft.Extensions.Options;
5 using MySql.Data.MySqlClient;
6 using Newtonsoft.Json;
7 using Npgsql;
8 using System;
9 using System.Collections.Generic;
10 using System.Data.Common;
11 using System.Data.SqlClient;
12 using System.Globalization;
13 using System.IO;
14 using System.Linq;
15 using System.Text;
16 using System.Text.RegularExpressions;
17 using System.Threading;
18 using System.Threading.Tasks;
22 using Tgstation.Server.Host.IO;
24 
25 namespace Tgstation.Server.Host.Setup
26 {
28  sealed class SetupWizard : IHostedService
29  {
34 
38  readonly IConsole console;
39 
43  readonly IHostEnvironment hostingEnvironment;
44 
49 
54 
59 
64 
68  readonly IHostApplicationLifetime applicationLifetime;
69 
74 
87  public SetupWizard(
88  IIOManager ioManager,
89  IConsole console,
90  IHostEnvironment hostingEnvironment,
91  IAssemblyInformationProvider assemblyInformationProvider,
92  IDatabaseConnectionFactory dbConnectionFactory,
93  IPlatformIdentifier platformIdentifier,
94  IAsyncDelayer asyncDelayer,
95  IHostApplicationLifetime applicationLifetime,
96  IOptions<GeneralConfiguration> generalConfigurationOptions)
97  {
98  this.ioManager = ioManager ?? throw new ArgumentNullException(nameof(ioManager));
99  this.console = console ?? throw new ArgumentNullException(nameof(console));
100  this.hostingEnvironment = hostingEnvironment ?? throw new ArgumentNullException(nameof(hostingEnvironment));
101  this.assemblyInformationProvider = assemblyInformationProvider ?? throw new ArgumentNullException(nameof(assemblyInformationProvider));
102  this.dbConnectionFactory = dbConnectionFactory ?? throw new ArgumentNullException(nameof(dbConnectionFactory));
103  this.platformIdentifier = platformIdentifier ?? throw new ArgumentNullException(nameof(platformIdentifier));
104  this.asyncDelayer = asyncDelayer ?? throw new ArgumentNullException(nameof(asyncDelayer));
105  this.applicationLifetime = applicationLifetime ?? throw new ArgumentNullException(nameof(applicationLifetime));
106  generalConfiguration = generalConfigurationOptions?.Value ?? throw new ArgumentNullException(nameof(generalConfigurationOptions));
107  }
108 
115  async Task<bool> PromptYesNo(string question, CancellationToken cancellationToken)
116  {
117  do
118  {
119  await console.WriteAsync(question, false, cancellationToken).ConfigureAwait(false);
120  var responseString = await console.ReadLineAsync(false, cancellationToken).ConfigureAwait(false);
121  var upperResponse = responseString.ToUpperInvariant();
122  if (upperResponse == "Y" || upperResponse == "YES")
123  return true;
124  else if (upperResponse == "N" || upperResponse == "NO")
125  return false;
126  await console.WriteAsync("Invalid response!", true, cancellationToken).ConfigureAwait(false);
127  }
128  while (true);
129  }
130 
136  async Task<ushort?> PromptForHostingPort(CancellationToken cancellationToken)
137  {
138  await console.WriteAsync(null, true, cancellationToken).ConfigureAwait(false);
139  await console.WriteAsync("What port would you like to connect to TGS on?", true, cancellationToken).ConfigureAwait(false);
140  await console.WriteAsync("Note: If this is a docker container with the default port already mapped, use the default.", true, cancellationToken).ConfigureAwait(false);
141 
142  do
143  {
144  await console.WriteAsync(
145  $"API Port (leave blank for default of {GeneralConfiguration.DefaultApiPort}): ",
146  false,
147  cancellationToken)
148  .ConfigureAwait(false);
149  var portString = await console.ReadLineAsync(false, cancellationToken).ConfigureAwait(false);
150  if (String.IsNullOrWhiteSpace(portString))
151  return null;
152  if (UInt16.TryParse(portString, out var port) && port != 0)
153  return port;
154  await console.WriteAsync("Invalid port! Please enter a value between 1 and 65535", true, cancellationToken).ConfigureAwait(false);
155  }
156  while (true);
157  }
158 
169  DbConnection testConnection,
170  DatabaseConfiguration databaseConfiguration,
171  string databaseName,
172  bool dbExists,
173  CancellationToken cancellationToken)
174  {
175  bool isSqliteDB = databaseConfiguration.DatabaseType == DatabaseType.Sqlite;
176  using (testConnection)
177  {
178  await console.WriteAsync("Testing connection...", true, cancellationToken).ConfigureAwait(false);
179  await testConnection.OpenAsync(cancellationToken).ConfigureAwait(false);
180  await console.WriteAsync("Connection successful!", true, cancellationToken).ConfigureAwait(false);
181 
182  if (databaseConfiguration.DatabaseType == DatabaseType.MariaDB
183  || databaseConfiguration.DatabaseType == DatabaseType.MySql
184  || databaseConfiguration.DatabaseType == DatabaseType.PostgresSql)
185  {
186  await console.WriteAsync($"Checking {databaseConfiguration.DatabaseType} version...", true, cancellationToken).ConfigureAwait(false);
187  using var command = testConnection.CreateCommand();
188  command.CommandText = "SELECT VERSION()";
189  var fullVersion = (string)await command.ExecuteScalarAsync(cancellationToken).ConfigureAwait(false);
190  await console.WriteAsync(String.Format(CultureInfo.InvariantCulture, "Found {0}", fullVersion), true, cancellationToken).ConfigureAwait(false);
191 
192  if (databaseConfiguration.DatabaseType == DatabaseType.PostgresSql)
193  {
194  var splits = fullVersion.Split(' ');
195  databaseConfiguration.ServerVersion = splits[1].TrimEnd(',');
196  }
197  else
198  {
199  var splits = fullVersion.Split('-');
200  databaseConfiguration.ServerVersion = splits.First();
201  }
202  }
203 
204  if (!isSqliteDB && !dbExists)
205  {
206  await console.WriteAsync("Testing create DB permission...", true, cancellationToken).ConfigureAwait(false);
207  using (var command = testConnection.CreateCommand())
208  {
209  // I really don't care about user sanitization here, they want to fuck their own DB? so be it
210 #pragma warning disable CA2100 // Review SQL queries for security vulnerabilities
211  command.CommandText = $"CREATE DATABASE {databaseName}";
212 #pragma warning restore CA2100 // Review SQL queries for security vulnerabilities
213  await command.ExecuteNonQueryAsync(cancellationToken).ConfigureAwait(false);
214  }
215 
216  await console.WriteAsync("Success!", true, cancellationToken).ConfigureAwait(false);
217  await console.WriteAsync("Dropping test database...", true, cancellationToken).ConfigureAwait(false);
218  using (var command = testConnection.CreateCommand())
219  {
220 #pragma warning disable CA2100 // Review SQL queries for security vulnerabilities
221  command.CommandText = $"DROP DATABASE {databaseName}";
222 #pragma warning restore CA2100 // Review SQL queries for security vulnerabilities
223  try
224  {
225  await command.ExecuteNonQueryAsync(cancellationToken).ConfigureAwait(false);
226  }
227  catch (OperationCanceledException)
228  {
229  throw;
230  }
231  catch (Exception e)
232  {
233  await console.WriteAsync(e.Message, true, cancellationToken).ConfigureAwait(false);
234  await console.WriteAsync(null, true, cancellationToken).ConfigureAwait(false);
235  await console.WriteAsync("This should be okay, but you may want to manually drop the database before continuing!", true, cancellationToken).ConfigureAwait(false);
236  await console.WriteAsync("Press any key to continue...", true, cancellationToken).ConfigureAwait(false);
237  await console.PressAnyKeyAsync(cancellationToken).ConfigureAwait(false);
238  }
239  }
240  }
241  }
242 
243  if (isSqliteDB && !dbExists)
244  await Task.WhenAll(
245  console.WriteAsync("Deleting test database file...", true, cancellationToken),
246  ioManager.DeleteFile(databaseName, cancellationToken)).ConfigureAwait(false);
247  }
248 
249  async Task<string> ValidateNonExistantSqliteDBName(string databaseName, CancellationToken cancellationToken)
250  {
251  var resolvedPath = ioManager.ResolvePath(databaseName);
252  try
253  {
254  var directoryName = ioManager.GetDirectoryName(resolvedPath);
255  bool directoryExisted = await ioManager.DirectoryExists(directoryName, cancellationToken).ConfigureAwait(false);
256  await ioManager.CreateDirectory(directoryName, cancellationToken).ConfigureAwait(false);
257  try
258  {
259  await ioManager.WriteAllBytes(resolvedPath, Array.Empty<byte>(), cancellationToken).ConfigureAwait(false);
260  }
261  catch
262  {
263  if (!directoryExisted)
264  await ioManager.DeleteDirectory(directoryName, cancellationToken).ConfigureAwait(false);
265  throw;
266  }
267  }
268  catch (IOException)
269  {
270  return null;
271  }
272 
273  if (!Path.IsPathRooted(databaseName))
274  {
275  await console.WriteAsync("Note, this relative path (currently) resolves to the following:", true, cancellationToken).ConfigureAwait(false);
276  await console.WriteAsync(resolvedPath, true, cancellationToken).ConfigureAwait(false);
277  bool writeResolved = await PromptYesNo(
278  "Would you like to save the relative path in the configuration? If not, the full path will be saved. (y/n): ",
279  cancellationToken)
280  .ConfigureAwait(false);
281 
282  if (writeResolved)
283  databaseName = resolvedPath;
284  }
285 
286  await ioManager.DeleteFile(databaseName, cancellationToken).ConfigureAwait(false);
287  return databaseName;
288  }
289 
296  async Task<DatabaseType> PromptDatabaseType(bool firstTime, CancellationToken cancellationToken)
297  {
298  if (firstTime)
299  {
300  await console.WriteAsync(String.Empty, true, cancellationToken).ConfigureAwait(false);
301  await console.WriteAsync(
302  "NOTE: It is HIGHLY reccommended that TGS runs on a complete relational database, specfically *NOT* Sqlite.",
303  true,
304  cancellationToken)
305  .ConfigureAwait(false);
306  await console.WriteAsync(
307  "Sqlite, by nature cannot perform several DDL operations. Because of this future compatiblility cannot be guaranteed.",
308  true,
309  cancellationToken)
310  .ConfigureAwait(false);
311  await console.WriteAsync(
312  "This means that you may not be able to update to the next minor version of TGS4 without a clean re-installation!",
313  true,
314  cancellationToken)
315  .ConfigureAwait(false);
316  await console.WriteAsync(
317  "Please consider taking the time to set up a relational database if this is meant to be a long-standing server.",
318  true,
319  cancellationToken)
320  .ConfigureAwait(false);
321  await console.WriteAsync(String.Empty, true, cancellationToken).ConfigureAwait(false);
322 
323  await asyncDelayer.Delay(TimeSpan.FromSeconds(3), cancellationToken).ConfigureAwait(false);
324  }
325 
326  await console.WriteAsync("What SQL database type will you be using?", true, cancellationToken).ConfigureAwait(false);
327  do
328  {
329  await console.WriteAsync(
330  String.Format(
331  CultureInfo.InvariantCulture,
332  "Please enter one of {0}, {1}, {2}, {3} or {4}: ",
333  DatabaseType.MariaDB,
334  DatabaseType.MySql,
335  DatabaseType.PostgresSql,
336  DatabaseType.SqlServer,
337  DatabaseType.Sqlite),
338  false,
339  cancellationToken)
340  .ConfigureAwait(false);
341  var databaseTypeString = await console.ReadLineAsync(false, cancellationToken).ConfigureAwait(false);
342  if (Enum.TryParse<DatabaseType>(databaseTypeString, out var databaseType))
343  return databaseType;
344 
345  await console.WriteAsync("Invalid database type!", true, cancellationToken).ConfigureAwait(false);
346  }
347  while (true);
348  }
349 
355  #pragma warning disable CA1502 // TODO: Decomplexify
356  async Task<DatabaseConfiguration> ConfigureDatabase(CancellationToken cancellationToken)
357  {
358  bool firstTime = true;
359  do
360  {
361  await console.WriteAsync(null, true, cancellationToken).ConfigureAwait(false);
362 
363  var databaseConfiguration = new DatabaseConfiguration
364  {
365  DatabaseType = await PromptDatabaseType(firstTime, cancellationToken).ConfigureAwait(false)
366  };
367  firstTime = false;
368 
369  string serverAddress = null;
370  ushort? serverPort = null;
371 
372  bool isSqliteDB = databaseConfiguration.DatabaseType == DatabaseType.Sqlite;
373  if (!isSqliteDB)
374  do
375  {
376  await console.WriteAsync(null, true, cancellationToken).ConfigureAwait(false);
377  await console.WriteAsync("Enter the server's address and port [<server>:<port> or <server>] (blank for local): ", false, cancellationToken).ConfigureAwait(false);
378  serverAddress = await console.ReadLineAsync(false, cancellationToken).ConfigureAwait(false);
379  if (String.IsNullOrWhiteSpace(serverAddress))
380  serverAddress = null;
381  else if (databaseConfiguration.DatabaseType == DatabaseType.SqlServer)
382  {
383  var match = Regex.Match(serverAddress, @"^(?<server>.+):(?<port>.+)$");
384  if (match.Success)
385  {
386  serverAddress = match.Groups["server"].Value;
387  var portString = match.Groups["port"].Value;
388  if (UInt16.TryParse(portString, out var port))
389  serverPort = port;
390  else
391  {
392  await console.WriteAsync($"Failed to parse port \"{portString}\", please try again.", true, cancellationToken).ConfigureAwait(false);
393  continue;
394  }
395  }
396  }
397 
398  break;
399  }
400  while (true);
401 
402  await console.WriteAsync(null, true, cancellationToken).ConfigureAwait(false);
403  await console.WriteAsync($"Enter the database {(isSqliteDB ? "file path" : "name")} (Can be from previous installation. Otherwise, should not exist): ", false, cancellationToken).ConfigureAwait(false);
404 
405  string databaseName;
406  bool dbExists = false;
407  do
408  {
409  databaseName = await console.ReadLineAsync(false, cancellationToken).ConfigureAwait(false);
410  if (!String.IsNullOrWhiteSpace(databaseName))
411  {
412  if (isSqliteDB)
413  {
414  dbExists = await ioManager.FileExists(databaseName, cancellationToken).ConfigureAwait(false);
415  if (!dbExists)
416  databaseName = await ValidateNonExistantSqliteDBName(databaseName, cancellationToken).ConfigureAwait(false);
417  }
418  else
419  dbExists = await PromptYesNo("Does this database already exist? If not, we will attempt to CREATE it. (y/n): ", cancellationToken).ConfigureAwait(false);
420  }
421 
422  if (String.IsNullOrWhiteSpace(databaseName))
423  await console.WriteAsync("Invalid database name!", true, cancellationToken).ConfigureAwait(false);
424  else
425  break;
426  }
427  while (true);
428 
429  bool useWinAuth;
430  if (databaseConfiguration.DatabaseType == DatabaseType.SqlServer && platformIdentifier.IsWindows)
431  useWinAuth = await PromptYesNo("Use Windows Authentication? (y/n): ", cancellationToken).ConfigureAwait(false);
432  else
433  useWinAuth = false;
434 
435  await console.WriteAsync(null, true, cancellationToken).ConfigureAwait(false);
436 
437  string username = null;
438  string password = null;
439  if (!isSqliteDB)
440  if (!useWinAuth)
441  {
442  await console.WriteAsync("Enter username: ", false, cancellationToken).ConfigureAwait(false);
443  username = await console.ReadLineAsync(false, cancellationToken).ConfigureAwait(false);
444  await console.WriteAsync("Enter password: ", false, cancellationToken).ConfigureAwait(false);
445  password = await console.ReadLineAsync(true, cancellationToken).ConfigureAwait(false);
446  }
447  else
448  {
449  await console.WriteAsync("IMPORTANT: If using the service runner, ensure this computer's LocalSystem account has CREATE DATABASE permissions on the target server!", true, cancellationToken).ConfigureAwait(false);
450  await console.WriteAsync("The account it uses in MSSQL is usually \"NT AUTHORITY\\SYSTEM\" and the role it needs is usually \"dbcreator\".", true, cancellationToken).ConfigureAwait(false);
451  await console.WriteAsync("We'll run a sanity test here, but it won't be indicative of the service's permissions if that is the case", true, cancellationToken).ConfigureAwait(false);
452  }
453 
454  await console.WriteAsync(null, true, cancellationToken).ConfigureAwait(false);
455 
456  DbConnection testConnection;
457  void CreateTestConnection(string connectionString) =>
458  testConnection = dbConnectionFactory.CreateConnection(
459  connectionString,
460  databaseConfiguration.DatabaseType);
461 
462  switch (databaseConfiguration.DatabaseType)
463  {
464  case DatabaseType.SqlServer:
465  {
466  var csb = new SqlConnectionStringBuilder
467  {
468  ApplicationName = assemblyInformationProvider.VersionPrefix,
469  DataSource = serverAddress ?? "(local)"
470  };
471 
472  if (useWinAuth)
473  csb.IntegratedSecurity = true;
474  else
475  {
476  csb.UserID = username;
477  csb.Password = password;
478  }
479 
480  CreateTestConnection(csb.ConnectionString);
481  csb.InitialCatalog = databaseName;
482  databaseConfiguration.ConnectionString = csb.ConnectionString;
483  }
484 
485  break;
486  case DatabaseType.MariaDB:
487  case DatabaseType.MySql:
488  {
489  // MySQL/MariaDB
490  var csb = new MySqlConnectionStringBuilder
491  {
492  Server = serverAddress ?? "127.0.0.1",
493  UserID = username,
494  Password = password
495  };
496 
497  if (serverPort.HasValue)
498  csb.Port = serverPort.Value;
499 
500  CreateTestConnection(csb.ConnectionString);
501  csb.Database = databaseName;
502  databaseConfiguration.ConnectionString = csb.ConnectionString;
503  }
504 
505  break;
506  case DatabaseType.Sqlite:
507  {
508  var csb = new SqliteConnectionStringBuilder
509  {
510  DataSource = databaseName,
511  Mode = dbExists ? SqliteOpenMode.ReadOnly : SqliteOpenMode.ReadWriteCreate
512  };
513 
514  CreateTestConnection(csb.ConnectionString);
515  databaseConfiguration.ConnectionString = csb.ConnectionString;
516  }
517 
518  break;
519  case DatabaseType.PostgresSql:
520  {
521  var csb = new NpgsqlConnectionStringBuilder
522  {
523  ApplicationName = assemblyInformationProvider.VersionPrefix,
524  Host = serverAddress ?? "127.0.0.1",
525  Password = password,
526  Username = username
527  };
528 
529  if (serverPort.HasValue)
530  csb.Port = serverPort.Value;
531 
532  CreateTestConnection(csb.ConnectionString);
533  csb.Database = databaseName;
534  databaseConfiguration.ConnectionString = csb.ConnectionString;
535  }
536 
537  break;
538  default:
539  throw new InvalidOperationException("Invalid DatabaseType!");
540  }
541 
542  try
543  {
544  await TestDatabaseConnection(testConnection, databaseConfiguration, databaseName, dbExists, cancellationToken).ConfigureAwait(false);
545 
546  return databaseConfiguration;
547  }
548  catch (OperationCanceledException)
549  {
550  throw;
551  }
552  catch (Exception e)
553  {
554  await console.WriteAsync(e.Message, true, cancellationToken).ConfigureAwait(false);
555  await console.WriteAsync(null, true, cancellationToken).ConfigureAwait(false);
556  await console.WriteAsync("Retrying database configuration...", true, cancellationToken).ConfigureAwait(false);
557  }
558  }
559  while (true);
560  }
561  #pragma warning restore CA1502
562 
568  async Task<GeneralConfiguration> ConfigureGeneral(CancellationToken cancellationToken)
569  {
570  var newGeneralConfiguration = new GeneralConfiguration
571  {
573  };
574 
575  do
576  {
577  await console.WriteAsync(null, true, cancellationToken).ConfigureAwait(false);
578  await console.WriteAsync(String.Format(CultureInfo.InvariantCulture, "Minimum database user password length (leave blank for default of {0}): ", newGeneralConfiguration.MinimumPasswordLength), false, cancellationToken).ConfigureAwait(false);
579  var passwordLengthString = await console.ReadLineAsync(false, cancellationToken).ConfigureAwait(false);
580  if (String.IsNullOrWhiteSpace(passwordLengthString))
581  break;
582  if (UInt32.TryParse(passwordLengthString, out var passwordLength) && passwordLength >= 0)
583  {
584  newGeneralConfiguration.MinimumPasswordLength = passwordLength;
585  break;
586  }
587 
588  await console.WriteAsync("Please enter a positive integer!", true, cancellationToken).ConfigureAwait(false);
589  }
590  while (true);
591 
592  do
593  {
594  await console.WriteAsync(null, true, cancellationToken).ConfigureAwait(false);
595  await console.WriteAsync(String.Format(CultureInfo.InvariantCulture, "Timeout for sending and receiving BYOND topics (ms, 0 for infinite, leave blank for default of {0}): ", newGeneralConfiguration.ByondTopicTimeout), false, cancellationToken).ConfigureAwait(false);
596  var topicTimeoutString = await console.ReadLineAsync(false, cancellationToken).ConfigureAwait(false);
597  if (String.IsNullOrWhiteSpace(topicTimeoutString))
598  break;
599  if (Int32.TryParse(topicTimeoutString, out var topicTimeout) && topicTimeout >= 0)
600  {
601  newGeneralConfiguration.ByondTopicTimeout = topicTimeout;
602  break;
603  }
604 
605  await console.WriteAsync("Please enter a positive integer!", true, cancellationToken).ConfigureAwait(false);
606  }
607  while (true);
608 
609  await console.WriteAsync(null, true, cancellationToken).ConfigureAwait(false);
610  await console.WriteAsync("Enter a GitHub personal access token to bypass some rate limits (this is optional and does not require any scopes)", true, cancellationToken).ConfigureAwait(false);
611  await console.WriteAsync("GitHub personal access token: ", false, cancellationToken).ConfigureAwait(false);
612  newGeneralConfiguration.GitHubAccessToken = await console.ReadLineAsync(true, cancellationToken).ConfigureAwait(false);
613  if (String.IsNullOrWhiteSpace(newGeneralConfiguration.GitHubAccessToken))
614  newGeneralConfiguration.GitHubAccessToken = null;
615 
616  // newGeneralConfiguration.UseExperimentalWatchdog = await PromptYesNo("Use the experimental watchdog (NOT RECOMMENDED)? (y/n): ", cancellationToken).ConfigureAwait(false);
617  return newGeneralConfiguration;
618  }
619 
625  async Task<FileLoggingConfiguration> ConfigureLogging(CancellationToken cancellationToken)
626  {
627  var fileLoggingConfiguration = new FileLoggingConfiguration();
628  await console.WriteAsync(null, true, cancellationToken).ConfigureAwait(false);
629  fileLoggingConfiguration.Disable = !await PromptYesNo("Enable file logging? (y/n): ", cancellationToken).ConfigureAwait(false);
630 
631  if (!fileLoggingConfiguration.Disable)
632  {
633  do
634  {
635  await console.WriteAsync("Log file directory path (leave blank for default): ", false, cancellationToken).ConfigureAwait(false);
636  fileLoggingConfiguration.Directory = await console.ReadLineAsync(false, cancellationToken).ConfigureAwait(false);
637  if (String.IsNullOrWhiteSpace(fileLoggingConfiguration.Directory))
638  {
639  fileLoggingConfiguration.Directory = null;
640  break;
641  }
642 
643  // test a write of it
644  await console.WriteAsync(null, true, cancellationToken).ConfigureAwait(false);
645  await console.WriteAsync("Testing directory access...", true, cancellationToken).ConfigureAwait(false);
646  try
647  {
648  await ioManager.CreateDirectory(fileLoggingConfiguration.Directory, cancellationToken).ConfigureAwait(false);
649  var testFile = ioManager.ConcatPath(fileLoggingConfiguration.Directory, String.Format(CultureInfo.InvariantCulture, "WizardAccesTest.{0}.deleteme", Guid.NewGuid()));
650  await ioManager.WriteAllBytes(testFile, Array.Empty<byte>(), cancellationToken).ConfigureAwait(false);
651  try
652  {
653  await ioManager.DeleteFile(testFile, cancellationToken).ConfigureAwait(false);
654  }
655  catch (OperationCanceledException)
656  {
657  throw;
658  }
659  catch (Exception e)
660  {
661  await console.WriteAsync(String.Format(CultureInfo.InvariantCulture, "Error deleting test log file: {0}", testFile), true, cancellationToken).ConfigureAwait(false);
662  await console.WriteAsync(e.Message, true, cancellationToken).ConfigureAwait(false);
663  await console.WriteAsync(null, true, cancellationToken).ConfigureAwait(false);
664  }
665 
666  break;
667  }
668  catch (OperationCanceledException)
669  {
670  throw;
671  }
672  catch (Exception e)
673  {
674  await console.WriteAsync(e.Message, true, cancellationToken).ConfigureAwait(false);
675  await console.WriteAsync(null, true, cancellationToken).ConfigureAwait(false);
676  await console.WriteAsync("Please verify the path is valid and you have access to it!", true, cancellationToken).ConfigureAwait(false);
677  }
678  }
679  while (true);
680 
681  async Task<LogLevel?> PromptLogLevel(string question)
682  {
683  do
684  {
685  await console.WriteAsync(null, true, cancellationToken).ConfigureAwait(false);
686  await console.WriteAsync(question, true, cancellationToken).ConfigureAwait(false);
687  await console.WriteAsync(String.Format(CultureInfo.InvariantCulture, "Enter one of {0}/{1}/{2}/{3}/{4}/{5} (leave blank for default): ", nameof(LogLevel.Trace), nameof(LogLevel.Debug), nameof(LogLevel.Information), nameof(LogLevel.Warning), nameof(LogLevel.Error), nameof(LogLevel.Critical)), false, cancellationToken).ConfigureAwait(false);
688  var responseString = await console.ReadLineAsync(false, cancellationToken).ConfigureAwait(false);
689  if (String.IsNullOrWhiteSpace(responseString))
690  return null;
691  if (Enum.TryParse<LogLevel>(responseString, out var logLevel) && logLevel != LogLevel.None)
692  return logLevel;
693  await console.WriteAsync("Invalid log level!", true, cancellationToken).ConfigureAwait(false);
694  }
695  while (true);
696  }
697 
698  fileLoggingConfiguration.LogLevel = await PromptLogLevel(String.Format(CultureInfo.InvariantCulture, "Enter the level limit for normal logs (default {0}).", fileLoggingConfiguration.LogLevel)).ConfigureAwait(false) ?? fileLoggingConfiguration.LogLevel;
699  fileLoggingConfiguration.MicrosoftLogLevel = await PromptLogLevel(String.Format(CultureInfo.InvariantCulture, "Enter the level limit for Microsoft logs (VERY verbose, default {0}).", fileLoggingConfiguration.MicrosoftLogLevel)).ConfigureAwait(false) ?? fileLoggingConfiguration.MicrosoftLogLevel;
700  }
701 
702  return fileLoggingConfiguration;
703  }
704 
710  async Task<ControlPanelConfiguration> ConfigureControlPanel(CancellationToken cancellationToken)
711  {
712  var config = new ControlPanelConfiguration
713  {
714  Enable = await PromptYesNo("Enable the web control panel (Incomplete)? (y/n): ", cancellationToken).ConfigureAwait(false),
715  AllowAnyOrigin = await PromptYesNo("Allow web control panels hosted elsewhere to access the server? (Access-Control-Allow-Origin: *) (y/n): ", cancellationToken).ConfigureAwait(false)
716  };
717 
718  if (!config.AllowAnyOrigin)
719  {
720  await console.WriteAsync("Enter a comma seperated list of CORS allowed origins (optional): ", false, cancellationToken).ConfigureAwait(false);
721  var commaSeperatedOrigins = await console.ReadLineAsync(false, cancellationToken).ConfigureAwait(false);
722  if (!String.IsNullOrWhiteSpace(commaSeperatedOrigins))
723  {
724  var splits = commaSeperatedOrigins.Split(',');
725  config.AllowedOrigins = new List<string>(splits.Select(x => x.Trim()));
726  }
727  }
728 
729  return config;
730  }
731 
743  async Task SaveConfiguration(string userConfigFileName, ushort? hostingPort, DatabaseConfiguration databaseConfiguration, GeneralConfiguration newGeneralConfiguration, FileLoggingConfiguration fileLoggingConfiguration, ControlPanelConfiguration controlPanelConfiguration, CancellationToken cancellationToken)
744  {
745  await console.WriteAsync(String.Format(CultureInfo.InvariantCulture, "Configuration complete! Saving to {0}", userConfigFileName), true, cancellationToken).ConfigureAwait(false);
746 
747  newGeneralConfiguration.ApiPort = hostingPort ?? GeneralConfiguration.DefaultApiPort;
748  var map = new Dictionary<string, object>()
749  {
750  { DatabaseConfiguration.Section, databaseConfiguration },
751  { GeneralConfiguration.Section, newGeneralConfiguration },
752  { FileLoggingConfiguration.Section, fileLoggingConfiguration },
753  { ControlPanelConfiguration.Section, controlPanelConfiguration }
754  };
755 
756  var json = JsonConvert.SerializeObject(map, Formatting.Indented);
757  var configBytes = Encoding.UTF8.GetBytes(json);
758 
759  try
760  {
761  await ioManager.WriteAllBytes(userConfigFileName, configBytes, cancellationToken).ConfigureAwait(false);
762  }
763  catch (OperationCanceledException)
764  {
765  throw;
766  }
767  catch (Exception e)
768  {
769  await console.WriteAsync(e.Message, true, cancellationToken).ConfigureAwait(false);
770  await console.WriteAsync(null, true, cancellationToken).ConfigureAwait(false);
771  await console.WriteAsync("For your convienence, here's the json we tried to write out:", true, cancellationToken).ConfigureAwait(false);
772  await console.WriteAsync(null, true, cancellationToken).ConfigureAwait(false);
773  await console.WriteAsync(json, true, cancellationToken).ConfigureAwait(false);
774  await console.WriteAsync(null, true, cancellationToken).ConfigureAwait(false);
775  await console.WriteAsync("Press any key to exit...", true, cancellationToken).ConfigureAwait(false);
776  await console.PressAnyKeyAsync(cancellationToken).ConfigureAwait(false);
777  throw new OperationCanceledException();
778  }
779  }
780 
787  async Task RunWizard(string userConfigFileName, CancellationToken cancellationToken)
788  {
789  // welcome message
790  await console.WriteAsync("Welcome to tgstation-server 4!", true, cancellationToken).ConfigureAwait(false);
791  await console.WriteAsync("This wizard will help you configure your server.", true, cancellationToken).ConfigureAwait(false);
792 
793  var hostingPort = await PromptForHostingPort(cancellationToken).ConfigureAwait(false);
794 
795  var databaseConfiguration = await ConfigureDatabase(cancellationToken).ConfigureAwait(false);
796 
797  var newGeneralConfiguration = await ConfigureGeneral(cancellationToken).ConfigureAwait(false);
798 
799  var fileLoggingConfiguration = await ConfigureLogging(cancellationToken).ConfigureAwait(false);
800 
801  var controlPanelConfiguration = await ConfigureControlPanel(cancellationToken).ConfigureAwait(false);
802 
803  await console.WriteAsync(null, true, cancellationToken).ConfigureAwait(false);
804 
805  await SaveConfiguration(userConfigFileName, hostingPort, databaseConfiguration, newGeneralConfiguration, fileLoggingConfiguration, controlPanelConfiguration, cancellationToken).ConfigureAwait(false);
806  }
807 
813  async Task CheckRunWizard(CancellationToken cancellationToken)
814  {
815  var setupWizardMode = generalConfiguration.SetupWizardMode;
816  if (setupWizardMode == SetupWizardMode.Never)
817  return;
818 
819  var forceRun = setupWizardMode == SetupWizardMode.Force || setupWizardMode == SetupWizardMode.Only;
820  if (!console.Available)
821  {
822  if (forceRun)
823  throw new InvalidOperationException("Asked to run setup wizard with no console avaliable!");
824  return;
825  }
826 
827  var userConfigFileName = String.Format(CultureInfo.InvariantCulture, "appsettings.{0}.json", hostingEnvironment.EnvironmentName);
828 
829  async Task HandleSetupCancel()
830  {
831  await console.WriteAsync(String.Empty, true, default).ConfigureAwait(false);
832  await console.WriteAsync("Aborting setup!", true, default).ConfigureAwait(false);
833  }
834 
835  // Link passed cancellationToken with cancel key press
836  Task finalTask = Task.CompletedTask;
837  using (var cts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, console.CancelKeyPress))
838  using ((cancellationToken = cts.Token).Register(() => finalTask = HandleSetupCancel()))
839  try
840  {
841  var exists = await ioManager.FileExists(userConfigFileName, cancellationToken).ConfigureAwait(false);
842 
843  bool shouldRunBasedOnAutodetect;
844  if (exists)
845  {
846  var bytes = await ioManager.ReadAllBytes(userConfigFileName, cancellationToken).ConfigureAwait(false);
847  var contents = Encoding.UTF8.GetString(bytes);
848  var existingConfigIsEmpty = String.IsNullOrWhiteSpace(contents) || contents.Trim() == "{}";
849  shouldRunBasedOnAutodetect = existingConfigIsEmpty;
850  }
851  else
852  shouldRunBasedOnAutodetect = true;
853 
854  if (!shouldRunBasedOnAutodetect)
855  {
856  if (forceRun)
857  {
858  await console.WriteAsync(String.Format(CultureInfo.InvariantCulture, "The configuration settings are requesting the setup wizard be run, but you already appear to have a configuration file ({0})!", userConfigFileName), true, cancellationToken).ConfigureAwait(false);
859 
860  forceRun = await PromptYesNo("Continue running setup wizard? (y/n): ", cancellationToken).ConfigureAwait(false);
861  }
862 
863  if (!forceRun)
864  return;
865  }
866 
867  // flush the logs to prevent console conflicts
868  await asyncDelayer.Delay(TimeSpan.FromSeconds(1), cancellationToken).ConfigureAwait(false);
869 
870  await RunWizard(userConfigFileName, cancellationToken).ConfigureAwait(false);
871  }
872  finally
873  {
874  await finalTask.ConfigureAwait(false);
875  }
876  }
877 
879  public async Task StartAsync(CancellationToken cancellationToken)
880  {
881  await CheckRunWizard(cancellationToken).ConfigureAwait(false);
882  applicationLifetime.StopApplication();
883  }
884 
886  public Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask;
887  }
888 }
List< string > AllowedOrigins
Origins allowed for CORS requests
readonly IAsyncDelayer asyncDelayer
The IAsyncDelayer for the SetupWizard
Definition: SetupWizard.cs:63
@@ -106,44 +106,44 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
Abstraction for global::System.Console
Definition: IConsole.cs:9
const string Section
The key for the Microsoft.Extensions.Configuration.IConfigurationSection the GeneralConfiguration res...
-
async Task< string > ValidateNonExistantSqliteDBName(string databaseName, CancellationToken cancellationToken)
Definition: SetupWizard.cs:239
-
async Task StartAsync(CancellationToken cancellationToken)
Definition: SetupWizard.cs:871
-
async Task< GeneralConfiguration > ConfigureGeneral(CancellationToken cancellationToken)
Prompts the user to create a GeneralConfiguration
Definition: SetupWizard.cs:560
+
async Task< string > ValidateNonExistantSqliteDBName(string databaseName, CancellationToken cancellationToken)
Definition: SetupWizard.cs:249
+
async Task StartAsync(CancellationToken cancellationToken)
Definition: SetupWizard.cs:879
+
async Task< GeneralConfiguration > ConfigureGeneral(CancellationToken cancellationToken)
Prompts the user to create a GeneralConfiguration
Definition: SetupWizard.cs:568
const string Section
The key for the Microsoft.Extensions.Configuration.IConfigurationSection the DatabaseConfiguration re...
DatabaseType DatabaseType
The Configuration.DatabaseType to create
-
async Task< DatabaseType > PromptDatabaseType(bool firstTime, CancellationToken cancellationToken)
Prompt the user for the DatabaseType.
Definition: SetupWizard.cs:286
-
async Task RunWizard(string userConfigFileName, CancellationToken cancellationToken)
Runs the SetupWizard
Definition: SetupWizard.cs:779
+
async Task< DatabaseType > PromptDatabaseType(bool firstTime, CancellationToken cancellationToken)
Prompt the user for the DatabaseType.
Definition: SetupWizard.cs:296
+
async Task RunWizard(string userConfigFileName, CancellationToken cancellationToken)
Runs the SetupWizard
Definition: SetupWizard.cs:787
const string Section
The key for the Microsoft.Extensions.Configuration.IConfigurationSection the ControlPanelConfiguratio...
async Task< ushort?> PromptForHostingPort(CancellationToken cancellationToken)
Prompts the user to enter the port to host TGS on
Definition: SetupWizard.cs:136
-
async Task< DatabaseConfiguration > ConfigureDatabase(CancellationToken cancellationToken)
Prompts the user to create a DatabaseConfiguration
Definition: SetupWizard.cs:348
+
async Task< DatabaseConfiguration > ConfigureDatabase(CancellationToken cancellationToken)
Prompts the user to create a DatabaseConfiguration
Definition: SetupWizard.cs:356
+
string ServerVersion
The string form of the global::System.Version of the target server
uint MinimumPasswordLength
Minimum length of database user passwords.
readonly IHostApplicationLifetime applicationLifetime
The IHostApplicationLifetime for the SetupWizard.
Definition: SetupWizard.cs:68
const ushort DefaultApiPort
The default value of ApiPort.
-
async Task< ControlPanelConfiguration > ConfigureControlPanel(CancellationToken cancellationToken)
Prompts the user to create a ControlPanelConfiguration
Definition: SetupWizard.cs:702
+
async Task< ControlPanelConfiguration > ConfigureControlPanel(CancellationToken cancellationToken)
Prompts the user to create a ControlPanelConfiguration
Definition: SetupWizard.cs:710
SetupWizard(IIOManager ioManager, IConsole console, IHostEnvironment hostingEnvironment, IAssemblyInformationProvider assemblyInformationProvider, IDatabaseConnectionFactory dbConnectionFactory, IPlatformIdentifier platformIdentifier, IAsyncDelayer asyncDelayer, IHostApplicationLifetime applicationLifetime, IOptions< GeneralConfiguration > generalConfigurationOptions)
Construct a SetupWizard
Definition: SetupWizard.cs:87
async Task< bool > PromptYesNo(string question, CancellationToken cancellationToken)
A prompt for a yes or no value
Definition: SetupWizard.cs:115
readonly IConsole console
The IConsole for the SetupWizard
Definition: SetupWizard.cs:38
-
string MySqlServerVersion
The string form of the global::System.Version of a target MySQL/MariaDB server
-
async Task CheckRunWizard(CancellationToken cancellationToken)
Check if it should and run the SetupWizard if necessary.
Definition: SetupWizard.cs:805
+
async Task CheckRunWizard(CancellationToken cancellationToken)
Check if it should and run the SetupWizard if necessary.
Definition: SetupWizard.cs:813
Configuration options for the Database.DatabaseContext
readonly IPlatformIdentifier platformIdentifier
The IPlatformIdentifier for the SetupWizard
Definition: SetupWizard.cs:58
-
async Task< FileLoggingConfiguration > ConfigureLogging(CancellationToken cancellationToken)
Prompts the user to create a FileLoggingConfiguration
Definition: SetupWizard.cs:617
+
async Task< FileLoggingConfiguration > ConfigureLogging(CancellationToken cancellationToken)
Prompts the user to create a FileLoggingConfiguration
Definition: SetupWizard.cs:625
DatabaseType
Type of database to user
Definition: DatabaseType.cs:6
readonly IAssemblyInformationProvider assemblyInformationProvider
The IAssemblyInformationProvider for the SetupWizard
Definition: SetupWizard.cs:48
-
async Task SaveConfiguration(string userConfigFileName, ushort?hostingPort, DatabaseConfiguration databaseConfiguration, GeneralConfiguration newGeneralConfiguration, FileLoggingConfiguration fileLoggingConfiguration, ControlPanelConfiguration controlPanelConfiguration, CancellationToken cancellationToken)
Saves a given Configuration set to userConfigFileName
Definition: SetupWizard.cs:735
+
async Task SaveConfiguration(string userConfigFileName, ushort?hostingPort, DatabaseConfiguration databaseConfiguration, GeneralConfiguration newGeneralConfiguration, FileLoggingConfiguration fileLoggingConfiguration, ControlPanelConfiguration controlPanelConfiguration, CancellationToken cancellationToken)
Saves a given Configuration set to userConfigFileName
Definition: SetupWizard.cs:743
const string Section
The key for the Microsoft.Extensions.Configuration.IConfigurationSection the FileLoggingConfiguration...
Interface for using filesystems
Definition: IIOManager.cs:11
diff --git a/annotated.html b/annotated.html index c8765a0715..15398835b2 100644 --- a/annotated.html +++ b/annotated.html @@ -385,22 +385,23 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');  CMYAddMinimumSecurityAdd the Api.Models.Internal.CompileJob.MinimumSecurityLevel and DreamMaker.ApiValidationSecurityLevel columns for MySQL/MariaDB  CMYCheckpointRebuildCheckpoint model rebuild for MYSQL.  CMYFixCascadingDeleteFix cascading data deletes for Models.Instances on MYSQL. - CMYFixRevInfoIndexMake commit shas non-unique per Instance for MySQL/MariaDB. - CMYInitialCreateThe initial database migration for MySQL/MariaDB - CMYKillJsonsAddDMApiVersionDrop chat reattach information and add Models.ReattachInformationBase.LaunchSecurityLevel for MYSQL. - CMYLimitsOnChatAdds chat limits for MYSQL. - CMYNullableAndForeignKeyCleanupCleans up of nullable columns and foreign keys MySQL/MariaDB - CMYReattachCompileJobRequiredMarks the Models.CompileJobs of Models.ReattachInformations as non-nullable for MySQL/MariaDB - CMYRemoveSoftColumnsRemoves various defunct columns for MYSQL. - CMySqlDatabaseContextModelSnapshot - CMYToggleTestmergeCommentsAdd the Api.Models.Internal.RepositorySettings.PostTestMergeComment column for MySQL/MariaDB - CMYTonsOfValidationReduces column sizes and fixes some broken indexes for MYSQL. - CPGCreateCreate initial schema for PostgreSQL. - CSLAddHeartbeatAdd heartbeat support for SQLite. - CSLRebuildRebuild of the schema for SQLite. - CSLRemoveSoftColumnsRemoves various defunct columns for SQLite. - CSqliteDatabaseContextModelSnapshot - CSqlServerDatabaseContextModelSnapshot + CMYFixForeignKeyFix the CompileJob/RevisionInformation foreign key for MySQL. + CMYFixRevInfoIndexMake commit shas non-unique per Instance for MySQL/MariaDB. + CMYInitialCreateThe initial database migration for MySQL/MariaDB + CMYKillJsonsAddDMApiVersionDrop chat reattach information and add Models.ReattachInformationBase.LaunchSecurityLevel for MYSQL. + CMYLimitsOnChatAdds chat limits for MYSQL. + CMYNullableAndForeignKeyCleanupCleans up of nullable columns and foreign keys MySQL/MariaDB + CMYReattachCompileJobRequiredMarks the Models.CompileJobs of Models.ReattachInformations as non-nullable for MySQL/MariaDB + CMYRemoveSoftColumnsRemoves various defunct columns for MYSQL. + CMySqlDatabaseContextModelSnapshot + CMYToggleTestmergeCommentsAdd the Api.Models.Internal.RepositorySettings.PostTestMergeComment column for MySQL/MariaDB + CMYTonsOfValidationReduces column sizes and fixes some broken indexes for MYSQL. + CPGCreateCreate initial schema for PostgreSQL. + CSLAddHeartbeatAdd heartbeat support for SQLite. + CSLRebuildRebuild of the schema for SQLite. + CSLRemoveSoftColumnsRemoves various defunct columns for SQLite. + CSqliteDatabaseContextModelSnapshot + CSqlServerDatabaseContextModelSnapshot  CDatabaseCollection  CDatabaseConnectionFactory  CDatabaseContextBackend abstract implementation of IDatabaseContext diff --git a/class_migration.html b/class_migration.html index 89659e7d55..1ec8528ce5 100644 --- a/class_migration.html +++ b/class_migration.html @@ -121,19 +121,20 @@ Inheritance diagram for Migration:
- - - - - - - - - - - - - + + + + + + + + + + + + + +
[legend]

The documentation for this class was generated from the following file:
diff --git a/class_tgstation_1_1_server_1_1_host_1_1_components_1_1_session_1_1_reattach_info_handler.html b/class_tgstation_1_1_server_1_1_host_1_1_components_1_1_session_1_1_reattach_info_handler.html index 334d786f67..e7dfdb8318 100644 --- a/class_tgstation_1_1_server_1_1_host_1_1_components_1_1_session_1_1_reattach_info_handler.html +++ b/class_tgstation_1_1_server_1_1_host_1_1_components_1_1_session_1_1_reattach_info_handler.html @@ -231,8 +231,8 @@ Private Attributes

Implements Tgstation.Server.Host.Components.Session.IReattachInfoHandler.

-

Definition at line 95 of file ReattachInfoHandler.cs.

-
96  {
97  Models.DualReattachInformation result = null;
98  await databaseContextFactory.UseContext(async (db) =>
99  {
100  var instance = await db.Instances
101  .AsQueryable()
102  .Where(x => x.Id == metadata.Id)
103  .Include(x => x.WatchdogReattachInformation).ThenInclude(x => x.Alpha).ThenInclude(x => x.CompileJob)
104  .Include(x => x.WatchdogReattachInformation).ThenInclude(x => x.Bravo).ThenInclude(x => x.CompileJob)
105  .FirstOrDefaultAsync(cancellationToken).ConfigureAwait(false);
106  result = instance.WatchdogReattachInformation;
107  if (result == default)
108  return;
109  instance.WatchdogReattachInformation = null;
110  db.WatchdogReattachInformations.Remove(result);
111  await db.Save(cancellationToken).ConfigureAwait(false);
112  }).ConfigureAwait(false);
113 
114  if (result == default)
115  {
116  logger.LogDebug("Reattach information not found!");
117  return null;
118  }
119 
120  Task<IDmbProvider> GetDmbForReattachInfo(Models.ReattachInformation reattachInformation) => reattachInformation != null
121  ? dmbFactory.FromCompileJob(reattachInformation.CompileJob, cancellationToken)
122  : Task.FromResult<IDmbProvider>(null);
123 
124  var bravoDmbTask = GetDmbForReattachInfo(result.Bravo);
125  var info = new DualReattachInformation(result, await GetDmbForReattachInfo(result.Alpha).ConfigureAwait(false), await bravoDmbTask.ConfigureAwait(false));
126  logger.LogDebug("Reattach information loaded: {0}", info);
127  return info;
128  }
readonly Api.Models.Instance metadata
The Api.Models.Instance for the ReattachInfoHandler
+

Definition at line 94 of file ReattachInfoHandler.cs.

+
95  {
96  Models.DualReattachInformation result = null;
97  await databaseContextFactory.UseContext(async (db) =>
98  {
99  var instance = await db.Instances
100  .AsQueryable()
101  .Where(x => x.Id == metadata.Id)
102  .Include(x => x.WatchdogReattachInformation).ThenInclude(x => x.Alpha).ThenInclude(x => x.CompileJob)
103  .Include(x => x.WatchdogReattachInformation).ThenInclude(x => x.Bravo).ThenInclude(x => x.CompileJob)
104  .FirstOrDefaultAsync(cancellationToken).ConfigureAwait(false);
105  result = instance.WatchdogReattachInformation;
106  if (result == default)
107  return;
108  instance.WatchdogReattachInformation = null;
109  db.WatchdogReattachInformations.Remove(result);
110  await db.Save(cancellationToken).ConfigureAwait(false);
111  }).ConfigureAwait(false);
112 
113  if (result == default)
114  {
115  logger.LogDebug("Reattach information not found!");
116  return null;
117  }
118 
119  Task<IDmbProvider> GetDmbForReattachInfo(Models.ReattachInformation reattachInformation) => reattachInformation != null
120  ? dmbFactory.FromCompileJob(reattachInformation.CompileJob, cancellationToken)
121  : Task.FromResult<IDmbProvider>(null);
122 
123  var bravoDmbTask = GetDmbForReattachInfo(result.Bravo);
124  var info = new DualReattachInformation(result, await GetDmbForReattachInfo(result.Alpha).ConfigureAwait(false), await bravoDmbTask.ConfigureAwait(false));
125  logger.LogDebug("Reattach information loaded: {0}", info);
126  return info;
127  }
readonly Api.Models.Instance metadata
The Api.Models.Instance for the ReattachInfoHandler
readonly ILogger< ReattachInfoHandler > logger
The ILogger for the ReattachInfoHandler
Task UseContext(Func< IDatabaseContext, Task > operation)
Run an operation in the scope of an IDatabaseContext
readonly IDatabaseContextFactory databaseContextFactory
The IDatabaseContextFactory for the ReattachInfoHandler
diff --git a/class_tgstation_1_1_server_1_1_host_1_1_configuration_1_1_control_panel_configuration.html b/class_tgstation_1_1_server_1_1_host_1_1_configuration_1_1_control_panel_configuration.html index 695ec4d902..f8c7932954 100644 --- a/class_tgstation_1_1_server_1_1_host_1_1_configuration_1_1_control_panel_configuration.html +++ b/class_tgstation_1_1_server_1_1_host_1_1_configuration_1_1_control_panel_configuration.html @@ -147,7 +147,7 @@ Properties

Definition at line 13 of file ControlPanelConfiguration.cs.

-

Referenced by Tgstation.Server.Host.Setup.SetupWizard.SaveConfiguration().

+

Referenced by Tgstation.Server.Host.Setup.SetupWizard.SaveConfiguration().

@@ -198,7 +198,7 @@ Properties

Definition at line 28 of file ControlPanelConfiguration.cs.

-

Referenced by Tgstation.Server.Host.Setup.SetupWizard.ConfigureControlPanel().

+

Referenced by Tgstation.Server.Host.Setup.SetupWizard.ConfigureControlPanel().

diff --git a/class_tgstation_1_1_server_1_1_host_1_1_configuration_1_1_database_configuration-members.html b/class_tgstation_1_1_server_1_1_host_1_1_configuration_1_1_database_configuration-members.html index 57fc3e67b6..4b0115198c 100644 --- a/class_tgstation_1_1_server_1_1_host_1_1_configuration_1_1_database_configuration-members.html +++ b/class_tgstation_1_1_server_1_1_host_1_1_configuration_1_1_database_configuration-members.html @@ -104,9 +104,9 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search'); DatabaseTypeTgstation.Server.Host.Configuration.DatabaseConfiguration DesignTimeTgstation.Server.Host.Configuration.DatabaseConfiguration DropDatabaseTgstation.Server.Host.Configuration.DatabaseConfiguration - MySqlServerVersionTgstation.Server.Host.Configuration.DatabaseConfiguration - ResetAdminPasswordTgstation.Server.Host.Configuration.DatabaseConfiguration - SectionTgstation.Server.Host.Configuration.DatabaseConfiguration + ResetAdminPasswordTgstation.Server.Host.Configuration.DatabaseConfiguration + SectionTgstation.Server.Host.Configuration.DatabaseConfiguration + ServerVersionTgstation.Server.Host.Configuration.DatabaseConfiguration
@@ -183,7 +183,7 @@ Properties

Definition at line 30 of file DatabaseConfiguration.cs.

-

Referenced by Tgstation.Server.Host.Database.SqlServerDatabaseContext.OnConfiguring(), Tgstation.Server.Host.Database.MySqlDatabaseContext.OnConfiguring(), Tgstation.Server.Host.Database.SqliteDatabaseContext.OnConfiguring(), and Tgstation.Server.Host.Database.PostgresSqlDatabaseContext.OnConfiguring().

+

Referenced by Tgstation.Server.Host.Database.SqlServerDatabaseContext.OnConfiguring(), Tgstation.Server.Host.Database.SqliteDatabaseContext.OnConfiguring(), Tgstation.Server.Host.Database.MySqlDatabaseContext.OnConfiguring(), and Tgstation.Server.Host.Database.PostgresSqlDatabaseContext.OnConfiguring().

@@ -209,7 +209,7 @@ Properties

Definition at line 20 of file DatabaseConfiguration.cs.

-

Referenced by Tgstation.Server.Host.Setup.SetupWizard.ConfigureDatabase(), Tgstation.Server.Host.Core.Application.ConfigureServices(), and Tgstation.Server.Host.Setup.SetupWizard.TestDatabaseConnection().

+

Referenced by Tgstation.Server.Host.Setup.SetupWizard.ConfigureDatabase(), Tgstation.Server.Host.Core.Application.ConfigureServices(), and Tgstation.Server.Host.Setup.SetupWizard.TestDatabaseConnection().

@@ -261,33 +261,7 @@ Properties

Definition at line 35 of file DatabaseConfiguration.cs.

-

Referenced by Tgstation.Server.Host.Database.DatabaseContext.Initialize().

- - - - -
-
- - - - - -
- - - - -
string Tgstation.Server.Host.Configuration.DatabaseConfiguration.MySqlServerVersion
-
-getset
-
- -

The string form of the global::System.Version of a target MySQL/MariaDB server

- -

Definition at line 45 of file DatabaseConfiguration.cs.

- -

Referenced by Tgstation.Server.Host.Setup.SetupWizard.TestDatabaseConnection().

+

Referenced by Tgstation.Server.Host.Database.DatabaseContext.Initialize().

@@ -313,7 +287,33 @@ Properties

Definition at line 25 of file DatabaseConfiguration.cs.

-

Referenced by Tgstation.Server.Host.Database.DatabaseContext.Initialize().

+

Referenced by Tgstation.Server.Host.Database.DatabaseContext.Initialize().

+ + + + +
+
+ + + + + +
+ + + + +
string Tgstation.Server.Host.Configuration.DatabaseConfiguration.ServerVersion
+
+getset
+
+ +

The string form of the global::System.Version of the target server

+ +

Definition at line 45 of file DatabaseConfiguration.cs.

+ +

Referenced by Tgstation.Server.Host.Setup.SetupWizard.TestDatabaseConnection().

diff --git a/class_tgstation_1_1_server_1_1_host_1_1_configuration_1_1_file_logging_configuration.html b/class_tgstation_1_1_server_1_1_host_1_1_configuration_1_1_file_logging_configuration.html index 311439471e..c186da4570 100644 --- a/class_tgstation_1_1_server_1_1_host_1_1_configuration_1_1_file_logging_configuration.html +++ b/class_tgstation_1_1_server_1_1_host_1_1_configuration_1_1_file_logging_configuration.html @@ -208,7 +208,7 @@ Private Attributes

Definition at line 15 of file FileLoggingConfiguration.cs.

-

Referenced by Tgstation.Server.Host.Setup.SetupWizard.SaveConfiguration().

+

Referenced by Tgstation.Server.Host.Setup.SetupWizard.SaveConfiguration().

diff --git a/class_tgstation_1_1_server_1_1_host_1_1_configuration_1_1_general_configuration.html b/class_tgstation_1_1_server_1_1_host_1_1_configuration_1_1_general_configuration.html index cc8630e26b..efd998930a 100644 --- a/class_tgstation_1_1_server_1_1_host_1_1_configuration_1_1_general_configuration.html +++ b/class_tgstation_1_1_server_1_1_host_1_1_configuration_1_1_general_configuration.html @@ -238,7 +238,7 @@ Private Attributes

Definition at line 20 of file GeneralConfiguration.cs.

-

Referenced by Tgstation.Server.Host.Setup.SetupWizard.SaveConfiguration().

+

Referenced by Tgstation.Server.Host.Setup.SetupWizard.SaveConfiguration().

@@ -376,7 +376,7 @@ Private Attributes

Definition at line 15 of file GeneralConfiguration.cs.

-

Referenced by Tgstation.Server.Host.Setup.SetupWizard.SaveConfiguration(), and Tgstation.Server.Host.Extensions.ServiceCollectionExtensions.UseStandardConfig< TConfig >().

+

Referenced by Tgstation.Server.Host.Setup.SetupWizard.SaveConfiguration(), and Tgstation.Server.Host.Extensions.ServiceCollectionExtensions.UseStandardConfig< TConfig >().

@@ -403,7 +403,7 @@ Private Attributes

Definition at line 50 of file GeneralConfiguration.cs.

-

Referenced by Tgstation.Server.Host.Setup.SetupWizard.SaveConfiguration().

+

Referenced by Tgstation.Server.Host.Setup.SetupWizard.SaveConfiguration().

diff --git a/class_tgstation_1_1_server_1_1_host_1_1_core_1_1_application.html b/class_tgstation_1_1_server_1_1_host_1_1_core_1_1_application.html index 060b6c7203..3929ba0b7e 100644 --- a/class_tgstation_1_1_server_1_1_host_1_1_core_1_1_application.html +++ b/class_tgstation_1_1_server_1_1_host_1_1_core_1_1_application.html @@ -294,8 +294,8 @@ Additional Inherited Members

Definition at line 323 of file Application.cs.

-

References Tgstation.Server.Host.Core.IServerControl.Die(), Tgstation.Server.Api.Models.Limits.MaximumStringLength, Tgstation.Server.Host.Components.IInstanceManager.Ready, Tgstation.Server.Host.Core.IServerControl.Restart(), and Tgstation.Server.Host.Core.IServerControl.WatchdogPresent.

-
331  {
332  if (applicationBuilder == null)
333  throw new ArgumentNullException(nameof(applicationBuilder));
334  if (serverControl == null)
335  throw new ArgumentNullException(nameof(serverControl));
336 
337  this.tokenFactory = tokenFactory ?? throw new ArgumentNullException(nameof(tokenFactory));
338 
339  var controlPanelConfiguration = controlPanelConfigurationOptions?.Value ?? throw new ArgumentNullException(nameof(controlPanelConfigurationOptions));
340  var generalConfiguration = generalConfigurationOptions?.Value ?? throw new ArgumentNullException(nameof(generalConfigurationOptions));
341 
342  if (logger == null)
343  throw new ArgumentNullException(nameof(logger));
344 
345  logger.LogDebug("Content Root: {0}", hostingEnvironment.ContentRootPath);
346  logger.LogTrace("Web Root: {0}", hostingEnvironment.WebRootPath);
347 
348  if (generalConfiguration.MinimumPasswordLength > Limits.MaximumStringLength)
349  {
350  logger.LogCritical("Configured minimum password length ({0}) is greater than the maximum database string length ({1})!");
351  serverControl.Die(new InvalidOperationException("Minimum password length greater than database limit!"));
352  return;
353  }
354 
355  // attempt to restart the server if the configuration changes
356  if (serverControl.WatchdogPresent)
357  ChangeToken.OnChange(Configuration.GetReloadToken, () => serverControl.Restart());
358 
359  // setup the HTTP request pipeline
360  // Final point where we wrap exceptions in a 500 (ErrorMessage) response
361  applicationBuilder.UseServerErrorHandling();
362 
363  // 503 requests made while the application is starting
364  applicationBuilder.UseAsyncInitialization(async (cancellationToken) =>
365  {
366  var tcs = new TaskCompletionSource<object>();
367  using (cancellationToken.Register(() => tcs.SetCanceled()))
368  await Task.WhenAny(tcs.Task, instanceManager.Ready).ConfigureAwait(false);
369  });
370 
371  // suppress OperationCancelledExceptions, they are just aborted HTTP requests
372  applicationBuilder.UseCancelledRequestSuppression();
373 
374  if (hostingEnvironment.IsDevelopment())
375  {
376  applicationBuilder.UseSwagger();
377  applicationBuilder.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "TGS API V4"));
378  logger.LogTrace("Swagger API generation enabled");
379  }
380 
381  // Set up CORS based on configuration if necessary
382  Action<CorsPolicyBuilder> corsBuilder = null;
383  if (controlPanelConfiguration.AllowAnyOrigin)
384  {
385  logger.LogTrace("Access-Control-Allow-Origin: *");
386  corsBuilder = builder => builder.AllowAnyOrigin();
387  }
388  else if (controlPanelConfiguration.AllowedOrigins?.Count > 0)
389  {
390  logger.LogTrace("Access-Control-Allow-Origin: ", String.Join(',', controlPanelConfiguration.AllowedOrigins));
391  corsBuilder = builder => builder.WithOrigins(controlPanelConfiguration.AllowedOrigins.ToArray());
392  }
393 
394  var originalBuilder = corsBuilder;
395  corsBuilder = builder =>
396  {
397  builder.AllowAnyHeader().AllowAnyMethod();
398  originalBuilder?.Invoke(builder);
399  };
400  applicationBuilder.UseCors(corsBuilder);
401 
402  // spa loading if necessary
403  if (controlPanelConfiguration.Enable)
404  {
405  logger.LogWarning("Web control panel enabled. This is a highly WIP feature!");
406  applicationBuilder.UseStaticFiles();
407  }
408  else
409  logger.LogDebug("Web control panel disabled!");
410 
411  // authenticate JWT tokens using our security pipeline if present, returns 401 if bad
412  applicationBuilder.UseAuthentication();
413 
414  // suppress and log database exceptions
415  applicationBuilder.UseDbConflictHandling();
416 
417  // majority of handling is done in the controllers
418  applicationBuilder.UseMvc();
419 
420  // 404 anything that gets this far
421  }
const int MaximumStringLength
Length limit for strings in fields.
Definition: Limits.cs:11
+

References Tgstation.Server.Host.Core.IServerControl.Die(), Tgstation.Server.Api.Models.Limits.MaximumStringLength, Tgstation.Server.Host.Components.IInstanceManager.Ready, and Tgstation.Server.Host.Core.IServerControl.WatchdogPresent.

+
331  {
332  if (applicationBuilder == null)
333  throw new ArgumentNullException(nameof(applicationBuilder));
334  if (serverControl == null)
335  throw new ArgumentNullException(nameof(serverControl));
336 
337  this.tokenFactory = tokenFactory ?? throw new ArgumentNullException(nameof(tokenFactory));
338 
339  var controlPanelConfiguration = controlPanelConfigurationOptions?.Value ?? throw new ArgumentNullException(nameof(controlPanelConfigurationOptions));
340  var generalConfiguration = generalConfigurationOptions?.Value ?? throw new ArgumentNullException(nameof(generalConfigurationOptions));
341 
342  if (logger == null)
343  throw new ArgumentNullException(nameof(logger));
344 
345  logger.LogDebug("Content Root: {0}", hostingEnvironment.ContentRootPath);
346  logger.LogTrace("Web Root: {0}", hostingEnvironment.WebRootPath);
347 
348  if (generalConfiguration.MinimumPasswordLength > Limits.MaximumStringLength)
349  {
350  logger.LogCritical("Configured minimum password length ({0}) is greater than the maximum database string length ({1})!");
351  serverControl.Die(new InvalidOperationException("Minimum password length greater than database limit!"));
352  return;
353  }
354 
355  // attempt to restart the server if the configuration changes
356  if (serverControl.WatchdogPresent)
357  ChangeToken.OnChange(Configuration.GetReloadToken, () =>
358  {
359  logger.LogInformation("Configuration change detected");
360  serverControl.Restart();
361  });
362 
363  // setup the HTTP request pipeline
364  // Final point where we wrap exceptions in a 500 (ErrorMessage) response
365  applicationBuilder.UseServerErrorHandling();
366 
367  // 503 requests made while the application is starting
368  applicationBuilder.UseAsyncInitialization(async (cancellationToken) =>
369  {
370  var tcs = new TaskCompletionSource<object>();
371  using (cancellationToken.Register(() => tcs.SetCanceled()))
372  await Task.WhenAny(tcs.Task, instanceManager.Ready).ConfigureAwait(false);
373  });
374 
375  // suppress OperationCancelledExceptions, they are just aborted HTTP requests
376  applicationBuilder.UseCancelledRequestSuppression();
377 
378  if (hostingEnvironment.IsDevelopment())
379  {
380  applicationBuilder.UseSwagger();
381  applicationBuilder.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "TGS API V4"));
382  logger.LogTrace("Swagger API generation enabled");
383  }
384 
385  // Set up CORS based on configuration if necessary
386  Action<CorsPolicyBuilder> corsBuilder = null;
387  if (controlPanelConfiguration.AllowAnyOrigin)
388  {
389  logger.LogTrace("Access-Control-Allow-Origin: *");
390  corsBuilder = builder => builder.AllowAnyOrigin();
391  }
392  else if (controlPanelConfiguration.AllowedOrigins?.Count > 0)
393  {
394  logger.LogTrace("Access-Control-Allow-Origin: ", String.Join(',', controlPanelConfiguration.AllowedOrigins));
395  corsBuilder = builder => builder.WithOrigins(controlPanelConfiguration.AllowedOrigins.ToArray());
396  }
397 
398  var originalBuilder = corsBuilder;
399  corsBuilder = builder =>
400  {
401  builder.AllowAnyHeader().AllowAnyMethod();
402  originalBuilder?.Invoke(builder);
403  };
404  applicationBuilder.UseCors(corsBuilder);
405 
406  // spa loading if necessary
407  if (controlPanelConfiguration.Enable)
408  {
409  logger.LogWarning("Web control panel enabled. This is a highly WIP feature!");
410  applicationBuilder.UseStaticFiles();
411  }
412  else
413  logger.LogDebug("Web control panel disabled!");
414 
415  // authenticate JWT tokens using our security pipeline if present, returns 401 if bad
416  applicationBuilder.UseAuthentication();
417 
418  // suppress and log database exceptions
419  applicationBuilder.UseDbConflictHandling();
420 
421  // majority of handling is done in the controllers
422  applicationBuilder.UseMvc();
423 
424  // 404 anything that gets this far
425  }
const int MaximumStringLength
Length limit for strings in fields.
Definition: Limits.cs:11
Sanity limits to prevent users from overloading
Definition: Limits.cs:6
Task Ready
Task that completes when the IInstanceManager finishes initializing.
readonly IWebHostEnvironment hostingEnvironment
The IWebHostEnvironment for the Application.
Definition: Application.cs:52
@@ -307,7 +307,6 @@ Here is the call graph for this function:
-

diff --git a/class_tgstation_1_1_server_1_1_host_1_1_core_1_1_application_a2c2b516c6696a0390b37c4983a52a235_cgraph.map b/class_tgstation_1_1_server_1_1_host_1_1_core_1_1_application_a2c2b516c6696a0390b37c4983a52a235_cgraph.map index 0f005bb7c0..d4762a800b 100644 --- a/class_tgstation_1_1_server_1_1_host_1_1_core_1_1_application_a2c2b516c6696a0390b37c4983a52a235_cgraph.map +++ b/class_tgstation_1_1_server_1_1_host_1_1_core_1_1_application_a2c2b516c6696a0390b37c4983a52a235_cgraph.map @@ -1,4 +1,3 @@ - diff --git a/class_tgstation_1_1_server_1_1_host_1_1_core_1_1_application_a2c2b516c6696a0390b37c4983a52a235_cgraph.md5 b/class_tgstation_1_1_server_1_1_host_1_1_core_1_1_application_a2c2b516c6696a0390b37c4983a52a235_cgraph.md5 index 9898bc0aad..18da7f778d 100644 --- a/class_tgstation_1_1_server_1_1_host_1_1_core_1_1_application_a2c2b516c6696a0390b37c4983a52a235_cgraph.md5 +++ b/class_tgstation_1_1_server_1_1_host_1_1_core_1_1_application_a2c2b516c6696a0390b37c4983a52a235_cgraph.md5 @@ -1 +1 @@ -e82da0fb88df5f429b2a5f08d4728d00 \ No newline at end of file +238c932174c6c7017f7fca15d0b4b641 \ No newline at end of file diff --git a/class_tgstation_1_1_server_1_1_host_1_1_core_1_1_application_a2c2b516c6696a0390b37c4983a52a235_cgraph.png b/class_tgstation_1_1_server_1_1_host_1_1_core_1_1_application_a2c2b516c6696a0390b37c4983a52a235_cgraph.png index f908c35bb866499ab4006e838c741a2672213930..d50578d9b0cd5cc33452eff5135ca5765f02a280 100644 GIT binary patch literal 4991 zcmZ`-XH=70vqowTML0kJL5j2^66pjYEp(77gkFVEgdjx}=>$PKg7hLyDIo{~(nOkq z5PFwh6qKrTX~MVheE0skcd}OUuB>28Y%`vM3=h2 zvjl_~d}EgsT)_{yotC-^(fP$Eucc2mPzv=xs;xO)%6ZVgIQbsU@I}w6Ka?wG8N^ny|5K`g{ z=hjwA@Q*YJi6AL4c7d9@j1EE?D$Y8gkR#_`>okz_>*qYao3$&fefq*I#*9xn$^KYr zEwNa>zKMx~_X=HxHo3t0UuD03%zpKd?(gq^iD5z$?`|zBlPZ2f*)|2997IC}QEm|Q zm)1oOSy@>$xpDIElV8trR1>0fwY6hQb?BTrqM=7eM;EJlf&lFVJf1;`u}BYca$^u@ z+Z7hmYalWwTib9DfA$qUY>HBT$rLFBjZQpeB^Bei;$>T?p&X@_eBIdET7ER1cXf4@ z(A~YWyK7N*pFQPvTzq_ac{xW=wX$j{Me@l~g3bET?sphd%n6V%kOJS`Z}(hX*;rWW8ynM7Q!lqn=5vpcbO(BMRqQVj81(e? zxD!ioxCYE*RSdK6jUlpT4a9vuPP*CajFdu&_miWdqC`bS5#B2aINWNMl)Hp(Wb z-rc>zrBN*`HI<1!iC`eF)=X&VzRycRc-A-S0azKwRLF?W!-Bc zEHWWGTf5NUo&;;e$4et6Yinzp({&YAjj3(6mQ8-pBax3EbRSxnn;-m~*DEwoIN1YM)>>tD^`OS<>loWDLZCon;IM0-j2`C zZZ0kk&Cbd>&w}025;6=ZN+QK81Afi=NU-*Gc6LroJnl{wfWzSnDBM*}JDOCYb~u9ZGI;wCmv2m(+S*<+~Bj-Jg)M3;7il&j{$uKPHyZ+uSU#zpz9|LW`McX}N+Lq~(xJcw1dnwF7cEK0a<(tSc!d zwzIvBD=9%%larHw`TF&BSs67oH8C-O;3>(;uZoJ~{q`V7BHwQc3sW%6f!=9sY-DC+T>Acy{Uyo7I-5oOOTOL@5L>$svq+$!C%^=K#dvUje+x!JwcQ5GP7zm6BD1ocMc8? z(K3erZfR}J#cY=mA>YmlbFxfII6QnTlxAULV`F6%XJvVEbhJ6!Sck{sK{7!sT@i>1 z4PIgfYj^jb3kwUWsj1%waxF|vHzupqT||*eN*_K((Bexk43y|)4p~T5xlN1pojXHA zLzO|lkMr`lG-W`7>)hwhrrleb{D0}f1o`+13knL#$~G!m&gdbeFeWPW?0etcAG3|z ziCsNCg5u%~ubdnmkB^W0(?#tE-;t1z{BQKb!^1ReaB3Wt=#%AGt$Q9+Wm5NN4v*I> zqExsOHK_mk>(VL+gq@uoIQ{hLQ(|JG!TPtQrHRQ&aC~bkE121L`*ThHJ157>X*NpA z%5kw51|HmJ>MvD&`1nyrN2gdP3-oTTeBceUv<|yOjaSLZj5ku&*4O=amNd6685tOS zK#TSD_If{j=(90#$IR?6m++@g>7!Ig6=mh*Vxpt<5#a!VZ5q7Dn#yn*R-#K7qaM~g z?*8_j<9%10RnM<$@us1P1LYfh{yR;DJ3k4g2~Jfn zU;pjfw|o2h4D|HnR*n2E6*DuI@GQ4`7nT7kCM%1oN;g|tkw597xA&vXDZYI+;>!`@ z!Dpun3$`RAPY@5jsi>(H;E|+?RaI5PBO{;_0A?kor$1gD=F^Yqa|GUbAoNU3k4_Ig z^YtUSBK5ELc6C8$X-(dgvv}2(I?(9^4llrOC^9onG{a!9K_h517JKP9 z4~G*#{G&>z0@`Q(!fbfyJsT`9M=)@52CocWBUPN5nrc4X(bA_s;TM6!)i^bHpQof4 zIXLizD>eA;7QKFbGqVsRm-l-*S8ey7^cEBsN4QbO{OM2ea!n0aSA0qe^jH=&X@WHh z#R#yMfx+I^w$-uvbA^qtSskcaP?fK7I7O35P&Q8v$lj&P`1}BbfJ_DP9p?cje}dH0 z69#Ywha2Tqap5MfYe=*-1686%J3L%9p0<*_=Ste}0^FXDK1!B@Hjtba5(?V7v!eqFg_2QF zaLNa;=rEp~$Hv83)!nzcfB!x>a8FNBQ~j=EsZR&{UplCim6iFZ`RO2NF$6E~tMc;8 zI-+p6eDGPo5yhJrpE5o`Co4{@$Ng*uR%D5iNY(794gk30Okr0!qXTbrv3}-lpP9Le znURu|#CrStpM3c6;o#t4X=y1Wgh=Af#>2ZJEP(m$V=#=A;Q{iT&Rqn*qu#zgMdrA; zI3^~h+br5#bQm``Lj!|WgC{E7l2TGJ@*yczAI(93Ok&`nTo}0j=W;9MQao1``2|JP z*VkLtd)(Eqij=(WWO@6Eh3!L6PejnEbnd|S-6KH$TyjD}@jc50wV`b*>+261EcJ`( z>P##w{HNaDb8~Y8q|9Yi#!_2h-x*(1cVV0|kF*mjWZKXP1hg><1pu5fT#4gZT1G~L z^ANX0Pha0nQPISnWyE-xbjenQuLo1_di<0 zR|Fj{Mudij9vnQvBDa84$b0iLk9IlMRhwNf=%n`ivrR4~DG&Z}!`+WhBO@b0zXFSl zgkX+esy`bPCTa`l930%*{@R(Rt*7@aCnqO9{&HH+Y;&ND2D9K(H*pxOtnkA4U@+}* zI5*Q)s>h>8{R0E;uC5FaQozoQK0mD+y!bL}z$V?MYvqDY>swk{nwxXlY&G20M)VW{ ze;+?IH=o*D9ogL2c#1M<;}ZN+TGKsP5()!D!=$7npbP+f0z{>VC%wL+jQDEc7xi@7 zI3zcBzS6$)^!M+vu`xMWSr-?V!M$e+XOHKb13`ML9s8yleS|YX>e=Y&cfR$pc_Xeu zp($Vwx@W82w+_9bf=@REd3bn4MCcUhD8sAnjqrDD&b;@1aQE(A0|Nsa8_q-xZ*Ol> zW1v2Wv8{HMk7eh2GsN493wc2?sz2i(d+_Pnm=UQN393EA6n67w((~tDUS9p#h>_`O z(0P_yMRx`x4&z;yt_w5_puerI={|T+{cG!uk9YR#qNnHOs$g|f(}@q^RK?mE^z`(^ z-+qG5kYJ7LS)Q7h2&uT_^K))k&v$ioPa-;pRbyZs zw~r4GxgcTz{s5e2W@jteh!Zrau*iCkMdIUerMBJMu6};UKqgF2O#xtU8zv#X!agWa zs(;9#NGE@;quc+w#`T$yl^@r&{3SrP9r(1Zt*zj*qllhmH5C=0jK7Y-(4p9AW1>(l z5FgOf#8}(q?i7n!Ok1S`1YMGM@>+Hd9~--~w>Lal9VaKd@xCdIrrJy_<2?}0XJ}^uKT8Fl^%NBB z^0?5V4MeLfF1;K`Kel7_0pvR5|F#qkhKAgzKz`PN*F@0J817!vq!dDbe|&7LWi954 z*a3!(9=!v?>EoH|0C{t9&DH7~9@J-`q-Zs6Ew_?U8>R2+5{;MeM8WNsJ*0(%gsxtu zNE=V4y2W!%D{`TD^z-N_X*^y0d`anzBJDfsbnU&`3L*(!>*F~#!L!v5zTcwPJSLkT zBJCYT3H^vjo8pLnptm=L2Q=H<-2CFjEa)gOP$~X+990@wN{rXwy&)uY`SNAph(o>K zfE0yuU;Ge0IXS6tc4%*Enm$~YGWXv1Wxl9lD&x!6))0sy9SzMxQpL#jRg2owf&F!v z^TV&Lb4|Ge89cv@D_!zORs-bE!K?uCMMFaaeAZ{@q3BR<0|WxDsEE9YD&#ZFDIJJt zEEWr32MAqIdiM#>`37?23~TNvt4U(_L5w5Cdc{M|00B%1P|EPoGbO2%+YZ%}CDdWr z-Wb)rMIc3s^>T|wR&4^d$)xepnN?xk0{fcq7)e^!S1@0-%1J4Bpja1CDIQNt9qIkB zSd=9`O(=Y(g|o-5L6gId-LS-XMh{UVr`e%|>yDU+N7wLk0?{QSBLmNBa_C6~!y`6z z`NU}>FSFGgg<4u%#BxPuWm(+4dkqfc;tfH;^--K55IR!EacaizSb(amz~i-ab^CjI zNMa&~lKD`&IK3*fGG9|F2ZDI_T@Xyr+0mEN(+W=6NQHn2|Bh$`kLM<(4@&SyarRWs zE4zG@jsiyA%iJ*RwMCs>;b-#w0Tx9vY)a=>NePL(Atx{$fs~pKr4A121L9Ox{+N)E z0HzJ!L2pqYtPc95=F_vX*4Ng^DJWh>y%f+Y$u^kJ>mx@0*S(9Yo>jvz>#Pa8@V}dH zQiM|R$vhv)Ti)ELt@OG~n^BVBX>?tgmNjdK+)cO97yy()>}? zaEG(Id)nwFRvj~?<>X^>H)9tb1OG(QZlZXdfA=z%7ajhbop4X*@5iz^uL5Q@Aio!T zoY_X_=Ys+8m(g2UThG+C}kP+lvr>$!^-y-HpLuTEBCIaydFW z?tUMjn2rQ;0~{$Z@L(FzeVf^S_T57q4kyb|0mi@*=J+Dm;#SJ>(uFrr<;+5sE1R3k z+m8Xtrx;l&`YLx_&olRIS;TK%LJM%-n9D>v2b~FCfT5qb%P#DKIL(rQq#DQrzi zNmpN=4lMC@ArlmuKcJN+G+XOtfkcLchm#ri=z{w2zBM~O4u`=oUq8j!{&m4g85uz6 zccaCag{`S6UxxZWH_19ZJ$3BMgtAqubC1GR{@4aF0R(Gv6Mc?C%J!L)vY$2 z28th0VBP)McOHvM9vp()++RS`03KOfTm)#EX7obsB?5t{G=-U%d=3i>i#qOk@s*e< zreo0qWC@4|OG`@$35hpff>zpS<>Z>;*rdAua?|Dq+)jNWBS$%|I(7gSuK@~Vk3sk-lmWbS#PRL99ZaEDx#Jrz;j%qj5jZ zA0)QhAi%vnR1K64qQdV1F!}$zkV)KGh9)#TK|UP-QydtWjNCec>jO{V2VK`lLuJ6- z5J-lT2#fvJmz(I*FB=;ho13p)1kZ9s9jMWEt+pcV d-2k02n-lj@Dwin8gBvv>O;uf$awXf){{ULyn{@yH literal 8107 zcmZXZ1yqz#*Y8JKKsrS_r4bM$L{hqiksL~-J0zq8q)|j#1j!)>Y3US)P`XpPy9T%i z-*3J5uJ0b!dYA>Q!+Flx`@ette)IB4K0KfjU< zzG0cGJePyq+Yl(Z} zzP2mjsfe_!Bp6cG`YNqpNXw;{C6*f*36&onjghZ3DC+%0m z#lUiUxm0~x_o{9>KEAx6S?Xds-M7DPHT~mQLfYSZ_vE!OAdtZ8WGY{qi;lUiEc#3#{c9m=-E;-(C+$-Pmtw z1!}R1h@7tUrFD>uBZ392XB*yF_ooY-aI~yRfBW-E8P@%Or^QF(fB1>iqTVS8FRH2S-6cf#RuSRjwx{ z)+rzMo1%@4jo-gDDWa^*%oa8`eNVO~q>W^n8OqVV;$LM9?kf@#6El8L{jOh`zGku1nMm~Tawl4{mBd9%DT^CKf8gPNMUuCA_&j%;BUD-gjTaXZq@t#a8F zH8eC-Rii^f(8Jr$QHKNj-}Ot4_D@b;@l#3A@o{nC`3vA$c-Y$7%E-#nK6tRTwG|u^ zl9rY>qSsF7fArCS&E*! z1nW{HydPN2&&ess;O@Ckf?aH5GR%ncF)8UecMvTXL^n4l2Pd@c;NW0zP=Y0XfG-^#9!AB+Uhj9)=V})Y7B>2xe*W}{ zjFNI}bo7^HMNSS2YPia7%yGIlDEl=OI=;{zzJJgjPI!3k$CmH}W*90X?Jpp#7XQnf zgq)mQUO~au<8JPqgkHP7%9uV#^y~U9sHB7h91bsEij2fXZFNMD4h;=$Zf+_~l6bhg z*Vfm=OTwg%_Vz$@w8SEjNJ&Y_ecYIsm~j^`ubSN4+{x=2`-w+(quf{_3kwT~w^u%X z=;9t5!y6m6TeXFS5#37*GovFTX5BAdyfEU`XG_T9;7sV5o}PXu!`NA^?8X@JB$cgT z+dMTjRXJC?DqnFlUKFNwm`$KHfslb=T14>%CAPxm`PdPC44!_g{TW}sGEhVbzd86D z9E?`w;51WDPDIpV`3NyDlRpIFg8mT|zJ!Tlt~OSPw23KCsEo_a_0@|aJ)V~p7+i?T zN|r8BQc}_e`cRo55u*lg7e`0l$m}Oip7=dAEvo)&LF4@I*xMI|M@Qk3@)cnnckEIr zJ&2&KP!Te5y=a-NTKDyj2??Ypn(JZT^7B{sm%3R=lmY^p<(OKhZ0q@yDyEpIvkV*@ z9IC6U#}P?MV@us}*y%PPi5lJ=GLBwqw$^0{b$*D@ zu^TOZ+34c|W->K3ee>r0U>P~$dE71DqZvFJDqWcp%|8E$ZgxW@n{cPbZfk$WGy07{mE!nZ$p8ktRAyR(~PWfrVPyh+JF)p?Uhxt6-`-o1Ny zvMozwIoIS5emMr;4#va-@;e-B-S+OTH^IAbLi+mJT71#BA4wd1bzJ}c{1A#q$w~gP z*Cjdde>N<0XX3;;^%1@Hu9~orRKPU~R46d-^EkShCjTFUFC*QA^0H=G&|#3W=IhQ% zo14FfgxizHySvTJd=y@`@F#wbm|%P8;cnHEsI0u+@L^wB6TizSg>Mt%xO&|3?RR?>bTf5{2m?;HNao6tjyVTG{w^YecCv>`>1y+2j>PiyP_ zqQbJuAjrJ#5LQ$CyWITz{I6eORI}v}=G3yXvb40RcoXwM7Ik^~5Tnxgo(lv?L{3pb z0Z@MfwghIJ#kJR`2S`USVJrcj@8v8bEk1+~V>CE87}RfzdzjIB$Rxgjo*wE!mD)PQ zx;tqdDm2qnk3%zbvBtP zRJ8-m;Mg-z+GNa-->oIDt}OMlhHPA)i1}6TQAcBc=N=ghlKH-FZ ztgruEUr%vc1VT{$Xks&Ryw7j;u#=8A=lrgY*E?v;Ao#>M#W2y0HSgHYz6+lSO^(7s z8+f73#?~h$9CKUS#l^P2t}heVp728XV~|$+-rRO}T}5S_0s4a-@4~VR!m)j?E;^l> zCG9X+Gtil7G4x=nk5U@4vS_2ecvJbGX`)bX=z|$nR`UA$PmFj$;;zStpOOy(T$u0;DwQ<^Y+BHf^{;f&o;$@^BY~iJ+-MjJ zrd~imfVYJM3%uAp`LToB^KQ4lr<4a0bW<}K+p8iYeK9eeI(Bw=v}KFyJ%uF>U-*O0 z_q(G)g8kOqW9B$f=c}0u>ReRX2H3X~jmS7G(RLCQ>ZRC*UHu@7vnj!xuMT+=fkl=b6_>?B)@pLF54halfyY zsvV|8AN2QUh-ajy*VosB4Dwp+xZ94c*G?262a*XyTU=C>#iJBL`lr2peZqb)57!No zs*M3ze{xe(FH_UgJ4I%uHnbb9fdcAIHE5QPUMeVbjTGstsi`IL7=BJki3pt;92}Gl zC%79b!Y*5&QL zG5(NDUL!;7g{J0jP+@>L5K5uqTVb%7QsWqlO~|uyo5ZTYv3v+OANJ-uFrU=?U z7U$wpaS)y^)X4{Y4qg`wdPyJmjp2u=;S$4|lbxB%?V7oMAT2yRY98r)jYorE^*eLT zb5ljA#;)(y1G7K8_;`4>kJg8-uCBUcY1=ifb^~t2z4zw95t#aJSE(fNE-cUapU$*} z;sK7qwpWpae^k9i$i&Z|d75)yk+B_@dIBQA)!T6NM*( zf9(_>@b@%8AgOEHdVz9GE}ouUvHR#4(%RbE>gl2`s52LF@gKFdwNKnu3aU#A3wwHd z{m%9l!1fVg2nbL!Op}%^FD`=j49;|Qc5Wfy=H}Kt&A|zVzI-YFZvf0C!v&0?qpj_^ zGabS39?JdpbjE8xwS(|NR7}hq+lY5;X6Ck4xW2v~D>Y7g>M!OQscgtfBaINJuJeRX*h1)qbhUL{Fay4!QaIxNMj&r~p;; z)#hIqcyhs7gE39tp6C8UPi%q*ElAJ}hRQIWOi~AQO-@bWg~~LjYiPtJCX$D=5=C|X z@Z4@gCc=r!sse7Vfqa2+b$M!6AEWrr35%43L|IvRa&q$G@{)#z2Iz={MHl|o@L;Zr z*Y+eU8SdrfC5ZZ*)S3r}2S`3IFRx6g01$Pq2Y-7Es$N{032%_*R3vv4p|+g@E?3f= z%4=qT%6NDjkCqtr@MXQTG$C6>YoW@iq22tI$aT6qM-Q5#{GpJLkloqFsKlG|fq>Zx zt0*Y<%D#7+h*KBtx!+qOBO`r%$`jW!P~yd$=UadbK6vneiHT{C=8f{m`>|iWy(pkL zL>T+0r=|#V%wv~QgQ^Vx+BP;e0N=PN?E2H6oE6B($h;OZ0C8qvu>`uUo9okNyA2j@ zZhIio-~_zy9#`wWPQ48PR}T+06%}!xqqPW9cF^v!sinTVd5_sCJiOZgp<*^U!wdqV zFDECrcD+6f2*&@QhaIGZ*a^Bqki%4U480WHqmvK;`AjJ`@A;OX(NR4F)Nj7!4$Aru z1OhAmV_gbiXU|Vgrbk7=5?B&A*#j;2@88F5-U7bLMFTm^``cyAIsh#z zLn=qqj*MHNy3jnfZ#iV4@)ddCUN=iyWK|z44d{k9?LDPygHt_9vTtj2yvG`+1;2kRV@@x(t(D#myk4I7R zeY_!=ejj4Bak)f);|)*_w0l9%zP`91&n_+H)fxKmDdGVH)DKvr%TicOjGCh2`^H8| zf#t~N<^a&>J3BkWVDnA>m*tsDY!BHVcM$w_J=Zt&CnqM}?-$>l&&(i;kTa}t1iE5W zVW|X;}lT7*Z|T3}NqXhozLQZJkk+NqnYFum5C&l>xCTQolDayb3y; zSFiFu6~_70bSw~KKTPNW4hbn06%`4|NAT`q-nro*v(L}Zk)L_+aB!XxVaWdh`Kq9x zfQ5yXmzQ^PbfkGUhY9%|>w>lkI2AY>#27E@-Pg|#S0nNr=bAD=ncEsifJ8h7T8;jh z3s@Krflu(=TEo-R)6Gp#@Ac2hcOl5({7D`c?+#*HIt|$%mfwA8Nf3rXb1!Xn#DLoLcpst7dW#h zlxxj$#|CnTpPzZU?eb*GX=-YUlE>g(5@?!3PD8yS7V zy_fRY**WC`YfI08-L@O z7FI`=nz6PvH0gK{!RDlj=s1Bv)E9csTwL}m zZHC@!*jrm$^YQVa9-D)`RrxmnPJr6AZwn@EIA2LyUtb6RWZ;Wr`*+Iwk9e|nQZ9bl zC=ZBQ{rc3<&;YOqtO-Djovpe+BO~M)W!Nmjq}G{{lT*2npPzpZ6zlQ6po#C+WhRXV zyh+#?7({LpHk|q-zyM2B#zd3W&=>$Jf{>BTV;z>%p?{*bP7OQ;pi~|gM>cE;!2UWP z(vT{2Ah_MnKt?4R(buVOwSC`R51(k#5U*2*8&0+WkOJXI?MnvJ%^F&FYCNXc$Bte& zTxA{zLdRxeX101Xp{O)4KKYA-6aII9oRnP*3dJAEG}6+_RUD?Ee$qXizD1Ju*kN$& z^a|1fTn1hi7K{L#AdkP)F6-0*S0kRwzqm1^Uklw2x9DE3xW(7amgS0a3xaO-(*gDe z63~Mo4MNXdUR712j#WGSGS@ob()pcjeQo7<20X^5Dxn6nqP($7XZpa`9l6%&!bRtQ zd$#iOV*imlF)r)z@kjc%1;r))lQT0>4DSI`w!L$K#xL)`jjDEZv=a>=_odAv_& z^nW@LK)!nIVPi-4%+A`bs1^Y%BEhqI^hQ=z+{M>WkFDN&-^4nf%t?NF>cE>DRBLXE z8-f=c@hdCVnQwg7cjArch^C`f^ctMRsODTW*PIcU%CWJDFciPbRN* zRI_)noFpF+;lykDw3~<&VnK(Y$8QdsGR&2!Gx{?;ynj3~L0E=Y*{^u`WR_!d(kmuiua+5)PB`JP#z*5>+9{w*EVUxTLYAEsxh=OdSY zowK-bM5ZPuOS~BjV^F1`p?;HTE0z7#q2U`9QzCqw74GToj@mT65%bzfS7b5grwTYn zngTq+K2?)4{%>ngU+;6c(#OimYHVx_>Imv=DXzV(t&*%^;Tw6fn`=~6)8m7QP zKT%;>($Oh05f?0kOr9KfxUJ3o!krSJe3Ho`on-1S7{{1yuvGZ-wQB(;reNf3oR{v~yF9Lm3h;M@kqX&s({v7?0 zo)p6(9jq@3?=ZvbmBR#y_x@ryA)VjbtBbthVJnaj`D$q}TX<&kA+UP0G@&LYCI$uu zy1MU@0BpO0L)+fo9xF4wSxz#IpF3?h`%WMozqhwHJ`Vk$sB3DvySP|kGsIRk;+*M! z0&ukRpT*o{tfyCP(M^ET1#|~WG4RG0JEJVj&2_uiS63-%X|I;zq<{sRs{_u32Y6Pz zyrm^2i9MF!Z_f(t#!6>uUC`zK0D~IX%S+3^YgsuwS7mc+sm2XO%TgV}DCA}XJEUc4 znWLAac^}XukN!v47 zLc%!UKyTGHG)xZ-k(T`)AAe5^zz!S)d^-Tt(C@GMKqS-A(E;nHTclhkU%jNXv@13( zHPu^!Zq~urnAX$i#f#2mSF1P^@`5I5ekD05jgXb4Qhp6pzV|b%+QEi5qcSmY*kR|aNySloz(Q(c=9~~V@yhYW@ zF|F_JCMmLb8>*eMDtQ{RFzXhTl=O{`Qiz`%9Qc4XDo_wupy?t`bdlL^tNlDYJR@+P zH|K$*9r1i@#F+zg2#fGZ9kMatbr$H{D|U_}hrmHu0d&oh?*Gexn z0?M7xLnrmN!DUH70&g-^*iNs^#Q4RFk+Cs-6O&kWJiPx=*#JoVVn#lwrD7JOw? z>n!b1!x8z)$_jY%WE2#OtYlO0e);W}|6V~kLtjr13mZF*Mx-BHaEy(KNJ*I%2@ebL zsp)8nKg^}we2+?|gM?Ir3lj`!cMTe-o*o4m+0oh`W`BI#XnUdiJbZkFmqTqWz!|PA zcbuuu%VW#ZOa_EbMrOIS5NL+$iV69ODIy{Qoa?5hrd*Yzyqp{W-oSMR{w4(mAY=x9 za4YDHT?5eFC-0JQJKFu$U4AxjYd(vdsh52FW&s=tojg@UMFr?wad2^&Rw(!b|NqOr z*5hy8jqKZA_K$B3g|~b2L1tJMpSA_T#o`ek6O#7$lNb3ECi<1%PQMMM7ZFm0zVXapq6YAAI@vM02yi#m$~^z>xHkF zb?=cWQ!=f`Sh{GEZEtOL40gXx{qt2&fb-rUKZ4-amWDyP)TTvhRej(SIDaUc6>@zT zKh7#vR#yX-?oSp7u@uJ2h|sc$x6Oy)$WnIRp$fzQZXxWr*wkt@sFM~$q;+1;xi3TZ zJ$*h@6c8i#ZbTmSdUC`EQAKpQCm0omK?42!LFCq-I{ZVBIr*MU@Z}CXt*sbnfhGst zQ$JDAAUQRihgm9!y)tJZAOW|jvi&VV>(XyU|#9RY{tJ%#aFr>*-kyR`W4J ztLPzqVpi-j1fMHx0&!o&1sKh2w?=bu*s$-=aCLe4@9ysBL(%R%9R==;BcDQi!kjD_^)%w37015m8Y|Yz?=(z?oi+<3HrDkS2feYq8;M$nV z(8$;r+(k!ZE@M4=e!H#x{T(D{E8wzvje6SRf8SsK=c0My20hf-SkN}b3S7rSAWHJ; Ka-}k`;Qs?ZdEeRq diff --git a/class_tgstation_1_1_server_1_1_host_1_1_database_1_1_database_context-members.html b/class_tgstation_1_1_server_1_1_host_1_1_database_1_1_database_context-members.html index 062d44de77..df73d31f92 100644 --- a/class_tgstation_1_1_server_1_1_host_1_1_database_1_1_database_context-members.html +++ b/class_tgstation_1_1_server_1_1_host_1_1_database_1_1_database_context-members.html @@ -137,20 +137,21 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search'); RepositorySettingsTgstation.Server.Host.Database.DatabaseContext RepositorySettingsTgstation.Server.Host.Database.DatabaseContextprivate repositorySettingsCollectionTgstation.Server.Host.Database.DatabaseContextprivate - RevInfoTestMergesTgstation.Server.Host.Database.DatabaseContext - RevisionInformationsTgstation.Server.Host.Database.DatabaseContext - RevisionInformationsTgstation.Server.Host.Database.DatabaseContextprivate - revisionInformationsCollectionTgstation.Server.Host.Database.DatabaseContextprivate - Save(CancellationToken cancellationToken)Tgstation.Server.Host.Database.DatabaseContext - SchemaDowngradeForServerVersion(Version version, CancellationToken cancellationToken)Tgstation.Server.Host.Database.DatabaseContext - TestMergesTgstation.Server.Host.Database.DatabaseContext - UsersTgstation.Server.Host.Database.DatabaseContext - UsersTgstation.Server.Host.Database.DatabaseContextprivate - usersCollectionTgstation.Server.Host.Database.DatabaseContextprivate - ValidateDatabaseType()Tgstation.Server.Host.Database.DatabaseContextprotectedpure virtual - WatchdogReattachInformationsTgstation.Server.Host.Database.DatabaseContext - WatchdogReattachInformationsTgstation.Server.Host.Database.DatabaseContextprivate - watchdogReattachInformationsCollectionTgstation.Server.Host.Database.DatabaseContextprivate + RevInfoCompileJobDeleteBehaviorTgstation.Server.Host.Database.DatabaseContextprotected + RevInfoTestMergesTgstation.Server.Host.Database.DatabaseContext + RevisionInformationsTgstation.Server.Host.Database.DatabaseContext + RevisionInformationsTgstation.Server.Host.Database.DatabaseContextprivate + revisionInformationsCollectionTgstation.Server.Host.Database.DatabaseContextprivate + Save(CancellationToken cancellationToken)Tgstation.Server.Host.Database.DatabaseContext + SchemaDowngradeForServerVersion(Version version, CancellationToken cancellationToken)Tgstation.Server.Host.Database.DatabaseContext + TestMergesTgstation.Server.Host.Database.DatabaseContext + UsersTgstation.Server.Host.Database.DatabaseContext + UsersTgstation.Server.Host.Database.DatabaseContextprivate + usersCollectionTgstation.Server.Host.Database.DatabaseContextprivate + ValidateDatabaseType()Tgstation.Server.Host.Database.DatabaseContextprotectedpure virtual + WatchdogReattachInformationsTgstation.Server.Host.Database.DatabaseContext + WatchdogReattachInformationsTgstation.Server.Host.Database.DatabaseContextprivate + watchdogReattachInformationsCollectionTgstation.Server.Host.Database.DatabaseContextprivate