diff --git a/20180906135553___m_s_initial_create_8_designer_8cs.html b/20180906135553___m_s_initial_create_8_designer_8cs.html new file mode 100644 index 0000000000..ac0e2c71b9 --- /dev/null +++ b/20180906135553___m_s_initial_create_8_designer_8cs.html @@ -0,0 +1,119 @@ + + + + + + +tgstation-server: src/Tgstation.Server.Host/Models/Migrations/20180906135553_MSInitialCreate.Designer.cs File Reference + + + + + + + + + + +
+
+ + + + + + + +
+
tgstation-server +
+
The /tg/station 13 server suite
+
+
+ + + + + + +
+
+ + +
+ +
+ + +
+
+
+Classes | +Namespaces
+
+
20180906135553_MSInitialCreate.Designer.cs File Reference
+
+
+ +

Go to the source code of this file.

+ + + + + +

+Classes

class  Tgstation.Server.Host.Models.Migrations.MSInitialCreate
 The initial database migration for MSSQL More...
 
+ + + +

+Namespaces

namespace  Tgstation.Server.Host.Models.Migrations
 
+
+ + + + diff --git a/20180906135553___m_s_initial_create_8_designer_8cs_source.html b/20180906135553___m_s_initial_create_8_designer_8cs_source.html new file mode 100644 index 0000000000..7aaea59edd --- /dev/null +++ b/20180906135553___m_s_initial_create_8_designer_8cs_source.html @@ -0,0 +1,108 @@ + + + + + + +tgstation-server: src/Tgstation.Server.Host/Models/Migrations/20180906135553_MSInitialCreate.Designer.cs Source File + + + + + + + + + + +
+
+ + + + + + + +
+
tgstation-server +
+
The /tg/station 13 server suite
+
+
+ + + + + + +
+
+ + +
+ +
+ + +
+
+
+
20180906135553_MSInitialCreate.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.Metadata;
6 using Microsoft.EntityFrameworkCore.Migrations;
7 using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
8 
9 namespace Tgstation.Server.Host.Models.Migrations
10 {
11  [DbContext(typeof(SqlServerDatabaseContext))]
12  [Migration("20180906135553_MSInitialCreate")]
13  partial class MSInitialCreate
14  {
19  protected override void BuildTargetModel(ModelBuilder modelBuilder)
20  {
21 #pragma warning disable 612, 618
22  modelBuilder
23  .HasAnnotation("ProductVersion", "2.1.2-rtm-30932")
24  .HasAnnotation("Relational:MaxIdentifierLength", 128)
25  .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
26 
27  modelBuilder.Entity("Tgstation.Server.Host.Models.ChatBot", b =>
28  {
29  b.Property<long>("Id")
30  .ValueGeneratedOnAdd()
31  .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
32 
33  b.Property<string>("ConnectionString")
34  .IsRequired();
35 
36  b.Property<bool?>("Enabled");
37 
38  b.Property<long>("InstanceId");
39 
40  b.Property<string>("Name")
41  .IsRequired();
42 
43  b.Property<int?>("Provider");
44 
45  b.HasKey("Id");
46 
47  b.HasIndex("InstanceId");
48 
49  b.HasIndex("Name")
50  .IsUnique();
51 
52  b.ToTable("ChatBots");
53  });
54 
55  modelBuilder.Entity("Tgstation.Server.Host.Models.ChatChannel", b =>
56  {
57  b.Property<long>("Id")
58  .ValueGeneratedOnAdd()
59  .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
60 
61  b.Property<long>("ChatSettingsId");
62 
63  b.Property<decimal?>("DiscordChannelId")
64  .HasConversion(new ValueConverter<decimal, decimal>(v => default(decimal), v => default(decimal), new ConverterMappingHints(precision: 20, scale: 0)));
65 
66  b.Property<string>("IrcChannel");
67 
68  b.Property<bool?>("IsAdminChannel")
69  .IsRequired();
70 
71  b.Property<bool?>("IsUpdatesChannel")
72  .IsRequired();
73 
74  b.Property<bool?>("IsWatchdogChannel")
75  .IsRequired();
76 
77  b.Property<string>("Tag");
78 
79  b.HasKey("Id");
80 
81  b.HasIndex("ChatSettingsId", "DiscordChannelId")
82  .IsUnique()
83  .HasFilter("[DiscordChannelId] IS NOT NULL");
84 
85  b.HasIndex("ChatSettingsId", "IrcChannel")
86  .IsUnique()
87  .HasFilter("[IrcChannel] IS NOT NULL");
88 
89  b.ToTable("ChatChannels");
90  });
91 
92  modelBuilder.Entity("Tgstation.Server.Host.Models.CompileJob", b =>
93  {
94  b.Property<long>("Id")
95  .ValueGeneratedOnAdd()
96  .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
97 
98  b.Property<string>("ByondVersion")
99  .IsRequired();
100 
101  b.Property<Guid?>("DirectoryName");
102 
103  b.Property<string>("DmeName");
104 
105  b.Property<long?>("JobId");
106 
107  b.Property<string>("Output");
108 
109  b.Property<long>("RevisionInformationId");
110 
111  b.HasKey("Id");
112 
113  b.HasIndex("DirectoryName");
114 
115  b.HasIndex("JobId");
116 
117  b.HasIndex("RevisionInformationId");
118 
119  b.ToTable("CompileJobs");
120  });
121 
122  modelBuilder.Entity("Tgstation.Server.Host.Models.DreamDaemonSettings", b =>
123  {
124  b.Property<long>("Id")
125  .ValueGeneratedOnAdd()
126  .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
127 
128  b.Property<string>("AccessToken");
129 
130  b.Property<bool?>("AllowWebClient")
131  .IsRequired();
132 
133  b.Property<bool?>("AutoStart")
134  .IsRequired();
135 
136  b.Property<long>("InstanceId");
137 
138  b.Property<int>("PrimaryPort");
139 
140  b.Property<int?>("ProcessId");
141 
142  b.Property<int>("SecondaryPort");
143 
144  b.Property<int>("SecurityLevel");
145 
146  b.Property<bool?>("SoftRestart")
147  .IsRequired();
148 
149  b.Property<bool?>("SoftShutdown")
150  .IsRequired();
151 
152  b.Property<long>("StartupTimeout");
153 
154  b.HasKey("Id");
155 
156  b.HasIndex("InstanceId")
157  .IsUnique();
158 
159  b.ToTable("DreamDaemonSettings");
160  });
161 
162  modelBuilder.Entity("Tgstation.Server.Host.Models.DreamMakerSettings", b =>
163  {
164  b.Property<long>("Id")
165  .ValueGeneratedOnAdd()
166  .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
167 
168  b.Property<int>("ApiValidationPort");
169 
170  b.Property<long>("InstanceId");
171 
172  b.Property<string>("ProjectName");
173 
174  b.HasKey("Id");
175 
176  b.HasIndex("InstanceId")
177  .IsUnique();
178 
179  b.ToTable("DreamMakerSettings");
180  });
181 
182  modelBuilder.Entity("Tgstation.Server.Host.Models.Instance", b =>
183  {
184  b.Property<long>("Id")
185  .ValueGeneratedOnAdd()
186  .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
187 
188  b.Property<long>("AutoUpdateInterval");
189 
190  b.Property<int>("ConfigurationType");
191 
192  b.Property<string>("Name")
193  .IsRequired();
194 
195  b.Property<bool?>("Online")
196  .IsRequired();
197 
198  b.Property<string>("Path")
199  .IsRequired();
200 
201  b.Property<long?>("WatchdogReattachInformationId");
202 
203  b.HasKey("Id");
204 
205  b.HasIndex("Path")
206  .IsUnique();
207 
208  b.HasIndex("WatchdogReattachInformationId");
209 
210  b.ToTable("Instances");
211  });
212 
213  modelBuilder.Entity("Tgstation.Server.Host.Models.InstanceUser", b =>
214  {
215  b.Property<long>("Id")
216  .ValueGeneratedOnAdd()
217  .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
218 
219  b.Property<decimal>("ByondRights")
220  .HasConversion(new ValueConverter<decimal, decimal>(v => default(decimal), v => default(decimal), new ConverterMappingHints(precision: 20, scale: 0)));
221 
222  b.Property<decimal>("ChatBotRights")
223  .HasConversion(new ValueConverter<decimal, decimal>(v => default(decimal), v => default(decimal), new ConverterMappingHints(precision: 20, scale: 0)));
224 
225  b.Property<decimal>("ConfigurationRights")
226  .HasConversion(new ValueConverter<decimal, decimal>(v => default(decimal), v => default(decimal), new ConverterMappingHints(precision: 20, scale: 0)));
227 
228  b.Property<decimal>("DreamDaemonRights")
229  .HasConversion(new ValueConverter<decimal, decimal>(v => default(decimal), v => default(decimal), new ConverterMappingHints(precision: 20, scale: 0)));
230 
231  b.Property<decimal>("DreamMakerRights")
232  .HasConversion(new ValueConverter<decimal, decimal>(v => default(decimal), v => default(decimal), new ConverterMappingHints(precision: 20, scale: 0)));
233 
234  b.Property<long>("InstanceId");
235 
236  b.Property<decimal>("InstanceUserRights")
237  .HasConversion(new ValueConverter<decimal, decimal>(v => default(decimal), v => default(decimal), new ConverterMappingHints(precision: 20, scale: 0)));
238 
239  b.Property<decimal>("RepositoryRights")
240  .HasConversion(new ValueConverter<decimal, decimal>(v => default(decimal), v => default(decimal), new ConverterMappingHints(precision: 20, scale: 0)));
241 
242  b.Property<long?>("UserId")
243  .IsRequired();
244 
245  b.HasKey("Id");
246 
247  b.HasIndex("InstanceId");
248 
249  b.HasIndex("UserId", "InstanceId")
250  .IsUnique();
251 
252  b.ToTable("InstanceUsers");
253  });
254 
255  modelBuilder.Entity("Tgstation.Server.Host.Models.Job", b =>
256  {
257  b.Property<long>("Id")
258  .ValueGeneratedOnAdd()
259  .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
260 
261  b.Property<decimal?>("CancelRight")
262  .HasConversion(new ValueConverter<decimal, decimal>(v => default(decimal), v => default(decimal), new ConverterMappingHints(precision: 20, scale: 0)));
263 
264  b.Property<decimal?>("CancelRightsType")
265  .HasConversion(new ValueConverter<decimal, decimal>(v => default(decimal), v => default(decimal), new ConverterMappingHints(precision: 20, scale: 0)));
266 
267  b.Property<bool?>("Cancelled")
268  .IsRequired();
269 
270  b.Property<long?>("CancelledById");
271 
272  b.Property<string>("Description")
273  .IsRequired();
274 
275  b.Property<string>("ExceptionDetails");
276 
277  b.Property<long>("InstanceId");
278 
279  b.Property<DateTimeOffset?>("StartedAt")
280  .IsRequired();
281 
282  b.Property<long>("StartedById");
283 
284  b.Property<DateTimeOffset?>("StoppedAt");
285 
286  b.HasKey("Id");
287 
288  b.HasIndex("CancelledById");
289 
290  b.HasIndex("InstanceId");
291 
292  b.HasIndex("StartedById");
293 
294  b.ToTable("Jobs");
295  });
296 
297  modelBuilder.Entity("Tgstation.Server.Host.Models.ReattachInformation", b =>
298  {
299  b.Property<long>("Id")
300  .ValueGeneratedOnAdd()
301  .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
302 
303  b.Property<string>("AccessIdentifier")
304  .IsRequired();
305 
306  b.Property<string>("ChatChannelsJson")
307  .IsRequired();
308 
309  b.Property<string>("ChatCommandsJson")
310  .IsRequired();
311 
312  b.Property<long?>("CompileJobId");
313 
314  b.Property<bool>("IsPrimary");
315 
316  b.Property<int>("Port");
317 
318  b.Property<int>("ProcessId");
319 
320  b.Property<int>("RebootState");
321 
322  b.Property<string>("ServerCommandsJson")
323  .IsRequired();
324 
325  b.HasKey("Id");
326 
327  b.HasIndex("CompileJobId");
328 
329  b.ToTable("ReattachInformations");
330  });
331 
332  modelBuilder.Entity("Tgstation.Server.Host.Models.RepositorySettings", b =>
333  {
334  b.Property<long>("Id")
335  .ValueGeneratedOnAdd()
336  .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
337 
338  b.Property<string>("AccessToken");
339 
340  b.Property<string>("AccessUser");
341 
342  b.Property<bool?>("AutoUpdatesKeepTestMerges")
343  .IsRequired();
344 
345  b.Property<bool?>("AutoUpdatesSynchronize")
346  .IsRequired();
347 
348  b.Property<string>("CommitterEmail")
349  .IsRequired();
350 
351  b.Property<string>("CommitterName")
352  .IsRequired();
353 
354  b.Property<long>("InstanceId");
355 
356  b.Property<bool?>("PushTestMergeCommits")
357  .IsRequired();
358 
359  b.Property<bool?>("ShowTestMergeCommitters")
360  .IsRequired();
361 
362  b.HasKey("Id");
363 
364  b.HasIndex("InstanceId")
365  .IsUnique();
366 
367  b.ToTable("RepositorySettings");
368  });
369 
370  modelBuilder.Entity("Tgstation.Server.Host.Models.RevInfoTestMerge", b =>
371  {
372  b.Property<long>("Id")
373  .ValueGeneratedOnAdd()
374  .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
375 
376  b.Property<long>("RevisionInformationId");
377 
378  b.Property<long>("TestMergeId");
379 
380  b.HasKey("Id");
381 
382  b.HasIndex("RevisionInformationId");
383 
384  b.HasIndex("TestMergeId");
385 
386  b.ToTable("RevInfoTestMerges");
387  });
388 
389  modelBuilder.Entity("Tgstation.Server.Host.Models.RevisionInformation", b =>
390  {
391  b.Property<long>("Id")
392  .ValueGeneratedOnAdd()
393  .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
394 
395  b.Property<string>("CommitSha")
396  .IsRequired()
397  .HasMaxLength(40);
398 
399  b.Property<long>("InstanceId");
400 
401  b.Property<string>("OriginCommitSha")
402  .IsRequired()
403  .HasMaxLength(40);
404 
405  b.HasKey("Id");
406 
407  b.HasIndex("CommitSha")
408  .IsUnique();
409 
410  b.HasIndex("InstanceId");
411 
412  b.ToTable("RevisionInformations");
413  });
414 
415  modelBuilder.Entity("Tgstation.Server.Host.Models.TestMerge", b =>
416  {
417  b.Property<long>("Id")
418  .ValueGeneratedOnAdd()
419  .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
420 
421  b.Property<string>("Author")
422  .IsRequired();
423 
424  b.Property<string>("BodyAtMerge")
425  .IsRequired();
426 
427  b.Property<string>("Comment");
428 
429  b.Property<DateTimeOffset>("MergedAt");
430 
431  b.Property<long>("MergedById");
432 
433  b.Property<int?>("Number")
434  .IsRequired();
435 
436  b.Property<long?>("PrimaryRevisionInformationId");
437 
438  b.Property<string>("PullRequestRevision")
439  .IsRequired();
440 
441  b.Property<string>("TitleAtMerge")
442  .IsRequired();
443 
444  b.Property<string>("Url")
445  .IsRequired();
446 
447  b.HasKey("Id");
448 
449  b.HasIndex("MergedById");
450 
451  b.HasIndex("PrimaryRevisionInformationId")
452  .IsUnique()
453  .HasFilter("[PrimaryRevisionInformationId] IS NOT NULL");
454 
455  b.ToTable("TestMerges");
456  });
457 
458  modelBuilder.Entity("Tgstation.Server.Host.Models.User", b =>
459  {
460  b.Property<long>("Id")
461  .ValueGeneratedOnAdd()
462  .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
463 
464  b.Property<decimal>("AdministrationRights")
465  .HasConversion(new ValueConverter<decimal, decimal>(v => default(decimal), v => default(decimal), new ConverterMappingHints(precision: 20, scale: 0)));
466 
467  b.Property<string>("CanonicalName")
468  .IsRequired();
469 
470  b.Property<DateTimeOffset?>("CreatedAt")
471  .IsRequired();
472 
473  b.Property<long?>("CreatedById");
474 
475  b.Property<bool?>("Enabled")
476  .IsRequired();
477 
478  b.Property<decimal>("InstanceManagerRights")
479  .HasConversion(new ValueConverter<decimal, decimal>(v => default(decimal), v => default(decimal), new ConverterMappingHints(precision: 20, scale: 0)));
480 
481  b.Property<DateTimeOffset?>("LastPasswordUpdate");
482 
483  b.Property<string>("Name")
484  .IsRequired();
485 
486  b.Property<string>("PasswordHash");
487 
488  b.Property<string>("SystemIdentifier");
489 
490  b.HasKey("Id");
491 
492  b.HasIndex("CanonicalName")
493  .IsUnique();
494 
495  b.HasIndex("CreatedById");
496 
497  b.ToTable("Users");
498  });
499 
500  modelBuilder.Entity("Tgstation.Server.Host.Models.WatchdogReattachInformation", b =>
501  {
502  b.Property<long>("Id")
503  .ValueGeneratedOnAdd()
504  .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
505 
506  b.Property<long?>("AlphaId");
507 
508  b.Property<bool>("AlphaIsActive");
509 
510  b.Property<long?>("BravoId");
511 
512  b.HasKey("Id");
513 
514  b.HasIndex("AlphaId");
515 
516  b.HasIndex("BravoId");
517 
518  b.ToTable("WatchdogReattachInformations");
519  });
520 
521  modelBuilder.Entity("Tgstation.Server.Host.Models.ChatBot", b =>
522  {
523  b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
524  .WithMany("ChatSettings")
525  .HasForeignKey("InstanceId")
526  .OnDelete(DeleteBehavior.Cascade);
527  });
528 
529  modelBuilder.Entity("Tgstation.Server.Host.Models.ChatChannel", b =>
530  {
531  b.HasOne("Tgstation.Server.Host.Models.ChatBot", "ChatSettings")
532  .WithMany("Channels")
533  .HasForeignKey("ChatSettingsId")
534  .OnDelete(DeleteBehavior.Cascade);
535  });
536 
537  modelBuilder.Entity("Tgstation.Server.Host.Models.CompileJob", b =>
538  {
539  b.HasOne("Tgstation.Server.Host.Models.Job", "Job")
540  .WithMany()
541  .HasForeignKey("JobId");
542 
543  b.HasOne("Tgstation.Server.Host.Models.RevisionInformation", "RevisionInformation")
544  .WithMany("CompileJobs")
545  .HasForeignKey("RevisionInformationId")
546  .OnDelete(DeleteBehavior.Cascade);
547  });
548 
549  modelBuilder.Entity("Tgstation.Server.Host.Models.DreamDaemonSettings", b =>
550  {
551  b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
552  .WithOne("DreamDaemonSettings")
553  .HasForeignKey("Tgstation.Server.Host.Models.DreamDaemonSettings", "InstanceId")
554  .OnDelete(DeleteBehavior.Cascade);
555  });
556 
557  modelBuilder.Entity("Tgstation.Server.Host.Models.DreamMakerSettings", b =>
558  {
559  b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
560  .WithOne("DreamMakerSettings")
561  .HasForeignKey("Tgstation.Server.Host.Models.DreamMakerSettings", "InstanceId")
562  .OnDelete(DeleteBehavior.Cascade);
563  });
564 
565  modelBuilder.Entity("Tgstation.Server.Host.Models.Instance", b =>
566  {
567  b.HasOne("Tgstation.Server.Host.Models.WatchdogReattachInformation", "WatchdogReattachInformation")
568  .WithMany()
569  .HasForeignKey("WatchdogReattachInformationId");
570  });
571 
572  modelBuilder.Entity("Tgstation.Server.Host.Models.InstanceUser", b =>
573  {
574  b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
575  .WithMany("InstanceUsers")
576  .HasForeignKey("InstanceId")
577  .OnDelete(DeleteBehavior.Cascade);
578 
579  b.HasOne("Tgstation.Server.Host.Models.User")
580  .WithMany("InstanceUsers")
581  .HasForeignKey("UserId")
582  .OnDelete(DeleteBehavior.Cascade);
583  });
584 
585  modelBuilder.Entity("Tgstation.Server.Host.Models.Job", b =>
586  {
587  b.HasOne("Tgstation.Server.Host.Models.User", "CancelledBy")
588  .WithMany()
589  .HasForeignKey("CancelledById");
590 
591  b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
592  .WithMany("Jobs")
593  .HasForeignKey("InstanceId")
594  .OnDelete(DeleteBehavior.Cascade);
595 
596  b.HasOne("Tgstation.Server.Host.Models.User", "StartedBy")
597  .WithMany()
598  .HasForeignKey("StartedById")
599  .OnDelete(DeleteBehavior.Cascade);
600  });
601 
602  modelBuilder.Entity("Tgstation.Server.Host.Models.ReattachInformation", b =>
603  {
604  b.HasOne("Tgstation.Server.Host.Models.CompileJob", "CompileJob")
605  .WithMany()
606  .HasForeignKey("CompileJobId");
607  });
608 
609  modelBuilder.Entity("Tgstation.Server.Host.Models.RepositorySettings", b =>
610  {
611  b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
612  .WithOne("RepositorySettings")
613  .HasForeignKey("Tgstation.Server.Host.Models.RepositorySettings", "InstanceId")
614  .OnDelete(DeleteBehavior.Cascade);
615  });
616 
617  modelBuilder.Entity("Tgstation.Server.Host.Models.RevInfoTestMerge", b =>
618  {
619  b.HasOne("Tgstation.Server.Host.Models.RevisionInformation", "RevisionInformation")
620  .WithMany("ActiveTestMerges")
621  .HasForeignKey("RevisionInformationId")
622  .OnDelete(DeleteBehavior.Cascade);
623 
624  b.HasOne("Tgstation.Server.Host.Models.TestMerge", "TestMerge")
625  .WithMany("RevisonInformations")
626  .HasForeignKey("TestMergeId")
627  .OnDelete(DeleteBehavior.Cascade);
628  });
629 
630  modelBuilder.Entity("Tgstation.Server.Host.Models.RevisionInformation", b =>
631  {
632  b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
633  .WithMany("RevisionInformations")
634  .HasForeignKey("InstanceId")
635  .OnDelete(DeleteBehavior.Cascade);
636  });
637 
638  modelBuilder.Entity("Tgstation.Server.Host.Models.TestMerge", b =>
639  {
640  b.HasOne("Tgstation.Server.Host.Models.User", "MergedBy")
641  .WithMany("TestMerges")
642  .HasForeignKey("MergedById")
643  .OnDelete(DeleteBehavior.Restrict);
644 
645  b.HasOne("Tgstation.Server.Host.Models.RevisionInformation", "PrimaryRevisionInformation")
646  .WithOne("PrimaryTestMerge")
647  .HasForeignKey("Tgstation.Server.Host.Models.TestMerge", "PrimaryRevisionInformationId")
648  .OnDelete(DeleteBehavior.SetNull);
649  });
650 
651  modelBuilder.Entity("Tgstation.Server.Host.Models.User", b =>
652  {
653  b.HasOne("Tgstation.Server.Host.Models.User", "CreatedBy")
654  .WithMany("CreatedUsers")
655  .HasForeignKey("CreatedById");
656  });
657 
658  modelBuilder.Entity("Tgstation.Server.Host.Models.WatchdogReattachInformation", b =>
659  {
660  b.HasOne("Tgstation.Server.Host.Models.ReattachInformation", "Alpha")
661  .WithMany()
662  .HasForeignKey("AlphaId");
663 
664  b.HasOne("Tgstation.Server.Host.Models.ReattachInformation", "Bravo")
665  .WithMany()
666  .HasForeignKey("BravoId");
667  });
668 #pragma warning restore 612, 618
669  }
670  }
671 }
+ + + +
override void BuildTargetModel(ModelBuilder modelBuilder)
Builds the target model
+ +
+ + + + diff --git a/_sqlite_database_context_8cs.html b/20180906135553___m_s_initial_create_8cs.html similarity index 82% rename from _sqlite_database_context_8cs.html rename to 20180906135553___m_s_initial_create_8cs.html index 0cd297972c..3e6c667b8c 100644 --- a/_sqlite_database_context_8cs.html +++ b/20180906135553___m_s_initial_create_8cs.html @@ -4,7 +4,7 @@ -tgstation-server: src/Tgstation.Server.Host/Models/SqliteDatabaseContext.cs File Reference +tgstation-server: src/Tgstation.Server.Host/Models/Migrations/20180906135553_MSInitialCreate.cs File Reference @@ -83,7 +83,7 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
@@ -91,21 +91,21 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search'); Classes | Namespaces
-
SqliteDatabaseContext.cs File Reference
+
20180906135553_MSInitialCreate.cs File Reference
-

Go to the source code of this file.

+

Go to the source code of this file.

- - + +

Classes

class  Tgstation.Server.Host.Models.SqliteDatabaseContext
 DatabaseContext<TParentContext> for Sqlite More...
class  Tgstation.Server.Host.Models.Migrations.MSInitialCreate
 The initial database migration for MSSQL More...
 
- +

Namespaces

namespace  Tgstation.Server.Host.Models
namespace  Tgstation.Server.Host.Models.Migrations
 
diff --git a/20180906135553___m_s_initial_create_8cs_source.html b/20180906135553___m_s_initial_create_8cs_source.html new file mode 100644 index 0000000000..a586923898 --- /dev/null +++ b/20180906135553___m_s_initial_create_8cs_source.html @@ -0,0 +1,120 @@ + + + + + + +tgstation-server: src/Tgstation.Server.Host/Models/Migrations/20180906135553_MSInitialCreate.cs Source File + + + + + + + + + + +
+
+ + + + + + + +
+
tgstation-server +
+
The /tg/station 13 server suite
+
+
+ + + + + + +
+
+ + +
+ +
+ + +
+
+
+
20180906135553_MSInitialCreate.cs
+
+
+Go to the documentation of this file.
1 using System;
2 using Microsoft.EntityFrameworkCore.Metadata;
3 using Microsoft.EntityFrameworkCore.Migrations;
4 
5 namespace Tgstation.Server.Host.Models.Migrations
6 {
10  public partial class MSInitialCreate : Migration
11  {
16  protected override void Up(MigrationBuilder migrationBuilder)
17  {
18  migrationBuilder.CreateTable(
19  name: "Users",
20  columns: table => new
21  {
22  Id = table.Column<long>(nullable: false)
23  .Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn),
24  Enabled = table.Column<bool>(nullable: false),
25  CreatedAt = table.Column<DateTimeOffset>(nullable: false),
26  SystemIdentifier = table.Column<string>(nullable: true),
27  Name = table.Column<string>(nullable: false),
28  AdministrationRights = table.Column<decimal>(nullable: false),
29  InstanceManagerRights = table.Column<decimal>(nullable: false),
30  PasswordHash = table.Column<string>(nullable: true),
31  CreatedById = table.Column<long>(nullable: true),
32  CanonicalName = table.Column<string>(nullable: false),
33  LastPasswordUpdate = table.Column<DateTimeOffset>(nullable: true)
34  },
35  constraints: table =>
36  {
37  table.PrimaryKey("PK_Users", x => x.Id);
38  table.ForeignKey(
39  name: "FK_Users_Users_CreatedById",
40  column: x => x.CreatedById,
41  principalTable: "Users",
42  principalColumn: "Id",
43  onDelete: ReferentialAction.Restrict);
44  });
45 
46  migrationBuilder.CreateTable(
47  name: "ChatChannels",
48  columns: table => new
49  {
50  IrcChannel = table.Column<string>(nullable: true),
51  DiscordChannelId = table.Column<decimal>(nullable: true),
52  IsAdminChannel = table.Column<bool>(nullable: false),
53  IsWatchdogChannel = table.Column<bool>(nullable: false),
54  IsUpdatesChannel = table.Column<bool>(nullable: false),
55  Tag = table.Column<string>(nullable: true),
56  Id = table.Column<long>(nullable: false)
57  .Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn),
58  ChatSettingsId = table.Column<long>(nullable: false)
59  },
60  constraints: table =>
61  {
62  table.PrimaryKey("PK_ChatChannels", x => x.Id);
63  });
64 
65  migrationBuilder.CreateTable(
66  name: "ReattachInformations",
67  columns: table => new
68  {
69  ChatCommandsJson = table.Column<string>(nullable: false),
70  ChatChannelsJson = table.Column<string>(nullable: false),
71  ServerCommandsJson = table.Column<string>(nullable: false),
72  AccessIdentifier = table.Column<string>(nullable: false),
73  ProcessId = table.Column<int>(nullable: false),
74  IsPrimary = table.Column<bool>(nullable: false),
75  Port = table.Column<int>(nullable: false),
76  RebootState = table.Column<int>(nullable: false),
77  Id = table.Column<long>(nullable: false)
78  .Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn),
79  CompileJobId = table.Column<long>(nullable: true)
80  },
81  constraints: table =>
82  {
83  table.PrimaryKey("PK_ReattachInformations", x => x.Id);
84  });
85 
86  migrationBuilder.CreateTable(
87  name: "WatchdogReattachInformations",
88  columns: table => new
89  {
90  AlphaIsActive = table.Column<bool>(nullable: false),
91  Id = table.Column<long>(nullable: false)
92  .Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn),
93  AlphaId = table.Column<long>(nullable: true),
94  BravoId = table.Column<long>(nullable: true)
95  },
96  constraints: table =>
97  {
98  table.PrimaryKey("PK_WatchdogReattachInformations", x => x.Id);
99  table.ForeignKey(
100  name: "FK_WatchdogReattachInformations_ReattachInformations_AlphaId",
101  column: x => x.AlphaId,
102  principalTable: "ReattachInformations",
103  principalColumn: "Id",
104  onDelete: ReferentialAction.Restrict);
105  table.ForeignKey(
106  name: "FK_WatchdogReattachInformations_ReattachInformations_BravoId",
107  column: x => x.BravoId,
108  principalTable: "ReattachInformations",
109  principalColumn: "Id",
110  onDelete: ReferentialAction.Restrict);
111  });
112 
113  migrationBuilder.CreateTable(
114  name: "Instances",
115  columns: table => new
116  {
117  Id = table.Column<long>(nullable: false)
118  .Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn),
119  Name = table.Column<string>(nullable: false),
120  Path = table.Column<string>(nullable: false),
121  Online = table.Column<bool>(nullable: false),
122  ConfigurationType = table.Column<int>(nullable: false),
123  AutoUpdateInterval = table.Column<long>(nullable: false),
124  WatchdogReattachInformationId = table.Column<long>(nullable: true)
125  },
126  constraints: table =>
127  {
128  table.PrimaryKey("PK_Instances", x => x.Id);
129  table.ForeignKey(
130  name: "FK_Instances_WatchdogReattachInformations_WatchdogReattachInformationId",
131  column: x => x.WatchdogReattachInformationId,
132  principalTable: "WatchdogReattachInformations",
133  principalColumn: "Id",
134  onDelete: ReferentialAction.Restrict);
135  });
136 
137  migrationBuilder.CreateTable(
138  name: "ChatBots",
139  columns: table => new
140  {
141  Id = table.Column<long>(nullable: false)
142  .Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn),
143  Name = table.Column<string>(nullable: false),
144  Enabled = table.Column<bool>(nullable: true),
145  Provider = table.Column<int>(nullable: true),
146  ConnectionString = table.Column<string>(nullable: false),
147  InstanceId = table.Column<long>(nullable: false)
148  },
149  constraints: table =>
150  {
151  table.PrimaryKey("PK_ChatBots", x => x.Id);
152  table.ForeignKey(
153  name: "FK_ChatBots_Instances_InstanceId",
154  column: x => x.InstanceId,
155  principalTable: "Instances",
156  principalColumn: "Id",
157  onDelete: ReferentialAction.Cascade);
158  });
159 
160  migrationBuilder.CreateTable(
161  name: "DreamDaemonSettings",
162  columns: table => new
163  {
164  AllowWebClient = table.Column<bool>(nullable: false),
165  SecurityLevel = table.Column<int>(nullable: false),
166  PrimaryPort = table.Column<int>(nullable: false),
167  SecondaryPort = table.Column<int>(nullable: false),
168  StartupTimeout = table.Column<long>(nullable: false),
169  AutoStart = table.Column<bool>(nullable: false),
170  SoftRestart = table.Column<bool>(nullable: false),
171  SoftShutdown = table.Column<bool>(nullable: false),
172  Id = table.Column<long>(nullable: false)
173  .Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn),
174  ProcessId = table.Column<int>(nullable: true),
175  AccessToken = table.Column<string>(nullable: true),
176  InstanceId = table.Column<long>(nullable: false)
177  },
178  constraints: table =>
179  {
180  table.PrimaryKey("PK_DreamDaemonSettings", x => x.Id);
181  table.ForeignKey(
182  name: "FK_DreamDaemonSettings_Instances_InstanceId",
183  column: x => x.InstanceId,
184  principalTable: "Instances",
185  principalColumn: "Id",
186  onDelete: ReferentialAction.Cascade);
187  });
188 
189  migrationBuilder.CreateTable(
190  name: "DreamMakerSettings",
191  columns: table => new
192  {
193  ProjectName = table.Column<string>(nullable: true),
194  ApiValidationPort = table.Column<int>(nullable: false),
195  Id = table.Column<long>(nullable: false)
196  .Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn),
197  InstanceId = table.Column<long>(nullable: false)
198  },
199  constraints: table =>
200  {
201  table.PrimaryKey("PK_DreamMakerSettings", x => x.Id);
202  table.ForeignKey(
203  name: "FK_DreamMakerSettings_Instances_InstanceId",
204  column: x => x.InstanceId,
205  principalTable: "Instances",
206  principalColumn: "Id",
207  onDelete: ReferentialAction.Cascade);
208  });
209 
210  migrationBuilder.CreateTable(
211  name: "InstanceUsers",
212  columns: table => new
213  {
214  UserId = table.Column<long>(nullable: false),
215  InstanceUserRights = table.Column<decimal>(nullable: false),
216  ByondRights = table.Column<decimal>(nullable: false),
217  DreamDaemonRights = table.Column<decimal>(nullable: false),
218  DreamMakerRights = table.Column<decimal>(nullable: false),
219  RepositoryRights = table.Column<decimal>(nullable: false),
220  ChatBotRights = table.Column<decimal>(nullable: false),
221  ConfigurationRights = table.Column<decimal>(nullable: false),
222  Id = table.Column<long>(nullable: false)
223  .Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn),
224  InstanceId = table.Column<long>(nullable: false)
225  },
226  constraints: table =>
227  {
228  table.PrimaryKey("PK_InstanceUsers", x => x.Id);
229  table.ForeignKey(
230  name: "FK_InstanceUsers_Instances_InstanceId",
231  column: x => x.InstanceId,
232  principalTable: "Instances",
233  principalColumn: "Id",
234  onDelete: ReferentialAction.Cascade);
235  table.ForeignKey(
236  name: "FK_InstanceUsers_Users_UserId",
237  column: x => x.UserId,
238  principalTable: "Users",
239  principalColumn: "Id",
240  onDelete: ReferentialAction.Cascade);
241  });
242 
243  migrationBuilder.CreateTable(
244  name: "Jobs",
245  columns: table => new
246  {
247  Id = table.Column<long>(nullable: false)
248  .Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn),
249  Description = table.Column<string>(nullable: false),
250  ExceptionDetails = table.Column<string>(nullable: true),
251  StartedAt = table.Column<DateTimeOffset>(nullable: false),
252  StoppedAt = table.Column<DateTimeOffset>(nullable: true),
253  Cancelled = table.Column<bool>(nullable: false),
254  CancelRightsType = table.Column<decimal>(nullable: true),
255  CancelRight = table.Column<decimal>(nullable: true),
256  StartedById = table.Column<long>(nullable: false),
257  CancelledById = table.Column<long>(nullable: true),
258  InstanceId = table.Column<long>(nullable: false)
259  },
260  constraints: table =>
261  {
262  table.PrimaryKey("PK_Jobs", x => x.Id);
263  table.ForeignKey(
264  name: "FK_Jobs_Users_CancelledById",
265  column: x => x.CancelledById,
266  principalTable: "Users",
267  principalColumn: "Id",
268  onDelete: ReferentialAction.Restrict);
269  table.ForeignKey(
270  name: "FK_Jobs_Instances_InstanceId",
271  column: x => x.InstanceId,
272  principalTable: "Instances",
273  principalColumn: "Id",
274  onDelete: ReferentialAction.Cascade);
275  table.ForeignKey(
276  name: "FK_Jobs_Users_StartedById",
277  column: x => x.StartedById,
278  principalTable: "Users",
279  principalColumn: "Id",
280  onDelete: ReferentialAction.Cascade);
281  });
282 
283  migrationBuilder.CreateTable(
284  name: "RepositorySettings",
285  columns: table => new
286  {
287  CommitterName = table.Column<string>(nullable: false),
288  CommitterEmail = table.Column<string>(nullable: false),
289  AccessUser = table.Column<string>(nullable: true),
290  AccessToken = table.Column<string>(nullable: true),
291  PushTestMergeCommits = table.Column<bool>(nullable: false),
292  ShowTestMergeCommitters = table.Column<bool>(nullable: false),
293  AutoUpdatesKeepTestMerges = table.Column<bool>(nullable: false),
294  AutoUpdatesSynchronize = table.Column<bool>(nullable: false),
295  Id = table.Column<long>(nullable: false)
296  .Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn),
297  InstanceId = table.Column<long>(nullable: false)
298  },
299  constraints: table =>
300  {
301  table.PrimaryKey("PK_RepositorySettings", x => x.Id);
302  table.ForeignKey(
303  name: "FK_RepositorySettings_Instances_InstanceId",
304  column: x => x.InstanceId,
305  principalTable: "Instances",
306  principalColumn: "Id",
307  onDelete: ReferentialAction.Cascade);
308  });
309 
310  migrationBuilder.CreateTable(
311  name: "RevisionInformations",
312  columns: table => new
313  {
314  CommitSha = table.Column<string>(maxLength: 40, nullable: false),
315  OriginCommitSha = table.Column<string>(maxLength: 40, nullable: false),
316  Id = table.Column<long>(nullable: false)
317  .Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn),
318  InstanceId = table.Column<long>(nullable: false)
319  },
320  constraints: table =>
321  {
322  table.PrimaryKey("PK_RevisionInformations", x => x.Id);
323  table.ForeignKey(
324  name: "FK_RevisionInformations_Instances_InstanceId",
325  column: x => x.InstanceId,
326  principalTable: "Instances",
327  principalColumn: "Id",
328  onDelete: ReferentialAction.Cascade);
329  });
330 
331  migrationBuilder.CreateTable(
332  name: "CompileJobs",
333  columns: table => new
334  {
335  Id = table.Column<long>(nullable: false)
336  .Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn),
337  DmeName = table.Column<string>(nullable: true),
338  Output = table.Column<string>(nullable: true),
339  DirectoryName = table.Column<Guid>(nullable: true),
340  JobId = table.Column<long>(nullable: true),
341  RevisionInformationId = table.Column<long>(nullable: false),
342  ByondVersion = table.Column<string>(nullable: false)
343  },
344  constraints: table =>
345  {
346  table.PrimaryKey("PK_CompileJobs", x => x.Id);
347  table.ForeignKey(
348  name: "FK_CompileJobs_Jobs_JobId",
349  column: x => x.JobId,
350  principalTable: "Jobs",
351  principalColumn: "Id",
352  onDelete: ReferentialAction.Restrict);
353  table.ForeignKey(
354  name: "FK_CompileJobs_RevisionInformations_RevisionInformationId",
355  column: x => x.RevisionInformationId,
356  principalTable: "RevisionInformations",
357  principalColumn: "Id",
358  onDelete: ReferentialAction.Cascade);
359  });
360 
361  migrationBuilder.CreateTable(
362  name: "TestMerges",
363  columns: table => new
364  {
365  Number = table.Column<int>(nullable: false),
366  PullRequestRevision = table.Column<string>(nullable: false),
367  Comment = table.Column<string>(nullable: true),
368  TitleAtMerge = table.Column<string>(nullable: false),
369  BodyAtMerge = table.Column<string>(nullable: false),
370  Url = table.Column<string>(nullable: false),
371  Author = table.Column<string>(nullable: false),
372  Id = table.Column<long>(nullable: false)
373  .Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn),
374  MergedAt = table.Column<DateTimeOffset>(nullable: false),
375  MergedById = table.Column<long>(nullable: false),
376  PrimaryRevisionInformationId = table.Column<long>(nullable: true)
377  },
378  constraints: table =>
379  {
380  table.PrimaryKey("PK_TestMerges", x => x.Id);
381  table.ForeignKey(
382  name: "FK_TestMerges_Users_MergedById",
383  column: x => x.MergedById,
384  principalTable: "Users",
385  principalColumn: "Id",
386  onDelete: ReferentialAction.Restrict);
387  table.ForeignKey(
388  name: "FK_TestMerges_RevisionInformations_PrimaryRevisionInformationId",
389  column: x => x.PrimaryRevisionInformationId,
390  principalTable: "RevisionInformations",
391  principalColumn: "Id",
392  onDelete: ReferentialAction.SetNull);
393  });
394 
395  migrationBuilder.CreateTable(
396  name: "RevInfoTestMerges",
397  columns: table => new
398  {
399  Id = table.Column<long>(nullable: false)
400  .Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn),
401  TestMergeId = table.Column<long>(nullable: false),
402  RevisionInformationId = table.Column<long>(nullable: false)
403  },
404  constraints: table =>
405  {
406  table.PrimaryKey("PK_RevInfoTestMerges", x => x.Id);
407  table.ForeignKey(
408  name: "FK_RevInfoTestMerges_RevisionInformations_RevisionInformationId",
409  column: x => x.RevisionInformationId,
410  principalTable: "RevisionInformations",
411  principalColumn: "Id",
412  onDelete: ReferentialAction.Cascade);
413  table.ForeignKey(
414  name: "FK_RevInfoTestMerges_TestMerges_TestMergeId",
415  column: x => x.TestMergeId,
416  principalTable: "TestMerges",
417  principalColumn: "Id",
418  onDelete: ReferentialAction.Cascade);
419  });
420 
421  migrationBuilder.CreateIndex(
422  name: "IX_ChatBots_InstanceId",
423  table: "ChatBots",
424  column: "InstanceId");
425 
426  migrationBuilder.CreateIndex(
427  name: "IX_ChatBots_Name",
428  table: "ChatBots",
429  column: "Name",
430  unique: true);
431 
432  migrationBuilder.CreateIndex(
433  name: "IX_ChatChannels_ChatSettingsId_DiscordChannelId",
434  table: "ChatChannels",
435  columns: new[] { "ChatSettingsId", "DiscordChannelId" },
436  unique: true,
437  filter: "[DiscordChannelId] IS NOT NULL");
438 
439  migrationBuilder.CreateIndex(
440  name: "IX_ChatChannels_ChatSettingsId_IrcChannel",
441  table: "ChatChannels",
442  columns: new[] { "ChatSettingsId", "IrcChannel" },
443  unique: true,
444  filter: "[IrcChannel] IS NOT NULL");
445 
446  migrationBuilder.CreateIndex(
447  name: "IX_CompileJobs_DirectoryName",
448  table: "CompileJobs",
449  column: "DirectoryName");
450 
451  migrationBuilder.CreateIndex(
452  name: "IX_CompileJobs_JobId",
453  table: "CompileJobs",
454  column: "JobId");
455 
456  migrationBuilder.CreateIndex(
457  name: "IX_CompileJobs_RevisionInformationId",
458  table: "CompileJobs",
459  column: "RevisionInformationId");
460 
461  migrationBuilder.CreateIndex(
462  name: "IX_DreamDaemonSettings_InstanceId",
463  table: "DreamDaemonSettings",
464  column: "InstanceId",
465  unique: true);
466 
467  migrationBuilder.CreateIndex(
468  name: "IX_DreamMakerSettings_InstanceId",
469  table: "DreamMakerSettings",
470  column: "InstanceId",
471  unique: true);
472 
473  migrationBuilder.CreateIndex(
474  name: "IX_Instances_Path",
475  table: "Instances",
476  column: "Path",
477  unique: true);
478 
479  migrationBuilder.CreateIndex(
480  name: "IX_Instances_WatchdogReattachInformationId",
481  table: "Instances",
482  column: "WatchdogReattachInformationId");
483 
484  migrationBuilder.CreateIndex(
485  name: "IX_InstanceUsers_InstanceId",
486  table: "InstanceUsers",
487  column: "InstanceId");
488 
489  migrationBuilder.CreateIndex(
490  name: "IX_InstanceUsers_UserId_InstanceId",
491  table: "InstanceUsers",
492  columns: new[] { "UserId", "InstanceId" },
493  unique: true);
494 
495  migrationBuilder.CreateIndex(
496  name: "IX_Jobs_CancelledById",
497  table: "Jobs",
498  column: "CancelledById");
499 
500  migrationBuilder.CreateIndex(
501  name: "IX_Jobs_InstanceId",
502  table: "Jobs",
503  column: "InstanceId");
504 
505  migrationBuilder.CreateIndex(
506  name: "IX_Jobs_StartedById",
507  table: "Jobs",
508  column: "StartedById");
509 
510  migrationBuilder.CreateIndex(
511  name: "IX_ReattachInformations_CompileJobId",
512  table: "ReattachInformations",
513  column: "CompileJobId");
514 
515  migrationBuilder.CreateIndex(
516  name: "IX_RepositorySettings_InstanceId",
517  table: "RepositorySettings",
518  column: "InstanceId",
519  unique: true);
520 
521  migrationBuilder.CreateIndex(
522  name: "IX_RevInfoTestMerges_RevisionInformationId",
523  table: "RevInfoTestMerges",
524  column: "RevisionInformationId");
525 
526  migrationBuilder.CreateIndex(
527  name: "IX_RevInfoTestMerges_TestMergeId",
528  table: "RevInfoTestMerges",
529  column: "TestMergeId");
530 
531  migrationBuilder.CreateIndex(
532  name: "IX_RevisionInformations_CommitSha",
533  table: "RevisionInformations",
534  column: "CommitSha",
535  unique: true);
536 
537  migrationBuilder.CreateIndex(
538  name: "IX_RevisionInformations_InstanceId",
539  table: "RevisionInformations",
540  column: "InstanceId");
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  filter: "[PrimaryRevisionInformationId] IS NOT NULL");
553 
554  migrationBuilder.CreateIndex(
555  name: "IX_Users_CanonicalName",
556  table: "Users",
557  column: "CanonicalName",
558  unique: true);
559 
560  migrationBuilder.CreateIndex(
561  name: "IX_Users_CreatedById",
562  table: "Users",
563  column: "CreatedById");
564 
565  migrationBuilder.CreateIndex(
566  name: "IX_WatchdogReattachInformations_AlphaId",
567  table: "WatchdogReattachInformations",
568  column: "AlphaId");
569 
570  migrationBuilder.CreateIndex(
571  name: "IX_WatchdogReattachInformations_BravoId",
572  table: "WatchdogReattachInformations",
573  column: "BravoId");
574 
575  migrationBuilder.AddForeignKey(
576  name: "FK_ChatChannels_ChatBots_ChatSettingsId",
577  table: "ChatChannels",
578  column: "ChatSettingsId",
579  principalTable: "ChatBots",
580  principalColumn: "Id",
581  onDelete: ReferentialAction.Cascade);
582 
583  migrationBuilder.AddForeignKey(
584  name: "FK_ReattachInformations_CompileJobs_CompileJobId",
585  table: "ReattachInformations",
586  column: "CompileJobId",
587  principalTable: "CompileJobs",
588  principalColumn: "Id",
589  onDelete: ReferentialAction.Restrict);
590  }
591 
596  protected override void Down(MigrationBuilder migrationBuilder)
597  {
598  migrationBuilder.DropForeignKey(
599  name: "FK_Jobs_Instances_InstanceId",
600  table: "Jobs");
601 
602  migrationBuilder.DropForeignKey(
603  name: "FK_RevisionInformations_Instances_InstanceId",
604  table: "RevisionInformations");
605 
606  migrationBuilder.DropTable(
607  name: "ChatChannels");
608 
609  migrationBuilder.DropTable(
610  name: "DreamDaemonSettings");
611 
612  migrationBuilder.DropTable(
613  name: "DreamMakerSettings");
614 
615  migrationBuilder.DropTable(
616  name: "InstanceUsers");
617 
618  migrationBuilder.DropTable(
619  name: "RepositorySettings");
620 
621  migrationBuilder.DropTable(
622  name: "RevInfoTestMerges");
623 
624  migrationBuilder.DropTable(
625  name: "ChatBots");
626 
627  migrationBuilder.DropTable(
628  name: "TestMerges");
629 
630  migrationBuilder.DropTable(
631  name: "Instances");
632 
633  migrationBuilder.DropTable(
634  name: "WatchdogReattachInformations");
635 
636  migrationBuilder.DropTable(
637  name: "ReattachInformations");
638 
639  migrationBuilder.DropTable(
640  name: "CompileJobs");
641 
642  migrationBuilder.DropTable(
643  name: "Jobs");
644 
645  migrationBuilder.DropTable(
646  name: "RevisionInformations");
647 
648  migrationBuilder.DropTable(
649  name: "Users");
650  }
651  }
652 }
ConfigurationRights
Rights for Models.ConfigurationFile
+
override void Down(MigrationBuilder migrationBuilder)
Unapplies the migration
+
override void Up(MigrationBuilder migrationBuilder)
Applies the migration
+
ByondRights
Rights for Models.Byond
Definition: ByondRights.cs:9
+
InstanceUserRights
Rights for an Models.Instance
+ +
AdministrationRights
Rights for Models.Administration
+
ConfigurationType
The type of configuration allowed on an Instance
+ +
DreamDaemonRights
Rights for Models.DreamDaemon
+
InstanceManagerRights
Rights for managing Models.Instances
+
DreamMakerRights
Rights for Models.DreamMaker
+
RepositoryRights
Rights for a Models.Repository
+ + +
ChatBotRights
Rights for Models.ChatBot
Definition: ChatBotRights.cs:9
+
RebootState
Represents the action to take when /world/Reboot() is called
Definition: RebootState.cs:6
+ +
+ + + + diff --git a/20180906143029___m_y_initial_create_8_designer_8cs.html b/20180906143029___m_y_initial_create_8_designer_8cs.html new file mode 100644 index 0000000000..9702507fa2 --- /dev/null +++ b/20180906143029___m_y_initial_create_8_designer_8cs.html @@ -0,0 +1,119 @@ + + + + + + +tgstation-server: src/Tgstation.Server.Host/Models/Migrations/20180906143029_MYInitialCreate.Designer.cs File Reference + + + + + + + + + + +
+
+ + + + + + + +
+
tgstation-server +
+
The /tg/station 13 server suite
+
+
+ + + + + + +
+
+ + +
+ +
+ + +
+
+
+Classes | +Namespaces
+
+
20180906143029_MYInitialCreate.Designer.cs File Reference
+
+
+ +

Go to the source code of this file.

+ + + + + +

+Classes

class  Tgstation.Server.Host.Models.Migrations.MYInitialCreate
 The initial database migration for MySQL/MariaDB More...
 
+ + + +

+Namespaces

namespace  Tgstation.Server.Host.Models.Migrations
 
+
+ + + + diff --git a/20180906143029___m_y_initial_create_8_designer_8cs_source.html b/20180906143029___m_y_initial_create_8_designer_8cs_source.html new file mode 100644 index 0000000000..4bf432bab3 --- /dev/null +++ b/20180906143029___m_y_initial_create_8_designer_8cs_source.html @@ -0,0 +1,108 @@ + + + + + + +tgstation-server: src/Tgstation.Server.Host/Models/Migrations/20180906143029_MYInitialCreate.Designer.cs Source File + + + + + + + + + + +
+
+ + + + + + + +
+
tgstation-server +
+
The /tg/station 13 server suite
+
+
+ + + + + + +
+
+ + +
+ +
+ + +
+
+
+
20180906143029_MYInitialCreate.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 Microsoft.EntityFrameworkCore.Storage.ValueConversion;
7 
8 namespace Tgstation.Server.Host.Models.Migrations
9 {
10  [DbContext(typeof(MySqlDatabaseContext))]
11  [Migration("20180906143029_MYInitialCreate")]
12  partial class MYInitialCreate
13  {
18  protected override void BuildTargetModel(ModelBuilder modelBuilder)
19  {
20 #pragma warning disable 612, 618
21  modelBuilder
22  .HasAnnotation("ProductVersion", "2.1.2-rtm-30932")
23  .HasAnnotation("Relational:MaxIdentifierLength", 64);
24 
25  modelBuilder.Entity("Tgstation.Server.Host.Models.ChatBot", b =>
26  {
27  b.Property<long>("Id")
28  .ValueGeneratedOnAdd();
29 
30  b.Property<string>("ConnectionString")
31  .IsRequired();
32 
33  b.Property<bool?>("Enabled");
34 
35  b.Property<long>("InstanceId");
36 
37  b.Property<string>("Name")
38  .IsRequired();
39 
40  b.Property<int?>("Provider");
41 
42  b.HasKey("Id");
43 
44  b.HasIndex("InstanceId");
45 
46  b.HasIndex("Name")
47  .IsUnique();
48 
49  b.ToTable("ChatBots");
50  });
51 
52  modelBuilder.Entity("Tgstation.Server.Host.Models.ChatChannel", b =>
53  {
54  b.Property<long>("Id")
55  .ValueGeneratedOnAdd();
56 
57  b.Property<long>("ChatSettingsId");
58 
59  b.Property<ulong?>("DiscordChannelId");
60 
61  b.Property<string>("IrcChannel");
62 
63  b.Property<bool?>("IsAdminChannel")
64  .IsRequired();
65 
66  b.Property<bool?>("IsUpdatesChannel")
67  .IsRequired();
68 
69  b.Property<bool?>("IsWatchdogChannel")
70  .IsRequired();
71 
72  b.Property<string>("Tag");
73 
74  b.HasKey("Id");
75 
76  b.HasIndex("ChatSettingsId", "DiscordChannelId")
77  .IsUnique();
78 
79  b.HasIndex("ChatSettingsId", "IrcChannel")
80  .IsUnique();
81 
82  b.ToTable("ChatChannels");
83  });
84 
85  modelBuilder.Entity("Tgstation.Server.Host.Models.CompileJob", b =>
86  {
87  b.Property<long>("Id")
88  .ValueGeneratedOnAdd();
89 
90  b.Property<string>("ByondVersion")
91  .IsRequired();
92 
93  b.Property<Guid?>("DirectoryName");
94 
95  b.Property<string>("DmeName");
96 
97  b.Property<long?>("JobId");
98 
99  b.Property<string>("Output");
100 
101  b.Property<long>("RevisionInformationId");
102 
103  b.HasKey("Id");
104 
105  b.HasIndex("DirectoryName");
106 
107  b.HasIndex("JobId");
108 
109  b.HasIndex("RevisionInformationId");
110 
111  b.ToTable("CompileJobs");
112  });
113 
114  modelBuilder.Entity("Tgstation.Server.Host.Models.DreamDaemonSettings", b =>
115  {
116  b.Property<long>("Id")
117  .ValueGeneratedOnAdd();
118 
119  b.Property<string>("AccessToken");
120 
121  b.Property<bool?>("AllowWebClient")
122  .IsRequired();
123 
124  b.Property<bool?>("AutoStart")
125  .IsRequired();
126 
127  b.Property<long>("InstanceId");
128 
129  b.Property<ushort?>("PrimaryPort")
130  .IsRequired();
131 
132  b.Property<int?>("ProcessId");
133 
134  b.Property<ushort?>("SecondaryPort")
135  .IsRequired();
136 
137  b.Property<int>("SecurityLevel");
138 
139  b.Property<bool?>("SoftRestart")
140  .IsRequired();
141 
142  b.Property<bool?>("SoftShutdown")
143  .IsRequired();
144 
145  b.Property<uint?>("StartupTimeout")
146  .IsRequired();
147 
148  b.HasKey("Id");
149 
150  b.HasIndex("InstanceId")
151  .IsUnique();
152 
153  b.ToTable("DreamDaemonSettings");
154  });
155 
156  modelBuilder.Entity("Tgstation.Server.Host.Models.DreamMakerSettings", b =>
157  {
158  b.Property<long>("Id")
159  .ValueGeneratedOnAdd();
160 
161  b.Property<ushort?>("ApiValidationPort")
162  .IsRequired();
163 
164  b.Property<long>("InstanceId");
165 
166  b.Property<string>("ProjectName");
167 
168  b.HasKey("Id");
169 
170  b.HasIndex("InstanceId")
171  .IsUnique();
172 
173  b.ToTable("DreamMakerSettings");
174  });
175 
176  modelBuilder.Entity("Tgstation.Server.Host.Models.Instance", b =>
177  {
178  b.Property<long>("Id")
179  .ValueGeneratedOnAdd();
180 
181  b.Property<uint?>("AutoUpdateInterval")
182  .IsRequired();
183 
184  b.Property<int>("ConfigurationType");
185 
186  b.Property<string>("Name")
187  .IsRequired();
188 
189  b.Property<bool?>("Online")
190  .IsRequired();
191 
192  b.Property<string>("Path")
193  .IsRequired();
194 
195  b.Property<long?>("WatchdogReattachInformationId");
196 
197  b.HasKey("Id");
198 
199  b.HasIndex("Path")
200  .IsUnique();
201 
202  b.HasIndex("WatchdogReattachInformationId");
203 
204  b.ToTable("Instances");
205  });
206 
207  modelBuilder.Entity("Tgstation.Server.Host.Models.InstanceUser", b =>
208  {
209  b.Property<long>("Id")
210  .ValueGeneratedOnAdd();
211 
212  b.Property<ulong>("ByondRights");
213 
214  b.Property<ulong>("ChatBotRights");
215 
216  b.Property<ulong>("ConfigurationRights");
217 
218  b.Property<ulong>("DreamDaemonRights");
219 
220  b.Property<ulong>("DreamMakerRights");
221 
222  b.Property<long>("InstanceId");
223 
224  b.Property<ulong>("InstanceUserRights");
225 
226  b.Property<ulong>("RepositoryRights");
227 
228  b.Property<long?>("UserId")
229  .IsRequired();
230 
231  b.HasKey("Id");
232 
233  b.HasIndex("InstanceId");
234 
235  b.HasIndex("UserId", "InstanceId")
236  .IsUnique();
237 
238  b.ToTable("InstanceUsers");
239  });
240 
241  modelBuilder.Entity("Tgstation.Server.Host.Models.Job", b =>
242  {
243  b.Property<long>("Id")
244  .ValueGeneratedOnAdd();
245 
246  b.Property<ulong?>("CancelRight");
247 
248  b.Property<ulong?>("CancelRightsType");
249 
250  b.Property<bool?>("Cancelled")
251  .IsRequired();
252 
253  b.Property<long?>("CancelledById");
254 
255  b.Property<string>("Description")
256  .IsRequired();
257 
258  b.Property<string>("ExceptionDetails");
259 
260  b.Property<long>("InstanceId");
261 
262  b.Property<DateTimeOffset?>("StartedAt")
263  .IsRequired();
264 
265  b.Property<long>("StartedById");
266 
267  b.Property<DateTimeOffset?>("StoppedAt");
268 
269  b.HasKey("Id");
270 
271  b.HasIndex("CancelledById");
272 
273  b.HasIndex("InstanceId");
274 
275  b.HasIndex("StartedById");
276 
277  b.ToTable("Jobs");
278  });
279 
280  modelBuilder.Entity("Tgstation.Server.Host.Models.ReattachInformation", b =>
281  {
282  b.Property<long>("Id")
283  .ValueGeneratedOnAdd();
284 
285  b.Property<string>("AccessIdentifier")
286  .IsRequired();
287 
288  b.Property<string>("ChatChannelsJson")
289  .IsRequired();
290 
291  b.Property<string>("ChatCommandsJson")
292  .IsRequired();
293 
294  b.Property<long?>("CompileJobId");
295 
296  b.Property<bool>("IsPrimary");
297 
298  b.Property<ushort>("Port");
299 
300  b.Property<int>("ProcessId");
301 
302  b.Property<int>("RebootState");
303 
304  b.Property<string>("ServerCommandsJson")
305  .IsRequired();
306 
307  b.HasKey("Id");
308 
309  b.HasIndex("CompileJobId");
310 
311  b.ToTable("ReattachInformations");
312  });
313 
314  modelBuilder.Entity("Tgstation.Server.Host.Models.RepositorySettings", b =>
315  {
316  b.Property<long>("Id")
317  .ValueGeneratedOnAdd();
318 
319  b.Property<string>("AccessToken");
320 
321  b.Property<string>("AccessUser");
322 
323  b.Property<bool?>("AutoUpdatesKeepTestMerges")
324  .IsRequired();
325 
326  b.Property<bool?>("AutoUpdatesSynchronize")
327  .IsRequired();
328 
329  b.Property<string>("CommitterEmail")
330  .IsRequired();
331 
332  b.Property<string>("CommitterName")
333  .IsRequired();
334 
335  b.Property<long>("InstanceId");
336 
337  b.Property<bool?>("PushTestMergeCommits")
338  .IsRequired();
339 
340  b.Property<bool?>("ShowTestMergeCommitters")
341  .IsRequired();
342 
343  b.HasKey("Id");
344 
345  b.HasIndex("InstanceId")
346  .IsUnique();
347 
348  b.ToTable("RepositorySettings");
349  });
350 
351  modelBuilder.Entity("Tgstation.Server.Host.Models.RevInfoTestMerge", b =>
352  {
353  b.Property<long>("Id")
354  .ValueGeneratedOnAdd();
355 
356  b.Property<long>("RevisionInformationId");
357 
358  b.Property<long>("TestMergeId");
359 
360  b.HasKey("Id");
361 
362  b.HasIndex("RevisionInformationId");
363 
364  b.HasIndex("TestMergeId");
365 
366  b.ToTable("RevInfoTestMerges");
367  });
368 
369  modelBuilder.Entity("Tgstation.Server.Host.Models.RevisionInformation", b =>
370  {
371  b.Property<long>("Id")
372  .ValueGeneratedOnAdd();
373 
374  b.Property<string>("CommitSha")
375  .IsRequired()
376  .HasMaxLength(40);
377 
378  b.Property<long>("InstanceId");
379 
380  b.Property<string>("OriginCommitSha")
381  .IsRequired()
382  .HasMaxLength(40);
383 
384  b.HasKey("Id");
385 
386  b.HasIndex("CommitSha")
387  .IsUnique();
388 
389  b.HasIndex("InstanceId");
390 
391  b.ToTable("RevisionInformations");
392  });
393 
394  modelBuilder.Entity("Tgstation.Server.Host.Models.TestMerge", b =>
395  {
396  b.Property<long>("Id")
397  .ValueGeneratedOnAdd();
398 
399  b.Property<string>("Author")
400  .IsRequired();
401 
402  b.Property<string>("BodyAtMerge")
403  .IsRequired();
404 
405  b.Property<string>("Comment");
406 
407  b.Property<DateTimeOffset>("MergedAt");
408 
409  b.Property<long>("MergedById");
410 
411  b.Property<int?>("Number")
412  .IsRequired();
413 
414  b.Property<long?>("PrimaryRevisionInformationId");
415 
416  b.Property<string>("PullRequestRevision")
417  .IsRequired();
418 
419  b.Property<string>("TitleAtMerge")
420  .IsRequired();
421 
422  b.Property<string>("Url")
423  .IsRequired();
424 
425  b.HasKey("Id");
426 
427  b.HasIndex("MergedById");
428 
429  b.HasIndex("PrimaryRevisionInformationId")
430  .IsUnique();
431 
432  b.ToTable("TestMerges");
433  });
434 
435  modelBuilder.Entity("Tgstation.Server.Host.Models.User", b =>
436  {
437  b.Property<long>("Id")
438  .ValueGeneratedOnAdd();
439 
440  b.Property<ulong>("AdministrationRights");
441 
442  b.Property<string>("CanonicalName")
443  .IsRequired();
444 
445  b.Property<DateTimeOffset?>("CreatedAt")
446  .IsRequired();
447 
448  b.Property<long?>("CreatedById");
449 
450  b.Property<bool?>("Enabled")
451  .IsRequired();
452 
453  b.Property<ulong>("InstanceManagerRights");
454 
455  b.Property<DateTimeOffset?>("LastPasswordUpdate");
456 
457  b.Property<string>("Name")
458  .IsRequired();
459 
460  b.Property<string>("PasswordHash");
461 
462  b.Property<string>("SystemIdentifier");
463 
464  b.HasKey("Id");
465 
466  b.HasIndex("CanonicalName")
467  .IsUnique();
468 
469  b.HasIndex("CreatedById");
470 
471  b.ToTable("Users");
472  });
473 
474  modelBuilder.Entity("Tgstation.Server.Host.Models.WatchdogReattachInformation", b =>
475  {
476  b.Property<long>("Id")
477  .ValueGeneratedOnAdd();
478 
479  b.Property<long?>("AlphaId");
480 
481  b.Property<bool>("AlphaIsActive");
482 
483  b.Property<long?>("BravoId");
484 
485  b.HasKey("Id");
486 
487  b.HasIndex("AlphaId");
488 
489  b.HasIndex("BravoId");
490 
491  b.ToTable("WatchdogReattachInformations");
492  });
493 
494  modelBuilder.Entity("Tgstation.Server.Host.Models.ChatBot", b =>
495  {
496  b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
497  .WithMany("ChatSettings")
498  .HasForeignKey("InstanceId")
499  .OnDelete(DeleteBehavior.Cascade);
500  });
501 
502  modelBuilder.Entity("Tgstation.Server.Host.Models.ChatChannel", b =>
503  {
504  b.HasOne("Tgstation.Server.Host.Models.ChatBot", "ChatSettings")
505  .WithMany("Channels")
506  .HasForeignKey("ChatSettingsId")
507  .OnDelete(DeleteBehavior.Cascade);
508  });
509 
510  modelBuilder.Entity("Tgstation.Server.Host.Models.CompileJob", b =>
511  {
512  b.HasOne("Tgstation.Server.Host.Models.Job", "Job")
513  .WithMany()
514  .HasForeignKey("JobId");
515 
516  b.HasOne("Tgstation.Server.Host.Models.RevisionInformation", "RevisionInformation")
517  .WithMany("CompileJobs")
518  .HasForeignKey("RevisionInformationId")
519  .OnDelete(DeleteBehavior.Cascade);
520  });
521 
522  modelBuilder.Entity("Tgstation.Server.Host.Models.DreamDaemonSettings", b =>
523  {
524  b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
525  .WithOne("DreamDaemonSettings")
526  .HasForeignKey("Tgstation.Server.Host.Models.DreamDaemonSettings", "InstanceId")
527  .OnDelete(DeleteBehavior.Cascade);
528  });
529 
530  modelBuilder.Entity("Tgstation.Server.Host.Models.DreamMakerSettings", b =>
531  {
532  b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
533  .WithOne("DreamMakerSettings")
534  .HasForeignKey("Tgstation.Server.Host.Models.DreamMakerSettings", "InstanceId")
535  .OnDelete(DeleteBehavior.Cascade);
536  });
537 
538  modelBuilder.Entity("Tgstation.Server.Host.Models.Instance", b =>
539  {
540  b.HasOne("Tgstation.Server.Host.Models.WatchdogReattachInformation", "WatchdogReattachInformation")
541  .WithMany()
542  .HasForeignKey("WatchdogReattachInformationId");
543  });
544 
545  modelBuilder.Entity("Tgstation.Server.Host.Models.InstanceUser", b =>
546  {
547  b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
548  .WithMany("InstanceUsers")
549  .HasForeignKey("InstanceId")
550  .OnDelete(DeleteBehavior.Cascade);
551 
552  b.HasOne("Tgstation.Server.Host.Models.User")
553  .WithMany("InstanceUsers")
554  .HasForeignKey("UserId")
555  .OnDelete(DeleteBehavior.Cascade);
556  });
557 
558  modelBuilder.Entity("Tgstation.Server.Host.Models.Job", b =>
559  {
560  b.HasOne("Tgstation.Server.Host.Models.User", "CancelledBy")
561  .WithMany()
562  .HasForeignKey("CancelledById");
563 
564  b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
565  .WithMany("Jobs")
566  .HasForeignKey("InstanceId")
567  .OnDelete(DeleteBehavior.Cascade);
568 
569  b.HasOne("Tgstation.Server.Host.Models.User", "StartedBy")
570  .WithMany()
571  .HasForeignKey("StartedById")
572  .OnDelete(DeleteBehavior.Cascade);
573  });
574 
575  modelBuilder.Entity("Tgstation.Server.Host.Models.ReattachInformation", b =>
576  {
577  b.HasOne("Tgstation.Server.Host.Models.CompileJob", "CompileJob")
578  .WithMany()
579  .HasForeignKey("CompileJobId");
580  });
581 
582  modelBuilder.Entity("Tgstation.Server.Host.Models.RepositorySettings", b =>
583  {
584  b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
585  .WithOne("RepositorySettings")
586  .HasForeignKey("Tgstation.Server.Host.Models.RepositorySettings", "InstanceId")
587  .OnDelete(DeleteBehavior.Cascade);
588  });
589 
590  modelBuilder.Entity("Tgstation.Server.Host.Models.RevInfoTestMerge", b =>
591  {
592  b.HasOne("Tgstation.Server.Host.Models.RevisionInformation", "RevisionInformation")
593  .WithMany("ActiveTestMerges")
594  .HasForeignKey("RevisionInformationId")
595  .OnDelete(DeleteBehavior.Cascade);
596 
597  b.HasOne("Tgstation.Server.Host.Models.TestMerge", "TestMerge")
598  .WithMany("RevisonInformations")
599  .HasForeignKey("TestMergeId")
600  .OnDelete(DeleteBehavior.Cascade);
601  });
602 
603  modelBuilder.Entity("Tgstation.Server.Host.Models.RevisionInformation", b =>
604  {
605  b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
606  .WithMany("RevisionInformations")
607  .HasForeignKey("InstanceId")
608  .OnDelete(DeleteBehavior.Cascade);
609  });
610 
611  modelBuilder.Entity("Tgstation.Server.Host.Models.TestMerge", b =>
612  {
613  b.HasOne("Tgstation.Server.Host.Models.User", "MergedBy")
614  .WithMany("TestMerges")
615  .HasForeignKey("MergedById")
616  .OnDelete(DeleteBehavior.Restrict);
617 
618  b.HasOne("Tgstation.Server.Host.Models.RevisionInformation", "PrimaryRevisionInformation")
619  .WithOne("PrimaryTestMerge")
620  .HasForeignKey("Tgstation.Server.Host.Models.TestMerge", "PrimaryRevisionInformationId")
621  .OnDelete(DeleteBehavior.SetNull);
622  });
623 
624  modelBuilder.Entity("Tgstation.Server.Host.Models.User", b =>
625  {
626  b.HasOne("Tgstation.Server.Host.Models.User", "CreatedBy")
627  .WithMany("CreatedUsers")
628  .HasForeignKey("CreatedById");
629  });
630 
631  modelBuilder.Entity("Tgstation.Server.Host.Models.WatchdogReattachInformation", b =>
632  {
633  b.HasOne("Tgstation.Server.Host.Models.ReattachInformation", "Alpha")
634  .WithMany()
635  .HasForeignKey("AlphaId");
636 
637  b.HasOne("Tgstation.Server.Host.Models.ReattachInformation", "Bravo")
638  .WithMany()
639  .HasForeignKey("BravoId");
640  });
641 #pragma warning restore 612, 618
642  }
643  }
644 }
+ + + +
override void BuildTargetModel(ModelBuilder modelBuilder)
Builds the target model
+ +
+ + + + diff --git a/20180906143029___m_y_initial_create_8cs.html b/20180906143029___m_y_initial_create_8cs.html new file mode 100644 index 0000000000..95c2064edc --- /dev/null +++ b/20180906143029___m_y_initial_create_8cs.html @@ -0,0 +1,119 @@ + + + + + + +tgstation-server: src/Tgstation.Server.Host/Models/Migrations/20180906143029_MYInitialCreate.cs File Reference + + + + + + + + + + +
+
+ + + + + + + +
+
tgstation-server +
+
The /tg/station 13 server suite
+
+
+ + + + + + +
+
+ + +
+ +
+ + +
+
+
+Classes | +Namespaces
+
+
20180906143029_MYInitialCreate.cs File Reference
+
+
+ +

Go to the source code of this file.

+ + + + + +

+Classes

class  Tgstation.Server.Host.Models.Migrations.MYInitialCreate
 The initial database migration for MySQL/MariaDB More...
 
+ + + +

+Namespaces

namespace  Tgstation.Server.Host.Models.Migrations
 
+
+ + + + diff --git a/20180906143029___m_y_initial_create_8cs_source.html b/20180906143029___m_y_initial_create_8cs_source.html new file mode 100644 index 0000000000..44286ee308 --- /dev/null +++ b/20180906143029___m_y_initial_create_8cs_source.html @@ -0,0 +1,120 @@ + + + + + + +tgstation-server: src/Tgstation.Server.Host/Models/Migrations/20180906143029_MYInitialCreate.cs Source File + + + + + + + + + + +
+
+ + + + + + + +
+
tgstation-server +
+
The /tg/station 13 server suite
+
+
+ + + + + + +
+
+ + +
+ +
+ + +
+
+
+
20180906143029_MYInitialCreate.cs
+
+
+Go to the documentation of this file.
1 using System;
2 using Microsoft.EntityFrameworkCore.Metadata;
3 using Microsoft.EntityFrameworkCore.Migrations;
4 
5 namespace Tgstation.Server.Host.Models.Migrations
6 {
10  public partial class MYInitialCreate : Migration
11  {
16  protected override void Up(MigrationBuilder migrationBuilder)
17  {
18  migrationBuilder.CreateTable(
19  name: "Users",
20  columns: table => new
21  {
22  Id = table.Column<long>(nullable: false)
23  .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn),
24  Enabled = table.Column<bool>(nullable: false),
25  CreatedAt = table.Column<DateTimeOffset>(nullable: false),
26  SystemIdentifier = table.Column<string>(nullable: true),
27  Name = table.Column<string>(nullable: false),
28  AdministrationRights = table.Column<ulong>(nullable: false),
29  InstanceManagerRights = table.Column<ulong>(nullable: false),
30  PasswordHash = table.Column<string>(nullable: true),
31  CreatedById = table.Column<long>(nullable: true),
32  CanonicalName = table.Column<string>(nullable: false),
33  LastPasswordUpdate = table.Column<DateTimeOffset>(nullable: true)
34  },
35  constraints: table =>
36  {
37  table.PrimaryKey("PK_Users", x => x.Id);
38  table.ForeignKey(
39  name: "FK_Users_Users_CreatedById",
40  column: x => x.CreatedById,
41  principalTable: "Users",
42  principalColumn: "Id",
43  onDelete: ReferentialAction.Restrict);
44  });
45 
46  migrationBuilder.CreateTable(
47  name: "ChatChannels",
48  columns: table => new
49  {
50  IrcChannel = table.Column<string>(nullable: true),
51  DiscordChannelId = table.Column<ulong>(nullable: true),
52  IsAdminChannel = table.Column<bool>(nullable: false),
53  IsWatchdogChannel = table.Column<bool>(nullable: false),
54  IsUpdatesChannel = table.Column<bool>(nullable: false),
55  Tag = table.Column<string>(nullable: true),
56  Id = table.Column<long>(nullable: false)
57  .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn),
58  ChatSettingsId = table.Column<long>(nullable: false)
59  },
60  constraints: table =>
61  {
62  table.PrimaryKey("PK_ChatChannels", x => x.Id);
63  });
64 
65  migrationBuilder.CreateTable(
66  name: "ReattachInformations",
67  columns: table => new
68  {
69  ChatCommandsJson = table.Column<string>(nullable: false),
70  ChatChannelsJson = table.Column<string>(nullable: false),
71  ServerCommandsJson = table.Column<string>(nullable: false),
72  AccessIdentifier = table.Column<string>(nullable: false),
73  ProcessId = table.Column<int>(nullable: false),
74  IsPrimary = table.Column<bool>(nullable: false),
75  Port = table.Column<ushort>(nullable: false),
76  RebootState = table.Column<int>(nullable: false),
77  Id = table.Column<long>(nullable: false)
78  .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn),
79  CompileJobId = table.Column<long>(nullable: true)
80  },
81  constraints: table =>
82  {
83  table.PrimaryKey("PK_ReattachInformations", x => x.Id);
84  });
85 
86  migrationBuilder.CreateTable(
87  name: "WatchdogReattachInformations",
88  columns: table => new
89  {
90  AlphaIsActive = table.Column<bool>(nullable: false),
91  Id = table.Column<long>(nullable: false)
92  .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn),
93  AlphaId = table.Column<long>(nullable: true),
94  BravoId = table.Column<long>(nullable: true)
95  },
96  constraints: table =>
97  {
98  table.PrimaryKey("PK_WatchdogReattachInformations", x => x.Id);
99  table.ForeignKey(
100  name: "FK_WatchdogReattachInformations_ReattachInformations_AlphaId",
101  column: x => x.AlphaId,
102  principalTable: "ReattachInformations",
103  principalColumn: "Id",
104  onDelete: ReferentialAction.Restrict);
105  table.ForeignKey(
106  name: "FK_WatchdogReattachInformations_ReattachInformations_BravoId",
107  column: x => x.BravoId,
108  principalTable: "ReattachInformations",
109  principalColumn: "Id",
110  onDelete: ReferentialAction.Restrict);
111  });
112 
113  migrationBuilder.CreateTable(
114  name: "Instances",
115  columns: table => new
116  {
117  Id = table.Column<long>(nullable: false)
118  .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn),
119  Name = table.Column<string>(nullable: false),
120  Path = table.Column<string>(nullable: false),
121  Online = table.Column<bool>(nullable: false),
122  ConfigurationType = table.Column<int>(nullable: false),
123  AutoUpdateInterval = table.Column<uint>(nullable: false),
124  WatchdogReattachInformationId = table.Column<long>(nullable: true)
125  },
126  constraints: table =>
127  {
128  table.PrimaryKey("PK_Instances", x => x.Id);
129  table.ForeignKey(
130  name: "FK_Instances_WatchdogReattachInformations_WatchdogReattachInfor~",
131  column: x => x.WatchdogReattachInformationId,
132  principalTable: "WatchdogReattachInformations",
133  principalColumn: "Id",
134  onDelete: ReferentialAction.Restrict);
135  });
136 
137  migrationBuilder.CreateTable(
138  name: "ChatBots",
139  columns: table => new
140  {
141  Id = table.Column<long>(nullable: false)
142  .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn),
143  Name = table.Column<string>(nullable: false),
144  Enabled = table.Column<bool>(nullable: true),
145  Provider = table.Column<int>(nullable: true),
146  ConnectionString = table.Column<string>(nullable: false),
147  InstanceId = table.Column<long>(nullable: false)
148  },
149  constraints: table =>
150  {
151  table.PrimaryKey("PK_ChatBots", x => x.Id);
152  table.ForeignKey(
153  name: "FK_ChatBots_Instances_InstanceId",
154  column: x => x.InstanceId,
155  principalTable: "Instances",
156  principalColumn: "Id",
157  onDelete: ReferentialAction.Cascade);
158  });
159 
160  migrationBuilder.CreateTable(
161  name: "DreamDaemonSettings",
162  columns: table => new
163  {
164  AllowWebClient = table.Column<bool>(nullable: false),
165  SecurityLevel = table.Column<int>(nullable: false),
166  PrimaryPort = table.Column<ushort>(nullable: false),
167  SecondaryPort = table.Column<ushort>(nullable: false),
168  StartupTimeout = table.Column<uint>(nullable: false),
169  AutoStart = table.Column<bool>(nullable: false),
170  SoftRestart = table.Column<bool>(nullable: false),
171  SoftShutdown = table.Column<bool>(nullable: false),
172  Id = table.Column<long>(nullable: false)
173  .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn),
174  ProcessId = table.Column<int>(nullable: true),
175  AccessToken = table.Column<string>(nullable: true),
176  InstanceId = table.Column<long>(nullable: false)
177  },
178  constraints: table =>
179  {
180  table.PrimaryKey("PK_DreamDaemonSettings", x => x.Id);
181  table.ForeignKey(
182  name: "FK_DreamDaemonSettings_Instances_InstanceId",
183  column: x => x.InstanceId,
184  principalTable: "Instances",
185  principalColumn: "Id",
186  onDelete: ReferentialAction.Cascade);
187  });
188 
189  migrationBuilder.CreateTable(
190  name: "DreamMakerSettings",
191  columns: table => new
192  {
193  ProjectName = table.Column<string>(nullable: true),
194  ApiValidationPort = table.Column<ushort>(nullable: false),
195  Id = table.Column<long>(nullable: false)
196  .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn),
197  InstanceId = table.Column<long>(nullable: false)
198  },
199  constraints: table =>
200  {
201  table.PrimaryKey("PK_DreamMakerSettings", x => x.Id);
202  table.ForeignKey(
203  name: "FK_DreamMakerSettings_Instances_InstanceId",
204  column: x => x.InstanceId,
205  principalTable: "Instances",
206  principalColumn: "Id",
207  onDelete: ReferentialAction.Cascade);
208  });
209 
210  migrationBuilder.CreateTable(
211  name: "InstanceUsers",
212  columns: table => new
213  {
214  UserId = table.Column<long>(nullable: false),
215  InstanceUserRights = table.Column<ulong>(nullable: false),
216  ByondRights = table.Column<ulong>(nullable: false),
217  DreamDaemonRights = table.Column<ulong>(nullable: false),
218  DreamMakerRights = table.Column<ulong>(nullable: false),
219  RepositoryRights = table.Column<ulong>(nullable: false),
220  ChatBotRights = table.Column<ulong>(nullable: false),
221  ConfigurationRights = table.Column<ulong>(nullable: false),
222  Id = table.Column<long>(nullable: false)
223  .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn),
224  InstanceId = table.Column<long>(nullable: false)
225  },
226  constraints: table =>
227  {
228  table.PrimaryKey("PK_InstanceUsers", x => x.Id);
229  table.ForeignKey(
230  name: "FK_InstanceUsers_Instances_InstanceId",
231  column: x => x.InstanceId,
232  principalTable: "Instances",
233  principalColumn: "Id",
234  onDelete: ReferentialAction.Cascade);
235  table.ForeignKey(
236  name: "FK_InstanceUsers_Users_UserId",
237  column: x => x.UserId,
238  principalTable: "Users",
239  principalColumn: "Id",
240  onDelete: ReferentialAction.Cascade);
241  });
242 
243  migrationBuilder.CreateTable(
244  name: "Jobs",
245  columns: table => new
246  {
247  Id = table.Column<long>(nullable: false)
248  .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn),
249  Description = table.Column<string>(nullable: false),
250  ExceptionDetails = table.Column<string>(nullable: true),
251  StartedAt = table.Column<DateTimeOffset>(nullable: false),
252  StoppedAt = table.Column<DateTimeOffset>(nullable: true),
253  Cancelled = table.Column<bool>(nullable: false),
254  CancelRightsType = table.Column<ulong>(nullable: true),
255  CancelRight = table.Column<ulong>(nullable: true),
256  StartedById = table.Column<long>(nullable: false),
257  CancelledById = table.Column<long>(nullable: true),
258  InstanceId = table.Column<long>(nullable: false)
259  },
260  constraints: table =>
261  {
262  table.PrimaryKey("PK_Jobs", x => x.Id);
263  table.ForeignKey(
264  name: "FK_Jobs_Users_CancelledById",
265  column: x => x.CancelledById,
266  principalTable: "Users",
267  principalColumn: "Id",
268  onDelete: ReferentialAction.Restrict);
269  table.ForeignKey(
270  name: "FK_Jobs_Instances_InstanceId",
271  column: x => x.InstanceId,
272  principalTable: "Instances",
273  principalColumn: "Id",
274  onDelete: ReferentialAction.Cascade);
275  table.ForeignKey(
276  name: "FK_Jobs_Users_StartedById",
277  column: x => x.StartedById,
278  principalTable: "Users",
279  principalColumn: "Id",
280  onDelete: ReferentialAction.Cascade);
281  });
282 
283  migrationBuilder.CreateTable(
284  name: "RepositorySettings",
285  columns: table => new
286  {
287  CommitterName = table.Column<string>(nullable: false),
288  CommitterEmail = table.Column<string>(nullable: false),
289  AccessUser = table.Column<string>(nullable: true),
290  AccessToken = table.Column<string>(nullable: true),
291  PushTestMergeCommits = table.Column<bool>(nullable: false),
292  ShowTestMergeCommitters = table.Column<bool>(nullable: false),
293  AutoUpdatesKeepTestMerges = table.Column<bool>(nullable: false),
294  AutoUpdatesSynchronize = table.Column<bool>(nullable: false),
295  Id = table.Column<long>(nullable: false)
296  .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn),
297  InstanceId = table.Column<long>(nullable: false)
298  },
299  constraints: table =>
300  {
301  table.PrimaryKey("PK_RepositorySettings", x => x.Id);
302  table.ForeignKey(
303  name: "FK_RepositorySettings_Instances_InstanceId",
304  column: x => x.InstanceId,
305  principalTable: "Instances",
306  principalColumn: "Id",
307  onDelete: ReferentialAction.Cascade);
308  });
309 
310  migrationBuilder.CreateTable(
311  name: "RevisionInformations",
312  columns: table => new
313  {
314  CommitSha = table.Column<string>(maxLength: 40, nullable: false),
315  OriginCommitSha = table.Column<string>(maxLength: 40, nullable: false),
316  Id = table.Column<long>(nullable: false)
317  .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn),
318  InstanceId = table.Column<long>(nullable: false)
319  },
320  constraints: table =>
321  {
322  table.PrimaryKey("PK_RevisionInformations", x => x.Id);
323  table.ForeignKey(
324  name: "FK_RevisionInformations_Instances_InstanceId",
325  column: x => x.InstanceId,
326  principalTable: "Instances",
327  principalColumn: "Id",
328  onDelete: ReferentialAction.Cascade);
329  });
330 
331  migrationBuilder.CreateTable(
332  name: "CompileJobs",
333  columns: table => new
334  {
335  Id = table.Column<long>(nullable: false)
336  .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn),
337  DmeName = table.Column<string>(nullable: true),
338  Output = table.Column<string>(nullable: true),
339  DirectoryName = table.Column<Guid>(nullable: true),
340  JobId = table.Column<long>(nullable: true),
341  RevisionInformationId = table.Column<long>(nullable: false),
342  ByondVersion = table.Column<string>(nullable: false)
343  },
344  constraints: table =>
345  {
346  table.PrimaryKey("PK_CompileJobs", x => x.Id);
347  table.ForeignKey(
348  name: "FK_CompileJobs_Jobs_JobId",
349  column: x => x.JobId,
350  principalTable: "Jobs",
351  principalColumn: "Id",
352  onDelete: ReferentialAction.Restrict);
353  table.ForeignKey(
354  name: "FK_CompileJobs_RevisionInformations_RevisionInformationId",
355  column: x => x.RevisionInformationId,
356  principalTable: "RevisionInformations",
357  principalColumn: "Id",
358  onDelete: ReferentialAction.Cascade);
359  });
360 
361  migrationBuilder.CreateTable(
362  name: "TestMerges",
363  columns: table => new
364  {
365  Number = table.Column<int>(nullable: false),
366  PullRequestRevision = table.Column<string>(nullable: false),
367  Comment = table.Column<string>(nullable: true),
368  TitleAtMerge = table.Column<string>(nullable: false),
369  BodyAtMerge = table.Column<string>(nullable: false),
370  Url = table.Column<string>(nullable: false),
371  Author = table.Column<string>(nullable: false),
372  Id = table.Column<long>(nullable: false)
373  .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn),
374  MergedAt = table.Column<DateTimeOffset>(nullable: false),
375  MergedById = table.Column<long>(nullable: false),
376  PrimaryRevisionInformationId = table.Column<long>(nullable: true)
377  },
378  constraints: table =>
379  {
380  table.PrimaryKey("PK_TestMerges", x => x.Id);
381  table.ForeignKey(
382  name: "FK_TestMerges_Users_MergedById",
383  column: x => x.MergedById,
384  principalTable: "Users",
385  principalColumn: "Id",
386  onDelete: ReferentialAction.Restrict);
387  table.ForeignKey(
388  name: "FK_TestMerges_RevisionInformations_PrimaryRevisionInformationId",
389  column: x => x.PrimaryRevisionInformationId,
390  principalTable: "RevisionInformations",
391  principalColumn: "Id",
392  onDelete: ReferentialAction.SetNull);
393  });
394 
395  migrationBuilder.CreateTable(
396  name: "RevInfoTestMerges",
397  columns: table => new
398  {
399  Id = table.Column<long>(nullable: false)
400  .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn),
401  TestMergeId = table.Column<long>(nullable: false),
402  RevisionInformationId = table.Column<long>(nullable: false)
403  },
404  constraints: table =>
405  {
406  table.PrimaryKey("PK_RevInfoTestMerges", x => x.Id);
407  table.ForeignKey(
408  name: "FK_RevInfoTestMerges_RevisionInformations_RevisionInformationId",
409  column: x => x.RevisionInformationId,
410  principalTable: "RevisionInformations",
411  principalColumn: "Id",
412  onDelete: ReferentialAction.Cascade);
413  table.ForeignKey(
414  name: "FK_RevInfoTestMerges_TestMerges_TestMergeId",
415  column: x => x.TestMergeId,
416  principalTable: "TestMerges",
417  principalColumn: "Id",
418  onDelete: ReferentialAction.Cascade);
419  });
420 
421  migrationBuilder.CreateIndex(
422  name: "IX_ChatBots_InstanceId",
423  table: "ChatBots",
424  column: "InstanceId");
425 
426  migrationBuilder.CreateIndex(
427  name: "IX_ChatBots_Name",
428  table: "ChatBots",
429  column: "Name",
430  unique: true);
431 
432  migrationBuilder.CreateIndex(
433  name: "IX_ChatChannels_ChatSettingsId_DiscordChannelId",
434  table: "ChatChannels",
435  columns: new[] { "ChatSettingsId", "DiscordChannelId" },
436  unique: true);
437 
438  migrationBuilder.CreateIndex(
439  name: "IX_ChatChannels_ChatSettingsId_IrcChannel",
440  table: "ChatChannels",
441  columns: new[] { "ChatSettingsId", "IrcChannel" },
442  unique: true);
443 
444  migrationBuilder.CreateIndex(
445  name: "IX_CompileJobs_DirectoryName",
446  table: "CompileJobs",
447  column: "DirectoryName");
448 
449  migrationBuilder.CreateIndex(
450  name: "IX_CompileJobs_JobId",
451  table: "CompileJobs",
452  column: "JobId");
453 
454  migrationBuilder.CreateIndex(
455  name: "IX_CompileJobs_RevisionInformationId",
456  table: "CompileJobs",
457  column: "RevisionInformationId");
458 
459  migrationBuilder.CreateIndex(
460  name: "IX_DreamDaemonSettings_InstanceId",
461  table: "DreamDaemonSettings",
462  column: "InstanceId",
463  unique: true);
464 
465  migrationBuilder.CreateIndex(
466  name: "IX_DreamMakerSettings_InstanceId",
467  table: "DreamMakerSettings",
468  column: "InstanceId",
469  unique: true);
470 
471  migrationBuilder.CreateIndex(
472  name: "IX_Instances_Path",
473  table: "Instances",
474  column: "Path",
475  unique: true);
476 
477  migrationBuilder.CreateIndex(
478  name: "IX_Instances_WatchdogReattachInformationId",
479  table: "Instances",
480  column: "WatchdogReattachInformationId");
481 
482  migrationBuilder.CreateIndex(
483  name: "IX_InstanceUsers_InstanceId",
484  table: "InstanceUsers",
485  column: "InstanceId");
486 
487  migrationBuilder.CreateIndex(
488  name: "IX_InstanceUsers_UserId_InstanceId",
489  table: "InstanceUsers",
490  columns: new[] { "UserId", "InstanceId" },
491  unique: true);
492 
493  migrationBuilder.CreateIndex(
494  name: "IX_Jobs_CancelledById",
495  table: "Jobs",
496  column: "CancelledById");
497 
498  migrationBuilder.CreateIndex(
499  name: "IX_Jobs_InstanceId",
500  table: "Jobs",
501  column: "InstanceId");
502 
503  migrationBuilder.CreateIndex(
504  name: "IX_Jobs_StartedById",
505  table: "Jobs",
506  column: "StartedById");
507 
508  migrationBuilder.CreateIndex(
509  name: "IX_ReattachInformations_CompileJobId",
510  table: "ReattachInformations",
511  column: "CompileJobId");
512 
513  migrationBuilder.CreateIndex(
514  name: "IX_RepositorySettings_InstanceId",
515  table: "RepositorySettings",
516  column: "InstanceId",
517  unique: true);
518 
519  migrationBuilder.CreateIndex(
520  name: "IX_RevInfoTestMerges_RevisionInformationId",
521  table: "RevInfoTestMerges",
522  column: "RevisionInformationId");
523 
524  migrationBuilder.CreateIndex(
525  name: "IX_RevInfoTestMerges_TestMergeId",
526  table: "RevInfoTestMerges",
527  column: "TestMergeId");
528 
529  migrationBuilder.CreateIndex(
530  name: "IX_RevisionInformations_CommitSha",
531  table: "RevisionInformations",
532  column: "CommitSha",
533  unique: true);
534 
535  migrationBuilder.CreateIndex(
536  name: "IX_RevisionInformations_InstanceId",
537  table: "RevisionInformations",
538  column: "InstanceId");
539 
540  migrationBuilder.CreateIndex(
541  name: "IX_TestMerges_MergedById",
542  table: "TestMerges",
543  column: "MergedById");
544 
545  migrationBuilder.CreateIndex(
546  name: "IX_TestMerges_PrimaryRevisionInformationId",
547  table: "TestMerges",
548  column: "PrimaryRevisionInformationId",
549  unique: true);
550 
551  migrationBuilder.CreateIndex(
552  name: "IX_Users_CanonicalName",
553  table: "Users",
554  column: "CanonicalName",
555  unique: true);
556 
557  migrationBuilder.CreateIndex(
558  name: "IX_Users_CreatedById",
559  table: "Users",
560  column: "CreatedById");
561 
562  migrationBuilder.CreateIndex(
563  name: "IX_WatchdogReattachInformations_AlphaId",
564  table: "WatchdogReattachInformations",
565  column: "AlphaId");
566 
567  migrationBuilder.CreateIndex(
568  name: "IX_WatchdogReattachInformations_BravoId",
569  table: "WatchdogReattachInformations",
570  column: "BravoId");
571 
572  migrationBuilder.AddForeignKey(
573  name: "FK_ChatChannels_ChatBots_ChatSettingsId",
574  table: "ChatChannels",
575  column: "ChatSettingsId",
576  principalTable: "ChatBots",
577  principalColumn: "Id",
578  onDelete: ReferentialAction.Cascade);
579 
580  migrationBuilder.AddForeignKey(
581  name: "FK_ReattachInformations_CompileJobs_CompileJobId",
582  table: "ReattachInformations",
583  column: "CompileJobId",
584  principalTable: "CompileJobs",
585  principalColumn: "Id",
586  onDelete: ReferentialAction.Restrict);
587  }
588 
593  protected override void Down(MigrationBuilder migrationBuilder)
594  {
595  migrationBuilder.DropForeignKey(
596  name: "FK_Jobs_Instances_InstanceId",
597  table: "Jobs");
598 
599  migrationBuilder.DropForeignKey(
600  name: "FK_RevisionInformations_Instances_InstanceId",
601  table: "RevisionInformations");
602 
603  migrationBuilder.DropTable(
604  name: "ChatChannels");
605 
606  migrationBuilder.DropTable(
607  name: "DreamDaemonSettings");
608 
609  migrationBuilder.DropTable(
610  name: "DreamMakerSettings");
611 
612  migrationBuilder.DropTable(
613  name: "InstanceUsers");
614 
615  migrationBuilder.DropTable(
616  name: "RepositorySettings");
617 
618  migrationBuilder.DropTable(
619  name: "RevInfoTestMerges");
620 
621  migrationBuilder.DropTable(
622  name: "ChatBots");
623 
624  migrationBuilder.DropTable(
625  name: "TestMerges");
626 
627  migrationBuilder.DropTable(
628  name: "Instances");
629 
630  migrationBuilder.DropTable(
631  name: "WatchdogReattachInformations");
632 
633  migrationBuilder.DropTable(
634  name: "ReattachInformations");
635 
636  migrationBuilder.DropTable(
637  name: "CompileJobs");
638 
639  migrationBuilder.DropTable(
640  name: "Jobs");
641 
642  migrationBuilder.DropTable(
643  name: "RevisionInformations");
644 
645  migrationBuilder.DropTable(
646  name: "Users");
647  }
648  }
649 }
ConfigurationRights
Rights for Models.ConfigurationFile
+
ByondRights
Rights for Models.Byond
Definition: ByondRights.cs:9
+
InstanceUserRights
Rights for an Models.Instance
+ +
AdministrationRights
Rights for Models.Administration
+
ConfigurationType
The type of configuration allowed on an Instance
+ +
DreamDaemonRights
Rights for Models.DreamDaemon
+
InstanceManagerRights
Rights for managing Models.Instances
+
DreamMakerRights
Rights for Models.DreamMaker
+ +
override void Up(MigrationBuilder migrationBuilder)
Applies the migration
+
RepositoryRights
Rights for a Models.Repository
+
The initial database migration for MySQL/MariaDB
+
ChatBotRights
Rights for Models.ChatBot
Definition: ChatBotRights.cs:9
+
override void Down(MigrationBuilder migrationBuilder)
Unapplies the migration
+
RebootState
Represents the action to take when /world/Reboot() is called
Definition: RebootState.cs:6
+ +
+ + + + diff --git a/_administration_test_8cs_source.html b/_administration_test_8cs_source.html index 58a503316c..85229ef751 100644 --- a/_administration_test_8cs_source.html +++ b/_administration_test_8cs_source.html @@ -91,15 +91,15 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
AdministrationTest.cs
-Go to the documentation of this file.
1 using Microsoft.VisualStudio.TestTools.UnitTesting;
2 using System;
3 using System.Runtime.InteropServices;
4 using System.Threading.Tasks;
6 
7 namespace Tgstation.Server.Tests
8 {
9  sealed class AdministrationTest
10  {
12 
14  {
15  this.client = client ?? throw new ArgumentNullException(nameof(client));
16  }
17 
18  public async Task Run()
19  {
20  await TestRead().ConfigureAwait(false);
21  }
22 
23  async Task TestRead()
24  {
25  var model = await client.Read(default).ConfigureAwait(false);
26  Assert.AreEqual(RuntimeInformation.IsOSPlatform(OSPlatform.Windows), model.WindowsHost);
27 
28  //uhh not much else to do
29  }
30  }
31 }
+Go to the documentation of this file.
1 using Microsoft.VisualStudio.TestTools.UnitTesting;
2 using System;
3 using System.Runtime.InteropServices;
4 using System.Threading;
5 using System.Threading.Tasks;
7 
8 namespace Tgstation.Server.Tests
9 {
10  sealed class AdministrationTest
11  {
13 
15  {
16  this.client = client ?? throw new ArgumentNullException(nameof(client));
17  }
18 
19  public async Task Run(CancellationToken cancellationToken)
20  {
21  await TestRead(cancellationToken).ConfigureAwait(false);
22  }
23 
24  async Task TestRead(CancellationToken cancellationToken)
25  {
26  var model = await client.Read(cancellationToken).ConfigureAwait(false);
27  Assert.AreEqual(RuntimeInformation.IsOSPlatform(OSPlatform.Windows), model.WindowsHost);
28 
29  //uhh not much else to do
30  }
31  }
32 }
async Task Run(CancellationToken cancellationToken)
+ +
async Task TestRead(CancellationToken cancellationToken)
- - -
AdministrationTest(IAdministrationClient client)
- + +
AdministrationTest(IAdministrationClient client)
-
readonly IAdministrationClient client
+
readonly IAdministrationClient client
diff --git a/_api_client_8cs_source.html b/_api_client_8cs_source.html index 6057ccdcf1..2fdf736790 100644 --- a/_api_client_8cs_source.html +++ b/_api_client_8cs_source.html @@ -91,12 +91,12 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
ApiClient.cs
-Go to the documentation of this file.
1 using Newtonsoft.Json;
2 using Newtonsoft.Json.Serialization;
3 using System;
4 using System.Collections.Generic;
5 using System.Linq;
6 using System.Net;
7 using System.Net.Http;
8 using System.Text;
9 using System.Threading;
10 using System.Threading.Tasks;
11 using Tgstation.Server.Api;
13 
14 namespace Tgstation.Server.Client
15 {
17  sealed class ApiClient : IApiClient
18  {
20  public Uri Url { get; }
21 
23  public ApiHeaders Headers { get; }
24 
26  public TimeSpan Timeout
27  {
28  get => httpClient.Timeout;
29  set => httpClient.Timeout = value;
30  }
31 
35  readonly HttpClient httpClient;
36 
40  readonly List<IRequestLogger> requestLoggers;
41 
47  public ApiClient(Uri url, ApiHeaders apiHeaders)
48  {
49  Url = url ?? throw new ArgumentNullException(nameof(url));
50  Headers = apiHeaders ?? throw new ArgumentNullException(nameof(apiHeaders));
51 
52  httpClient = new HttpClient();
53  requestLoggers = new List<IRequestLogger>();
54  }
55 
57  public void Dispose() => httpClient.Dispose();
58 
68  async Task<TResult> RunRequest<TResult>(string route, object body, HttpMethod method, long? instanceId, CancellationToken cancellationToken)
69  {
70  if (route == null)
71  throw new ArgumentNullException(nameof(route));
72  if (method == null)
73  throw new ArgumentNullException(nameof(method));
74  if (body == null && (method == HttpMethod.Post || method == HttpMethod.Put))
75  throw new InvalidOperationException("Body cannot be null for POST or PUT!");
76 
77  var fullUri = new Uri(Url, route);
78 
79  var message = new HttpRequestMessage(method, fullUri);
80 
81  var serializerSettings = new JsonSerializerSettings
82  {
83  ContractResolver = new CamelCasePropertyNamesContractResolver()
84  };
85 
86  if (body != null)
87  message.Content = new StringContent(JsonConvert.SerializeObject(body, serializerSettings), Encoding.UTF8, ApiHeaders.ApplicationJson);
88 
89  Headers.SetRequestHeaders(message.Headers, instanceId);
90 
91  await Task.WhenAll(requestLoggers.Select(x => x.LogRequest(message, cancellationToken))).ConfigureAwait(false);
92 
93  var response = await httpClient.SendAsync(message, cancellationToken).ConfigureAwait(false);
94 
95  await Task.WhenAll(requestLoggers.Select(x => x.LogResponse(response, cancellationToken))).ConfigureAwait(false);
96 
97  var json = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
98 
99  if (!response.IsSuccessStatusCode)
100  {
101  ErrorMessage errorMessage = null;
102  try
103  {
104  //check if json serializes to an error message
105  errorMessage = JsonConvert.DeserializeObject<ErrorMessage>(json, serializerSettings);
106  }
107  catch (JsonException) { }
108 
109  const string BadSpecExtension = " This is not part of TGS4 communication specification and should be reported if it was returned from a TGS4 server!";
110 
111  switch (response.StatusCode)
112  {
113  case HttpStatusCode.UpgradeRequired:
114  throw new ApiMismatchException(errorMessage ?? new ErrorMessage
115  {
116  Message = "API Mismatch but no current API version provided!" + BadSpecExtension,
117  SeverApiVersion = null
118  });
119  case HttpStatusCode.Unauthorized:
120  throw new UnauthorizedException();
121  case HttpStatusCode.RequestTimeout:
122  throw new RequestTimeoutException();
123  case HttpStatusCode.Forbidden:
125  case HttpStatusCode.ServiceUnavailable:
126  throw new ServiceUnavailableException();
127  case HttpStatusCode.Gone:
128  errorMessage = errorMessage ?? new ErrorMessage
129  {
130  Message = "The requested resource could not be found!",
131  SeverApiVersion = null
132  };
133  goto case HttpStatusCode.Conflict;
134  case HttpStatusCode.NotFound: //our fault somehow
135  errorMessage = errorMessage ?? new ErrorMessage
136  {
137  Message = "This is not a valid route!" + BadSpecExtension,
138  SeverApiVersion = null
139  };
140  goto case HttpStatusCode.Conflict;
141  case HttpStatusCode.Conflict:
142  throw new ConflictException(errorMessage ?? new ErrorMessage
143  {
144  Message = "An undescribed conflict occurred!" + BadSpecExtension,
145  SeverApiVersion = null
146  }, response.StatusCode);
147  case HttpStatusCode.NotImplemented:
148  case (HttpStatusCode)422: //unprocessable entity
149  throw new MethodNotSupportedException();
150  case HttpStatusCode.InternalServerError:
151  //response
152  throw new ServerErrorException(json); //json is html
153  case (HttpStatusCode)429: //rate limited
154  response.Headers.TryGetValues("Retry-After", out var values);
155  throw new RateLimitException(values?.FirstOrDefault());
156  default:
157  throw new ApiConflictException(errorMessage, response.StatusCode);
158  }
159  }
160 
161  if (String.IsNullOrWhiteSpace(json))
162  json = JsonConvert.SerializeObject(new object());
163 
164  return JsonConvert.DeserializeObject<TResult>(json, serializerSettings);
165  }
166 
168  public Task<TResult> Create<TResult>(string route, CancellationToken cancellationToken) => RunRequest<TResult>(route, new object(), HttpMethod.Put, null, cancellationToken);
169 
171  public Task<TResult> Read<TResult>(string route, CancellationToken cancellationToken) => RunRequest<TResult>(route, null, HttpMethod.Get, null, cancellationToken);
172 
174  public Task<TResult> Update<TResult>(string route, CancellationToken cancellationToken) => RunRequest<TResult>(route, new object(), HttpMethod.Post, null, cancellationToken);
175 
177  public Task<TResult> Update<TBody, TResult>(string route, TBody body, CancellationToken cancellationToken) => RunRequest<TResult>(route, body, HttpMethod.Post, null, cancellationToken);
178 
180  public Task Update<TBody>(string route, TBody body, CancellationToken cancellationToken) => RunRequest<object>(route, body, HttpMethod.Post, null, cancellationToken);
181 
183  public Task<TResult> Create<TBody, TResult>(string route, TBody body, CancellationToken cancellationToken) => RunRequest<TResult>(route, body, HttpMethod.Put, null, cancellationToken);
184 
186  public Task Delete(string route, CancellationToken cancellationToken) => RunRequest<object>(route, null, HttpMethod.Delete, null, cancellationToken);
187 
189  public Task<TResult> Create<TBody, TResult>(string route, TBody body, long instanceId, CancellationToken cancellationToken) => RunRequest<TResult>(route, body, HttpMethod.Put, instanceId, cancellationToken);
190 
192  public Task<TResult> Read<TResult>(string route, long instanceId, CancellationToken cancellationToken) => RunRequest<TResult>(route, null, HttpMethod.Get, instanceId, cancellationToken);
193 
195  public Task<TResult> Update<TBody, TResult>(string route, TBody body, long instanceId, CancellationToken cancellationToken) => RunRequest<TResult>(route, body, HttpMethod.Post, instanceId, cancellationToken);
196 
198  public Task Delete(string route, long instanceId, CancellationToken cancellationToken) => RunRequest<object>(route, null, HttpMethod.Delete, instanceId, cancellationToken);
199 
201  public Task<TResult> Delete<TResult>(string route, long instanceId, CancellationToken cancellationToken) => RunRequest<TResult>(route, null, HttpMethod.Delete, instanceId, cancellationToken);
202 
204  public Task<TResult> Create<TResult>(string route, long instanceId, CancellationToken cancellationToken) => RunRequest<TResult>(route, new object(), HttpMethod.Put, instanceId, cancellationToken);
205 
207  public void AddRequestLogger(IRequestLogger requestLogger) => requestLoggers.Add(requestLogger ?? throw new ArgumentNullException(nameof(requestLogger)));
208  }
209 }
+Go to the documentation of this file.
1 using Newtonsoft.Json;
2 using Newtonsoft.Json.Serialization;
3 using System;
4 using System.Collections.Generic;
5 using System.Linq;
6 using System.Net;
7 using System.Net.Http;
8 using System.Text;
9 using System.Threading;
10 using System.Threading.Tasks;
11 using Tgstation.Server.Api;
13 
14 namespace Tgstation.Server.Client
15 {
17  sealed class ApiClient : IApiClient
18  {
20  public Uri Url { get; }
21 
23  public ApiHeaders Headers { get; }
24 
26  public TimeSpan Timeout
27  {
28  get => httpClient.Timeout;
29  set => httpClient.Timeout = value;
30  }
31 
35  readonly HttpClient httpClient;
36 
40  readonly List<IRequestLogger> requestLoggers;
41 
47  public ApiClient(Uri url, ApiHeaders apiHeaders)
48  {
49  Url = url ?? throw new ArgumentNullException(nameof(url));
50  Headers = apiHeaders ?? throw new ArgumentNullException(nameof(apiHeaders));
51 
52  httpClient = new HttpClient();
53  requestLoggers = new List<IRequestLogger>();
54  }
55 
57  public void Dispose() => httpClient.Dispose();
58 
68  async Task<TResult> RunRequest<TResult>(string route, object body, HttpMethod method, long? instanceId, CancellationToken cancellationToken)
69  {
70  if (route == null)
71  throw new ArgumentNullException(nameof(route));
72  if (method == null)
73  throw new ArgumentNullException(nameof(method));
74  if (body == null && (method == HttpMethod.Post || method == HttpMethod.Put))
75  throw new InvalidOperationException("Body cannot be null for POST or PUT!");
76 
77  var fullUri = new Uri(Url, route);
78 
79  var message = new HttpRequestMessage(method, fullUri);
80 
81  var serializerSettings = new JsonSerializerSettings
82  {
83  ContractResolver = new CamelCasePropertyNamesContractResolver()
84  };
85 
86  if (body != null)
87  message.Content = new StringContent(JsonConvert.SerializeObject(body, serializerSettings), Encoding.UTF8, ApiHeaders.ApplicationJson);
88 
89  Headers.SetRequestHeaders(message.Headers, instanceId);
90 
91  await Task.WhenAll(requestLoggers.Select(x => x.LogRequest(message, cancellationToken))).ConfigureAwait(false);
92 
93  var response = await httpClient.SendAsync(message, cancellationToken).ConfigureAwait(false);
94 
95  await Task.WhenAll(requestLoggers.Select(x => x.LogResponse(response, cancellationToken))).ConfigureAwait(false);
96 
97  var json = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
98 
99  if (!response.IsSuccessStatusCode)
100  {
101  ErrorMessage errorMessage = null;
102  try
103  {
104  //check if json serializes to an error message
105  errorMessage = JsonConvert.DeserializeObject<ErrorMessage>(json, serializerSettings);
106  }
107  catch (JsonException) { }
108 
109  const string BadSpecExtension = " This is not part of TGS4 communication specification and should be reported if it was returned from a TGS4 server!";
110 
111  switch (response.StatusCode)
112  {
113  case HttpStatusCode.UpgradeRequired:
114  throw new ApiMismatchException(errorMessage ?? new ErrorMessage
115  {
116  Message = "API Mismatch but no current API version provided!" + BadSpecExtension,
117  SeverApiVersion = null
118  });
119  case HttpStatusCode.Unauthorized:
120  throw new UnauthorizedException();
121  case HttpStatusCode.RequestTimeout:
122  throw new RequestTimeoutException();
123  case HttpStatusCode.Forbidden:
125  case HttpStatusCode.ServiceUnavailable:
126  throw new ServiceUnavailableException();
127  case HttpStatusCode.Gone:
128  errorMessage = errorMessage ?? new ErrorMessage
129  {
130  Message = "The requested resource could not be found!",
131  SeverApiVersion = null
132  };
133  goto case HttpStatusCode.Conflict;
134  case HttpStatusCode.NotFound: //our fault somehow
135  errorMessage = errorMessage ?? new ErrorMessage
136  {
137  Message = "This is not a valid route!" + BadSpecExtension,
138  SeverApiVersion = null
139  };
140  goto case HttpStatusCode.Conflict;
141  case HttpStatusCode.Conflict:
142  throw new ConflictException(errorMessage ?? new ErrorMessage
143  {
144  Message = "An undescribed conflict occurred!" + BadSpecExtension,
145  SeverApiVersion = null
146  }, response.StatusCode);
147  case HttpStatusCode.NotImplemented:
148  case (HttpStatusCode)422: //unprocessable entity
149  throw new MethodNotSupportedException();
150  case HttpStatusCode.InternalServerError:
151  //response
152  throw new ServerErrorException(json); //json is html
153  case (HttpStatusCode)429: //rate limited
154  response.Headers.TryGetValues("Retry-After", out var values);
155  throw new RateLimitException(values?.FirstOrDefault());
156  default:
157  throw new ApiConflictException(errorMessage, response.StatusCode);
158  }
159  }
160 
161  if (String.IsNullOrWhiteSpace(json))
162  json = JsonConvert.SerializeObject(new object());
163 
164  return JsonConvert.DeserializeObject<TResult>(json, serializerSettings);
165  }
166 
168  public Task<TResult> Create<TResult>(string route, CancellationToken cancellationToken) => RunRequest<TResult>(route, new object(), HttpMethod.Put, null, cancellationToken);
169 
171  public Task<TResult> Read<TResult>(string route, CancellationToken cancellationToken) => RunRequest<TResult>(route, null, HttpMethod.Get, null, cancellationToken);
172 
174  public Task<TResult> Update<TResult>(string route, CancellationToken cancellationToken) => RunRequest<TResult>(route, new object(), HttpMethod.Post, null, cancellationToken);
175 
177  public Task<TResult> Update<TBody, TResult>(string route, TBody body, CancellationToken cancellationToken) => RunRequest<TResult>(route, body, HttpMethod.Post, null, cancellationToken);
178 
180  public Task Update<TBody>(string route, TBody body, CancellationToken cancellationToken) => RunRequest<object>(route, body, HttpMethod.Post, null, cancellationToken);
181 
183  public Task<TResult> Create<TBody, TResult>(string route, TBody body, CancellationToken cancellationToken) => RunRequest<TResult>(route, body, HttpMethod.Put, null, cancellationToken);
184 
186  public Task Delete(string route, CancellationToken cancellationToken) => RunRequest<object>(route, null, HttpMethod.Delete, null, cancellationToken);
187 
189  public Task<TResult> Create<TBody, TResult>(string route, TBody body, long instanceId, CancellationToken cancellationToken) => RunRequest<TResult>(route, body, HttpMethod.Put, instanceId, cancellationToken);
190 
192  public Task<TResult> Read<TResult>(string route, long instanceId, CancellationToken cancellationToken) => RunRequest<TResult>(route, null, HttpMethod.Get, instanceId, cancellationToken);
193 
195  public Task<TResult> Update<TBody, TResult>(string route, TBody body, long instanceId, CancellationToken cancellationToken) => RunRequest<TResult>(route, body, HttpMethod.Post, instanceId, cancellationToken);
196 
198  public Task Delete(string route, long instanceId, CancellationToken cancellationToken) => RunRequest<object>(route, null, HttpMethod.Delete, instanceId, cancellationToken);
199 
201  public Task Delete<TBody>(string route, TBody body, long instanceId, CancellationToken cancellationToken) => RunRequest<object>(route, body, HttpMethod.Delete, instanceId, cancellationToken);
202 
204  public Task<TResult> Delete<TResult>(string route, long instanceId, CancellationToken cancellationToken) => RunRequest<TResult>(route, null, HttpMethod.Delete, instanceId, cancellationToken);
205 
207  public Task<TResult> Create<TResult>(string route, long instanceId, CancellationToken cancellationToken) => RunRequest<TResult>(route, new object(), HttpMethod.Put, instanceId, cancellationToken);
208 
210  public void AddRequestLogger(IRequestLogger requestLogger) => requestLoggers.Add(requestLogger ?? throw new ArgumentNullException(nameof(requestLogger)));
211  }
212 }
const string ApplicationJson
TODO: Remove this when https://github.com/dotnet/corefx/pull/26701 makes it into the sdk ...
Definition: ApiHeaders.cs:21
Occurs when a GitHub rate limit occurs
-
Occurs when an error occurs in the server
+
Occurs when an error occurs in the server
Occurs when the server returns an unknown response
Represents the header that must be present for every server request
Definition: ApiHeaders.cs:16
diff --git a/_application_8cs_source.html b/_application_8cs_source.html index 5757b4906e..534e33151a 100644 --- a/_application_8cs_source.html +++ b/_application_8cs_source.html @@ -91,16 +91,16 @@ 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.Hosting;
6 using Microsoft.AspNetCore.Hosting.Server.Features;
7 using Microsoft.AspNetCore.Identity;
8 using Microsoft.EntityFrameworkCore;
9 using Microsoft.Extensions.Configuration;
10 using Microsoft.Extensions.DependencyInjection;
11 using Microsoft.Extensions.Hosting;
12 using Microsoft.Extensions.Logging;
13 using Microsoft.IdentityModel.Tokens;
14 using Newtonsoft.Json;
15 using System;
16 using System.Globalization;
17 using System.IdentityModel.Tokens.Jwt;
18 using System.Reflection;
19 using System.Runtime.InteropServices;
20 using System.Threading.Tasks;
27 using Tgstation.Server.Host.IO;
30 
31 namespace Tgstation.Server.Host.Core
32 {
34  sealed class Application : IApplication
35  {
37  public string VersionPrefix => "tgstation-server";
38 
40  public Version Version { get; }
41 
43  public string VersionString { get; }
44 
48  readonly Microsoft.Extensions.Configuration.IConfiguration configuration;
49 
53  readonly Microsoft.AspNetCore.Hosting.IHostingEnvironment hostingEnvironment;
54 
55  readonly TaskCompletionSource<object> startupTcs;
56 
62  public Application(Microsoft.Extensions.Configuration.IConfiguration configuration, Microsoft.AspNetCore.Hosting.IHostingEnvironment hostingEnvironment)
63  {
64  this.configuration = configuration ?? throw new ArgumentNullException(nameof(configuration));
65  this.hostingEnvironment = hostingEnvironment ?? throw new ArgumentNullException(nameof(hostingEnvironment));
66 
67  startupTcs = new TaskCompletionSource<object>();
68 
69  Version = Assembly.GetExecutingAssembly().GetName().Version;
70  VersionString = String.Format(CultureInfo.InvariantCulture, "{0} v{1}", VersionPrefix, Version);
71  }
72 
77 #pragma warning disable CA1822 // Mark members as static
78  public void ConfigureServices(IServiceCollection services)
79 #pragma warning restore CA1822 // Mark members as static
80  {
81  if (services == null)
82  throw new ArgumentNullException(nameof(services));
83 
84  services.Configure<UpdatesConfiguration>(configuration.GetSection(UpdatesConfiguration.Section));
85  var databaseConfigurationSection = configuration.GetSection(DatabaseConfiguration.Section);
86  services.Configure<DatabaseConfiguration>(databaseConfigurationSection);
87  var generalConfigurationSection = configuration.GetSection(GeneralConfiguration.Section);
88  services.Configure<GeneralConfiguration>(generalConfigurationSection);
89 
90  //remember, anything you .Get manually can be null if the config is missing
91  var generalConfiguration = generalConfigurationSection.Get<GeneralConfiguration>();
92  var isWindows = RuntimeInformation.IsOSPlatform(OSPlatform.Windows);
93  var ioManager = new DefaultIOManager();
94 
95  if (generalConfiguration?.DisableFileLogging != true)
96  {
97  var logPath = !String.IsNullOrEmpty(generalConfiguration?.LogFileDirectory) ? generalConfiguration.LogFileDirectory : ioManager.ConcatPath(Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData), VersionPrefix, "Logs");
98  services.AddLogging(builder => builder.AddFile(ioManager.ConcatPath(logPath, "tgs-{Date}.log")));
99  }
100 
101  services.AddOptions();
102 
103  const string scheme = "JwtBearer";
104  services.AddAuthentication((options) =>
105  {
106  options.DefaultAuthenticateScheme = scheme;
107  options.DefaultChallengeScheme = scheme;
108  }).AddJwtBearer(scheme, jwtBearerOptions =>
109  {
110  jwtBearerOptions.TokenValidationParameters = new TokenValidationParameters
111  {
112  ValidateIssuerSigningKey = true,
113  IssuerSigningKey = new SymmetricSecurityKey(TokenFactory.TokenSigningKey),
114 
115  ValidateIssuer = true,
116  ValidIssuer = TokenFactory.TokenIssuer,
117 
118  ValidateLifetime = true,
119  ValidateAudience = true,
120  ValidAudience = TokenFactory.TokenAudience,
121 
122  ClockSkew = TimeSpan.FromMinutes(5),
123 
124  RequireSignedTokens = true,
125 
126  RequireExpirationTime = true
127  };
128  jwtBearerOptions.Events = new JwtBearerEvents
129  {
130  OnTokenValidated = ApiController.OnTokenValidated
131  };
132  });
133  JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear(); //fucking converts 'sub' to M$ bs
134 
135  services.AddMvc().AddJsonOptions(options =>
136  {
137  options.AllowInputFormatterExceptionMessages = true;
138  options.SerializerSettings.NullValueHandling = NullValueHandling.Ignore;
139  options.SerializerSettings.CheckAdditionalContent = true;
140  options.SerializerSettings.MissingMemberHandling = MissingMemberHandling.Error;
141  options.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
142  });
143 
144  var databaseConfiguration = databaseConfigurationSection.Get<DatabaseConfiguration>();
145 
146  void ConfigureDatabase(DbContextOptionsBuilder builder)
147  {
148  if (hostingEnvironment.IsDevelopment())
149  builder.EnableSensitiveDataLogging();
150  };
151 
152  switch (databaseConfiguration?.DatabaseType ?? DatabaseType.Sqlite)
153  {
154  case DatabaseType.MySql:
155  services.AddDbContext<MySqlDatabaseContext>(ConfigureDatabase);
156  services.AddScoped<IDatabaseContext>(x => x.GetRequiredService<MySqlDatabaseContext>());
157  break;
158  case DatabaseType.Sqlite:
159  services.AddDbContext<SqliteDatabaseContext>(ConfigureDatabase);
160  services.AddScoped<IDatabaseContext>(x => x.GetRequiredService<SqliteDatabaseContext>());
161  break;
162  case DatabaseType.SqlServer:
163  services.AddDbContext<SqlServerDatabaseContext>(ConfigureDatabase);
164  services.AddScoped<IDatabaseContext>(x => x.GetRequiredService<SqlServerDatabaseContext>());
165  break;
166  default:
167  throw new InvalidOperationException(String.Format(CultureInfo.InvariantCulture, "Invalid {0}!", nameof(DatabaseType)));
168  }
169 
171  services.AddSingleton<IIdentityCache, IdentityCache>();
172 
173  services.AddSingleton<ICryptographySuite, CryptographySuite>();
174  services.AddSingleton<IDatabaseSeeder, DatabaseSeeder>();
175  services.AddSingleton<IPasswordHasher<Models.User>, PasswordHasher<Models.User>>();
176  services.AddSingleton<ITokenFactory, TokenFactory>();
177  services.AddSingleton<ISynchronousIOManager, SynchronousIOManager>();
178 
179  services.AddSingleton<IGitHubClientFactory, GitHubClientFactory>();
180  services.AddSingleton(x => x.GetRequiredService<IGitHubClientFactory>().CreateClient());
181 
182  if (isWindows)
183  {
184  services.AddSingleton<ISystemIdentityFactory, WindowsSystemIdentityFactory>();
185  services.AddSingleton<ISymlinkFactory, WindowsSymlinkFactory>();
186  services.AddSingleton<IByondInstaller, WindowsByondInstaller>();
187  services.AddSingleton<IPostWriteHandler, WindowsPostWriteHandler>();
188  }
189  else
190  {
191  services.AddSingleton<ISystemIdentityFactory, PosixSystemIdentityFactory>();
192  services.AddSingleton<ISymlinkFactory, PosixSymlinkFactory>();
193  services.AddSingleton<IByondInstaller, PosixByondInstaller>();
194  services.AddSingleton<IPostWriteHandler, PosixPostWriteHandler>();
195  }
196 
197  services.AddSingleton<IProcessExecutor, ProcessExecutor>();
198  services.AddSingleton<IProviderFactory, ProviderFactory>();
199  services.AddSingleton<IByondTopicSender>(new ByondTopicSender
200  {
201  ReceiveTimeout = 5000,
202  SendTimeout = 5000
203  });
204 
205  services.AddSingleton<InstanceFactory>();
206  services.AddSingleton<IInstanceFactory>(x => x.GetRequiredService<InstanceFactory>());
207  services.AddSingleton<InstanceManager>();
208  services.AddSingleton<IInstanceManager>(x => x.GetRequiredService<InstanceManager>());
209  services.AddSingleton<IHostedService>(x => x.GetRequiredService<InstanceManager>());
210 
211  services.AddSingleton<IJobManager, JobManager>();
212 
213  services.AddSingleton<IIOManager>(ioManager);
214 
215  services.AddSingleton<DatabaseContextFactory>();
216  services.AddSingleton<IDatabaseContextFactory>(x => x.GetRequiredService<DatabaseContextFactory>());
217 
218  services.AddSingleton<IApplication>(this);
219  }
220 
226  public void Configure(IApplicationBuilder applicationBuilder, ILogger<Application> logger)
227  {
228  if (applicationBuilder == null)
229  throw new ArgumentNullException(nameof(applicationBuilder));
230  if (logger == null)
231  throw new ArgumentNullException(nameof(logger));
232 
233  logger.LogInformation(VersionString);
234 
235  applicationBuilder.UseDeveloperExceptionPage(); //it is not worth it to limit this, you should only ever get it if you're an authorized user
236 
237  applicationBuilder.UseAsyncInitialization(async cancellationToken =>
238  {
239  using (cancellationToken.Register(() => startupTcs.SetCanceled()))
240  await startupTcs.Task.ConfigureAwait(false);
241  });
242 
243  applicationBuilder.UseAuthentication();
244 
245  applicationBuilder.UseDbConflictHandling();
246 
247  applicationBuilder.UseCancelledRequestSuppression();
248 
249  applicationBuilder.UseMvc();
250  }
251 
253  public void Ready(Exception initializationError)
254  {
255  if (startupTcs.Task.IsCompleted)
256  throw new InvalidOperationException("Ready has already been called!");
257  if (initializationError == null)
258  startupTcs.SetResult(null);
259  else
260  startupTcs.SetException(initializationError);
261  }
262  }
263 }
+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.Hosting;
6 using Microsoft.AspNetCore.Hosting.Server.Features;
7 using Microsoft.AspNetCore.Identity;
8 using Microsoft.EntityFrameworkCore;
9 using Microsoft.Extensions.Configuration;
10 using Microsoft.Extensions.DependencyInjection;
11 using Microsoft.Extensions.Hosting;
12 using Microsoft.Extensions.Logging;
13 using Microsoft.IdentityModel.Tokens;
14 using Newtonsoft.Json;
15 using System;
16 using System.Globalization;
17 using System.IdentityModel.Tokens.Jwt;
18 using System.Reflection;
19 using System.Runtime.InteropServices;
20 using System.Threading.Tasks;
27 using Tgstation.Server.Host.IO;
30 
31 namespace Tgstation.Server.Host.Core
32 {
34  sealed class Application : IApplication
35  {
37  public string VersionPrefix => "tgstation-server";
38 
40  public Version Version { get; }
41 
43  public string VersionString { get; }
44 
48  readonly Microsoft.Extensions.Configuration.IConfiguration configuration;
49 
53  readonly Microsoft.AspNetCore.Hosting.IHostingEnvironment hostingEnvironment;
54 
55  readonly TaskCompletionSource<object> startupTcs;
56 
62  public Application(Microsoft.Extensions.Configuration.IConfiguration configuration, Microsoft.AspNetCore.Hosting.IHostingEnvironment hostingEnvironment)
63  {
64  this.configuration = configuration ?? throw new ArgumentNullException(nameof(configuration));
65  this.hostingEnvironment = hostingEnvironment ?? throw new ArgumentNullException(nameof(hostingEnvironment));
66 
67  startupTcs = new TaskCompletionSource<object>();
68 
69  Version = Assembly.GetExecutingAssembly().GetName().Version;
70  VersionString = String.Format(CultureInfo.InvariantCulture, "{0} v{1}", VersionPrefix, Version);
71  }
72 
77 #pragma warning disable CA1822 // Mark members as static
78  public void ConfigureServices(IServiceCollection services)
79 #pragma warning restore CA1822 // Mark members as static
80  {
81  if (services == null)
82  throw new ArgumentNullException(nameof(services));
83 
84  services.Configure<UpdatesConfiguration>(configuration.GetSection(UpdatesConfiguration.Section));
85  var databaseConfigurationSection = configuration.GetSection(DatabaseConfiguration.Section);
86  services.Configure<DatabaseConfiguration>(databaseConfigurationSection);
87  var generalConfigurationSection = configuration.GetSection(GeneralConfiguration.Section);
88  services.Configure<GeneralConfiguration>(generalConfigurationSection);
89 
90  //remember, anything you .Get manually can be null if the config is missing
91  var generalConfiguration = generalConfigurationSection.Get<GeneralConfiguration>();
92  var isWindows = RuntimeInformation.IsOSPlatform(OSPlatform.Windows);
93  var ioManager = new DefaultIOManager();
94 
95  if (generalConfiguration?.DisableFileLogging != true)
96  {
97  var logPath = !String.IsNullOrEmpty(generalConfiguration?.LogFileDirectory) ? generalConfiguration.LogFileDirectory : ioManager.ConcatPath(Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData), VersionPrefix, "Logs");
98  services.AddLogging(builder => builder.AddFile(ioManager.ConcatPath(logPath, "tgs-{Date}.log")));
99  }
100 
101  services.AddOptions();
102 
103  const string scheme = "JwtBearer";
104  services.AddAuthentication((options) =>
105  {
106  options.DefaultAuthenticateScheme = scheme;
107  options.DefaultChallengeScheme = scheme;
108  }).AddJwtBearer(scheme, jwtBearerOptions =>
109  {
110  jwtBearerOptions.TokenValidationParameters = new TokenValidationParameters
111  {
112  ValidateIssuerSigningKey = true,
113  IssuerSigningKey = new SymmetricSecurityKey(TokenFactory.TokenSigningKey),
114 
115  ValidateIssuer = true,
116  ValidIssuer = TokenFactory.TokenIssuer,
117 
118  ValidateLifetime = true,
119  ValidateAudience = true,
120  ValidAudience = TokenFactory.TokenAudience,
121 
122  ClockSkew = TimeSpan.FromMinutes(5),
123 
124  RequireSignedTokens = true,
125 
126  RequireExpirationTime = true
127  };
128  jwtBearerOptions.Events = new JwtBearerEvents
129  {
130  OnTokenValidated = ApiController.OnTokenValidated
131  };
132  });
133  JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear(); //fucking converts 'sub' to M$ bs
134 
135  services.AddMvc().AddJsonOptions(options =>
136  {
137  options.AllowInputFormatterExceptionMessages = true;
138  options.SerializerSettings.NullValueHandling = NullValueHandling.Ignore;
139  options.SerializerSettings.CheckAdditionalContent = true;
140  options.SerializerSettings.MissingMemberHandling = MissingMemberHandling.Error;
141  options.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
142  });
143 
144  var databaseConfiguration = databaseConfigurationSection.Get<DatabaseConfiguration>();
145 
146  void ConfigureDatabase(DbContextOptionsBuilder builder)
147  {
148  if (hostingEnvironment.IsDevelopment())
149  builder.EnableSensitiveDataLogging();
150  };
151 
152  var dbType = databaseConfiguration?.DatabaseType;
153  switch (dbType)
154  {
155  case DatabaseType.MySql:
156  case DatabaseType.MariaDB:
157  services.AddDbContext<MySqlDatabaseContext>(ConfigureDatabase);
158  services.AddScoped<IDatabaseContext>(x => x.GetRequiredService<MySqlDatabaseContext>());
159  break;
160  case DatabaseType.SqlServer:
161  services.AddDbContext<SqlServerDatabaseContext>(ConfigureDatabase);
162  services.AddScoped<IDatabaseContext>(x => x.GetRequiredService<SqlServerDatabaseContext>());
163  break;
164  default:
165  throw new InvalidOperationException(String.Format(CultureInfo.InvariantCulture, "Invalid {0}: {1}!", nameof(DatabaseType), dbType));
166  }
167 
169  services.AddSingleton<IIdentityCache, IdentityCache>();
170 
171  services.AddSingleton<ICryptographySuite, CryptographySuite>();
172  services.AddSingleton<IDatabaseSeeder, DatabaseSeeder>();
173  services.AddSingleton<IPasswordHasher<Models.User>, PasswordHasher<Models.User>>();
174  services.AddSingleton<ITokenFactory, TokenFactory>();
175  services.AddSingleton<ISynchronousIOManager, SynchronousIOManager>();
176 
177  services.AddSingleton<IGitHubClientFactory, GitHubClientFactory>();
178  services.AddSingleton(x => x.GetRequiredService<IGitHubClientFactory>().CreateClient());
179 
180  if (isWindows)
181  {
182  services.AddSingleton<ISystemIdentityFactory, WindowsSystemIdentityFactory>();
183  services.AddSingleton<ISymlinkFactory, WindowsSymlinkFactory>();
184  services.AddSingleton<IByondInstaller, WindowsByondInstaller>();
185  services.AddSingleton<IPostWriteHandler, WindowsPostWriteHandler>();
186  }
187  else
188  {
189  services.AddSingleton<ISystemIdentityFactory, PosixSystemIdentityFactory>();
190  services.AddSingleton<ISymlinkFactory, PosixSymlinkFactory>();
191  services.AddSingleton<IByondInstaller, PosixByondInstaller>();
192  services.AddSingleton<IPostWriteHandler, PosixPostWriteHandler>();
193  }
194 
195  services.AddSingleton<IProcessExecutor, ProcessExecutor>();
196  services.AddSingleton<IProviderFactory, ProviderFactory>();
197  services.AddSingleton<IByondTopicSender>(new ByondTopicSender
198  {
199  ReceiveTimeout = 5000,
200  SendTimeout = 5000
201  });
202 
203  services.AddSingleton<InstanceFactory>();
204  services.AddSingleton<IInstanceFactory>(x => x.GetRequiredService<InstanceFactory>());
205  services.AddSingleton<InstanceManager>();
206  services.AddSingleton<IInstanceManager>(x => x.GetRequiredService<InstanceManager>());
207  services.AddSingleton<IHostedService>(x => x.GetRequiredService<InstanceManager>());
208 
209  services.AddSingleton<IJobManager, JobManager>();
210 
211  services.AddSingleton<IIOManager>(ioManager);
212 
213  services.AddSingleton<DatabaseContextFactory>();
214  services.AddSingleton<IDatabaseContextFactory>(x => x.GetRequiredService<DatabaseContextFactory>());
215 
216  services.AddSingleton<IApplication>(this);
217  }
218 
224  public void Configure(IApplicationBuilder applicationBuilder, ILogger<Application> logger)
225  {
226  if (applicationBuilder == null)
227  throw new ArgumentNullException(nameof(applicationBuilder));
228  if (logger == null)
229  throw new ArgumentNullException(nameof(logger));
230 
231  logger.LogInformation(VersionString);
232 
233  applicationBuilder.UseDeveloperExceptionPage(); //it is not worth it to limit this, you should only ever get it if you're an authorized user
234 
235  applicationBuilder.UseAsyncInitialization(async cancellationToken =>
236  {
237  using (cancellationToken.Register(() => startupTcs.SetCanceled()))
238  await startupTcs.Task.ConfigureAwait(false);
239  });
240 
241  applicationBuilder.UseAuthentication();
242 
243  applicationBuilder.UseDbConflictHandling();
244 
245  applicationBuilder.UseCancelledRequestSuppression();
246 
247  applicationBuilder.UseMvc();
248  }
249 
251  public void Ready(Exception initializationError)
252  {
253  if (startupTcs.Task.IsCompleted)
254  throw new InvalidOperationException("Ready has already been called!");
255  if (initializationError == null)
256  startupTcs.SetResult(null);
257  else
258  startupTcs.SetException(initializationError);
259  }
260  }
261 }
-
Manages the runtime of Jobs
Definition: IJobManager.cs:13
+
Manages the runtime of Jobs
Definition: IJobManager.cs:12
readonly Microsoft.AspNetCore.Hosting.IHostingEnvironment hostingEnvironment
The Microsoft.AspNetCore.Hosting.IHostingEnvironment for the Application
Definition: Application.cs:53
-
void Configure(IApplicationBuilder applicationBuilder, ILogger< Application > logger)
Configure the Application
Definition: Application.cs:226
+
void Configure(IApplicationBuilder applicationBuilder, ILogger< Application > logger)
Configure the Application
Definition: Application.cs:224
Configures the ASP.NET Core web application
Definition: IApplication.cs:8
readonly Microsoft.Extensions.Configuration.IConfiguration configuration
The IConfiguration for the Application
Definition: Application.cs:48
@@ -116,7 +116,6 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search'); -
DatabaseContext<TParentContext> for Sqlite
const string Section
The key for the Microsoft.Extensions.Configuration.IConfigurationSection the DatabaseConfiguration re...
ISystemIdentityFactory for windows systems. Uses long running tasks due to potential networked domain...
@@ -130,8 +129,9 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
DatabaseContext<TParentContext> for Sqlserver
For downloading and installing BYOND extractions for a given system
For accessing the disk in a synchronous manner
+
DatabaseType DatabaseType
The Configuration.DatabaseType to create
-
void Ready(Exception initializationError)
Mark the IApplication as ready to run
Definition: Application.cs:253
+
void Ready(Exception initializationError)
Mark the IApplication as ready to run
Definition: Application.cs:251
IPostWriteHandler for Windows systems
@@ -166,7 +166,7 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search'); -
DatabaseContext<TParentContext> for MySQL
+
DatabaseContext<TParentContext> for MySQL
diff --git a/_byond_controller_8cs_source.html b/_byond_controller_8cs_source.html index 17e1b953c6..59257396ac 100644 --- a/_byond_controller_8cs_source.html +++ b/_byond_controller_8cs_source.html @@ -93,7 +93,7 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
Go to the documentation of this file.
1 using Microsoft.AspNetCore.Mvc;
2 using Microsoft.Extensions.Logging;
3 using System;
4 using System.Globalization;
5 using System.Linq;
6 using System.Threading;
7 using System.Threading.Tasks;
8 using Tgstation.Server.Api;
15 
16 namespace Tgstation.Server.Host.Controllers
17 {
21  [Route(Routes.Byond)]
22  public sealed class ByondController : ModelController<Api.Models.Byond>
23  {
28 
33 
42  public ByondController(IDatabaseContext databaseContext, IAuthenticationContextFactory authenticationContextFactory, IInstanceManager instanceManager, IJobManager jobManager, ILogger<ByondController> logger) : base(databaseContext, authenticationContextFactory, logger, true)
43  {
44  this.instanceManager = instanceManager ?? throw new ArgumentNullException(nameof(instanceManager));
45  this.jobManager = jobManager ?? throw new ArgumentNullException(nameof(jobManager));
46  }
47 
49  [TgsAuthorize(ByondRights.ReadActive)]
50  public override Task<IActionResult> Read(CancellationToken cancellationToken) => Task.FromResult((IActionResult)
51  Json(new Api.Models.Byond
52  {
53  Version = instanceManager.GetInstance(Instance).ByondManager.ActiveVersion
54  }));
55 
57  [TgsAuthorize(ByondRights.ListInstalled)]
58  public override Task<IActionResult> List(CancellationToken cancellationToken) => Task.FromResult((IActionResult)
59  Json(instanceManager.GetInstance(Instance).ByondManager.InstalledVersions.Select(x => new Api.Models.Byond
60  {
61  Version = x
62  })));
63 
65  [TgsAuthorize(ByondRights.ChangeVersion)]
66  public override async Task<IActionResult> Update([FromBody] Api.Models.Byond model, CancellationToken cancellationToken)
67  {
68  if (model == null)
69  throw new ArgumentNullException(nameof(model));
70 
71  if (model.Version == null)
72  return BadRequest(new ErrorMessage { Message = "Missing version!" });
73 
74  var byondManager = instanceManager.GetInstance(Instance).ByondManager;
75 
76  //remove cruff fields
77  var installingVersion = new Version(model.Version.Major, model.Version.Minor);
78 
79  var result = new Api.Models.Byond();
80 
81  if (byondManager.InstalledVersions.Any(x => x == model.Version))
82  {
83  Logger.LogInformation("User ID {0} changing instance ID {1} BYOND version to {2}", AuthenticationContext.User.Id, Instance.Id, installingVersion);
84  await byondManager.ChangeVersion(model.Version, cancellationToken).ConfigureAwait(false);
85  }
86  else
87  {
88  Logger.LogInformation("User ID {0} installing BYOND version to {2} on instance ID {1}", AuthenticationContext.User.Id, Instance.Id, installingVersion);
89  //run the install through the job manager
90  var job = new Models.Job
91  {
92  Description = String.Format(CultureInfo.InvariantCulture, "Install BYOND version {0}", installingVersion),
93  StartedBy = AuthenticationContext.User,
94  CancelRightsType = RightsType.Byond,
95  CancelRight = (ulong)ByondRights.CancelInstall,
97  };
98  await jobManager.RegisterOperation(job, (paramJob, serviceProvicer, progressHandler, ct) => byondManager.ChangeVersion(installingVersion, ct), cancellationToken).ConfigureAwait(false);
99  result.InstallJob = job.ToApi();
100  }
101  result.Version = byondManager.ActiveVersion;
102  return result.InstallJob != null ? (IActionResult)Accepted(result) : Json(result);
103  }
104  }
105 }
const string Byond
The Models.Byond controller
Definition: Routes.cs:34
-
Manages the runtime of Jobs
Definition: IJobManager.cs:13
+
Manages the runtime of Jobs
Definition: IJobManager.cs:12
ByondRights
Rights for Models.Byond
Definition: ByondRights.cs:9
diff --git a/_configuration_8cs_source.html b/_configuration_8cs_source.html index 470d7fb5c6..817762a519 100644 --- a/_configuration_8cs_source.html +++ b/_configuration_8cs_source.html @@ -91,30 +91,31 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
Configuration.cs
-Go to the documentation of this file.
1 using Microsoft.Extensions.Logging;
2 using System;
3 using System.Collections.Generic;
4 using System.Globalization;
5 using System.IO;
6 using System.Linq;
7 using System.Runtime.InteropServices;
8 using System.Security.Cryptography;
9 using System.Threading;
10 using System.Threading.Tasks;
13 using Tgstation.Server.Host.IO;
15 
16 namespace Tgstation.Server.Host.Components.StaticFiles
17 {
20  {
21  const string CodeModificationsSubdirectory = "CodeModifications";
22  const string EventScriptsSubdirectory = "EventScripts";
23  const string GameStaticFilesSubdirectory = "GameStaticFiles";
24 
25  const string CodeModificationsHeadFile = "HeadInclude.dm";
26  const string CodeModificationsTailFile = "TailInclude.dm";
27 
28  static readonly IReadOnlyDictionary<EventType, string> EventTypeScriptFileNameMap = new Dictionary<EventType, string>
29  {
30  { EventType.CompileStart, "PreCompile" },
31  { EventType.CompileComplete, "PostCompile" },
32  { EventType.RepoPreSynchronize, "PreSynchronize" }
33  };
34 
35  static readonly string SystemScriptFileExtension = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? "bat" : "sh";
36 
41 
46 
51 
56 
61 
65  readonly ILogger<Configuration> logger;
66 
70  readonly SemaphoreSlim semaphore;
71 
81  public Configuration(IIOManager ioManager, ISynchronousIOManager synchronousIOManager, ISymlinkFactory symlinkFactory, IProcessExecutor processExecutor, IPostWriteHandler postWriteHandler, ILogger<Configuration> logger)
82  {
83  this.ioManager = ioManager ?? throw new ArgumentNullException(nameof(ioManager));
84  this.synchronousIOManager = synchronousIOManager ?? throw new ArgumentNullException(nameof(synchronousIOManager));
85  this.symlinkFactory = symlinkFactory ?? throw new ArgumentNullException(nameof(symlinkFactory));
86  this.processExecutor = processExecutor ?? throw new ArgumentNullException(nameof(processExecutor));
87  this.postWriteHandler = postWriteHandler ?? throw new ArgumentNullException(nameof(postWriteHandler));
88  this.logger = logger ?? throw new ArgumentNullException(nameof(logger));
89 
90  semaphore = new SemaphoreSlim(1);
91  }
92 
94  public void Dispose() => semaphore.Dispose();
95 
101  Task EnsureDirectories(CancellationToken cancellationToken) => Task.WhenAll(ioManager.CreateDirectory(CodeModificationsSubdirectory, cancellationToken), ioManager.CreateDirectory(EventScriptsSubdirectory, cancellationToken), ioManager.CreateDirectory(GameStaticFilesSubdirectory, cancellationToken));
102 
104  public async Task<ServerSideModifications> CopyDMFilesTo(string dmeFile, string destination, CancellationToken cancellationToken)
105  {
106  await EnsureDirectories(cancellationToken).ConfigureAwait(false);
107 
108  //just assume no other fs race conditions here
109  var dmeExistsTask = ioManager.FileExists(ioManager.ConcatPath(CodeModificationsSubdirectory, dmeFile), cancellationToken);
110  var headFileExistsTask = ioManager.FileExists(ioManager.ConcatPath(CodeModificationsSubdirectory, CodeModificationsHeadFile), cancellationToken);
111  var tailFileExistsTask = ioManager.FileExists(ioManager.ConcatPath(CodeModificationsSubdirectory, CodeModificationsTailFile), cancellationToken);
112 
113  await Task.WhenAll(dmeExistsTask, headFileExistsTask, tailFileExistsTask).ConfigureAwait(false);
114 
115  if (!dmeExistsTask.Result && !headFileExistsTask.Result && !tailFileExistsTask.Result)
116  return null;
117 
118  var copyTask = ioManager.CopyDirectory(CodeModificationsSubdirectory, destination, null, cancellationToken);
119 
120  if (dmeExistsTask.Result)
121  {
122  await copyTask.ConfigureAwait(false);
123  return new ServerSideModifications(null, null, true);
124  }
125 
126  if (!headFileExistsTask.Result && !tailFileExistsTask.Result)
127  {
128  await copyTask.ConfigureAwait(false);
129  return null;
130  }
131 
132  string IncludeLine(string filePath) => String.Format(CultureInfo.InvariantCulture, "#include \"{0}\"", filePath);
133 
134  await copyTask.ConfigureAwait(false);
135  return new ServerSideModifications(headFileExistsTask.Result ? IncludeLine(CodeModificationsHeadFile) : null, tailFileExistsTask.Result ? IncludeLine(CodeModificationsTailFile) : null, false);
136  }
137 
138  string ValidateConfigRelativePath(string configurationRelativePath)
139  {
140  var nullOrEmptyCheck = String.IsNullOrEmpty(configurationRelativePath);
141  if (nullOrEmptyCheck)
142  configurationRelativePath = ".";
143  var resolved = ioManager.ResolvePath(configurationRelativePath);
144  var local = !nullOrEmptyCheck ? ioManager.ResolvePath(".") : null;
145  if (!nullOrEmptyCheck && resolved.Length < local.Length) //.. fuccbois
146  throw new InvalidOperationException("Attempted to access file outside of configuration manager!");
147  return resolved;
148  }
149 
151  public async Task<IReadOnlyList<ConfigurationFile>> ListDirectory(string configurationRelativePath, ISystemIdentity systemIdentity, CancellationToken cancellationToken)
152  {
153  await EnsureDirectories(cancellationToken).ConfigureAwait(false);
154  var path = ValidateConfigRelativePath(configurationRelativePath);
155 
156  List<ConfigurationFile> result = new List<ConfigurationFile>();
157 
158  void ListImpl()
159  {
160  var enumerator = synchronousIOManager.GetDirectories(path, cancellationToken);
161  try
162  {
163  result.AddRange(enumerator.Select(x => new ConfigurationFile
164  {
165  IsDirectory = true,
166  Path = ioManager.ConcatPath(path, x),
167  }));
168  }
169  catch (IOException e)
170  {
171  logger.LogDebug("IOException while writing {0}: {1}", path, e);
172  result = null;
173  return;
174  }
175  enumerator = synchronousIOManager.GetFiles(path, cancellationToken);
176  result.AddRange(enumerator.Select(x => new ConfigurationFile
177  {
178  IsDirectory = false,
179  Path = ioManager.ConcatPath(path, x),
180  }));
181  }
182 
183  if (systemIdentity == null)
184  ListImpl();
185  else
186  await systemIdentity.RunImpersonated(ListImpl, cancellationToken).ConfigureAwait(false);
187 
188  return result;
189  }
190 
192  public async Task<ConfigurationFile> Read(string configurationRelativePath, ISystemIdentity systemIdentity, CancellationToken cancellationToken)
193  {
194  await EnsureDirectories(cancellationToken).ConfigureAwait(false);
195  var path = ValidateConfigRelativePath(configurationRelativePath);
196 
197  ConfigurationFile result = null;
198 
199  void ReadImpl()
200  {
201  lock (this)
202  try
203  {
204  var content = synchronousIOManager.ReadFile(path);
205  string sha1String;
206 #pragma warning disable CA5350 // Do not use insecure cryptographic algorithm SHA1.
207  using (var sha1 = new SHA1Managed())
208 #pragma warning restore CA5350 // Do not use insecure cryptographic algorithm SHA1.
209  sha1String = String.Join("", sha1.ComputeHash(content).Select(b => b.ToString("x2", CultureInfo.InvariantCulture)));
210  result = new ConfigurationFile
211  {
212  Content = content,
213  IsDirectory = false,
214  LastReadHash = sha1String,
215  AccessDenied = false,
216  Path = configurationRelativePath
217  };
218  }
219  catch (UnauthorizedAccessException)
220  {
221  //this happens on windows, dunno about linux
222  bool isDirectory;
223  try
224  {
225  isDirectory = synchronousIOManager.IsDirectory(path);
226  }
227  catch
228  {
229  isDirectory = false;
230  }
231 
232  result = new ConfigurationFile
233  {
234  Path = configurationRelativePath
235  };
236  if (!isDirectory)
237  result.AccessDenied = true;
238  else
239  result.IsDirectory = true;
240  }
241  }
242 
243  if (systemIdentity == null)
244  await Task.Factory.StartNew(ReadImpl, cancellationToken, TaskCreationOptions.LongRunning, TaskScheduler.Current).ConfigureAwait(false);
245  else
246  await systemIdentity.RunImpersonated(ReadImpl, cancellationToken).ConfigureAwait(false);
247 
248  return result;
249  }
250 
252  public async Task SymlinkStaticFilesTo(string destination, CancellationToken cancellationToken)
253  {
254  await EnsureDirectories(cancellationToken).ConfigureAwait(false);
255  async Task SymlinkBase(bool files)
256  {
257  Task<IReadOnlyList<string>> task;
258  if (files)
259  task = ioManager.GetFiles(GameStaticFilesSubdirectory, cancellationToken);
260  else
261  task = ioManager.GetDirectories(GameStaticFilesSubdirectory, cancellationToken);
262  var entries = await task.ConfigureAwait(false);
263 
264  await Task.WhenAll(task.Result.Select(async x =>
265  {
266  var destPath = ioManager.ConcatPath(destination, ioManager.GetFileName(x));
267  logger.LogTrace("Symlinking {0} to {1}...", x, destPath);
268  var fileExistsTask = ioManager.FileExists(destPath, cancellationToken);
269  if (await ioManager.DirectoryExists(destPath, cancellationToken).ConfigureAwait(false))
270  await ioManager.DeleteDirectory(destPath, cancellationToken).ConfigureAwait(false);
271  var fileExists = await fileExistsTask.ConfigureAwait(false);
272  if (fileExists)
273  await ioManager.DeleteFile(destPath, cancellationToken).ConfigureAwait(false);
274  await symlinkFactory.CreateSymbolicLink(ioManager.ResolvePath(x), ioManager.ResolvePath(destPath), cancellationToken).ConfigureAwait(false);
275  })).ConfigureAwait(false);
276  }
277 
278  await Task.WhenAll(SymlinkBase(true), SymlinkBase(false)).ConfigureAwait(false);
279  }
280 
282  public async Task<ConfigurationFile> Write(string configurationRelativePath, ISystemIdentity systemIdentity, byte[] data, string previousHash, CancellationToken cancellationToken)
283  {
284  await EnsureDirectories(cancellationToken).ConfigureAwait(false);
285  var path = ValidateConfigRelativePath(configurationRelativePath);
286 
287  ConfigurationFile result = null;
288 
289  void WriteImpl()
290  {
291  lock (this)
292  try
293  {
294  var fileHash = previousHash;
295  var success = synchronousIOManager.WriteFileChecked(path, data, ref fileHash, cancellationToken);
296  if (!success)
297  return;
298  result = new ConfigurationFile
299  {
300  Content = data,
301  IsDirectory = false,
302  LastReadHash = fileHash,
303  AccessDenied = false,
304  Path = configurationRelativePath
305  };
306  }
307  catch (UnauthorizedAccessException)
308  {
309  //this happens on windows, dunno about linux
310  bool isDirectory;
311  try
312  {
313  isDirectory = synchronousIOManager.IsDirectory(path);
314  }
315  catch
316  {
317  isDirectory = false;
318  }
319 
320  result = new ConfigurationFile
321  {
322  Path = configurationRelativePath
323  };
324  if (!isDirectory)
325  result.AccessDenied = true;
326  else
327  result.IsDirectory = true;
328  }
329  }
330 
331  if (systemIdentity == null)
332  await Task.Factory.StartNew(WriteImpl, cancellationToken, TaskCreationOptions.LongRunning, TaskScheduler.Current).ConfigureAwait(false);
333  else
334  await systemIdentity.RunImpersonated(WriteImpl, cancellationToken).ConfigureAwait(false);
335 
336  if (result != null && data == null) //make sure these directories always exist
337  await EnsureDirectories(cancellationToken).ConfigureAwait(false);
338 
339  return result;
340  }
341 
343  public async Task<bool> CreateDirectory(string configurationRelativePath, ISystemIdentity systemIdentity, CancellationToken cancellationToken)
344  {
345  await EnsureDirectories(cancellationToken).ConfigureAwait(false);
346  var path = ValidateConfigRelativePath(configurationRelativePath);
347 
348  bool? result = null;
349  void DoCreate() => result = synchronousIOManager.CreateDirectory(path, cancellationToken);
350  if (systemIdentity == null)
351  await Task.Factory.StartNew(DoCreate, cancellationToken, TaskCreationOptions.LongRunning, TaskScheduler.Current).ConfigureAwait(false);
352  else
353  await systemIdentity.RunImpersonated(DoCreate, cancellationToken).ConfigureAwait(false);
354 
355  return result.Value;
356  }
357 
359  public Task StartAsync(CancellationToken cancellationToken) => EnsureDirectories(cancellationToken);
360 
362  public Task StopAsync(CancellationToken cancellationToken) => EnsureDirectories(cancellationToken);
363 
365  public async Task<bool> HandleEvent(EventType eventType, IEnumerable<string> parameters, CancellationToken cancellationToken)
366  {
367  await EnsureDirectories(cancellationToken).ConfigureAwait(false);
368 
369  if (!EventTypeScriptFileNameMap.TryGetValue(eventType, out var scriptName))
370  return true;
371 
372  //always execute in serial
373  using (await SemaphoreSlimContext.Lock(semaphore, cancellationToken).ConfigureAwait(false))
374  {
375  var files = await ioManager.GetFilesWithExtension(EventScriptsSubdirectory, SystemScriptFileExtension, cancellationToken).ConfigureAwait(false);
376  var resolvedScriptsDir = ioManager.ResolvePath(EventScriptsSubdirectory);
377 
378  foreach (var I in files.Select(x => ioManager.GetFileName(x)).Where(x => x.StartsWith(scriptName, StringComparison.Ordinal)))
379  using (var script = processExecutor.LaunchProcess(ioManager.ConcatPath(resolvedScriptsDir, I), resolvedScriptsDir, String.Join(' ', parameters), noShellExecute: true))
380  using (cancellationToken.Register(() => script.Terminate()))
381  {
382  var exitCode = await script.Lifetime.ConfigureAwait(false);
383  cancellationToken.ThrowIfCancellationRequested();
384  if (exitCode != 0)
385  return false;
386  }
387  }
388  return true;
389  }
390  }
391 }
readonly ISynchronousIOManager synchronousIOManager
The ISynchronousIOManager for Configuration
+Go to the documentation of this file.
1 using Microsoft.Extensions.Logging;
2 using System;
3 using System.Collections.Generic;
4 using System.Globalization;
5 using System.IO;
6 using System.Linq;
7 using System.Runtime.InteropServices;
8 using System.Security.Cryptography;
9 using System.Threading;
10 using System.Threading.Tasks;
13 using Tgstation.Server.Host.IO;
15 
16 namespace Tgstation.Server.Host.Components.StaticFiles
17 {
20  {
21  const string CodeModificationsSubdirectory = "CodeModifications";
22  const string EventScriptsSubdirectory = "EventScripts";
23  const string GameStaticFilesSubdirectory = "GameStaticFiles";
24 
25  const string CodeModificationsHeadFile = "HeadInclude.dm";
26  const string CodeModificationsTailFile = "TailInclude.dm";
27 
28  static readonly IReadOnlyDictionary<EventType, string> EventTypeScriptFileNameMap = new Dictionary<EventType, string>
29  {
30  { EventType.CompileStart, "PreCompile" },
31  { EventType.CompileComplete, "PostCompile" },
32  { EventType.RepoPreSynchronize, "PreSynchronize" }
33  };
34 
35  static readonly string SystemScriptFileExtension = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? "bat" : "sh";
36 
41 
46 
51 
56 
61 
65  readonly ILogger<Configuration> logger;
66 
70  readonly SemaphoreSlim semaphore;
71 
81  public Configuration(IIOManager ioManager, ISynchronousIOManager synchronousIOManager, ISymlinkFactory symlinkFactory, IProcessExecutor processExecutor, IPostWriteHandler postWriteHandler, ILogger<Configuration> logger)
82  {
83  this.ioManager = ioManager ?? throw new ArgumentNullException(nameof(ioManager));
84  this.synchronousIOManager = synchronousIOManager ?? throw new ArgumentNullException(nameof(synchronousIOManager));
85  this.symlinkFactory = symlinkFactory ?? throw new ArgumentNullException(nameof(symlinkFactory));
86  this.processExecutor = processExecutor ?? throw new ArgumentNullException(nameof(processExecutor));
87  this.postWriteHandler = postWriteHandler ?? throw new ArgumentNullException(nameof(postWriteHandler));
88  this.logger = logger ?? throw new ArgumentNullException(nameof(logger));
89 
90  semaphore = new SemaphoreSlim(1);
91  }
92 
94  public void Dispose() => semaphore.Dispose();
95 
101  Task EnsureDirectories(CancellationToken cancellationToken) => Task.WhenAll(ioManager.CreateDirectory(CodeModificationsSubdirectory, cancellationToken), ioManager.CreateDirectory(EventScriptsSubdirectory, cancellationToken), ioManager.CreateDirectory(GameStaticFilesSubdirectory, cancellationToken));
102 
104  public async Task<ServerSideModifications> CopyDMFilesTo(string dmeFile, string destination, CancellationToken cancellationToken)
105  {
106 
107  using (await SemaphoreSlimContext.Lock(semaphore, cancellationToken).ConfigureAwait(false))
108  {
109  await EnsureDirectories(cancellationToken).ConfigureAwait(false);
110 
111  //just assume no other fs race conditions here
112  var dmeExistsTask = ioManager.FileExists(ioManager.ConcatPath(CodeModificationsSubdirectory, dmeFile), cancellationToken);
113  var headFileExistsTask = ioManager.FileExists(ioManager.ConcatPath(CodeModificationsSubdirectory, CodeModificationsHeadFile), cancellationToken);
114  var tailFileExistsTask = ioManager.FileExists(ioManager.ConcatPath(CodeModificationsSubdirectory, CodeModificationsTailFile), cancellationToken);
115 
116  await Task.WhenAll(dmeExistsTask, headFileExistsTask, tailFileExistsTask).ConfigureAwait(false);
117 
118  if (!dmeExistsTask.Result && !headFileExistsTask.Result && !tailFileExistsTask.Result)
119  return null;
120 
121  var copyTask = ioManager.CopyDirectory(CodeModificationsSubdirectory, destination, null, cancellationToken);
122 
123  if (dmeExistsTask.Result)
124  {
125  await copyTask.ConfigureAwait(false);
126  return new ServerSideModifications(null, null, true);
127  }
128 
129  if (!headFileExistsTask.Result && !tailFileExistsTask.Result)
130  {
131  await copyTask.ConfigureAwait(false);
132  return null;
133  }
134 
135  string IncludeLine(string filePath) => String.Format(CultureInfo.InvariantCulture, "#include \"{0}\"", filePath);
136 
137  await copyTask.ConfigureAwait(false);
138  return new ServerSideModifications(headFileExistsTask.Result ? IncludeLine(CodeModificationsHeadFile) : null, tailFileExistsTask.Result ? IncludeLine(CodeModificationsTailFile) : null, false);
139  }
140  }
141 
142  string ValidateConfigRelativePath(string configurationRelativePath)
143  {
144  var nullOrEmptyCheck = String.IsNullOrEmpty(configurationRelativePath);
145  if (nullOrEmptyCheck)
146  configurationRelativePath = ".";
147  if (configurationRelativePath[0] == Path.DirectorySeparatorChar || configurationRelativePath[0] == Path.AltDirectorySeparatorChar)
148  configurationRelativePath = '.' + configurationRelativePath;
149  var resolved = ioManager.ResolvePath(configurationRelativePath);
150  var local = !nullOrEmptyCheck ? ioManager.ResolvePath(".") : null;
151  if (!nullOrEmptyCheck && resolved.Length < local.Length) //.. fuccbois
152  throw new InvalidOperationException("Attempted to access file outside of configuration manager!");
153  return resolved;
154  }
155 
157  public async Task<IReadOnlyList<ConfigurationFile>> ListDirectory(string configurationRelativePath, ISystemIdentity systemIdentity, CancellationToken cancellationToken)
158  {
159  await EnsureDirectories(cancellationToken).ConfigureAwait(false);
160  var path = ValidateConfigRelativePath(configurationRelativePath);
161 
162  List<ConfigurationFile> result = new List<ConfigurationFile>();
163 
164  void ListImpl()
165  {
166  var enumerator = synchronousIOManager.GetDirectories(path, cancellationToken);
167  try
168  {
169  result.AddRange(enumerator.Select(x => new ConfigurationFile
170  {
171  IsDirectory = true,
172  Path = ioManager.ConcatPath(path, x),
173  }));
174  }
175  catch (IOException e)
176  {
177  logger.LogDebug("IOException while writing {0}: {1}", path, e);
178  result = null;
179  return;
180  }
181  enumerator = synchronousIOManager.GetFiles(path, cancellationToken);
182  result.AddRange(enumerator.Select(x => new ConfigurationFile
183  {
184  IsDirectory = false,
185  Path = ioManager.ConcatPath(path, x),
186  }));
187  }
188 
189  using (await SemaphoreSlimContext.Lock(semaphore, cancellationToken).ConfigureAwait(false))
190  if (systemIdentity == null)
191  ListImpl();
192  else
193  await systemIdentity.RunImpersonated(ListImpl, cancellationToken).ConfigureAwait(false);
194 
195  return result;
196  }
197 
199  public async Task<ConfigurationFile> Read(string configurationRelativePath, ISystemIdentity systemIdentity, CancellationToken cancellationToken)
200  {
201  await EnsureDirectories(cancellationToken).ConfigureAwait(false);
202  var path = ValidateConfigRelativePath(configurationRelativePath);
203 
204  ConfigurationFile result = null;
205 
206  void ReadImpl()
207  {
208  lock (this)
209  try
210  {
211  var content = synchronousIOManager.ReadFile(path);
212  string sha1String;
213 #pragma warning disable CA5350 // Do not use insecure cryptographic algorithm SHA1.
214  using (var sha1 = new SHA1Managed())
215 #pragma warning restore CA5350 // Do not use insecure cryptographic algorithm SHA1.
216  sha1String = String.Join("", sha1.ComputeHash(content).Select(b => b.ToString("x2", CultureInfo.InvariantCulture)));
217  result = new ConfigurationFile
218  {
219  Content = content,
220  IsDirectory = false,
221  LastReadHash = sha1String,
222  AccessDenied = false,
223  Path = configurationRelativePath
224  };
225  }
226  catch (UnauthorizedAccessException)
227  {
228  //this happens on windows, dunno about linux
229  bool isDirectory;
230  try
231  {
232  isDirectory = synchronousIOManager.IsDirectory(path);
233  }
234  catch
235  {
236  isDirectory = false;
237  }
238 
239  result = new ConfigurationFile
240  {
241  Path = configurationRelativePath
242  };
243  if (!isDirectory)
244  result.AccessDenied = true;
245  else
246  result.IsDirectory = true;
247  }
248  }
249 
250  using (await SemaphoreSlimContext.Lock(semaphore, cancellationToken).ConfigureAwait(false))
251  if (systemIdentity == null)
252  await Task.Factory.StartNew(ReadImpl, cancellationToken, TaskCreationOptions.LongRunning, TaskScheduler.Current).ConfigureAwait(false);
253  else
254  await systemIdentity.RunImpersonated(ReadImpl, cancellationToken).ConfigureAwait(false);
255 
256  return result;
257  }
258 
260  public async Task SymlinkStaticFilesTo(string destination, CancellationToken cancellationToken)
261  {
262  async Task SymlinkBase(bool files)
263  {
264  Task<IReadOnlyList<string>> task;
265  if (files)
266  task = ioManager.GetFiles(GameStaticFilesSubdirectory, cancellationToken);
267  else
268  task = ioManager.GetDirectories(GameStaticFilesSubdirectory, cancellationToken);
269  var entries = await task.ConfigureAwait(false);
270 
271  await Task.WhenAll(task.Result.Select(async x =>
272  {
273  var destPath = ioManager.ConcatPath(destination, ioManager.GetFileName(x));
274  logger.LogTrace("Symlinking {0} to {1}...", x, destPath);
275  var fileExistsTask = ioManager.FileExists(destPath, cancellationToken);
276  if (await ioManager.DirectoryExists(destPath, cancellationToken).ConfigureAwait(false))
277  await ioManager.DeleteDirectory(destPath, cancellationToken).ConfigureAwait(false);
278  var fileExists = await fileExistsTask.ConfigureAwait(false);
279  if (fileExists)
280  await ioManager.DeleteFile(destPath, cancellationToken).ConfigureAwait(false);
281  await symlinkFactory.CreateSymbolicLink(ioManager.ResolvePath(x), ioManager.ResolvePath(destPath), cancellationToken).ConfigureAwait(false);
282  })).ConfigureAwait(false);
283  }
284 
285  using (await SemaphoreSlimContext.Lock(semaphore, cancellationToken).ConfigureAwait(false))
286  {
287  await EnsureDirectories(cancellationToken).ConfigureAwait(false);
288  await Task.WhenAll(SymlinkBase(true), SymlinkBase(false)).ConfigureAwait(false);
289  }
290  }
291 
293  public async Task<ConfigurationFile> Write(string configurationRelativePath, ISystemIdentity systemIdentity, byte[] data, string previousHash, CancellationToken cancellationToken)
294  {
295  await EnsureDirectories(cancellationToken).ConfigureAwait(false);
296  var path = ValidateConfigRelativePath(configurationRelativePath);
297 
298  ConfigurationFile result = null;
299 
300  void WriteImpl()
301  {
302  lock (this)
303  try
304  {
305  var fileHash = previousHash;
306  var success = synchronousIOManager.WriteFileChecked(path, data, ref fileHash, cancellationToken);
307  if (!success)
308  return;
309  result = new ConfigurationFile
310  {
311  Content = data,
312  IsDirectory = false,
313  LastReadHash = fileHash,
314  AccessDenied = false,
315  Path = configurationRelativePath
316  };
317  }
318  catch (UnauthorizedAccessException)
319  {
320  //this happens on windows, dunno about linux
321  bool isDirectory;
322  try
323  {
324  isDirectory = synchronousIOManager.IsDirectory(path);
325  }
326  catch
327  {
328  isDirectory = false;
329  }
330 
331  result = new ConfigurationFile
332  {
333  Path = configurationRelativePath
334  };
335  if (!isDirectory)
336  result.AccessDenied = true;
337  else
338  result.IsDirectory = true;
339  }
340  }
341 
342  using (await SemaphoreSlimContext.Lock(semaphore, cancellationToken).ConfigureAwait(false))
343  if (systemIdentity == null)
344  await Task.Factory.StartNew(WriteImpl, cancellationToken, TaskCreationOptions.LongRunning, TaskScheduler.Current).ConfigureAwait(false);
345  else
346  await systemIdentity.RunImpersonated(WriteImpl, cancellationToken).ConfigureAwait(false);
347 
348  return result;
349  }
350 
352  public async Task<bool> CreateDirectory(string configurationRelativePath, ISystemIdentity systemIdentity, CancellationToken cancellationToken)
353  {
354  await EnsureDirectories(cancellationToken).ConfigureAwait(false);
355  var path = ValidateConfigRelativePath(configurationRelativePath);
356 
357  bool? result = null;
358  void DoCreate() => result = synchronousIOManager.CreateDirectory(path, cancellationToken);
359 
360  using (await SemaphoreSlimContext.Lock(semaphore, cancellationToken).ConfigureAwait(false))
361  if (systemIdentity == null)
362  await Task.Factory.StartNew(DoCreate, cancellationToken, TaskCreationOptions.LongRunning, TaskScheduler.Current).ConfigureAwait(false);
363  else
364  await systemIdentity.RunImpersonated(DoCreate, cancellationToken).ConfigureAwait(false);
365 
366  return result.Value;
367  }
368 
370  public Task StartAsync(CancellationToken cancellationToken) => EnsureDirectories(cancellationToken);
371 
373  public Task StopAsync(CancellationToken cancellationToken) => EnsureDirectories(cancellationToken);
374 
376  public async Task<bool> HandleEvent(EventType eventType, IEnumerable<string> parameters, CancellationToken cancellationToken)
377  {
378  await EnsureDirectories(cancellationToken).ConfigureAwait(false);
379 
380  if (!EventTypeScriptFileNameMap.TryGetValue(eventType, out var scriptName))
381  return true;
382 
383  //always execute in serial
384  using (await SemaphoreSlimContext.Lock(semaphore, cancellationToken).ConfigureAwait(false))
385  {
386  var files = await ioManager.GetFilesWithExtension(EventScriptsSubdirectory, SystemScriptFileExtension, cancellationToken).ConfigureAwait(false);
387  var resolvedScriptsDir = ioManager.ResolvePath(EventScriptsSubdirectory);
388 
389  foreach (var I in files.Select(x => ioManager.GetFileName(x)).Where(x => x.StartsWith(scriptName, StringComparison.Ordinal)))
390  using (var script = processExecutor.LaunchProcess(ioManager.ConcatPath(resolvedScriptsDir, I), resolvedScriptsDir, String.Join(' ', parameters), noShellExecute: true))
391  using (cancellationToken.Register(() => script.Terminate()))
392  {
393  var exitCode = await script.Lifetime.ConfigureAwait(false);
394  cancellationToken.ThrowIfCancellationRequested();
395  if (exitCode != 0)
396  return false;
397  }
398  }
399  return true;
400  }
401 
403  public async Task<bool> DeleteDirectory(string configurationRelativePath, ISystemIdentity systemIdentity, CancellationToken cancellationToken)
404  {
405  await EnsureDirectories(cancellationToken).ConfigureAwait(false);
406  var path = ValidateConfigRelativePath(configurationRelativePath);
407 
408  var result = false;
409  using (await SemaphoreSlimContext.Lock(semaphore, cancellationToken).ConfigureAwait(false))
410  {
411  void CheckDeleteImpl() => result = synchronousIOManager.DeleteDirectory(path);
412 
413  if (systemIdentity != null)
414  await systemIdentity.RunImpersonated(CheckDeleteImpl, cancellationToken).ConfigureAwait(false);
415  else
416  CheckDeleteImpl();
417  }
418  return result;
419  }
420  }
421 }
readonly ISynchronousIOManager synchronousIOManager
The ISynchronousIOManager for Configuration
+
async Task< bool > DeleteDirectory(string configurationRelativePath, ISystemIdentity systemIdentity, CancellationToken cancellationToken)
Attempt to delete an empty directory at configurationRelativePath
async Task< ServerSideModifications > CopyDMFilesTo(string dmeFile, string destination, CancellationToken cancellationToken)
Copies all files in the CodeModifications directory to destination
-
async Task< bool > CreateDirectory(string configurationRelativePath, ISystemIdentity systemIdentity, CancellationToken cancellationToken)
Create an empty directory at configurationRelativePath
-
async Task< ConfigurationFile > Write(string configurationRelativePath, ISystemIdentity systemIdentity, byte[] data, string previousHash, CancellationToken cancellationToken)
Writes to a given configurationRelativePath
+
async Task< bool > CreateDirectory(string configurationRelativePath, ISystemIdentity systemIdentity, CancellationToken cancellationToken)
Create an empty directory at configurationRelativePath
+
async Task< ConfigurationFile > Write(string configurationRelativePath, ISystemIdentity systemIdentity, byte[] data, string previousHash, CancellationToken cancellationToken)
Writes to a given configurationRelativePath
Represents a user on the current System.Runtime.InteropServices.OSPlatform
-
async Task< IReadOnlyList< ConfigurationFile > > ListDirectory(string configurationRelativePath, ISystemIdentity systemIdentity, CancellationToken cancellationToken)
Get ConfigurationFile for all items in a given configurationRelativePath
+
async Task< IReadOnlyList< ConfigurationFile > > ListDirectory(string configurationRelativePath, ISystemIdentity systemIdentity, CancellationToken cancellationToken)
Get ConfigurationFile for all items in a given configurationRelativePath
readonly IProcessExecutor processExecutor
The IProcessExecutor for Configuration
bool IsDirectory
If Path represents a directory
readonly ILogger< Configuration > logger
The ILogger for Configuration
-
async Task< bool > HandleEvent(EventType eventType, IEnumerable< string > parameters, CancellationToken cancellationToken)
Handle a given eventType
+
async Task< bool > HandleEvent(EventType eventType, IEnumerable< string > parameters, CancellationToken cancellationToken)
Handle a given eventType
For accessing the disk in a synchronous manner
-
string ValidateConfigRelativePath(string configurationRelativePath)
+
string ValidateConfigRelativePath(string configurationRelativePath)
readonly ISymlinkFactory symlinkFactory
The ISymlinkFactory for Configuration
Task RunImpersonated(Action action, CancellationToken cancellationToken)
Runs a given action in the context of the ISystemIdentity
readonly IIOManager ioManager
The IIOManager for Configuration
-
async Task< ConfigurationFile > Read(string configurationRelativePath, ISystemIdentity systemIdentity, CancellationToken cancellationToken)
Reads a given configurationRelativePath
+
async Task< ConfigurationFile > Read(string configurationRelativePath, ISystemIdentity systemIdentity, CancellationToken cancellationToken)
Reads a given configurationRelativePath
Configuration(IIOManager ioManager, ISynchronousIOManager synchronousIOManager, ISymlinkFactory symlinkFactory, IProcessExecutor processExecutor, IPostWriteHandler postWriteHandler, ILogger< Configuration > logger)
Construct Configuration
readonly SemaphoreSlim semaphore
The SemaphoreSlim for Configuration
Represents a game configuration file. Create and delete actions uncerimonuously overwrite/delete file...
@@ -127,7 +128,7 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
Interface for using filesystems
Definition: IIOManager.cs:11
-
async Task SymlinkStaticFilesTo(string destination, CancellationToken cancellationToken)
Symlinks all directories in the GameData directory to destination
+
async Task SymlinkStaticFilesTo(string destination, CancellationToken cancellationToken)
Symlinks all directories in the GameData directory to destination
static async Task< SemaphoreSlimContext > Lock(SemaphoreSlim semaphore, CancellationToken cancellationToken)
Asyncronously locks a semaphore
diff --git a/_configuration_client_8cs_source.html b/_configuration_client_8cs_source.html index a21c1cab5f..8d0f182804 100644 --- a/_configuration_client_8cs_source.html +++ b/_configuration_client_8cs_source.html @@ -91,19 +91,19 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
ConfigurationClient.cs
-Go to the documentation of this file.
1 using System;
2 using System.Collections.Generic;
3 using System.Threading;
4 using System.Threading.Tasks;
5 using Tgstation.Server.Api;
7 
8 namespace Tgstation.Server.Client.Components
9 {
12  {
20  readonly Instance instance;
21 
27  public ConfigurationClient(IApiClient apiClient, Instance instance)
28  {
29  this.apiClient = apiClient ?? throw new ArgumentNullException(nameof(apiClient));
30  this.instance = instance ?? throw new ArgumentNullException(nameof(instance));
31  }
32 
34  public Task<IReadOnlyList<ConfigurationFile>> List(string directory, CancellationToken cancellationToken)
35  {
36  if (directory == null)
37  directory = String.Empty;
38  return apiClient.Read<IReadOnlyList<ConfigurationFile>>(Routes.List(Routes.Configuration) + directory, instance.Id, cancellationToken);
39  }
40 
42  public Task<ConfigurationFile> Read(ConfigurationFile file, CancellationToken cancellationToken)
43  {
44  if (file == null)
45  throw new ArgumentNullException(nameof(file));
46  return apiClient.Read<ConfigurationFile>(Routes.Configuration + file.Path, instance.Id, cancellationToken);
47  }
48 
50  public Task<ConfigurationFile> Write(ConfigurationFile file, CancellationToken cancellationToken) => apiClient.Update<ConfigurationFile, ConfigurationFile>(Routes.Configuration, file ?? throw new ArgumentNullException(nameof(file)), instance.Id, cancellationToken);
51  }
52 }
const string Configuration
The Models.ConfigurationFile controller
Definition: Routes.cs:49
+Go to the documentation of this file.
1 using System;
2 using System.Collections.Generic;
3 using System.Threading;
4 using System.Threading.Tasks;
5 using Tgstation.Server.Api;
7 
8 namespace Tgstation.Server.Client.Components
9 {
12  {
20  readonly Instance instance;
21 
27  static string SanitizeGetPath(string path)
28  {
29  if (path == null)
30  path = String.Empty;
31  if (path.Length == 0 || path[0] != '/')
32  path = '/' + path;
33  return path;
34  }
35 
41  public ConfigurationClient(IApiClient apiClient, Instance instance)
42  {
43  this.apiClient = apiClient ?? throw new ArgumentNullException(nameof(apiClient));
44  this.instance = instance ?? throw new ArgumentNullException(nameof(instance));
45  }
46 
48  public Task DeleteEmptyDirectory(ConfigurationFile directory, CancellationToken cancellationToken) => apiClient.Delete(Routes.Configuration, directory, instance.Id, cancellationToken);
49 
51  public Task<IReadOnlyList<ConfigurationFile>> List(string directory, CancellationToken cancellationToken) => apiClient.Read<IReadOnlyList<ConfigurationFile>>(Routes.List(Routes.Configuration) + SanitizeGetPath(directory), instance.Id, cancellationToken);
52 
54  public Task<ConfigurationFile> Read(ConfigurationFile file, CancellationToken cancellationToken)
55  {
56  if (file == null)
57  throw new ArgumentNullException(nameof(file));
58  return apiClient.Read<ConfigurationFile>(Routes.Configuration + SanitizeGetPath(file.Path), instance.Id, cancellationToken);
59  }
60 
62  public Task<ConfigurationFile> Write(ConfigurationFile file, CancellationToken cancellationToken) => apiClient.Update<ConfigurationFile, ConfigurationFile>(Routes.Configuration, file ?? throw new ArgumentNullException(nameof(file)), instance.Id, cancellationToken);
63  }
64 }
const string Configuration
The Models.ConfigurationFile controller
Definition: Routes.cs:49
readonly IApiClient apiClient
The IApiClient for the ConfigurationClient
-
Task< ConfigurationFile > Read(ConfigurationFile file, CancellationToken cancellationToken)
Read a ConfigurationFile file
+
Task< ConfigurationFile > Read(ConfigurationFile file, CancellationToken cancellationToken)
Read a ConfigurationFile file
string Path
The path to the ConfigurationFile file
Routes to a server actions
Definition: Routes.cs:9
readonly Instance instance
The Instance for the ConfigurationClient
+
static string SanitizeGetPath(string path)
Sanitize a ConfigurationFile path for use in a GET Uri
Represents a game configuration file. Create and delete actions uncerimonuously overwrite/delete file...
-
Task< IReadOnlyList< ConfigurationFile > > List(string directory, CancellationToken cancellationToken)
List configuration files
-
ConfigurationClient(IApiClient apiClient, Instance instance)
Construct a ConfigurationClient
+
ConfigurationClient(IApiClient apiClient, Instance instance)
Construct a ConfigurationClient
Metadata about a server instance
Definition: Instance.cs:9
diff --git a/_configuration_controller_8cs_source.html b/_configuration_controller_8cs_source.html index f316f86e1b..00520fa699 100644 --- a/_configuration_controller_8cs_source.html +++ b/_configuration_controller_8cs_source.html @@ -91,7 +91,7 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
ConfigurationController.cs
-Go to the documentation of this file.
1 using Microsoft.AspNetCore.Mvc;
2 using Microsoft.Extensions.Logging;
3 using System;
4 using System.IO;
5 using System.Net;
6 using System.Threading;
7 using System.Threading.Tasks;
8 using Tgstation.Server.Api;
14 
15 namespace Tgstation.Server.Host.Controllers
16 {
20  [Route(Routes.Configuration)]
21  public sealed class ConfigurationController : ModelController<ConfigurationFile>
22  {
27 
35  public ConfigurationController(IDatabaseContext databaseContext, IAuthenticationContextFactory authenticationContextFactory, IInstanceManager instanceManager, ILogger<ConfigurationController> logger) : base(databaseContext, authenticationContextFactory, logger, true)
36  {
37  this.instanceManager = instanceManager ?? throw new ArgumentNullException(nameof(instanceManager));
38  }
39 
44  bool ForbidDueToModeConflicts() => Instance.ConfigurationType == ConfigurationType.Disallowed || (Instance.ConfigurationType == ConfigurationType.SystemIdentityWrite && AuthenticationContext.SystemIdentity == null);
45 
47  [TgsAuthorize(ConfigurationRights.Write)]
48  public override async Task<IActionResult> Update([FromBody] ConfigurationFile model, CancellationToken cancellationToken)
49  {
50  if (ForbidDueToModeConflicts())
51  return Forbid();
52 
53  var config = instanceManager.GetInstance(Instance).Configuration;
54  try
55  {
56  var newFile = await config.Write(model.Path, AuthenticationContext.SystemIdentity, model.Content, model.LastReadHash, cancellationToken).ConfigureAwait(false);
57  if (newFile == null)
58  return Conflict(new ErrorMessage
59  {
60  Message = ""
61  });
62 
63  newFile.Content = null;
64 
65  return model.LastReadHash == null ? (IActionResult)StatusCode((int)HttpStatusCode.Created, newFile) : Json(newFile);
66  }
67  catch(IOException e)
68  {
69  Logger.LogInformation("IOException while updating file {0}: {1}", model.Path, e);
70  return Conflict(new ErrorMessage
71  {
72  Message = e.Message
73  });
74  }
75  catch (NotImplementedException)
76  {
77  return StatusCode((int)HttpStatusCode.NotImplemented);
78  }
79  }
80 
87  [HttpGet("File/{*filePath}")]
88  [TgsAuthorize(ConfigurationRights.Read)]
89  public async Task<IActionResult> File(string filePath, CancellationToken cancellationToken)
90  {
91  if (ForbidDueToModeConflicts())
92  return Forbid();
93 
94  try
95  {
96  var result = await instanceManager.GetInstance(Instance).Configuration.Read(filePath, AuthenticationContext.SystemIdentity, cancellationToken).ConfigureAwait(false);
97  if (result == null)
98  return StatusCode((int)HttpStatusCode.Gone);
99 
100  return Json(result);
101  }
102  catch (IOException e)
103  {
104  Logger.LogInformation("IOException while reading file {0}: {1}", filePath, e);
105  return Conflict(new ErrorMessage
106  {
107  Message = e.Message
108  });
109  }
110  catch (NotImplementedException)
111  {
112  return StatusCode((int)HttpStatusCode.NotImplemented);
113  }
114  }
115 
122  [HttpGet("List/{*directoryPath}")]
123  [TgsAuthorize(ConfigurationRights.List)]
124  public async Task<IActionResult> Directory(string directoryPath, CancellationToken cancellationToken)
125  {
126  if (ForbidDueToModeConflicts())
127  return Forbid();
128 
129  try
130  {
131  var result = await instanceManager.GetInstance(Instance).Configuration.ListDirectory(directoryPath, AuthenticationContext.SystemIdentity, cancellationToken).ConfigureAwait(false);
132  if (result == null)
133  return StatusCode((int)HttpStatusCode.Gone);
134 
135  return Json(result);
136  }
137  catch (NotImplementedException)
138  {
139  return StatusCode((int)HttpStatusCode.NotImplemented);
140  }
141  catch (UnauthorizedAccessException)
142  {
143  return Forbid();
144  }
145  }
146 
148  [TgsAuthorize(ConfigurationRights.List)]
149  public override Task<IActionResult> List(CancellationToken cancellationToken) => Directory(null, cancellationToken);
150 
152  [TgsAuthorize(ConfigurationRights.Write)]
153  public override async Task<IActionResult> Create([FromBody] ConfigurationFile model, CancellationToken cancellationToken)
154  {
155  if (ForbidDueToModeConflicts())
156  return Forbid();
157 
158  try
159  {
160  return await instanceManager.GetInstance(Instance).Configuration.CreateDirectory(model.Path, AuthenticationContext.SystemIdentity, cancellationToken).ConfigureAwait(false) ? (IActionResult)Json(model) : StatusCode((int)HttpStatusCode.Created, model);
161  }
162  catch (NotImplementedException)
163  {
164  return StatusCode((int)HttpStatusCode.NotImplemented);
165  }
166  catch (UnauthorizedAccessException)
167  {
168  return Forbid();
169  }
170  }
171  }
172 }
+Go to the documentation of this file.
1 using Microsoft.AspNetCore.Mvc;
2 using Microsoft.Extensions.Logging;
3 using System;
4 using System.IO;
5 using System.Net;
6 using System.Threading;
7 using System.Threading.Tasks;
8 using Tgstation.Server.Api;
12 using Tgstation.Server.Host.IO;
15 
16 namespace Tgstation.Server.Host.Controllers
17 {
21  [Route(Routes.Configuration)]
22  public sealed class ConfigurationController : ModelController<ConfigurationFile>
23  {
28 
33 
42  public ConfigurationController(IDatabaseContext databaseContext, IAuthenticationContextFactory authenticationContextFactory, IInstanceManager instanceManager, IIOManager ioManager, ILogger<ConfigurationController> logger) : base(databaseContext, authenticationContextFactory, logger, true)
43  {
44  this.instanceManager = instanceManager ?? throw new ArgumentNullException(nameof(instanceManager));
45  this.ioManager = ioManager ?? throw new ArgumentNullException(nameof(ioManager));
46  }
47 
53  bool ForbidDueToModeConflicts(string path) => Instance.ConfigurationType == ConfigurationType.Disallowed || (Instance.ConfigurationType == ConfigurationType.SystemIdentityWrite && AuthenticationContext.SystemIdentity == null) || (path != null && ioManager.PathContainsParentAccess(path));
54 
56  [TgsAuthorize(ConfigurationRights.Write)]
57  public override async Task<IActionResult> Update([FromBody] ConfigurationFile model, CancellationToken cancellationToken)
58  {
59  if (model == null)
60  throw new ArgumentNullException(nameof(model));
61  if (ForbidDueToModeConflicts(model.Path))
62  return Forbid();
63 
64  var config = instanceManager.GetInstance(Instance).Configuration;
65  try
66  {
67  var newFile = await config.Write(model.Path, AuthenticationContext.SystemIdentity, model.Content, model.LastReadHash, cancellationToken).ConfigureAwait(false);
68  if (newFile == null)
69  return Conflict(new ErrorMessage
70  {
71  Message = ""
72  });
73 
74  newFile.Content = null;
75 
76  return model.LastReadHash == null ? (IActionResult)StatusCode((int)HttpStatusCode.Created, newFile) : Json(newFile);
77  }
78  catch(IOException e)
79  {
80  Logger.LogInformation("IOException while updating file {0}: {1}", model.Path, e);
81  return Conflict(new ErrorMessage
82  {
83  Message = e.Message
84  });
85  }
86  catch (NotImplementedException)
87  {
88  return StatusCode((int)HttpStatusCode.NotImplemented);
89  }
90  }
91 
98  [HttpGet("File/{*filePath}")]
99  [TgsAuthorize(ConfigurationRights.Read)]
100  public async Task<IActionResult> File(string filePath, CancellationToken cancellationToken)
101  {
102  if (ForbidDueToModeConflicts(filePath))
103  return Forbid();
104 
105  try
106  {
107  var result = await instanceManager.GetInstance(Instance).Configuration.Read(filePath, AuthenticationContext.SystemIdentity, cancellationToken).ConfigureAwait(false);
108  if (result == null)
109  return StatusCode((int)HttpStatusCode.Gone);
110 
111  return Json(result);
112  }
113  catch (IOException e)
114  {
115  Logger.LogInformation("IOException while reading file {0}: {1}", filePath, e);
116  return Conflict(new ErrorMessage
117  {
118  Message = e.Message
119  });
120  }
121  catch (NotImplementedException)
122  {
123  return StatusCode((int)HttpStatusCode.NotImplemented);
124  }
125  }
126 
133  [HttpGet("List/{*directoryPath}")]
134  [TgsAuthorize(ConfigurationRights.List)]
135  public async Task<IActionResult> Directory(string directoryPath, CancellationToken cancellationToken)
136  {
137  if (ForbidDueToModeConflicts(directoryPath))
138  return Forbid();
139 
140  try
141  {
142  var result = await instanceManager.GetInstance(Instance).Configuration.ListDirectory(directoryPath, AuthenticationContext.SystemIdentity, cancellationToken).ConfigureAwait(false);
143  if (result == null)
144  return StatusCode((int)HttpStatusCode.Gone);
145 
146  return Json(result);
147  }
148  catch (NotImplementedException)
149  {
150  return StatusCode((int)HttpStatusCode.NotImplemented);
151  }
152  catch (UnauthorizedAccessException)
153  {
154  return Forbid();
155  }
156  }
157 
159  [TgsAuthorize(ConfigurationRights.List)]
160  public override Task<IActionResult> List(CancellationToken cancellationToken) => Directory(null, cancellationToken);
161 
163  [TgsAuthorize(ConfigurationRights.Write)]
164  public override async Task<IActionResult> Create([FromBody] ConfigurationFile model, CancellationToken cancellationToken)
165  {
166  if (model == null)
167  throw new ArgumentNullException(nameof(model));
168 
169  if (ForbidDueToModeConflicts(model.Path))
170  return Forbid();
171 
172  try
173  {
174  return await instanceManager.GetInstance(Instance).Configuration.CreateDirectory(model.Path, AuthenticationContext.SystemIdentity, cancellationToken).ConfigureAwait(false) ? (IActionResult)Json(model) : StatusCode((int)HttpStatusCode.Created, model);
175  }
176  catch (NotImplementedException)
177  {
178  return StatusCode((int)HttpStatusCode.NotImplemented);
179  }
180  catch (UnauthorizedAccessException)
181  {
182  return Forbid();
183  }
184  }
185 
192  [HttpDelete]
193  [TgsAuthorize(ConfigurationRights.Delete)]
194  public async Task<IActionResult> Delete([FromBody] ConfigurationFile directory, CancellationToken cancellationToken)
195  {
196  if (directory == null)
197  throw new ArgumentNullException(nameof(directory));
198 
199  if (ForbidDueToModeConflicts(directory.Path))
200  return Forbid();
201 
202  try
203  {
204  return await instanceManager.GetInstance(Instance).Configuration.DeleteDirectory(directory.Path, AuthenticationContext.SystemIdentity, cancellationToken).ConfigureAwait(false) ? (IActionResult)Ok() : Conflict(new ErrorMessage
205  {
206  Message = "Directory not empty!"
207  });
208  }
209  catch (NotImplementedException)
210  {
211  return StatusCode((int)HttpStatusCode.NotImplemented);
212  }
213  catch (UnauthorizedAccessException)
214  {
215  return Forbid();
216  }
217  }
218  }
219 }
ConfigurationRights
Rights for Models.ConfigurationFile
@@ -100,13 +100,15 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search'); -
override async Task< IActionResult > Create([FromBody] ConfigurationFile model, CancellationToken cancellationToken)
-
readonly IInstanceManager instanceManager
The IInstanceManager for the ConfigurationController
+
override async Task< IActionResult > Create([FromBody] ConfigurationFile model, CancellationToken cancellationToken)
+
readonly IInstanceManager instanceManager
The IInstanceManager for the ConfigurationController
+
async Task< IActionResult > Delete([FromBody] ConfigurationFile directory, CancellationToken cancellationToken)
Deletes an empty directory
ConfigurationType
The type of configuration allowed on an Instance
+
ConfigurationController(IDatabaseContext databaseContext, IAuthenticationContextFactory authenticationContextFactory, IInstanceManager instanceManager, IIOManager ioManager, ILogger< ConfigurationController > logger)
Construct a UserController
-
The ModelController<TModel> for ConfigurationFiles
+
The ModelController<TModel> for ConfigurationFiles
Routes to a server actions
Definition: Routes.cs:9
-
ConfigurationController(IDatabaseContext databaseContext, IAuthenticationContextFactory authenticationContextFactory, IInstanceManager instanceManager, ILogger< ConfigurationController > logger)
Construct a UserController
+
readonly IIOManager ioManager
The IIOManager for the ConfigurationController
string Message
A human readable description of the error
Definition: ErrorMessage.cs:13
Represents a game configuration file. Create and delete actions uncerimonuously overwrite/delete file...
@@ -114,16 +116,18 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search'); -
async Task< IActionResult > File(string filePath, CancellationToken cancellationToken)
Get the contents of a file at a filePath
+
async Task< IActionResult > File(string filePath, CancellationToken cancellationToken)
Get the contents of a file at a filePath
Metadata about a server instance
Definition: Instance.cs:9
-
async Task< IActionResult > Directory(string directoryPath, CancellationToken cancellationToken)
Get the contents of a directory at a directoryPath
+
Interface for using filesystems
Definition: IIOManager.cs:11
+
async Task< IActionResult > Directory(string directoryPath, CancellationToken cancellationToken)
Get the contents of a directory at a directoryPath
Represents an error message returned by the server
Definition: ErrorMessage.cs:8
-
override async Task< IActionResult > Update([FromBody] ConfigurationFile model, CancellationToken cancellationToken)
+
override async Task< IActionResult > Update([FromBody] ConfigurationFile model, CancellationToken cancellationToken)
+
ConfigurationType ConfigurationType
If ConfigurationFile can be used on the Instance
Definition: Instance.cs:38
diff --git a/_configuration_rights_8cs.html b/_configuration_rights_8cs.html index b2574ceb45..4fa5112fcc 100644 --- a/_configuration_rights_8cs.html +++ b/_configuration_rights_8cs.html @@ -104,10 +104,14 @@ Namespaces - diff --git a/_configuration_rights_8cs_source.html b/_configuration_rights_8cs_source.html index 942ca184bc..20c490c671 100644 --- a/_configuration_rights_8cs_source.html +++ b/_configuration_rights_8cs_source.html @@ -91,9 +91,10 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
ConfigurationRights.cs
-Go to the documentation of this file.
1 using System;
2 
3 namespace Tgstation.Server.Api.Rights
4 {
8  [Flags]
9  public enum ConfigurationRights : ulong
10  {
14  None = 0,
18  Read = 1,
22  Write = 2,
26  List = 4
27  }
28 }
ConfigurationRights
Rights for Models.ConfigurationFile
+Go to the documentation of this file.
1 using System;
2 
3 namespace Tgstation.Server.Api.Rights
4 {
8  [Flags]
9  public enum ConfigurationRights : ulong
10  {
14  None = 0,
18  Read = 1,
22  Write = 2,
26  List = 4,
30  Delete = 8
31  }
32 }
ConfigurationRights
Rights for Models.ConfigurationFile
+
User can read all chat settings except Models.Internal.ChatBot.ConnectionString
diff --git a/_test_application_8cs.html b/_configuration_test_8cs.html similarity index 82% rename from _test_application_8cs.html rename to _configuration_test_8cs.html index 428048ff98..87c718b32b 100644 --- a/_test_application_8cs.html +++ b/_configuration_test_8cs.html @@ -4,7 +4,7 @@ -tgstation-server: tests/Tgstation.Server.Host.Tests/Core/TestApplication.cs File Reference +tgstation-server: tests/Tgstation.Server.Tests/Instance/ConfigurationTest.cs File Reference @@ -83,7 +83,7 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
@@ -91,20 +91,20 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search'); Classes | Namespaces
-
TestApplication.cs File Reference
+
ConfigurationTest.cs File Reference

Enumerations

enum  Tgstation.Server.Api.Rights.ConfigurationRights : ulong { Tgstation.Server.Api.Rights.ConfigurationRights.None = 0, +
enum  Tgstation.Server.Api.Rights.ConfigurationRights : ulong {
+  Tgstation.Server.Api.Rights.ConfigurationRights.None = 0, Tgstation.Server.Api.Rights.ConfigurationRights.Read = 1, Tgstation.Server.Api.Rights.ConfigurationRights.Write = 2, -Tgstation.Server.Api.Rights.ConfigurationRights.List = 4 +Tgstation.Server.Api.Rights.ConfigurationRights.List = 4, +
+  Tgstation.Server.Api.Rights.ConfigurationRights.Delete = 8 +
}
 Rights for Models.ConfigurationFile More...
 
- +

Classes

class  Tgstation.Server.Host.Core.Tests.TestApplication
class  Tgstation.Server.Tests.Instance.ConfigurationTest
 
- +

Namespaces

namespace  Tgstation.Server.Host.Core.Tests
namespace  Tgstation.Server.Tests.Instance
 
diff --git a/_configuration_test_8cs_source.html b/_configuration_test_8cs_source.html new file mode 100644 index 0000000000..8bf4831df7 --- /dev/null +++ b/_configuration_test_8cs_source.html @@ -0,0 +1,121 @@ + + + + + + +tgstation-server: tests/Tgstation.Server.Tests/Instance/ConfigurationTest.cs Source File + + + + + + + + + + +
+
+ + + + + + + +
+
tgstation-server +
+
The /tg/station 13 server suite
+
+
+ + + + + + +
+
+ + +
+ +
+ + +
+
+
+
ConfigurationTest.cs
+
+
+Go to the documentation of this file.
1 using Microsoft.VisualStudio.TestTools.UnitTesting;
2 using System;
3 using System.IO;
4 using System.Text;
5 using System.Threading;
6 using System.Threading.Tasks;
10 
11 namespace Tgstation.Server.Tests.Instance
12 {
13  sealed class ConfigurationTest
14  {
16  readonly Api.Models.Instance instance;
17 
18  public ConfigurationTest(IConfigurationClient configurationClient, Api.Models.Instance instance)
19  {
20  this.configurationClient = configurationClient ?? throw new ArgumentNullException(nameof(configurationClient));
21  this.instance = instance ?? throw new ArgumentNullException(nameof(instance));
22  }
23 
25  {
26  var tmp = (file.Path?.StartsWith('/') ?? false) ? '.' + file.Path : file.Path;
27  var path = Path.Combine(instance.Path, "Configuration", tmp);
28  return File.Exists(path);
29  }
30 
31  async Task TestDeleteDirectory(CancellationToken cancellationToken)
32  {
33  //try to delete non-existent
34  var TestDir = new ConfigurationFile
35  {
36  Path = "/TestDeleteDir"
37  };
38 
39  await configurationClient.DeleteEmptyDirectory(TestDir, cancellationToken).ConfigureAwait(false);
40 
41  //try to delete non-empty
42  var file = await configurationClient.Write(new ConfigurationFile
43  {
44  Content = Encoding.UTF8.GetBytes("Hello world!"),
45  Path = TestDir.Path + "/test.txt"
46  }, cancellationToken).ConfigureAwait(false);
47 
48  Assert.IsTrue(FileExists(file));
49 
50  await Assert.ThrowsExceptionAsync<ConflictException>(() => configurationClient.DeleteEmptyDirectory(TestDir, cancellationToken)).ConfigureAwait(false);
51 
52  file.Content = null;
53  await configurationClient.Write(file, cancellationToken).ConfigureAwait(false);
54 
55  await configurationClient.DeleteEmptyDirectory(TestDir, cancellationToken).ConfigureAwait(false);
56  }
57 
58  public async Task Run(CancellationToken cancellationToken)
59  {
60  await TestDeleteDirectory(cancellationToken).ConfigureAwait(false);
61  }
62  }
63 }
+ + + + + + +
string Path
The path to the ConfigurationFile file
+ +
async Task TestDeleteDirectory(CancellationToken cancellationToken)
+
Represents a game configuration file. Create and delete actions uncerimonuously overwrite/delete file...
+
Occurs when the client performs an action that would result in data conflict
+
Metadata about a server instance
Definition: Instance.cs:9
+ + + +
ConfigurationTest(IConfigurationClient configurationClient, Api.Models.Instance instance)
+
async Task Run(CancellationToken cancellationToken)
+ +
+ + + + diff --git a/_database_configuration_8cs_source.html b/_database_configuration_8cs_source.html index 1ca05925b4..9b2299f3d4 100644 --- a/_database_configuration_8cs_source.html +++ b/_database_configuration_8cs_source.html @@ -91,7 +91,7 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
DatabaseConfiguration.cs
-Go to the documentation of this file.
1 namespace Tgstation.Server.Host.Configuration
2 {
6  sealed class DatabaseConfiguration
7  {
11  public const string Section = "Database";
12 
16  public DatabaseType DatabaseType { get; set; }
17 
21  public bool ResetAdminPassword { get; set; }
22 
26  public string ConnectionString { get; set; }
27 
31  public bool NoMigrations { get; set; }
32  }
33 }
Configuration options for the Models.DatabaseContext<TParentContext>
+Go to the documentation of this file.
1 namespace Tgstation.Server.Host.Configuration
2 {
6  sealed class DatabaseConfiguration
7  {
11  public const string Section = "Database";
12 
16  public DatabaseType DatabaseType { get; set; }
17 
21  public bool ResetAdminPassword { get; set; }
22 
26  public string ConnectionString { get; set; }
27 
31  public bool DropDatabase { get; set; }
32 
36  public string MySqlServerVersion { get; set; }
37  }
38 }
Configuration options for the Models.DatabaseContext<TParentContext>
DatabaseType
Type of database to user
Definition: DatabaseType.cs:6
diff --git a/_database_context_8cs_source.html b/_database_context_8cs_source.html index 272334fa86..ae68d0700f 100644 --- a/_database_context_8cs_source.html +++ b/_database_context_8cs_source.html @@ -91,7 +91,7 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
DatabaseContext.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.Linq;
6 using System.Threading;
7 using System.Threading.Tasks;
9 
10 namespace Tgstation.Server.Host.Models
11 {
13  abstract class DatabaseContext<TParentContext> : DbContext, IDatabaseContext where TParentContext : DbContext
14  {
16  public DbSet<User> Users { get; set; }
17 
19  public DbSet<Instance> Instances { get; set; }
20 
22  public DbSet<CompileJob> CompileJobs { get; set; }
23 
25  public DbSet<RevisionInformation> RevisionInformations { get; set; }
26 
28  public DbSet<DreamMakerSettings> DreamMakerSettings { get; set; }
29 
31  public DbSet<ChatBot> ChatBots { get; set; }
32 
34  public DbSet<DreamDaemonSettings> DreamDaemonSettings { get; set; }
35 
37  public DbSet<RepositorySettings> RepositorySettings { get; set; }
38 
40  public DbSet<InstanceUser> InstanceUsers { get; set; }
41 
43  public DbSet<ChatChannel> ChatChannels { get; set; }
44 
46  public DbSet<Job> Jobs { get; set; }
47 
49  public DbSet<ReattachInformation> ReattachInformations { get; set; }
50 
52  public DbSet<WatchdogReattachInformation> WatchdogReattachInformations { get; set; }
53 
57  public DbSet<TestMerge> TestMerges { get; set; }
58 
62  public DbSet<RevInfoTestMerge> RevInfoTestMerges { get; set; }
63 
67  protected ILogger Logger { get; }
68 
72  protected string ConnectionString => databaseConfiguration.ConnectionString;
73 
78 
83 
91  public DatabaseContext(DbContextOptions<TParentContext> dbContextOptions, IOptions<DatabaseConfiguration> databaseConfigurationOptions, IDatabaseSeeder databaseSeeder, ILogger logger) : base(dbContextOptions)
92  {
93  databaseConfiguration = databaseConfigurationOptions?.Value ?? throw new ArgumentNullException(nameof(databaseConfigurationOptions));
94  this.databaseSeeder = databaseSeeder ?? throw new ArgumentNullException(nameof(databaseSeeder));
95  Logger = logger ?? throw new ArgumentNullException(nameof(logger));
96  }
97 
99  protected override void OnModelCreating(ModelBuilder modelBuilder)
100  {
101  Logger.LogTrace("Building entity framework context...");
102  base.OnModelCreating(modelBuilder);
103 
104  var userModel = modelBuilder.Entity<User>();
105  userModel.HasIndex(x => x.CanonicalName).IsUnique();
106  userModel.HasMany(x => x.TestMerges).WithOne(x => x.MergedBy).OnDelete(DeleteBehavior.Restrict);
107 
108  modelBuilder.Entity<InstanceUser>().HasIndex(x => new { x.UserId, x.InstanceId }).IsUnique();
109 
110  modelBuilder.Entity<TestMerge>().HasMany(x => x.RevisonInformations).WithOne(x => x.TestMerge).OnDelete(DeleteBehavior.Cascade);
111 
112  var revInfo = modelBuilder.Entity<RevisionInformation>();
113  revInfo.HasMany(x => x.CompileJobs).WithOne(x => x.RevisionInformation).OnDelete(DeleteBehavior.Cascade);
114  revInfo.HasMany(x => x.ActiveTestMerges).WithOne(x => x.RevisionInformation).OnDelete(DeleteBehavior.Cascade);
115  revInfo.HasOne(x => x.PrimaryTestMerge).WithOne(x => x.PrimaryRevisionInformation).OnDelete(DeleteBehavior.SetNull);
116  revInfo.HasIndex(x => x.CommitSha).IsUnique();
117 
118  modelBuilder.Entity<CompileJob>().HasIndex(x => x.DirectoryName);
119 
120  var chatChannel = modelBuilder.Entity<ChatChannel>();
121  chatChannel.HasIndex(x => new { x.ChatSettingsId, x.IrcChannel }).IsUnique();
122  chatChannel.HasIndex(x => new { x.ChatSettingsId, x.DiscordChannelId }).IsUnique();
123  chatChannel.HasOne(x => x.ChatSettings).WithMany(x => x.Channels).HasForeignKey(x => x.ChatSettingsId).OnDelete(DeleteBehavior.Cascade);
124 
125  modelBuilder.Entity<ChatBot>().HasIndex(x => x.Name).IsUnique();
126 
127  var instanceModel = modelBuilder.Entity<Instance>();
128  instanceModel.HasIndex(x => x.Path).IsUnique();
129  instanceModel.HasMany(x => x.ChatSettings).WithOne(x => x.Instance).OnDelete(DeleteBehavior.Cascade);
130  instanceModel.HasOne(x => x.DreamDaemonSettings).WithOne(x => x.Instance).OnDelete(DeleteBehavior.Cascade);
131  instanceModel.HasOne(x => x.DreamMakerSettings).WithOne(x => x.Instance).OnDelete(DeleteBehavior.Cascade);
132  instanceModel.HasOne(x => x.RepositorySettings).WithOne(x => x.Instance).OnDelete(DeleteBehavior.Cascade);
133  instanceModel.HasMany(x => x.RevisionInformations).WithOne(x => x.Instance).OnDelete(DeleteBehavior.Cascade);
134  instanceModel.HasMany(x => x.InstanceUsers).WithOne(x => x.Instance).OnDelete(DeleteBehavior.Cascade);
135  instanceModel.HasMany(x => x.Jobs).WithOne(x => x.Instance).OnDelete(DeleteBehavior.Cascade);
136  }
137 
139  public async Task Initialize(CancellationToken cancellationToken)
140  {
141  Logger.LogInformation("Migrating database...");
142 
143  var wasEmpty = false;
144  if (databaseConfiguration.NoMigrations)
145  {
146  Logger.LogWarning("Using all or nothing migration strategy!");
147  await Database.EnsureCreatedAsync(cancellationToken).ConfigureAwait(false);
148  }
149  else
150  {
151  var migrations = await Database.GetAppliedMigrationsAsync(cancellationToken).ConfigureAwait(false);
152  wasEmpty = !migrations.Any();
153  await Database.MigrateAsync(cancellationToken).ConfigureAwait(false);
154  }
155 
156  wasEmpty |= (await Users.CountAsync(cancellationToken).ConfigureAwait(false)) == 0;
157 
158  if (wasEmpty)
159  {
160  Logger.LogInformation("Seeding database...");
161  await databaseSeeder.SeedDatabase(this, cancellationToken).ConfigureAwait(false);
162  }
163  else
164  {
165  Logger.LogDebug("No migrations applied!");
166  if (databaseConfiguration.ResetAdminPassword)
167  {
168  Logger.LogWarning("Enabling and resetting admin password due to configuration!");
169  await databaseSeeder.ResetAdminPassword(this, cancellationToken).ConfigureAwait(false);
170  }
171  }
172  }
173 
175  public Task Save(CancellationToken cancellationToken) => SaveChangesAsync(cancellationToken);
176  }
177 }
+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.Linq;
6 using System.Threading;
7 using System.Threading.Tasks;
9 
10 namespace Tgstation.Server.Host.Models
11 {
13  abstract class DatabaseContext<TParentContext> : DbContext, IDatabaseContext where TParentContext : DbContext
14  {
16  public DbSet<User> Users { get; set; }
17 
19  public DbSet<Instance> Instances { get; set; }
20 
22  public DbSet<CompileJob> CompileJobs { get; set; }
23 
25  public DbSet<RevisionInformation> RevisionInformations { get; set; }
26 
28  public DbSet<DreamMakerSettings> DreamMakerSettings { get; set; }
29 
31  public DbSet<ChatBot> ChatBots { get; set; }
32 
34  public DbSet<DreamDaemonSettings> DreamDaemonSettings { get; set; }
35 
37  public DbSet<RepositorySettings> RepositorySettings { get; set; }
38 
40  public DbSet<InstanceUser> InstanceUsers { get; set; }
41 
43  public DbSet<ChatChannel> ChatChannels { get; set; }
44 
46  public DbSet<Job> Jobs { get; set; }
47 
49  public DbSet<ReattachInformation> ReattachInformations { get; set; }
50 
52  public DbSet<WatchdogReattachInformation> WatchdogReattachInformations { get; set; }
53 
57  public DbSet<TestMerge> TestMerges { get; set; }
58 
62  public DbSet<RevInfoTestMerge> RevInfoTestMerges { get; set; }
63 
67  protected ILogger Logger { get; }
68 
73 
78 
86  public DatabaseContext(DbContextOptions<TParentContext> dbContextOptions, IOptions<DatabaseConfiguration> databaseConfigurationOptions, IDatabaseSeeder databaseSeeder, ILogger logger) : base(dbContextOptions)
87  {
88  DatabaseConfiguration = databaseConfigurationOptions?.Value ?? throw new ArgumentNullException(nameof(databaseConfigurationOptions));
89  this.databaseSeeder = databaseSeeder ?? throw new ArgumentNullException(nameof(databaseSeeder));
90  Logger = logger ?? throw new ArgumentNullException(nameof(logger));
91  }
92 
94  protected override void OnModelCreating(ModelBuilder modelBuilder)
95  {
96  Logger.LogTrace("Building entity framework context...");
97  base.OnModelCreating(modelBuilder);
98 
99  var userModel = modelBuilder.Entity<User>();
100  userModel.HasIndex(x => x.CanonicalName).IsUnique();
101  userModel.HasMany(x => x.TestMerges).WithOne(x => x.MergedBy).OnDelete(DeleteBehavior.Restrict);
102 
103  modelBuilder.Entity<InstanceUser>().HasIndex(x => new { x.UserId, x.InstanceId }).IsUnique();
104 
105  modelBuilder.Entity<TestMerge>().HasMany(x => x.RevisonInformations).WithOne(x => x.TestMerge).OnDelete(DeleteBehavior.Cascade);
106 
107  var revInfo = modelBuilder.Entity<RevisionInformation>();
108  revInfo.HasMany(x => x.CompileJobs).WithOne(x => x.RevisionInformation).OnDelete(DeleteBehavior.Cascade);
109  revInfo.HasMany(x => x.ActiveTestMerges).WithOne(x => x.RevisionInformation).OnDelete(DeleteBehavior.Cascade);
110  revInfo.HasOne(x => x.PrimaryTestMerge).WithOne(x => x.PrimaryRevisionInformation).OnDelete(DeleteBehavior.SetNull);
111  revInfo.HasIndex(x => x.CommitSha).IsUnique();
112 
113  modelBuilder.Entity<CompileJob>().HasIndex(x => x.DirectoryName);
114 
115  var chatChannel = modelBuilder.Entity<ChatChannel>();
116  chatChannel.HasIndex(x => new { x.ChatSettingsId, x.IrcChannel }).IsUnique();
117  chatChannel.HasIndex(x => new { x.ChatSettingsId, x.DiscordChannelId }).IsUnique();
118  chatChannel.HasOne(x => x.ChatSettings).WithMany(x => x.Channels).HasForeignKey(x => x.ChatSettingsId).OnDelete(DeleteBehavior.Cascade);
119 
120  modelBuilder.Entity<ChatBot>().HasIndex(x => x.Name).IsUnique();
121 
122  var instanceModel = modelBuilder.Entity<Instance>();
123  instanceModel.HasIndex(x => x.Path).IsUnique();
124  instanceModel.HasMany(x => x.ChatSettings).WithOne(x => x.Instance).OnDelete(DeleteBehavior.Cascade);
125  instanceModel.HasOne(x => x.DreamDaemonSettings).WithOne(x => x.Instance).OnDelete(DeleteBehavior.Cascade);
126  instanceModel.HasOne(x => x.DreamMakerSettings).WithOne(x => x.Instance).OnDelete(DeleteBehavior.Cascade);
127  instanceModel.HasOne(x => x.RepositorySettings).WithOne(x => x.Instance).OnDelete(DeleteBehavior.Cascade);
128  instanceModel.HasMany(x => x.RevisionInformations).WithOne(x => x.Instance).OnDelete(DeleteBehavior.Cascade);
129  instanceModel.HasMany(x => x.InstanceUsers).WithOne(x => x.Instance).OnDelete(DeleteBehavior.Cascade);
130  instanceModel.HasMany(x => x.Jobs).WithOne(x => x.Instance).OnDelete(DeleteBehavior.Cascade);
131  }
132 
134  public async Task Initialize(CancellationToken cancellationToken)
135  {
137  {
138  Logger.LogCritical("DropDatabase configuration option set! Dropping any existing database...");
139  await Database.EnsureDeletedAsync(cancellationToken).ConfigureAwait(false);
140  }
141 
142  var migrations = await Database.GetAppliedMigrationsAsync(cancellationToken).ConfigureAwait(false);
143  var wasEmpty = !migrations.Any();
144 
145  if (wasEmpty || (await Database.GetPendingMigrationsAsync(cancellationToken).ConfigureAwait(false)).Any())
146  {
147  Logger.LogInformation("Migrating database...");
148  await Database.MigrateAsync(cancellationToken).ConfigureAwait(false);
149  }
150  else
151  Logger.LogDebug("No migrations to apply.");
152 
153  wasEmpty |= (await Users.CountAsync(cancellationToken).ConfigureAwait(false)) == 0;
154 
155  if (wasEmpty)
156  {
157  Logger.LogInformation("Seeding database...");
158  await databaseSeeder.SeedDatabase(this, cancellationToken).ConfigureAwait(false);
159  }
161  {
162  Logger.LogWarning("Enabling and resetting admin password due to configuration!");
163  await databaseSeeder.ResetAdminPassword(this, cancellationToken).ConfigureAwait(false);
164  }
165  }
166 
168  public Task Save(CancellationToken cancellationToken) => SaveChangesAsync(cancellationToken);
169  }
170 }
@@ -100,18 +100,19 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
For initially seeding a database
-
async Task Initialize(CancellationToken cancellationToken)
Creates and migrates the IDatabaseContext
+
async Task Initialize(CancellationToken cancellationToken)
Creates and migrates the IDatabaseContext
-
override void OnModelCreating(ModelBuilder modelBuilder)
+
bool ResetAdminPassword
If the admin user should be enabled and have it&#39;s password reset
+
override void OnModelCreating(ModelBuilder modelBuilder)
Represents an Api.Models.Instance in the database
Definition: Instance.cs:8
-
DatabaseContext(DbContextOptions< TParentContext > dbContextOptions, IOptions< DatabaseConfiguration > databaseConfigurationOptions, IDatabaseSeeder databaseSeeder, ILogger logger)
Construct a DatabaseContext<TParentContext>
+
DatabaseContext(DbContextOptions< TParentContext > dbContextOptions, IOptions< DatabaseConfiguration > databaseConfigurationOptions, IDatabaseSeeder databaseSeeder, ILogger logger)
Construct a DatabaseContext<TParentContext>
Configuration options for the Models.DatabaseContext<TParentContext>
-
readonly DatabaseConfiguration databaseConfiguration
The DatabaseConfiguration for the DatabaseContext<TParentContext>
-
readonly IDatabaseSeeder databaseSeeder
The IDatabaseSeeder for the DatabaseContext<TParentContext>
+
bool DropDatabase
If the database should be deleted on application startup. Should not be used in production! ...
+
readonly IDatabaseSeeder databaseSeeder
The IDatabaseSeeder for the DatabaseContext<TParentContext>
diff --git a/_database_type_8cs.html b/_database_type_8cs.html index 93337fd3ba..5a8acbd56f 100644 --- a/_database_type_8cs.html +++ b/_database_type_8cs.html @@ -106,7 +106,7 @@ Namespaces Enumerations enum  Tgstation.Server.Host.Configuration.DatabaseType { Tgstation.Server.Host.Configuration.DatabaseType.SqlServer, Tgstation.Server.Host.Configuration.DatabaseType.MySql, -Tgstation.Server.Host.Configuration.DatabaseType.Sqlite +Tgstation.Server.Host.Configuration.DatabaseType.MariaDB } Type of database to user More...
  diff --git a/_database_type_8cs_source.html b/_database_type_8cs_source.html index 827f4c0a43..7ec41adbb0 100644 --- a/_database_type_8cs_source.html +++ b/_database_type_8cs_source.html @@ -91,11 +91,11 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
DatabaseType.cs
-Go to the documentation of this file. diff --git a/_default_i_o_manager_8cs_source.html b/_default_i_o_manager_8cs_source.html index 79e5b603fa..9ee024ef4d 100644 --- a/_default_i_o_manager_8cs_source.html +++ b/_default_i_o_manager_8cs_source.html @@ -91,7 +91,7 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
DefaultIOManager.cs
-Go to the documentation of this file.
1 using System;
2 using System.Collections.Generic;
3 using System.Globalization;
4 using System.IO;
5 using System.IO.Compression;
6 using System.Linq;
7 using System.Net;
8 using System.Text;
9 using System.Threading;
10 using System.Threading.Tasks;
11 
12 namespace Tgstation.Server.Host.IO
13 {
18  {
22  public const int DefaultBufferSize = 4096;
23 
30  static async Task NormalizeAndDelete(DirectoryInfo dir, CancellationToken cancellationToken)
31  {
32  var tasks = new List<Task>();
33 
34  foreach (var subDir in dir.EnumerateDirectories())
35  {
36  cancellationToken.ThrowIfCancellationRequested();
37  if (!subDir.Attributes.HasFlag(FileAttributes.Directory) || subDir.Attributes.HasFlag(FileAttributes.ReparsePoint))
38  //this is probably a symlink
39  subDir.Delete();
40  else
41  tasks.Add(NormalizeAndDelete(subDir, cancellationToken));
42  }
43  foreach (var file in dir.EnumerateFiles())
44  {
45  cancellationToken.ThrowIfCancellationRequested();
46  file.Attributes = FileAttributes.Normal;
47  file.Delete();
48  }
49  await Task.WhenAll(tasks).ConfigureAwait(false);
50  cancellationToken.ThrowIfCancellationRequested();
51  dir.Delete(true);
52  }
53 
59  static FileStream OpenWriteStream(string path) => new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.ReadWrite, DefaultBufferSize, true);
60 
69  IEnumerable<Task> CopyDirectoryImpl(string src, string dest, IEnumerable<string> ignore, CancellationToken cancellationToken)
70  {
71  var dir = new DirectoryInfo(src);
72  var atLeastOneSubDir = false;
73  foreach (var I in dir.EnumerateDirectories())
74  {
75  if (ignore != null && ignore.Contains(I.Name))
76  continue;
77  foreach (var J in CopyDirectoryImpl(I.FullName, Path.Combine(dest, I.Name), null, cancellationToken))
78  {
79  atLeastOneSubDir = true;
80  yield return J;
81  }
82  }
83 
84  async Task CopyThisDirectory()
85  {
86  if (!atLeastOneSubDir)
87  await CreateDirectory(dest, cancellationToken).ConfigureAwait(false); //save on createdir calls
88 
89  var tasks = new List<Task>();
90 
91  await dir.EnumerateFiles().ToAsyncEnumerable().ForEachAsync(I =>
92  {
93  if (ignore != null && ignore.Contains(I.Name))
94  return;
95  tasks.Add(CopyFile(I.FullName, Path.Combine(dest, I.Name), cancellationToken));
96  }).ConfigureAwait(false);
97 
98  await Task.WhenAll(tasks).ConfigureAwait(false);
99  };
100 
101  yield return CopyThisDirectory();
102  }
103 
105  public async Task CopyDirectory(string src, string dest, IEnumerable<string> ignore, CancellationToken cancellationToken)
106  {
107  if (dest == null)
108  throw new ArgumentNullException(nameof(src));
109  if (dest == null)
110  throw new ArgumentNullException(nameof(src));
111 
112  src = ResolvePath(src);
113  dest = ResolvePath(dest);
114  foreach (var directoryCopy in CopyDirectoryImpl(src, dest, ignore, cancellationToken))
115  await directoryCopy.ConfigureAwait(false);
116  }
117 
119  public async Task AppendAllText(string path, string additional_contents, CancellationToken cancellationToken)
120  {
121  if (additional_contents == null)
122  throw new ArgumentNullException(nameof(additional_contents));
123  using (var destStream = new FileStream(ResolvePath(path), FileMode.Append, FileAccess.Write, FileShare.ReadWrite | FileShare.Delete, DefaultBufferSize, true))
124  {
125  var buf = Encoding.UTF8.GetBytes(additional_contents);
126  await destStream.WriteAsync(buf, 0, buf.Length, cancellationToken).ConfigureAwait(false);
127  }
128  }
129 
131  public string ConcatPath(params string[] paths)
132  {
133  if (paths == null)
134  throw new ArgumentNullException(nameof(paths));
135  return Path.Combine(paths);
136  }
137 
139  public async Task CopyFile(string src, string dest, CancellationToken cancellationToken)
140  {
141  if (src == null)
142  throw new ArgumentNullException(nameof(src));
143  if (dest == null)
144  throw new ArgumentNullException(nameof(dest));
145  using (var srcStream = new FileStream(ResolvePath(src), FileMode.Open, FileAccess.Read, FileShare.Read | FileShare.Delete, DefaultBufferSize, true))
146  using (var destStream = new FileStream(ResolvePath(dest), FileMode.Create, FileAccess.Write, FileShare.ReadWrite | FileShare.Delete, DefaultBufferSize, true))
147  await srcStream.CopyToAsync(destStream, 81920, cancellationToken).ConfigureAwait(false);
148  }
149 
151  public Task CreateDirectory(string path, CancellationToken cancellationToken) => Task.Factory.StartNew(() => Directory.CreateDirectory(ResolvePath(path)), cancellationToken, TaskCreationOptions.LongRunning, TaskScheduler.Current);
152 
154  public async Task DeleteDirectory(string path, CancellationToken cancellationToken)
155  {
156  path = ResolvePath(path);
157  var di = new DirectoryInfo(path);
158  if (!di.Exists)
159  return;
160  await NormalizeAndDelete(di, cancellationToken).ConfigureAwait(false);
161  }
162 
164  public Task DeleteFile(string path, CancellationToken cancellationToken) => Task.Factory.StartNew(() => File.Delete(ResolvePath(path)), cancellationToken, TaskCreationOptions.LongRunning, TaskScheduler.Current);
165 
167  public Task<bool> FileExists(string path, CancellationToken cancellationToken) => Task.Factory.StartNew(() => File.Exists(ResolvePath(path)), cancellationToken, TaskCreationOptions.LongRunning, TaskScheduler.Current);
168 
170  public Task<bool> DirectoryExists(string path, CancellationToken cancellationToken) => Task.Factory.StartNew(() => Directory.Exists(ResolvePath(path)), cancellationToken, TaskCreationOptions.LongRunning, TaskScheduler.Current);
171 
173  public string GetDirectoryName(string path) => Path.GetDirectoryName(path ?? throw new ArgumentNullException(nameof(path)));
174 
176  public string GetFileName(string path) => Path.GetFileName(path ?? throw new ArgumentNullException(nameof(path)));
177 
179  public string GetFileNameWithoutExtension(string path) => Path.GetFileNameWithoutExtension(path ?? throw new ArgumentNullException(nameof(path)));
180 
182  public Task<List<string>> GetFilesWithExtension(string path, string extension, CancellationToken cancellationToken) => Task.Factory.StartNew(() =>
183  {
184  path = ResolvePath(path);
185  if (extension == null)
186  throw new ArgumentNullException(extension);
187  var results = new List<string>();
188  foreach (var I in Directory.EnumerateFiles(path, String.Format(CultureInfo.InvariantCulture, "*.{0}", extension), SearchOption.TopDirectoryOnly))
189  {
190  cancellationToken.ThrowIfCancellationRequested();
191  results.Add(I);
192  }
193  return results;
194  }, cancellationToken, TaskCreationOptions.LongRunning, TaskScheduler.Current);
195 
197  public Task MoveFile(string source, string destination, CancellationToken cancellationToken) => Task.Factory.StartNew(() =>
198  {
199  if (destination == null)
200  throw new ArgumentNullException(nameof(destination));
201  source = ResolvePath(source ?? throw new ArgumentNullException(nameof(source)));
202  destination = ResolvePath(destination);
203  File.Move(source, destination);
204  }, cancellationToken, TaskCreationOptions.LongRunning, TaskScheduler.Current);
205 
207  public Task MoveDirectory(string source, string destination, CancellationToken cancellationToken) => Task.Factory.StartNew(() =>
208  {
209  if (destination == null)
210  throw new ArgumentNullException(nameof(destination));
211  source = ResolvePath(source ?? throw new ArgumentNullException(nameof(source)));
212  destination = ResolvePath(destination);
213  Directory.Move(source, destination);
214  }, cancellationToken, TaskCreationOptions.LongRunning, TaskScheduler.Current);
215 
217  public async Task<byte[]> ReadAllBytes(string path, CancellationToken cancellationToken)
218  {
219  path = ResolvePath(path);
220  using (var file = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read | FileShare.Delete, DefaultBufferSize, true))
221  {
222  byte[] buf;
223  buf = new byte[file.Length];
224  await file.ReadAsync(buf, 0, (int)file.Length, cancellationToken).ConfigureAwait(false);
225  return buf;
226  }
227  }
228 
230  public virtual string ResolvePath(string path) => Path.GetFullPath(path ?? throw new ArgumentNullException(nameof(path)));
231 
233  public async Task WriteAllBytes(string path, byte[] contents, CancellationToken cancellationToken)
234  {
235  path = ResolvePath(path);
236  using (var file = OpenWriteStream(path))
237  await file.WriteAsync(contents, 0, contents.Length, cancellationToken).ConfigureAwait(false);
238  }
239 
241  public Task<IReadOnlyList<string>> GetDirectories(string path, CancellationToken cancellationToken) => Task.Factory.StartNew(() =>
242  {
243  path = ResolvePath(path);
244  var results = new List<string>();
245  cancellationToken.ThrowIfCancellationRequested();
246  foreach (var I in Directory.EnumerateDirectories(path))
247  {
248  results.Add(I);
249  cancellationToken.ThrowIfCancellationRequested();
250  }
251  return (IReadOnlyList<string>)results;
252  }, cancellationToken, TaskCreationOptions.LongRunning, TaskScheduler.Current);
253 
255  public Task<IReadOnlyList<string>> GetFiles(string path, CancellationToken cancellationToken) => Task.Factory.StartNew(() =>
256  {
257  path = ResolvePath(path);
258  var results = new List<string>();
259  cancellationToken.ThrowIfCancellationRequested();
260  foreach (var I in Directory.EnumerateFiles(path))
261  {
262  results.Add(I);
263  cancellationToken.ThrowIfCancellationRequested();
264  }
265  return (IReadOnlyList<string>)results;
266  }, cancellationToken, TaskCreationOptions.LongRunning, TaskScheduler.Current);
267 
269  public Task CreateSymlink(string target, string link, CancellationToken cancellationToken) => Task.Factory.StartNew(() =>
270  {
271  target = ResolvePath(target);
272  link = ResolvePath(link);
273 
274  }, cancellationToken, TaskCreationOptions.LongRunning, TaskScheduler.Current);
275 
277  public async Task<byte[]> DownloadFile(Uri url, CancellationToken cancellationToken)
278  {
279  //DownloadDataTaskAsync can't be cancelled and is shittily written, don't use it
280  using (var wc = new WebClient())
281  {
282  var tcs = new TaskCompletionSource<byte[]>();
283  wc.DownloadDataCompleted += (a, b) =>
284  {
285  if (b.Error != null)
286  tcs.TrySetException(b.Error);
287  else if (b.Cancelled)
288  tcs.TrySetCanceled();
289  else
290  tcs.TrySetResult(b.Result);
291  };
292  wc.DownloadDataAsync(url);
293  using (cancellationToken.Register(() =>
294  {
295  wc.CancelAsync(); //cancelasync alone doesnt do it either! who wrote this!!
296  tcs.SetCanceled();
297  }))
298  return await tcs.Task.ConfigureAwait(false);
299  } //ITS STILL FUCKING DOWNLOADING!!!
300  }
301 
303  public Task ZipToDirectory(string path, byte[] zipFileBytes, CancellationToken cancellationToken) => Task.Factory.StartNew(() =>
304  {
305  path = ResolvePath(path);
306  if (zipFileBytes == null)
307  throw new ArgumentNullException(nameof(zipFileBytes));
308 
309  using (var ms = new MemoryStream(zipFileBytes))
310  using (var archive = new ZipArchive(ms, ZipArchiveMode.Read))
311  archive.ExtractToDirectory(path);
312  }, cancellationToken, TaskCreationOptions.LongRunning, TaskScheduler.Current);
313  }
314 }
async Task WriteAllBytes(string path, byte[] contents, CancellationToken cancellationToken)
Writes some contents to a file at path overwriting previous content
+Go to the documentation of this file.
1 using System;
2 using System.Collections.Generic;
3 using System.Globalization;
4 using System.IO;
5 using System.IO.Compression;
6 using System.Linq;
7 using System.Net;
8 using System.Text;
9 using System.Threading;
10 using System.Threading.Tasks;
11 
12 namespace Tgstation.Server.Host.IO
13 {
18  {
22  public const int DefaultBufferSize = 4096;
23 
30  static async Task NormalizeAndDelete(DirectoryInfo dir, CancellationToken cancellationToken)
31  {
32  var tasks = new List<Task>();
33 
34  foreach (var subDir in dir.EnumerateDirectories())
35  {
36  cancellationToken.ThrowIfCancellationRequested();
37  if (!subDir.Attributes.HasFlag(FileAttributes.Directory) || subDir.Attributes.HasFlag(FileAttributes.ReparsePoint))
38  //this is probably a symlink
39  subDir.Delete();
40  else
41  tasks.Add(NormalizeAndDelete(subDir, cancellationToken));
42  }
43  foreach (var file in dir.EnumerateFiles())
44  {
45  cancellationToken.ThrowIfCancellationRequested();
46  file.Attributes = FileAttributes.Normal;
47  file.Delete();
48  }
49  await Task.WhenAll(tasks).ConfigureAwait(false);
50  cancellationToken.ThrowIfCancellationRequested();
51  dir.Delete(true);
52  }
53 
59  static FileStream OpenWriteStream(string path) => new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.ReadWrite, DefaultBufferSize, true);
60 
69  IEnumerable<Task> CopyDirectoryImpl(string src, string dest, IEnumerable<string> ignore, CancellationToken cancellationToken)
70  {
71  var dir = new DirectoryInfo(src);
72  var atLeastOneSubDir = false;
73  foreach (var I in dir.EnumerateDirectories())
74  {
75  if (ignore != null && ignore.Contains(I.Name))
76  continue;
77  foreach (var J in CopyDirectoryImpl(I.FullName, Path.Combine(dest, I.Name), null, cancellationToken))
78  {
79  atLeastOneSubDir = true;
80  yield return J;
81  }
82  }
83 
84  async Task CopyThisDirectory()
85  {
86  if (!atLeastOneSubDir)
87  await CreateDirectory(dest, cancellationToken).ConfigureAwait(false); //save on createdir calls
88 
89  var tasks = new List<Task>();
90 
91  await dir.EnumerateFiles().ToAsyncEnumerable().ForEachAsync(I =>
92  {
93  if (ignore != null && ignore.Contains(I.Name))
94  return;
95  tasks.Add(CopyFile(I.FullName, Path.Combine(dest, I.Name), cancellationToken));
96  }).ConfigureAwait(false);
97 
98  await Task.WhenAll(tasks).ConfigureAwait(false);
99  };
100 
101  yield return CopyThisDirectory();
102  }
103 
105  public async Task CopyDirectory(string src, string dest, IEnumerable<string> ignore, CancellationToken cancellationToken)
106  {
107  if (dest == null)
108  throw new ArgumentNullException(nameof(src));
109  if (dest == null)
110  throw new ArgumentNullException(nameof(src));
111 
112  src = ResolvePath(src);
113  dest = ResolvePath(dest);
114  foreach (var directoryCopy in CopyDirectoryImpl(src, dest, ignore, cancellationToken))
115  await directoryCopy.ConfigureAwait(false);
116  }
117 
119  public async Task AppendAllText(string path, string additional_contents, CancellationToken cancellationToken)
120  {
121  if (additional_contents == null)
122  throw new ArgumentNullException(nameof(additional_contents));
123  using (var destStream = new FileStream(ResolvePath(path), FileMode.Append, FileAccess.Write, FileShare.ReadWrite | FileShare.Delete, DefaultBufferSize, true))
124  {
125  var buf = Encoding.UTF8.GetBytes(additional_contents);
126  await destStream.WriteAsync(buf, 0, buf.Length, cancellationToken).ConfigureAwait(false);
127  }
128  }
129 
131  public string ConcatPath(params string[] paths)
132  {
133  if (paths == null)
134  throw new ArgumentNullException(nameof(paths));
135  return Path.Combine(paths);
136  }
137 
139  public async Task CopyFile(string src, string dest, CancellationToken cancellationToken)
140  {
141  if (src == null)
142  throw new ArgumentNullException(nameof(src));
143  if (dest == null)
144  throw new ArgumentNullException(nameof(dest));
145  using (var srcStream = new FileStream(ResolvePath(src), FileMode.Open, FileAccess.Read, FileShare.Read | FileShare.Delete, DefaultBufferSize, true))
146  using (var destStream = new FileStream(ResolvePath(dest), FileMode.Create, FileAccess.Write, FileShare.ReadWrite | FileShare.Delete, DefaultBufferSize, true))
147  await srcStream.CopyToAsync(destStream, 81920, cancellationToken).ConfigureAwait(false);
148  }
149 
151  public Task CreateDirectory(string path, CancellationToken cancellationToken) => Task.Factory.StartNew(() => Directory.CreateDirectory(ResolvePath(path)), cancellationToken, TaskCreationOptions.LongRunning, TaskScheduler.Current);
152 
154  public async Task DeleteDirectory(string path, CancellationToken cancellationToken)
155  {
156  path = ResolvePath(path);
157  var di = new DirectoryInfo(path);
158  if (!di.Exists)
159  return;
160  await NormalizeAndDelete(di, cancellationToken).ConfigureAwait(false);
161  }
162 
164  public Task DeleteFile(string path, CancellationToken cancellationToken) => Task.Factory.StartNew(() => File.Delete(ResolvePath(path)), cancellationToken, TaskCreationOptions.LongRunning, TaskScheduler.Current);
165 
167  public Task<bool> FileExists(string path, CancellationToken cancellationToken) => Task.Factory.StartNew(() => File.Exists(ResolvePath(path)), cancellationToken, TaskCreationOptions.LongRunning, TaskScheduler.Current);
168 
170  public Task<bool> DirectoryExists(string path, CancellationToken cancellationToken) => Task.Factory.StartNew(() => Directory.Exists(ResolvePath(path)), cancellationToken, TaskCreationOptions.LongRunning, TaskScheduler.Current);
171 
173  public string GetDirectoryName(string path) => Path.GetDirectoryName(path ?? throw new ArgumentNullException(nameof(path)));
174 
176  public string GetFileName(string path) => Path.GetFileName(path ?? throw new ArgumentNullException(nameof(path)));
177 
179  public string GetFileNameWithoutExtension(string path) => Path.GetFileNameWithoutExtension(path ?? throw new ArgumentNullException(nameof(path)));
180 
182  public Task<List<string>> GetFilesWithExtension(string path, string extension, CancellationToken cancellationToken) => Task.Factory.StartNew(() =>
183  {
184  path = ResolvePath(path);
185  if (extension == null)
186  throw new ArgumentNullException(extension);
187  var results = new List<string>();
188  foreach (var I in Directory.EnumerateFiles(path, String.Format(CultureInfo.InvariantCulture, "*.{0}", extension), SearchOption.TopDirectoryOnly))
189  {
190  cancellationToken.ThrowIfCancellationRequested();
191  results.Add(I);
192  }
193  return results;
194  }, cancellationToken, TaskCreationOptions.LongRunning, TaskScheduler.Current);
195 
197  public Task MoveFile(string source, string destination, CancellationToken cancellationToken) => Task.Factory.StartNew(() =>
198  {
199  if (destination == null)
200  throw new ArgumentNullException(nameof(destination));
201  source = ResolvePath(source ?? throw new ArgumentNullException(nameof(source)));
202  destination = ResolvePath(destination);
203  File.Move(source, destination);
204  }, cancellationToken, TaskCreationOptions.LongRunning, TaskScheduler.Current);
205 
207  public Task MoveDirectory(string source, string destination, CancellationToken cancellationToken) => Task.Factory.StartNew(() =>
208  {
209  if (destination == null)
210  throw new ArgumentNullException(nameof(destination));
211  source = ResolvePath(source ?? throw new ArgumentNullException(nameof(source)));
212  destination = ResolvePath(destination);
213  Directory.Move(source, destination);
214  }, cancellationToken, TaskCreationOptions.LongRunning, TaskScheduler.Current);
215 
217  public async Task<byte[]> ReadAllBytes(string path, CancellationToken cancellationToken)
218  {
219  path = ResolvePath(path);
220  using (var file = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read | FileShare.Delete, DefaultBufferSize, true))
221  {
222  byte[] buf;
223  buf = new byte[file.Length];
224  await file.ReadAsync(buf, 0, (int)file.Length, cancellationToken).ConfigureAwait(false);
225  return buf;
226  }
227  }
228 
230  public virtual string ResolvePath(string path) => Path.GetFullPath(path ?? throw new ArgumentNullException(nameof(path)));
231 
233  public async Task WriteAllBytes(string path, byte[] contents, CancellationToken cancellationToken)
234  {
235  path = ResolvePath(path);
236  using (var file = OpenWriteStream(path))
237  await file.WriteAsync(contents, 0, contents.Length, cancellationToken).ConfigureAwait(false);
238  }
239 
241  public Task<IReadOnlyList<string>> GetDirectories(string path, CancellationToken cancellationToken) => Task.Factory.StartNew(() =>
242  {
243  path = ResolvePath(path);
244  var results = new List<string>();
245  cancellationToken.ThrowIfCancellationRequested();
246  foreach (var I in Directory.EnumerateDirectories(path))
247  {
248  results.Add(I);
249  cancellationToken.ThrowIfCancellationRequested();
250  }
251  return (IReadOnlyList<string>)results;
252  }, cancellationToken, TaskCreationOptions.LongRunning, TaskScheduler.Current);
253 
255  public Task<IReadOnlyList<string>> GetFiles(string path, CancellationToken cancellationToken) => Task.Factory.StartNew(() =>
256  {
257  path = ResolvePath(path);
258  var results = new List<string>();
259  cancellationToken.ThrowIfCancellationRequested();
260  foreach (var I in Directory.EnumerateFiles(path))
261  {
262  results.Add(I);
263  cancellationToken.ThrowIfCancellationRequested();
264  }
265  return (IReadOnlyList<string>)results;
266  }, cancellationToken, TaskCreationOptions.LongRunning, TaskScheduler.Current);
267 
269  public Task CreateSymlink(string target, string link, CancellationToken cancellationToken) => Task.Factory.StartNew(() =>
270  {
271  target = ResolvePath(target);
272  link = ResolvePath(link);
273 
274  }, cancellationToken, TaskCreationOptions.LongRunning, TaskScheduler.Current);
275 
277  public async Task<byte[]> DownloadFile(Uri url, CancellationToken cancellationToken)
278  {
279  //DownloadDataTaskAsync can't be cancelled and is shittily written, don't use it
280  using (var wc = new WebClient())
281  {
282  var tcs = new TaskCompletionSource<byte[]>();
283  wc.DownloadDataCompleted += (a, b) =>
284  {
285  if (b.Error != null)
286  tcs.TrySetException(b.Error);
287  else if (b.Cancelled)
288  tcs.TrySetCanceled();
289  else
290  tcs.TrySetResult(b.Result);
291  };
292  wc.DownloadDataAsync(url);
293  using (cancellationToken.Register(() =>
294  {
295  wc.CancelAsync(); //cancelasync alone doesnt do it either! who wrote this!!
296  tcs.SetCanceled();
297  }))
298  return await tcs.Task.ConfigureAwait(false);
299  } //ITS STILL FUCKING DOWNLOADING!!!
300  }
301 
303  public Task ZipToDirectory(string path, byte[] zipFileBytes, CancellationToken cancellationToken) => Task.Factory.StartNew(() =>
304  {
305  path = ResolvePath(path);
306  if (zipFileBytes == null)
307  throw new ArgumentNullException(nameof(zipFileBytes));
308 
309  using (var ms = new MemoryStream(zipFileBytes))
310  using (var archive = new ZipArchive(ms, ZipArchiveMode.Read))
311  archive.ExtractToDirectory(path);
312  }, cancellationToken, TaskCreationOptions.LongRunning, TaskScheduler.Current);
313 
315  public bool PathContainsParentAccess(string path) => path?.Split(new[] { Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar }).Any(x => x == "..") ?? throw new ArgumentNullException(nameof(path));
316  }
317 }
async Task WriteAllBytes(string path, byte[] contents, CancellationToken cancellationToken)
Writes some contents to a file at path overwriting previous content
string ConcatPath(params string[] paths)
Combines an array of strings into a path
async Task DeleteDirectory(string path, CancellationToken cancellationToken)
Recursively delete a directory
async Task CopyFile(string src, string dest, CancellationToken cancellationToken)
Copy a file from src to dest
diff --git a/_design_time_db_context_factory_helpers_8cs_source.html b/_design_time_db_context_factory_helpers_8cs_source.html index f7bf84339c..c7324595f0 100644 --- a/_design_time_db_context_factory_helpers_8cs_source.html +++ b/_design_time_db_context_factory_helpers_8cs_source.html @@ -91,14 +91,14 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
DesignTimeDbContextFactoryHelpers.cs
-Go to the documentation of this file.
1 using Microsoft.Extensions.Configuration;
2 using Microsoft.Extensions.Options;
3 using System.IO;
4 using System.Reflection;
6 
7 namespace Tgstation.Server.Host.Models.Migrations
8 {
13  {
17  const string MigrationsJson = "appsettings.Development.json";
18 
20  public static IOptions<DatabaseConfiguration> GetDbContextOptions()
21  {
22  var builder = new ConfigurationBuilder();
23  builder.SetBasePath(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location));
24  builder.AddJsonFile(MigrationsJson);
25  var configuration = builder.Build();
26  return Options.Create(configuration.GetSection(DatabaseConfiguration.Section).Get<DatabaseConfiguration>());
27  }
28  }
29 }
+Go to the documentation of this file.
1 using Microsoft.Extensions.Configuration;
2 using Microsoft.Extensions.Options;
3 using System.IO;
4 using System.Reflection;
6 
7 namespace Tgstation.Server.Host.Models.Migrations
8 {
13  {
17  const string RootJson = "appsettings.json";
21  const string DevJson = "appsettings.Development.json";
22 
24  public static IOptions<DatabaseConfiguration> GetDbContextOptions()
25  {
26  var builder = new ConfigurationBuilder();
27  builder.SetBasePath(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location));
28  builder.AddJsonFile(RootJson);
29  builder.AddJsonFile(DevJson);
30  var configuration = builder.Build();
31  return Options.Create(configuration.GetSection(DatabaseConfiguration.Section).Get<DatabaseConfiguration>());
32  }
33  }
34 }
Contains helpers for creating design time Models.DatabaseContext<TParentContext>s ...
const string Section
The key for the Microsoft.Extensions.Configuration.IConfigurationSection the DatabaseConfiguration re...
+
Configuration options for the Models.DatabaseContext<TParentContext>
-
diff --git a/_dream_daemon_controller_8cs_source.html b/_dream_daemon_controller_8cs_source.html index d35eb7c04f..4bef370b70 100644 --- a/_dream_daemon_controller_8cs_source.html +++ b/_dream_daemon_controller_8cs_source.html @@ -93,7 +93,7 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
Go to the documentation of this file.
1 using Microsoft.AspNetCore.Mvc;
2 using Microsoft.EntityFrameworkCore;
3 using Microsoft.Extensions.Logging;
4 using System;
5 using System.Linq;
6 using System.Linq.Expressions;
7 using System.Net;
8 using System.Reflection;
9 using System.Threading;
10 using System.Threading.Tasks;
11 using Tgstation.Server.Api;
19 
20 namespace Tgstation.Server.Host.Controllers
21 {
25  [Route(Routes.DreamDaemon)]
26  public sealed class DreamDaemonController : ModelController<DreamDaemon>
27  {
36 
45  public DreamDaemonController(IDatabaseContext databaseContext, IAuthenticationContextFactory authenticationContextFactory, IJobManager jobManager, IInstanceManager instanceManager, ILogger<DreamDaemonController> logger) : base(databaseContext, authenticationContextFactory, logger, true)
46  {
47  this.jobManager = jobManager ?? throw new ArgumentNullException(nameof(jobManager));
48  this.instanceManager = instanceManager ?? throw new ArgumentNullException(nameof(instanceManager));
49  }
50 
52  [TgsAuthorize(DreamDaemonRights.Start)]
53  public override async Task<IActionResult> Create([FromBody] DreamDaemon model, CancellationToken cancellationToken)
54  {
55  //alias for launching DD
56  var instance = instanceManager.GetInstance(Instance);
57 
58  if (instance.Watchdog.Running)
59  return StatusCode((int)HttpStatusCode.Gone);
60 
61  var job = new Models.Job
62  {
63  Description = "Launch DreamDaemon",
64  CancelRight = (ulong)DreamDaemonRights.Shutdown,
65  CancelRightsType = RightsType.DreamDaemon,
67  StartedBy = AuthenticationContext.User
68  };
69  await jobManager.RegisterOperation(job,
70  async (paramJob, serviceProvider, progressHandler, innerCt) =>
71  {
72  var result = await instance.Watchdog.Launch(innerCt).ConfigureAwait(false);
73  if (result == null)
74  throw new InvalidOperationException("Watchdog already running!");
75  },
76  cancellationToken).ConfigureAwait(false);
77  return Accepted(job.ToApi());
78  }
79 
81  [TgsAuthorize(DreamDaemonRights.ReadMetadata | DreamDaemonRights.ReadRevision)]
82  public override Task<IActionResult> Read(CancellationToken cancellationToken) => ReadImpl(null, cancellationToken);
83 
90  async Task<IActionResult> ReadImpl(DreamDaemonSettings settings, CancellationToken cancellationToken)
91  {
92  var instance = instanceManager.GetInstance(Instance);
93  var dd = instance.Watchdog;
94 
95  var metadata = (AuthenticationContext.GetRight(RightsType.DreamDaemon) & (ulong)DreamDaemonRights.ReadMetadata) != 0;
96  var revision = (AuthenticationContext.GetRight(RightsType.DreamDaemon) & (ulong)DreamDaemonRights.ReadRevision) != 0;
97 
98  if (settings == null)
99  {
100  settings = await DatabaseContext.Instances.Where(x => x.Id == Instance.Id).Select(x => x.DreamDaemonSettings).FirstOrDefaultAsync(cancellationToken).ConfigureAwait(false);
101  if (settings == default)
102  return StatusCode((int)HttpStatusCode.Gone);
103  }
104 
105  var result = new DreamDaemon();
106  if (metadata)
107  {
108  var alphaActive = dd.AlphaIsActive;
109  var llp = dd.LastLaunchParameters;
110  var rstate = dd.RebootState;
111  result.AutoStart = settings.AutoStart;
112  result.CurrentPort = alphaActive ? llp?.PrimaryPort : llp?.SecondaryPort;
113  result.CurrentSecurity = llp?.SecurityLevel;
114  result.CurrentAllowWebclient = llp?.AllowWebClient;
115  result.PrimaryPort = settings.PrimaryPort;
116  result.AllowWebClient = settings.AllowWebClient;
117  result.Running = dd.Running;
118  result.SecondaryPort = settings.SecondaryPort;
119  result.SecurityLevel = settings.SecurityLevel;
120  result.SoftRestart = rstate == RebootState.Restart;
121  result.SoftShutdown = rstate == RebootState.Shutdown;
122  };
123 
124  if (revision)
125  {
126  result.ActiveCompileJob = dd.ActiveCompileJob?.ToApi();
127  var compileJob = instance.LatestCompileJob();
128  result.StagedCompileJob = compileJob?.ToApi();
129  }
130 
131  return Json(result);
132  }
133 
135  [HttpDelete]
136  [TgsAuthorize(DreamDaemonRights.Shutdown)]
137  public async Task<IActionResult> Delete(CancellationToken cancellationToken)
138  {
139  //alias for stopping DD
140  var instance = instanceManager.GetInstance(Instance);
141  await instance.Watchdog.Terminate(false, cancellationToken).ConfigureAwait(false);
142  return Ok();
143  }
144 
146  [TgsAuthorize(DreamDaemonRights.SetAutoStart | DreamDaemonRights.SetPorts | DreamDaemonRights.SetSecurity | DreamDaemonRights.SetWebClient | DreamDaemonRights.SoftRestart | DreamDaemonRights.SoftShutdown | DreamDaemonRights.Start | DreamDaemonRights.SetStartupTimeout)]
147  public override async Task<IActionResult> Update([FromBody] DreamDaemon model, CancellationToken cancellationToken)
148  {
149  if (model == null)
150  throw new ArgumentNullException(nameof(model));
151 
152  //alias for changing DD settings
153  var current = await DatabaseContext.Instances.Where(x => x.Id == Instance.Id).Select(x => x.DreamDaemonSettings).FirstOrDefaultAsync(cancellationToken).ConfigureAwait(false);
154 
155  if (current == default)
156  return StatusCode((int)HttpStatusCode.Gone);
157 
158  var userRights = (DreamDaemonRights)AuthenticationContext.GetRight(RightsType.DreamDaemon);
159 
160  bool CheckModified<T>(Expression<Func<Api.Models.Internal.DreamDaemonSettings, T>> expression, DreamDaemonRights requiredRight)
161  {
162  var memberSelectorExpression = (MemberExpression)expression.Body;
163  var property = (PropertyInfo)memberSelectorExpression.Member;
164 
165  var newVal = property.GetValue(model);
166  if (newVal == null)
167  return false;
168  if (!userRights.HasFlag(requiredRight) && property.GetValue(current) != newVal)
169  return true;
170 
171  property.SetValue(current, newVal);
172  return false;
173  };
174 
175  var oldSoftRestart = current.SoftRestart;
176  var oldSoftShutdown = current.SoftShutdown;
177 
178  if (CheckModified(x => x.AllowWebClient, DreamDaemonRights.SetWebClient)
179  || CheckModified(x => x.AutoStart, DreamDaemonRights.SetAutoStart)
180  || CheckModified(x => x.PrimaryPort, DreamDaemonRights.SetPorts)
181  || CheckModified(x => x.SecondaryPort, DreamDaemonRights.SetPorts)
182  || CheckModified(x => x.SecurityLevel, DreamDaemonRights.SetSecurity)
183  || CheckModified(x => x.SoftRestart, DreamDaemonRights.SoftRestart)
184  || CheckModified(x => x.SoftShutdown, DreamDaemonRights.SoftShutdown)
185  || CheckModified(x => x.StartupTimeout, DreamDaemonRights.SetStartupTimeout))
186  return Forbid();
187 
188  if (current.SecurityLevel == DreamDaemonSecurity.Ultrasafe)
189  return BadRequest(new ErrorMessage { Message = "This version of TGS does not support the ultrasafe DreamDaemon configuration!" });
190 
191  var wd = instanceManager.GetInstance(Instance).Watchdog;
192 
193  //run these in parallel because they are equally as important
194  await Task.WhenAll(DatabaseContext.Save(cancellationToken), wd.ChangeSettings(current, cancellationToken)).ConfigureAwait(false);
195 
196  //soft shutdown/restart can't be cancelled because of how many things rely on them
197  //They can be alternated though
198  if (!oldSoftRestart.Value && current.SoftRestart.Value)
199  await wd.Restart(true, cancellationToken).ConfigureAwait(false);
200  else if (!oldSoftShutdown.Value && current.SoftShutdown.Value)
201  await wd.Terminate(true, cancellationToken).ConfigureAwait(false);
202 
203  return await ReadImpl(current, cancellationToken).ConfigureAwait(false);
204  }
205  }
206 }
async Task< IActionResult > Delete(CancellationToken cancellationToken)
-
Manages the runtime of Jobs
Definition: IJobManager.cs:13
+
Manages the runtime of Jobs
Definition: IJobManager.cs:12
diff --git a/_dream_maker_controller_8cs_source.html b/_dream_maker_controller_8cs_source.html index cd90ac4ae7..71217ef7a2 100644 --- a/_dream_maker_controller_8cs_source.html +++ b/_dream_maker_controller_8cs_source.html @@ -92,7 +92,7 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
Go to the documentation of this file.
1 using Microsoft.AspNetCore.Mvc;
2 using Microsoft.EntityFrameworkCore;
3 using Microsoft.Extensions.DependencyInjection;
4 using Microsoft.Extensions.Logging;
5 using System;
6 using System.Linq;
7 using System.Net;
8 using System.Threading;
9 using System.Threading.Tasks;
10 using Tgstation.Server.Api;
16 
17 namespace Tgstation.Server.Host.Controllers
18 {
22  [Route(Routes.DreamMaker)]
23  public sealed class DreamMakerController : ModelController<Api.Models.DreamMaker>
24  {
33 
42  public DreamMakerController(IDatabaseContext databaseContext, IAuthenticationContextFactory authenticationContextFactory, IJobManager jobManager, IInstanceManager instanceManager, ILogger<DreamMakerController> logger) : base(databaseContext, authenticationContextFactory, logger, true)
43  {
44  this.jobManager = jobManager ?? throw new ArgumentNullException(nameof(jobManager));
45  this.instanceManager = instanceManager ?? throw new ArgumentNullException(nameof(instanceManager));
46  }
47 
49  [TgsAuthorize(DreamMakerRights.Read)]
50  public override async Task<IActionResult> Read(CancellationToken cancellationToken)
51  {
52  var instance = instanceManager.GetInstance(Instance);
53  var projectName = await DatabaseContext.DreamMakerSettings.Where(x => x.InstanceId == Instance.Id).Select(x => x.ProjectName).FirstOrDefaultAsync(cancellationToken).ConfigureAwait(false);
54  return Json(new Api.Models.DreamMaker
55  {
56  ProjectName = projectName,
57  Status = instance.DreamMaker.Status
58  });
59  }
60 
62  [TgsAuthorize(DreamMakerRights.CompileJobs)]
63  public override async Task<IActionResult> GetId(long id, CancellationToken cancellationToken)
64  {
65  var compileJob = await DatabaseContext.CompileJobs
66  .Where(x => x.Id == id && x.Job.Instance.Id == Instance.Id)
67  .Include(x => x.Job).ThenInclude(x => x.StartedBy)
68  .Include(x => x.RevisionInformation).ThenInclude(x => x.PrimaryTestMerge)
69  .Include(x => x.RevisionInformation).ThenInclude(x => x.ActiveTestMerges).ThenInclude(x => x.TestMerge)
70  .FirstOrDefaultAsync(cancellationToken).ConfigureAwait(false);
71  if (compileJob == default)
72  return NotFound();
73  return Json(compileJob.ToApi());
74  }
75 
77  [TgsAuthorize(DreamMakerRights.CompileJobs)]
78  public override async Task<IActionResult> List(CancellationToken cancellationToken)
79  {
80  var compileJobs = await DatabaseContext.CompileJobs.Where(x => x.Job.Instance.Id == Instance.Id).OrderByDescending(x => x.Job.StartedAt).Select(x => new Api.Models.CompileJob
81  {
82  Id = x.Id
83  }).ToListAsync(cancellationToken).ConfigureAwait(false);
84  return Json(compileJobs);
85  }
86 
88  [TgsAuthorize(DreamMakerRights.Compile)]
89  public override async Task<IActionResult> Create([FromBody] Api.Models.DreamMaker model, CancellationToken cancellationToken)
90  {
91  var job = new Job
92  {
93  Description = "Compile active repository code",
94  StartedBy = AuthenticationContext.User,
95  CancelRightsType = RightsType.DreamMaker,
96  CancelRight = (ulong)DreamMakerRights.CancelCompile,
98  };
99  await jobManager.RegisterOperation(job, (paramJob, serviceProvider, progressReporter, ct) => RunCompile(paramJob, serviceProvider, Instance, ct), cancellationToken).ConfigureAwait(false);
100  return Accepted(job.ToApi());
101  }
102 
104  [TgsAuthorize(DreamMakerRights.SetDme | DreamMakerRights.SetApiValidationPort)]
105  public override async Task<IActionResult> Update([FromBody] Api.Models.DreamMaker model, CancellationToken cancellationToken)
106  {
107  var hostModel = await DatabaseContext.DreamMakerSettings.Where(x => x.InstanceId == Instance.Id).FirstOrDefaultAsync(cancellationToken).ConfigureAwait(false);
108  if (hostModel == null)
109  return StatusCode((int)HttpStatusCode.Gone);
110 
111  if (model.ProjectName != null)
112  {
114  return Forbid();
115  if (model.ProjectName.Length == 0)
116  hostModel.ProjectName = null;
117  else
118  hostModel.ProjectName = model.ProjectName;
119  }
120 
121  if (model.ApiValidationPort.HasValue)
122  {
123  if (!AuthenticationContext.InstanceUser.DreamMakerRights.Value.HasFlag(DreamMakerRights.SetApiValidationPort))
124  return Forbid();
125  hostModel.ApiValidationPort = model.ApiValidationPort;
126  }
127 
128  await DatabaseContext.Save(cancellationToken).ConfigureAwait(false);
129  return await Read(cancellationToken).ConfigureAwait(false);
130  }
131 
140  async Task RunCompile(Job job, IServiceProvider serviceProvider, Models.Instance instanceModel, CancellationToken cancellationToken)
141  {
142  var instanceManager = serviceProvider.GetRequiredService<IInstanceManager>();
143  var databaseContext = serviceProvider.GetRequiredService<IDatabaseContext>();
144 
145  var ddSettingsTask = databaseContext.DreamDaemonSettings.Where(x => x.InstanceId == instanceModel.Id).Select(x => new DreamDaemonSettings
146  {
147  StartupTimeout = x.StartupTimeout,
148  SecurityLevel = x.SecurityLevel
149  }).FirstOrDefaultAsync(cancellationToken);
150 
151 
152  var dreamMakerSettings = await databaseContext.DreamMakerSettings.Where(x => x.InstanceId == instanceModel.Id).FirstAsync(cancellationToken).ConfigureAwait(false);
153  if (dreamMakerSettings == default)
154  throw new JobException("Missing DreamMakerSettings in DB!");
155  var ddSettings = await ddSettingsTask.ConfigureAwait(false);
156  if (ddSettings == default)
157  throw new JobException("Missing DreamDaemonSettings in DB!");
158 
159  var instance = instanceManager.GetInstance(instanceModel);
160 
161  CompileJob compileJob;
162  RevisionInformation revInfo;
163  using (var repo = await instance.RepositoryManager.LoadRepository(cancellationToken).ConfigureAwait(false))
164  {
165  if (repo == null)
166  throw new JobException("Missing Repository!");
167 
168  var repoSha = repo.Head;
169  revInfo = await databaseContext.RevisionInformations.Where(x => x.CommitSha == repoSha).Include(x => x.ActiveTestMerges).ThenInclude(x => x.TestMerge).FirstOrDefaultAsync().ConfigureAwait(false);
170 
171  if (revInfo == default)
172  {
173  revInfo = new RevisionInformation
174  {
175  CommitSha = repoSha,
176  OriginCommitSha = repoSha,
177  Instance = new Models.Instance
178  {
179  Id = Instance.Id
180  }
181  };
182  databaseContext.Instances.Attach(revInfo.Instance);
183  }
184 
185  compileJob = await instance.DreamMaker.Compile(revInfo, dreamMakerSettings, ddSettings.SecurityLevel.Value, ddSettings.StartupTimeout.Value, repo, cancellationToken).ConfigureAwait(false);
186  }
187 
188  compileJob.Job = job;
189 
190  databaseContext.CompileJobs.Add(compileJob);
191  await databaseContext.Save(cancellationToken).ConfigureAwait(false);
192 
193  await instance.CompileJobConsumer.LoadCompileJob(compileJob, cancellationToken).ConfigureAwait(false);
194  }
195  }
196 }
-
Manages the runtime of Jobs
Definition: IJobManager.cs:13
+
Manages the runtime of Jobs
Definition: IJobManager.cs:12
readonly IInstanceManager instanceManager
The IInstanceManager for the DreamMakerController
diff --git a/_i_api_client_8cs_source.html b/_i_api_client_8cs_source.html index ec69611b29..a045da6398 100644 --- a/_i_api_client_8cs_source.html +++ b/_i_api_client_8cs_source.html @@ -91,7 +91,7 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
IApiClient.cs
-Go to the documentation of this file.
1 using System;
2 using System.Threading;
3 using System.Threading.Tasks;
4 using Tgstation.Server.Api;
5 
6 namespace Tgstation.Server.Client
7 {
11  interface IApiClient : IDisposable
12  {
13  ApiHeaders Headers { get; }
14 
15  Uri Url { get; }
16 
17  TimeSpan Timeout { get; set; }
18 
23  void AddRequestLogger(IRequestLogger requestLogger);
24 
25  Task<TResult> Create<TBody, TResult>(string route, TBody body, CancellationToken cancellationToken);
26  Task<TResult> Create<TResult>(string route, CancellationToken cancellationToken);
27  Task<TResult> Read<TResult>(string route, CancellationToken cancellationToken);
28  Task<TResult> Update<TBody, TResult>(string route, TBody body, CancellationToken cancellationToken);
29  Task<TResult> Update<TResult>(string route, CancellationToken cancellationToken);
30  Task Update<TBody>(string route, TBody body, CancellationToken cancellationToken);
31  Task Delete(string route, CancellationToken cancellationToken);
32 
33  Task<TResult> Create<TBody, TResult>(string route, TBody body, long instanceId, CancellationToken cancellationToken);
34  Task<TResult> Create<TResult>(string route, long instanceId, CancellationToken cancellationToken);
35  Task<TResult> Read<TResult>(string route, long instanceId, CancellationToken cancellationToken);
36  Task<TResult> Update<TBody, TResult>(string route, TBody body, long instanceId, CancellationToken cancellationToken);
37  Task Delete(string route, long instanceId, CancellationToken cancellationToken);
38  Task<TResult> Delete<TResult>(string route, long instanceId, CancellationToken cancellationToken);
39  }
40 }
+Go to the documentation of this file.
1 using System;
2 using System.Threading;
3 using System.Threading.Tasks;
4 using Tgstation.Server.Api;
5 
6 namespace Tgstation.Server.Client
7 {
11  interface IApiClient : IDisposable
12  {
13  ApiHeaders Headers { get; }
14 
15  Uri Url { get; }
16 
17  TimeSpan Timeout { get; set; }
18 
23  void AddRequestLogger(IRequestLogger requestLogger);
24 
25  Task<TResult> Create<TBody, TResult>(string route, TBody body, CancellationToken cancellationToken);
26  Task<TResult> Create<TResult>(string route, CancellationToken cancellationToken);
27  Task<TResult> Read<TResult>(string route, CancellationToken cancellationToken);
28  Task<TResult> Update<TBody, TResult>(string route, TBody body, CancellationToken cancellationToken);
29  Task<TResult> Update<TResult>(string route, CancellationToken cancellationToken);
30  Task Update<TBody>(string route, TBody body, CancellationToken cancellationToken);
31  Task Delete(string route, CancellationToken cancellationToken);
32 
33  Task<TResult> Create<TBody, TResult>(string route, TBody body, long instanceId, CancellationToken cancellationToken);
34  Task<TResult> Create<TResult>(string route, long instanceId, CancellationToken cancellationToken);
35  Task<TResult> Read<TResult>(string route, long instanceId, CancellationToken cancellationToken);
36  Task<TResult> Update<TBody, TResult>(string route, TBody body, long instanceId, CancellationToken cancellationToken);
37  Task Delete(string route, long instanceId, CancellationToken cancellationToken);
38  Task Delete<TBody>(string route, TBody body, long instanceId, CancellationToken cancellationToken);
39  Task<TResult> Delete<TResult>(string route, long instanceId, CancellationToken cancellationToken);
40  }
41 }
Represents the header that must be present for every server request
Definition: ApiHeaders.cs:16
diff --git a/_i_configuration_8cs_source.html b/_i_configuration_8cs_source.html index be384f7e81..56379bbd7e 100644 --- a/_i_configuration_8cs_source.html +++ b/_i_configuration_8cs_source.html @@ -91,7 +91,7 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
IConfiguration.cs
-Go to the documentation of this file.
1 using Microsoft.Extensions.Hosting;
2 using System;
3 using System.Collections.Generic;
4 using System.Threading;
5 using System.Threading.Tasks;
8 
9 namespace Tgstation.Server.Host.Components.StaticFiles
10 {
15  {
23  Task<ServerSideModifications> CopyDMFilesTo(string dmeFile, string destination, CancellationToken cancellationToken);
24 
31  Task SymlinkStaticFilesTo(string destination, CancellationToken cancellationToken);
32 
40  Task<IReadOnlyList<ConfigurationFile>> ListDirectory(string configurationRelativePath, ISystemIdentity systemIdentity, CancellationToken cancellationToken);
41 
49  Task<ConfigurationFile> Read(string configurationRelativePath, ISystemIdentity systemIdentity, CancellationToken cancellationToken);
50 
58  Task<bool> CreateDirectory(string configurationRelativePath, ISystemIdentity systemIdentity, CancellationToken cancellationToken);
59 
69  Task<ConfigurationFile> Write(string configurationRelativePath, ISystemIdentity systemIdentity, byte[] data, string previousHash, CancellationToken cancellationToken);
70  }
71 }
Consumes EventTypes and takes the appropriate actions
+Go to the documentation of this file.
1 using Microsoft.Extensions.Hosting;
2 using System;
3 using System.Collections.Generic;
4 using System.Threading;
5 using System.Threading.Tasks;
8 
9 namespace Tgstation.Server.Host.Components.StaticFiles
10 {
15  {
23  Task<ServerSideModifications> CopyDMFilesTo(string dmeFile, string destination, CancellationToken cancellationToken);
24 
31  Task SymlinkStaticFilesTo(string destination, CancellationToken cancellationToken);
32 
40  Task<IReadOnlyList<ConfigurationFile>> ListDirectory(string configurationRelativePath, ISystemIdentity systemIdentity, CancellationToken cancellationToken);
41 
49  Task<ConfigurationFile> Read(string configurationRelativePath, ISystemIdentity systemIdentity, CancellationToken cancellationToken);
50 
58  Task<bool> CreateDirectory(string configurationRelativePath, ISystemIdentity systemIdentity, CancellationToken cancellationToken);
59 
67  Task<bool> DeleteDirectory(string configurationRelativePath, ISystemIdentity systemIdentity, CancellationToken cancellationToken);
68 
78  Task<ConfigurationFile> Write(string configurationRelativePath, ISystemIdentity systemIdentity, byte[] data, string previousHash, CancellationToken cancellationToken);
79  }
80 }
Consumes EventTypes and takes the appropriate actions
Represents a user on the current System.Runtime.InteropServices.OSPlatform
diff --git a/_i_configuration_client_8cs_source.html b/_i_configuration_client_8cs_source.html index 74f6f07c2b..75daa11728 100644 --- a/_i_configuration_client_8cs_source.html +++ b/_i_configuration_client_8cs_source.html @@ -91,7 +91,7 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
IConfigurationClient.cs
-Go to the documentation of this file.
1 using System.Collections.Generic;
2 using System.Threading;
3 using System.Threading.Tasks;
5 
6 namespace Tgstation.Server.Client.Components
7 {
11  public interface IConfigurationClient
12  {
19  Task<IReadOnlyList<ConfigurationFile>> List(string directory, CancellationToken cancellationToken);
20 
27  Task<ConfigurationFile> Read(ConfigurationFile file, CancellationToken cancellationToken);
28 
35  Task<ConfigurationFile> Write(ConfigurationFile file, CancellationToken cancellationToken);
36  }
37 }
+Go to the documentation of this file.
1 using System.Collections.Generic;
2 using System.Threading;
3 using System.Threading.Tasks;
5 
6 namespace Tgstation.Server.Client.Components
7 {
11  public interface IConfigurationClient
12  {
19  Task<IReadOnlyList<ConfigurationFile>> List(string directory, CancellationToken cancellationToken);
20 
27  Task<ConfigurationFile> Read(ConfigurationFile file, CancellationToken cancellationToken);
28 
35  Task<ConfigurationFile> Write(ConfigurationFile file, CancellationToken cancellationToken);
36 
43  Task DeleteEmptyDirectory(ConfigurationFile directory, CancellationToken cancellationToken);
44  }
45 }
diff --git a/_i_i_o_manager_8cs_source.html b/_i_i_o_manager_8cs_source.html index 421c537090..2622842020 100644 --- a/_i_i_o_manager_8cs_source.html +++ b/_i_i_o_manager_8cs_source.html @@ -91,7 +91,7 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
IIOManager.cs
-Go to the documentation of this file.
1 using System;
2 using System.Collections.Generic;
3 using System.Threading;
4 using System.Threading.Tasks;
5 
6 namespace Tgstation.Server.Host.IO
7 {
11  public interface IIOManager
12  {
18  string ResolvePath(string path);
19 
25  string GetFileName(string path);
26 
32  string GetFileNameWithoutExtension(string path);
33 
42  Task CopyDirectory(string src, string dest, IEnumerable<string> ignore, CancellationToken cancellationToken);
43 
50  Task<bool> FileExists(string path, CancellationToken cancellationToken);
51 
58  Task<bool> DirectoryExists(string path, CancellationToken cancellationToken);
59 
66  Task<byte[]> ReadAllBytes(string path, CancellationToken cancellationToken);
67 
74  Task<IReadOnlyList<string>> GetDirectories(string path, CancellationToken cancellationToken);
75 
82  Task<IReadOnlyList<string>> GetFiles(string path, CancellationToken cancellationToken);
83 
91  Task WriteAllBytes(string path, byte[] contents, CancellationToken cancellationToken);
92 
100  Task CopyFile(string src, string dest, CancellationToken cancellationToken);
101 
107  string GetDirectoryName(string path);
108 
116  Task<List<string>> GetFilesWithExtension(string path, string extension, CancellationToken cancellationToken);
117 
124  Task DeleteFile(string path, CancellationToken cancellationToken);
125 
132  Task CreateDirectory(string path, CancellationToken cancellationToken);
133 
140  Task DeleteDirectory(string path, CancellationToken cancellationToken);
141 
147  string ConcatPath(params string[] paths);
148 
156  Task MoveFile(string source, string destination, CancellationToken cancellationToken);
157 
165  Task MoveDirectory(string source, string destination, CancellationToken cancellationToken);
166 
173  Task<byte[]> DownloadFile(Uri url, CancellationToken cancellationToken);
174 
182  Task ZipToDirectory(string path, byte[] zipFileBytes, CancellationToken cancellationToken);
183  }
184 }
+Go to the documentation of this file.
1 using System;
2 using System.Collections.Generic;
3 using System.Threading;
4 using System.Threading.Tasks;
5 
6 namespace Tgstation.Server.Host.IO
7 {
11  public interface IIOManager
12  {
18  string ResolvePath(string path);
19 
25  string GetFileName(string path);
26 
32  string GetFileNameWithoutExtension(string path);
33 
39  bool PathContainsParentAccess(string path);
40 
49  Task CopyDirectory(string src, string dest, IEnumerable<string> ignore, CancellationToken cancellationToken);
50 
57  Task<bool> FileExists(string path, CancellationToken cancellationToken);
58 
65  Task<bool> DirectoryExists(string path, CancellationToken cancellationToken);
66 
73  Task<byte[]> ReadAllBytes(string path, CancellationToken cancellationToken);
74 
81  Task<IReadOnlyList<string>> GetDirectories(string path, CancellationToken cancellationToken);
82 
89  Task<IReadOnlyList<string>> GetFiles(string path, CancellationToken cancellationToken);
90 
98  Task WriteAllBytes(string path, byte[] contents, CancellationToken cancellationToken);
99 
107  Task CopyFile(string src, string dest, CancellationToken cancellationToken);
108 
114  string GetDirectoryName(string path);
115 
123  Task<List<string>> GetFilesWithExtension(string path, string extension, CancellationToken cancellationToken);
124 
131  Task DeleteFile(string path, CancellationToken cancellationToken);
132 
139  Task CreateDirectory(string path, CancellationToken cancellationToken);
140 
147  Task DeleteDirectory(string path, CancellationToken cancellationToken);
148 
154  string ConcatPath(params string[] paths);
155 
163  Task MoveFile(string source, string destination, CancellationToken cancellationToken);
164 
172  Task MoveDirectory(string source, string destination, CancellationToken cancellationToken);
173 
180  Task<byte[]> DownloadFile(Uri url, CancellationToken cancellationToken);
181 
189  Task ZipToDirectory(string path, byte[] zipFileBytes, CancellationToken cancellationToken);
190  }
191 }
Interface for using filesystems
Definition: IIOManager.cs:11
diff --git a/_i_instance_manager_8cs_source.html b/_i_instance_manager_8cs_source.html index 9c8932be9a..77bd4a674b 100644 --- a/_i_instance_manager_8cs_source.html +++ b/_i_instance_manager_8cs_source.html @@ -91,10 +91,14 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
IInstanceManager.cs
-Go to the documentation of this file.
1 using Microsoft.AspNetCore.Http;
2 using System.Threading;
3 using System.Threading.Tasks;
4 
5 namespace Tgstation.Server.Host.Components
6 {
10  public interface IInstanceManager
11  {
17  IInstance GetInstance(Models.Instance metadata);
18 
25  Task OnlineInstance(Models.Instance metadata, CancellationToken cancellationToken);
26 
33  Task OfflineInstance(Models.Instance metadata, CancellationToken cancellationToken);
34 
42  Task MoveInstance(Models.Instance metadata, string newPath, CancellationToken cancellationToken);
43  }
44 }
+Go to the documentation of this file.
1 using System.Threading;
2 using System.Threading.Tasks;
4 
5 namespace Tgstation.Server.Host.Components
6 {
10  public interface IInstanceManager
11  {
17  IInstance GetInstance(Models.Instance metadata);
18 
25  Task OnlineInstance(Models.Instance metadata, CancellationToken cancellationToken);
26 
34  Task OfflineInstance(Models.Instance metadata, User user, CancellationToken cancellationToken);
35 
43  Task MoveInstance(Models.Instance metadata, string newPath, CancellationToken cancellationToken);
44  }
45 }
+ +
For interacting with the instance services
Definition: IInstance.cs:17
+ +
diff --git a/_i_instance_manager_client_8cs_source.html b/_i_instance_manager_client_8cs_source.html index 994865e28b..f41ca76d9f 100644 --- a/_i_instance_manager_client_8cs_source.html +++ b/_i_instance_manager_client_8cs_source.html @@ -91,7 +91,7 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
IInstanceManagerClient.cs
-Go to the documentation of this file.
1 using System.Collections.Generic;
2 using System.Threading;
3 using System.Threading.Tasks;
6 
7 namespace Tgstation.Server.Client
8 {
12  public interface IInstanceManagerClient
13  {
19  Task<IReadOnlyList<Instance>> List(CancellationToken cancellationToken);
20 
27  Task<Instance> Create(Instance instance, CancellationToken cancellationToken);
28 
35  Task<Instance> Update(Instance instance, CancellationToken cancellationToken);
36 
43  Task<Instance> GetId(Instance instance, CancellationToken cancellationToken);
44 
51  Task Delete(Instance instance, CancellationToken cancellationToken);
52 
58  IInstanceClient CreateClient(Instance instance);
59  }
60 }
+Go to the documentation of this file.
1 using System.Collections.Generic;
2 using System.Threading;
3 using System.Threading.Tasks;
6 
7 namespace Tgstation.Server.Client
8 {
12  public interface IInstanceManagerClient
13  {
19  Task<IReadOnlyList<Instance>> List(CancellationToken cancellationToken);
20 
27  Task<Instance> CreateOrAttach(Instance instance, CancellationToken cancellationToken);
28 
35  Task<Instance> Update(Instance instance, CancellationToken cancellationToken);
36 
43  Task<Instance> GetId(Instance instance, CancellationToken cancellationToken);
44 
51  Task Detach(Instance instance, CancellationToken cancellationToken);
52 
58  IInstanceClient CreateClient(Instance instance);
59  }
60 }
diff --git a/_i_job_manager_8cs_source.html b/_i_job_manager_8cs_source.html index e6aa5aefe4..3c0bf7d924 100644 --- a/_i_job_manager_8cs_source.html +++ b/_i_job_manager_8cs_source.html @@ -91,7 +91,7 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
IJobManager.cs
-Go to the documentation of this file.
1 using Microsoft.Extensions.DependencyInjection;
2 using Microsoft.Extensions.Hosting;
3 using System;
4 using System.Threading;
5 using System.Threading.Tasks;
7 
8 namespace Tgstation.Server.Host.Core
9 {
13  public interface IJobManager : IHostedService
14  {
18  int? JobProgress(Job job);
19 
27  Task RegisterOperation(Job job, Func<Job, IServiceProvider, Action<int>, CancellationToken, Task> operation, CancellationToken cancellationToken);
28 
36  Task CancelJob(Job job, User user, CancellationToken cancellationToken);
37  }
38 }
Manages the runtime of Jobs
Definition: IJobManager.cs:13
+Go to the documentation of this file.
1 using Microsoft.Extensions.Hosting;
2 using System;
3 using System.Threading;
4 using System.Threading.Tasks;
6 
7 namespace Tgstation.Server.Host.Core
8 {
12  public interface IJobManager : IHostedService
13  {
17  int? JobProgress(Job job);
18 
26  Task RegisterOperation(Job job, Func<Job, IServiceProvider, Action<int>, CancellationToken, Task> operation, CancellationToken cancellationToken);
27 
36  Task<bool> CancelJob(Job job, User user, bool blocking, CancellationToken cancellationToken);
37  }
38 }
Manages the runtime of Jobs
Definition: IJobManager.cs:12
diff --git a/_i_synchronous_i_o_manager_8cs_source.html b/_i_synchronous_i_o_manager_8cs_source.html index d3b4e4f8a8..1e1ac47d81 100644 --- a/_i_synchronous_i_o_manager_8cs_source.html +++ b/_i_synchronous_i_o_manager_8cs_source.html @@ -91,7 +91,7 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
ISynchronousIOManager.cs
-Go to the documentation of this file.
1 using System.Collections.Generic;
2 using System.Threading;
3 
4 namespace Tgstation.Server.Host.IO
5 {
10  {
17  IEnumerable<string> GetFiles(string path, CancellationToken cancellationToken);
18 
25  IEnumerable<string> GetDirectories(string path, CancellationToken cancellationToken);
26 
33  bool CreateDirectory(string path, CancellationToken cancellationToken);
34 
40  byte[] ReadFile(string path);
41 
50  bool WriteFileChecked(string path, byte[] data, ref string sha1InOut, CancellationToken cancellationToken);
51 
57  bool IsDirectory(string path);
58  }
59 }
+Go to the documentation of this file.
1 using System.Collections.Generic;
2 using System.Threading;
3 
4 namespace Tgstation.Server.Host.IO
5 {
10  {
17  IEnumerable<string> GetFiles(string path, CancellationToken cancellationToken);
18 
25  IEnumerable<string> GetDirectories(string path, CancellationToken cancellationToken);
26 
33  bool CreateDirectory(string path, CancellationToken cancellationToken);
34 
40  byte[] ReadFile(string path);
41 
47  bool DeleteDirectory(string path);
48 
57  bool WriteFileChecked(string path, byte[] data, ref string sha1InOut, CancellationToken cancellationToken);
58 
64  bool IsDirectory(string path);
65  }
66 }
For accessing the disk in a synchronous manner
diff --git a/_instance_controller_8cs_source.html b/_instance_controller_8cs_source.html index 4375896443..22e4b62368 100644 --- a/_instance_controller_8cs_source.html +++ b/_instance_controller_8cs_source.html @@ -91,10 +91,10 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
InstanceController.cs
-Go to the documentation of this file.
1 using Microsoft.AspNetCore.Mvc;
2 using Microsoft.EntityFrameworkCore;
3 using Microsoft.Extensions.Logging;
4 using System;
5 using System.Collections.Generic;
6 using System.Globalization;
7 using System.IO;
8 using System.Linq;
9 using System.Linq.Expressions;
10 using System.Net;
11 using System.Reflection;
12 using System.Runtime.InteropServices;
13 using System.Threading;
14 using System.Threading.Tasks;
15 using Tgstation.Server.Api;
20 using Tgstation.Server.Host.IO;
23 
24 namespace Tgstation.Server.Host.Controllers
25 {
29  [Route(Routes.InstanceManager)]
30  public sealed class InstanceController : ModelController<Api.Models.Instance>
31  {
35  const string InstanceAttachFileName = "TGS4_ALLOW_INSTANCE_ATTACH";
36 
49 
54 
65  public InstanceController(IDatabaseContext databaseContext, IAuthenticationContextFactory authenticationContextFactory, IJobManager jobManager, IInstanceManager instanceManager, IIOManager ioManager, IApplication application, ILogger<InstanceController> logger) : base(databaseContext, authenticationContextFactory, logger, false)
66  {
67  this.jobManager = jobManager ?? throw new ArgumentNullException(nameof(jobManager));
68  this.instanceManager = instanceManager ?? throw new ArgumentNullException(nameof(instanceManager));
69  this.ioManager = ioManager ?? throw new ArgumentNullException(nameof(ioManager));
70  this.application = application ?? throw new ArgumentNullException(nameof(application));
71  }
72 
73  void NormalizeModelPath(Api.Models.Instance model, out string absolutePath)
74  {
75  if (model.Path == null)
76  {
77  absolutePath = null;
78  return;
79  }
80  absolutePath = ioManager.ResolvePath(model.Path);
81  if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
82  model.Path = absolutePath.ToUpperInvariant();
83  else
84  model.Path = absolutePath;
85  }
86 
87  Models.InstanceUser InstanceAdminUser() => new Models.InstanceUser
88  {
89  ByondRights = (ByondRights)~0U,
97  };
98 
100  [TgsAuthorize(InstanceManagerRights.Create)]
101  public override async Task<IActionResult> Create([FromBody] Api.Models.Instance model, CancellationToken cancellationToken)
102  {
103  if (model == null)
104  throw new ArgumentNullException(nameof(model));
105 
106  if (String.IsNullOrWhiteSpace(model.Name))
107  return BadRequest(new ErrorMessage { Message = "name must not be empty!" });
108 
109  if (model.Path == null)
110  return BadRequest(new ErrorMessage { Message = "path must not be empty!" });
111 
112  NormalizeModelPath(model, out var rawPath);
113  var dirExistsTask = ioManager.DirectoryExists(model.Path, cancellationToken);
114  bool attached = false;
115  if (await ioManager.FileExists(model.Path, cancellationToken).ConfigureAwait(false) || await dirExistsTask.ConfigureAwait(false))
116  if (!await ioManager.FileExists(ioManager.ConcatPath(model.Path, InstanceAttachFileName), cancellationToken).ConfigureAwait(false))
117  return Conflict(new ErrorMessage { Message = "Path not empty!" });
118  else
119  attached = true;
120 
121  var newInstance = new Models.Instance
122  {
123  ConfigurationType = model.ConfigurationType ?? ConfigurationType.Disallowed,
125  {
126  AllowWebClient = false,
127  AutoStart = false,
128  PrimaryPort = 1337,
129  SecondaryPort = 1338,
130  SecurityLevel = DreamDaemonSecurity.Safe,
131  SoftRestart = false,
132  SoftShutdown = false,
133  StartupTimeout = 20
134  },
136  {
137  ApiValidationPort = 1339
138  },
139  Name = model.Name,
140  Online = false,
141  Path = model.Path,
142  AutoUpdateInterval = model.AutoUpdateInterval ?? 0,
144  {
145  CommitterEmail = "tgstation-server@users.noreply.github.com",
146  CommitterName = application.VersionString,
147  PushTestMergeCommits = false,
148  ShowTestMergeCommitters = false,
149  AutoUpdatesKeepTestMerges = false,
150  AutoUpdatesSynchronize = false
151  },
152  //give this user full privileges on the instance
153  InstanceUsers = new List<Models.InstanceUser>
154  {
155  InstanceAdminUser()
156  }
157  };
158 
159  DatabaseContext.Instances.Add(newInstance);
160  try
161  {
162  await DatabaseContext.Save(cancellationToken).ConfigureAwait(false);
163 
164  try
165  {
166  //actually reserve it now
167  await ioManager.CreateDirectory(rawPath, cancellationToken).ConfigureAwait(false);
168  await ioManager.DeleteFile(ioManager.ConcatPath(rawPath, InstanceAttachFileName), cancellationToken).ConfigureAwait(false);
169  }
170  catch
171  {
172  //oh shit delete the model
173  DatabaseContext.Instances.Remove(newInstance);
174 
175  await DatabaseContext.Save(default).ConfigureAwait(false);
176  throw;
177  }
178  }
179  catch (IOException e)
180  {
181  return Conflict(new ErrorMessage { Message = e.Message });
182  }
183  catch (DbUpdateException e)
184  {
185  return Conflict(new ErrorMessage { Message = e.Message });
186  }
187 
188  Logger.LogInformation("{0} {1} instance {2}: {3} ({4})", AuthenticationContext.User.Name, attached ? "attached" : "created", newInstance.Name, newInstance.Id, newInstance.Path);
189 
190  var api = newInstance.ToApi();
191  return attached ? (IActionResult)Json(api) : StatusCode((int)HttpStatusCode.Created, api);
192  }
193 
195  [TgsAuthorize(InstanceManagerRights.Delete)]
196  public override async Task<IActionResult> Delete(long id, CancellationToken cancellationToken)
197  {
198  var originalModel = await DatabaseContext.Instances.Where(x => x.Id == id)
199  .Include(x => x.WatchdogReattachInformation)
200  .Include(x => x.WatchdogReattachInformation.Alpha)
201  .Include(x => x.WatchdogReattachInformation.Bravo)
202  .FirstOrDefaultAsync(cancellationToken).ConfigureAwait(false);
203  if (originalModel == default)
204  return StatusCode((int)HttpStatusCode.Gone);
205 
206  if (originalModel.WatchdogReattachInformation != null)
207  {
208  DatabaseContext.WatchdogReattachInformations.Remove(originalModel.WatchdogReattachInformation);
209  if (originalModel.WatchdogReattachInformation.Alpha != null)
210  DatabaseContext.ReattachInformations.Remove(originalModel.WatchdogReattachInformation.Alpha);
211  if (originalModel.WatchdogReattachInformation.Bravo != null)
212  DatabaseContext.ReattachInformations.Remove(originalModel.WatchdogReattachInformation.Bravo);
213  }
214 
215  DatabaseContext.Instances.Remove(originalModel);
216 
217  var attachFileName = ioManager.ConcatPath(originalModel.Path, InstanceAttachFileName);
218  await ioManager.WriteAllBytes(attachFileName, Array.Empty<byte>(), default).ConfigureAwait(false);
219  await DatabaseContext.Save(cancellationToken).ConfigureAwait(false); //cascades everything
220  return Ok();
221  }
222 
224  [TgsAuthorize(InstanceManagerRights.Relocate | InstanceManagerRights.Rename | InstanceManagerRights.SetAutoUpdate | InstanceManagerRights.SetConfiguration | InstanceManagerRights.SetOnline)]
225  public override async Task<IActionResult> Update([FromBody] Api.Models.Instance model, CancellationToken cancellationToken)
226  {
227  var instanceQuery = DatabaseContext.Instances.Where(x => x.Id == model.Id);
228 
229  var usersInstanceUserTask = instanceQuery.SelectMany(x => x.InstanceUsers).Where(x => x.UserId == AuthenticationContext.User.Id).FirstOrDefaultAsync(cancellationToken);
230 
231  var originalModel = await instanceQuery
232  .Include(x => x.RepositorySettings)
233  .Include(x => x.ChatSettings)
234  .ThenInclude(x => x.Channels)
235  .Include(x => x.DreamDaemonSettings) //need these for onlining
236  .FirstOrDefaultAsync(cancellationToken).ConfigureAwait(false);
237  if (originalModel == default(Models.Instance))
238  return StatusCode((int)HttpStatusCode.Gone);
239 
240  var userRights = (InstanceManagerRights)AuthenticationContext.GetRight(RightsType.InstanceManager);
241  bool CheckModified<T>(Expression<Func<Api.Models.Instance, T>> expression, InstanceManagerRights requiredRight)
242  {
243  var memberSelectorExpression = (MemberExpression)expression.Body;
244  var property = (PropertyInfo)memberSelectorExpression.Member;
245 
246  var newVal = property.GetValue(model);
247  if (newVal == null)
248  return false;
249  if (!userRights.HasFlag(requiredRight) && property.GetValue(originalModel) != newVal)
250  return true;
251 
252  property.SetValue(originalModel, newVal);
253  return false;
254  };
255 
256  string originalModelPath = null;
257  string rawPath = null;
258  if (model.Path != null)
259  {
260  NormalizeModelPath(model, out rawPath);
261 
262  if (model.Path != originalModel.Path)
263  {
264  if (!userRights.HasFlag(InstanceManagerRights.Relocate))
265  return Forbid();
266  if (originalModel.Online.Value && model.Online != true)
267  return Conflict(new ErrorMessage { Message = "Cannot relocate an online instance!" });
268 
269  var dirExistsTask = ioManager.DirectoryExists(model.Path, cancellationToken);
270  if (await ioManager.FileExists(model.Path, cancellationToken).ConfigureAwait(false) || await dirExistsTask.ConfigureAwait(false))
271  return Conflict(new ErrorMessage { Message = "Path not empty!" });
272 
273  originalModelPath = originalModel.Path;
274  originalModel.Path = model.Path;
275  }
276  }
277 
278  var originalOnline = originalModel.Online.Value;
279 
280  if (CheckModified(x => x.AutoUpdateInterval, InstanceManagerRights.SetAutoUpdate)
281  || CheckModified(x => x.ConfigurationType, InstanceManagerRights.SetConfiguration)
282  || CheckModified(x => x.Name, InstanceManagerRights.Rename)
283  || CheckModified(x => x.Online, InstanceManagerRights.SetOnline))
284  return Forbid();
285 
286  //ensure the current user has write privilege on the instance
287  var usersInstanceUser = await usersInstanceUserTask.ConfigureAwait(false);
288  if (usersInstanceUser == default)
289  originalModel.InstanceUsers.Add(InstanceAdminUser());
290  else
291  usersInstanceUser.InstanceUserRights |= InstanceUserRights.WriteUsers;
292 
293  await DatabaseContext.Save(cancellationToken).ConfigureAwait(false);
294 
295  var oldAutoStart = originalModel.DreamDaemonSettings.AutoStart;
296  try
297  {
298  if (originalOnline && model.Online == false)
299  await instanceManager.OfflineInstance(originalModel, cancellationToken).ConfigureAwait(false);
300  else if (!originalOnline && model.Online == true)
301  {
302  //force autostart false here because we don't want any long running jobs right now
303  //remember to document this
304  originalModel.DreamDaemonSettings.AutoStart = false;
305  await instanceManager.OnlineInstance(originalModel, cancellationToken).ConfigureAwait(false);
306  }
307  }
308  catch (Exception e)
309  {
310  Logger.LogError("Error changing instance online state! Exception: {0}", e);
311  originalModel.Online = originalOnline;
312  originalModel.DreamDaemonSettings.AutoStart = oldAutoStart;
313  if (originalModelPath != null)
314  originalModel.Path = originalModelPath;
315  await DatabaseContext.Save(default).ConfigureAwait(false);
316  throw;
317  }
318 
319  var api = originalModel.ToApi();
320  if (originalModelPath != null)
321  {
322  var job = new Models.Job
323  {
324  Description = String.Format(CultureInfo.InvariantCulture, "Move instance ID {0} from {1} to {2}", Instance.Id, Instance.Path, rawPath),
325  Instance = Instance,
326  CancelRightsType = RightsType.InstanceManager,
327  CancelRight = (ulong)InstanceManagerRights.Relocate,
328  StartedBy = AuthenticationContext.User
329  };
330 
331  await jobManager.RegisterOperation(job, async (paramJob, serviceProvider, progressHandler, ct) =>
332  {
333  try
334  {
335  await instanceManager.MoveInstance(Instance, rawPath, ct).ConfigureAwait(false);
336  }
337  catch
338  {
339  originalModel.Path = originalModelPath;
340  await DatabaseContext.Save(default).ConfigureAwait(false);
341  throw;
342  }
343  }, cancellationToken).ConfigureAwait(false);
344  api.MoveJob = job.ToApi();
345  }
346 
347  return Json(api);
348  }
349 
351  [TgsAuthorize(InstanceManagerRights.List | InstanceManagerRights.Read)]
352  public override async Task<IActionResult> List(CancellationToken cancellationToken)
353  {
354  IQueryable<Models.Instance> query = DatabaseContext.Instances;
356  query = query.Where(x => x.InstanceUsers.Any(y => y.UserId == AuthenticationContext.User.Id)).Where(x => x.InstanceUsers.Any(y => y.AnyRights));
357  var instances = await query.ToListAsync(cancellationToken).ConfigureAwait(false);
358  return Json(instances.Select(x => x.ToApi()));
359  }
360 
362  [TgsAuthorize(InstanceManagerRights.List | InstanceManagerRights.Read)]
363  public override async Task<IActionResult> GetId(long id, CancellationToken cancellationToken)
364  {
365  var query = DatabaseContext.Instances.Where(x => x.Id == id);
366  var cantList = !AuthenticationContext.User.InstanceManagerRights.Value.HasFlag(InstanceManagerRights.List);
367 
368  if (cantList)
369  query = query.Include(x => x.InstanceUsers);
370 
371  var instance = await query.FirstOrDefaultAsync(cancellationToken).ConfigureAwait(false);
372 
373  if (instance == null)
374  return StatusCode((int)HttpStatusCode.Gone);
375 
376  if (cantList && !instance.InstanceUsers.Any(x => x.UserId == AuthenticationContext.User.Id && x.AnyRights))
377  return Forbid();
378 
379  return Json(instance.ToApi());
380  }
381  }
382 }
string Name
The name of the User
Definition: User.cs:38
+Go to the documentation of this file.
1 using Microsoft.AspNetCore.Mvc;
2 using Microsoft.EntityFrameworkCore;
3 using Microsoft.Extensions.Logging;
4 using System;
5 using System.Collections.Generic;
6 using System.Globalization;
7 using System.IO;
8 using System.Linq;
9 using System.Linq.Expressions;
10 using System.Net;
11 using System.Reflection;
12 using System.Runtime.InteropServices;
13 using System.Threading;
14 using System.Threading.Tasks;
15 using Tgstation.Server.Api;
20 using Tgstation.Server.Host.IO;
23 
24 namespace Tgstation.Server.Host.Controllers
25 {
29  [Route(Routes.InstanceManager)]
30  public sealed class InstanceController : ModelController<Api.Models.Instance>
31  {
35  const string InstanceAttachFileName = "TGS4_ALLOW_INSTANCE_ATTACH";
36 
37  const string MoveInstanceJobPrefix = "Move instance ID ";
38 
51 
56 
67  public InstanceController(IDatabaseContext databaseContext, IAuthenticationContextFactory authenticationContextFactory, IJobManager jobManager, IInstanceManager instanceManager, IIOManager ioManager, IApplication application, ILogger<InstanceController> logger) : base(databaseContext, authenticationContextFactory, logger, false)
68  {
69  this.jobManager = jobManager ?? throw new ArgumentNullException(nameof(jobManager));
70  this.instanceManager = instanceManager ?? throw new ArgumentNullException(nameof(instanceManager));
71  this.ioManager = ioManager ?? throw new ArgumentNullException(nameof(ioManager));
72  this.application = application ?? throw new ArgumentNullException(nameof(application));
73  }
74 
75  void NormalizeModelPath(Api.Models.Instance model, out string absolutePath)
76  {
77  if (model.Path == null)
78  {
79  absolutePath = null;
80  return;
81  }
82  absolutePath = ioManager.ResolvePath(model.Path);
83  if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
84  model.Path = absolutePath.ToUpperInvariant();
85  else
86  model.Path = absolutePath;
87  }
88 
89  Models.InstanceUser InstanceAdminUser() => new Models.InstanceUser
90  {
91  ByondRights = (ByondRights)~0U,
99  };
100 
102  [TgsAuthorize(InstanceManagerRights.Create)]
103  public override async Task<IActionResult> Create([FromBody] Api.Models.Instance model, CancellationToken cancellationToken)
104  {
105  if (model == null)
106  throw new ArgumentNullException(nameof(model));
107 
108  if (String.IsNullOrWhiteSpace(model.Name))
109  return BadRequest(new ErrorMessage { Message = "name must not be empty!" });
110 
111  if (model.Path == null)
112  return BadRequest(new ErrorMessage { Message = "path must not be empty!" });
113 
114  NormalizeModelPath(model, out var rawPath);
115  var dirExistsTask = ioManager.DirectoryExists(model.Path, cancellationToken);
116  bool attached = false;
117  if (await ioManager.FileExists(model.Path, cancellationToken).ConfigureAwait(false) || await dirExistsTask.ConfigureAwait(false))
118  if (!await ioManager.FileExists(ioManager.ConcatPath(model.Path, InstanceAttachFileName), cancellationToken).ConfigureAwait(false))
119  return Conflict(new ErrorMessage { Message = "Path not empty!" });
120  else
121  attached = true;
122 
123  var newInstance = new Models.Instance
124  {
125  ConfigurationType = model.ConfigurationType ?? ConfigurationType.Disallowed,
127  {
128  AllowWebClient = false,
129  AutoStart = false,
130  PrimaryPort = 1337,
131  SecondaryPort = 1338,
132  SecurityLevel = DreamDaemonSecurity.Safe,
133  SoftRestart = false,
134  SoftShutdown = false,
135  StartupTimeout = 20
136  },
138  {
139  ApiValidationPort = 1339
140  },
141  Name = model.Name,
142  Online = false,
143  Path = model.Path,
144  AutoUpdateInterval = model.AutoUpdateInterval ?? 0,
146  {
147  CommitterEmail = "tgstation-server@users.noreply.github.com",
148  CommitterName = application.VersionString,
149  PushTestMergeCommits = false,
150  ShowTestMergeCommitters = false,
151  AutoUpdatesKeepTestMerges = false,
152  AutoUpdatesSynchronize = false
153  },
154  //give this user full privileges on the instance
155  InstanceUsers = new List<Models.InstanceUser>
156  {
157  InstanceAdminUser()
158  }
159  };
160 
161  DatabaseContext.Instances.Add(newInstance);
162  try
163  {
164  await DatabaseContext.Save(cancellationToken).ConfigureAwait(false);
165 
166  try
167  {
168  //actually reserve it now
169  await ioManager.CreateDirectory(rawPath, cancellationToken).ConfigureAwait(false);
170  await ioManager.DeleteFile(ioManager.ConcatPath(rawPath, InstanceAttachFileName), cancellationToken).ConfigureAwait(false);
171  }
172  catch
173  {
174  //oh shit delete the model
175  DatabaseContext.Instances.Remove(newInstance);
176 
177  await DatabaseContext.Save(default).ConfigureAwait(false);
178  throw;
179  }
180  }
181  catch (IOException e)
182  {
183  return Conflict(new ErrorMessage { Message = e.Message });
184  }
185  catch (DbUpdateException e)
186  {
187  return Conflict(new ErrorMessage { Message = e.Message });
188  }
189 
190  Logger.LogInformation("{0} {1} instance {2}: {3} ({4})", AuthenticationContext.User.Name, attached ? "attached" : "created", newInstance.Name, newInstance.Id, newInstance.Path);
191 
192  var api = newInstance.ToApi();
193  return attached ? (IActionResult)Json(api) : StatusCode((int)HttpStatusCode.Created, api);
194  }
195 
197  [TgsAuthorize(InstanceManagerRights.Delete)]
198  public override async Task<IActionResult> Delete(long id, CancellationToken cancellationToken)
199  {
200  var originalModel = await DatabaseContext.Instances.Where(x => x.Id == id)
201  .Include(x => x.WatchdogReattachInformation)
202  .Include(x => x.WatchdogReattachInformation.Alpha)
203  .Include(x => x.WatchdogReattachInformation.Bravo)
204  .FirstOrDefaultAsync(cancellationToken).ConfigureAwait(false);
205  if (originalModel == default)
206  return StatusCode((int)HttpStatusCode.Gone);
207  if (originalModel.Online.Value)
208  return Conflict(new ErrorMessage
209  {
210  Message = "Cannot detach an online instance!"
211  });
212 
213  if (originalModel.WatchdogReattachInformation != null)
214  {
215  DatabaseContext.WatchdogReattachInformations.Remove(originalModel.WatchdogReattachInformation);
216  if (originalModel.WatchdogReattachInformation.Alpha != null)
217  DatabaseContext.ReattachInformations.Remove(originalModel.WatchdogReattachInformation.Alpha);
218  if (originalModel.WatchdogReattachInformation.Bravo != null)
219  DatabaseContext.ReattachInformations.Remove(originalModel.WatchdogReattachInformation.Bravo);
220  }
221 
222  DatabaseContext.Instances.Remove(originalModel);
223 
224  var attachFileName = ioManager.ConcatPath(originalModel.Path, InstanceAttachFileName);
225  await ioManager.WriteAllBytes(attachFileName, Array.Empty<byte>(), default).ConfigureAwait(false);
226  await DatabaseContext.Save(cancellationToken).ConfigureAwait(false); //cascades everything
227  return Ok();
228  }
229 
231  [TgsAuthorize(InstanceManagerRights.Relocate | InstanceManagerRights.Rename | InstanceManagerRights.SetAutoUpdate | InstanceManagerRights.SetConfiguration | InstanceManagerRights.SetOnline)]
232  public override async Task<IActionResult> Update([FromBody] Api.Models.Instance model, CancellationToken cancellationToken)
233  {
234  var instanceQuery = DatabaseContext.Instances.Where(x => x.Id == model.Id);
235 
236  var moveJob = await instanceQuery
237  .SelectMany(x => x.Jobs).
238  Where(x => !x.StoppedAt.HasValue && x.Description.StartsWith(MoveInstanceJobPrefix, StringComparison.Ordinal))
239  .Select(x => new Models.Job
240  {
241  Id = x.Id
242  }).FirstOrDefaultAsync(cancellationToken).ConfigureAwait(false);
243 
244  if (moveJob != default)
245  //cancel it now
246  await jobManager.CancelJob(moveJob, AuthenticationContext.User, true, cancellationToken).ConfigureAwait(false);
247 
248  var usersInstanceUserTask = instanceQuery.SelectMany(x => x.InstanceUsers).Where(x => x.UserId == AuthenticationContext.User.Id).FirstOrDefaultAsync(cancellationToken);
249 
250  var originalModel = await instanceQuery
251  .Include(x => x.RepositorySettings)
252  .Include(x => x.ChatSettings)
253  .ThenInclude(x => x.Channels)
254  .Include(x => x.DreamDaemonSettings) //need these for onlining
255  .FirstOrDefaultAsync(cancellationToken).ConfigureAwait(false);
256  if (originalModel == default(Models.Instance))
257  return StatusCode((int)HttpStatusCode.Gone);
258 
259  var userRights = (InstanceManagerRights)AuthenticationContext.GetRight(RightsType.InstanceManager);
260  bool CheckModified<T>(Expression<Func<Api.Models.Instance, T>> expression, InstanceManagerRights requiredRight)
261  {
262  var memberSelectorExpression = (MemberExpression)expression.Body;
263  var property = (PropertyInfo)memberSelectorExpression.Member;
264 
265  var newVal = property.GetValue(model);
266  if (newVal == null)
267  return false;
268  if (!userRights.HasFlag(requiredRight) && property.GetValue(originalModel) != newVal)
269  return true;
270 
271  property.SetValue(originalModel, newVal);
272  return false;
273  };
274 
275  string originalModelPath = null;
276  string rawPath = null;
277  if (model.Path != null)
278  {
279  NormalizeModelPath(model, out rawPath);
280 
281  if (model.Path != originalModel.Path)
282  {
283  if (!userRights.HasFlag(InstanceManagerRights.Relocate))
284  return Forbid();
285  if (originalModel.Online.Value && model.Online != true)
286  return Conflict(new ErrorMessage { Message = "Cannot relocate an online instance!" });
287 
288  var dirExistsTask = ioManager.DirectoryExists(model.Path, cancellationToken);
289  if (await ioManager.FileExists(model.Path, cancellationToken).ConfigureAwait(false) || await dirExistsTask.ConfigureAwait(false))
290  return Conflict(new ErrorMessage { Message = "Path not empty!" });
291 
292  originalModelPath = originalModel.Path;
293  originalModel.Path = model.Path;
294  }
295  }
296 
297  var originalOnline = originalModel.Online.Value;
298 
299  if (CheckModified(x => x.AutoUpdateInterval, InstanceManagerRights.SetAutoUpdate)
300  || CheckModified(x => x.ConfigurationType, InstanceManagerRights.SetConfiguration)
301  || CheckModified(x => x.Name, InstanceManagerRights.Rename)
302  || CheckModified(x => x.Online, InstanceManagerRights.SetOnline))
303  return Forbid();
304 
305  //ensure the current user has write privilege on the instance
306  var usersInstanceUser = await usersInstanceUserTask.ConfigureAwait(false);
307  if (usersInstanceUser == default)
308  originalModel.InstanceUsers.Add(InstanceAdminUser());
309  else
310  usersInstanceUser.InstanceUserRights |= InstanceUserRights.WriteUsers;
311 
312  await DatabaseContext.Save(cancellationToken).ConfigureAwait(false);
313 
314  var oldAutoStart = originalModel.DreamDaemonSettings.AutoStart;
315  try
316  {
317  if (originalOnline && model.Online == false)
318  await instanceManager.OfflineInstance(originalModel, AuthenticationContext.User, cancellationToken).ConfigureAwait(false);
319  else if (!originalOnline && model.Online == true)
320  {
321  //force autostart false here because we don't want any long running jobs right now
322  //remember to document this
323  originalModel.DreamDaemonSettings.AutoStart = false;
324  await instanceManager.OnlineInstance(originalModel, cancellationToken).ConfigureAwait(false);
325  }
326  }
327  catch (Exception e)
328  {
329  Logger.LogError("Error changing instance online state! Exception: {0}", e);
330  originalModel.Online = originalOnline;
331  originalModel.DreamDaemonSettings.AutoStart = oldAutoStart;
332  if (originalModelPath != null)
333  originalModel.Path = originalModelPath;
334  await DatabaseContext.Save(default).ConfigureAwait(false);
335  throw;
336  }
337 
338  var api = originalModel.ToApi();
339  if (originalModelPath != null)
340  {
341  var job = new Models.Job
342  {
343  Description = String.Format(CultureInfo.InvariantCulture, MoveInstanceJobPrefix + "{0} from {1} to {2}", originalModel.Id, originalModel.Path, rawPath),
344  Instance = originalModel,
345  CancelRightsType = RightsType.InstanceManager,
346  CancelRight = (ulong)InstanceManagerRights.Relocate,
347  StartedBy = AuthenticationContext.User
348  };
349 
350  await jobManager.RegisterOperation(job, (paramJob, serviceProvider, progressHandler, ct) => instanceManager.MoveInstance(originalModel, rawPath, ct), cancellationToken).ConfigureAwait(false);
351  api.MoveJob = job.ToApi();
352  }
353 
354  return Json(api);
355  }
356 
358  [TgsAuthorize(InstanceManagerRights.List | InstanceManagerRights.Read)]
359  public override async Task<IActionResult> List(CancellationToken cancellationToken)
360  {
361  IQueryable<Models.Instance> query = DatabaseContext.Instances;
363  query = query.Where(x => x.InstanceUsers.Any(y => y.UserId == AuthenticationContext.User.Id)).Where(x => x.InstanceUsers.Any(y => y.AnyRights));
364 
365  var moveJobTasks = query
366  .SelectMany(x => x.Jobs)
367  .Where(x => !x.StoppedAt.HasValue && x.Description.StartsWith(MoveInstanceJobPrefix, StringComparison.Ordinal))
368  .Include(x => x.StartedBy).ThenInclude(x => x.CreatedBy)
369  .Include(x => x.Instance)
370  .ToListAsync(cancellationToken);
371  var instances = await query.ToListAsync(cancellationToken).ConfigureAwait(false);
372  var apis = instances.Select(x => x.ToApi());
373  var moveJobs = await moveJobTasks.ConfigureAwait(false);
374  foreach(var I in moveJobs)
375  apis.Where(x => x.Id == I.Instance.Id).First().MoveJob = I.ToApi(); //if this .First() fails i will personally murder kevinz000 because I just know he is somehow responsible
376  return Json(apis);
377  }
378 
380  [TgsAuthorize(InstanceManagerRights.List | InstanceManagerRights.Read)]
381  public override async Task<IActionResult> GetId(long id, CancellationToken cancellationToken)
382  {
383  var query = DatabaseContext.Instances.Where(x => x.Id == id);
384  var cantList = !AuthenticationContext.User.InstanceManagerRights.Value.HasFlag(InstanceManagerRights.List);
385 
386  if (cantList)
387  query = query.Include(x => x.InstanceUsers);
388 
389  var moveJobTask = query
390  .SelectMany(x => x.Jobs)
391  .Where(x => !x.StoppedAt.HasValue && x.Description.StartsWith(MoveInstanceJobPrefix, StringComparison.Ordinal))
392  .Include(x => x.StartedBy).ThenInclude(x => x.CreatedBy)
393  .FirstOrDefaultAsync(cancellationToken);
394  var instance = await query.FirstOrDefaultAsync(cancellationToken).ConfigureAwait(false);
395 
396  if (instance == null)
397  return StatusCode((int)HttpStatusCode.Gone);
398 
399  if (cantList && !instance.InstanceUsers.Any(x => x.UserId == AuthenticationContext.User.Id && x.AnyRights))
400  return Forbid();
401 
402  var api = instance.ToApi();
403  api.MoveJob = (await moveJobTask.ConfigureAwait(false))?.ToApi();
404  return Json(api);
405  }
406  }
407 }
string Name
The name of the User
Definition: User.cs:38
-
Manages the runtime of Jobs
Definition: IJobManager.cs:13
+
Manages the runtime of Jobs
Definition: IJobManager.cs:12
ConfigurationRights
Rights for Models.ConfigurationFile
@@ -108,8 +108,8 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
InstanceUserRights
Rights for an Models.Instance
Configures the ASP.NET Core web application
Definition: IApplication.cs:8
InstanceManagerRights InstanceManagerRights
The Rights.InstanceManagerRights for the User
Definition: User.cs:50
-
readonly IInstanceManager instanceManager
The IInstanceManager for the InstanceController
-
readonly IApplication application
The IApplication for the InstanceController
+
readonly IInstanceManager instanceManager
The IInstanceManager for the InstanceController
+
readonly IApplication application
The IApplication for the InstanceController
@@ -120,10 +120,9 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
ulong GetRight(RightsType rightsType)
Get the value of a given rightsType
Controller for managing Components.Instances
-
readonly IIOManager ioManager
The IIOManager for the InstanceController
-
readonly IJobManager jobManager
The IJobManager for the InstanceController
+
readonly IIOManager ioManager
The IIOManager for the InstanceController
+
readonly IJobManager jobManager
The IJobManager for the InstanceController
-
string Path
The path to where the Instance is located. Can only be changed while the Instance is offline...
Definition: Instance.cs:26
Routes to a server actions
Definition: Routes.cs:9
DreamDaemonRights
Rights for Models.DreamDaemon
InstanceManagerRights
Rights for managing Models.Instances
@@ -131,28 +130,27 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
DreamMakerRights
Rights for Models.DreamMaker
-
override async Task< IActionResult > Create([FromBody] Api.Models.Instance model, CancellationToken cancellationToken)
+
override async Task< IActionResult > Create([FromBody] Api.Models.Instance model, CancellationToken cancellationToken)
RepositoryRights
Rights for a Models.Repository
-
long Id
The id of the Instance. Not modifiable
Definition: Instance.cs:14
An ApiController representing a TModel
-
override async Task< IActionResult > List(CancellationToken cancellationToken)
Attempt to list entries of the TModel
+
override async Task< IActionResult > List(CancellationToken cancellationToken)
Attempt to list entries of the TModel
const string InstanceManager
The Models.Instance controller
Definition: Routes.cs:29
-
InstanceController(IDatabaseContext databaseContext, IAuthenticationContextFactory authenticationContextFactory, IJobManager jobManager, IInstanceManager instanceManager, IIOManager ioManager, IApplication application, ILogger< InstanceController > logger)
Construct a InstanceController
+
InstanceController(IDatabaseContext databaseContext, IAuthenticationContextFactory authenticationContextFactory, IJobManager jobManager, IInstanceManager instanceManager, IIOManager ioManager, IApplication application, ILogger< InstanceController > logger)
Construct a InstanceController
long Id
The ID of the User
Definition: User.cs:15
Metadata about a server instance
Definition: Instance.cs:9
-
override async Task< IActionResult > Delete(long id, CancellationToken cancellationToken)
Attempt to delete a model with a particular id
+
override async Task< IActionResult > Delete(long id, CancellationToken cancellationToken)
Attempt to delete a model with a particular id
DbSet< WatchdogReattachInformation > WatchdogReattachInformations
DreamDaemonSecurity
DreamDaemon&#39;s security level
Interface for using filesystems
Definition: IIOManager.cs:11
-
override async Task< IActionResult > Update([FromBody] Api.Models.Instance model, CancellationToken cancellationToken)
-
override async Task< IActionResult > GetId(long id, CancellationToken cancellationToken)
Attempt to get a specific a TModel
+
override async Task< IActionResult > Update([FromBody] Api.Models.Instance model, CancellationToken cancellationToken)
+
override async Task< IActionResult > GetId(long id, CancellationToken cancellationToken)
Attempt to get a specific a TModel
ChatBotRights
Rights for Models.ChatBot
Definition: ChatBotRights.cs:9
-
void NormalizeModelPath(Api.Models.Instance model, out string absolutePath)
+
void NormalizeModelPath(Api.Models.Instance model, out string absolutePath)
Represents an error message returned by the server
Definition: ErrorMessage.cs:8
User can enable Models.Internal.DreamDaemonSettings.SoftShutdown
diff --git a/_instance_manager_8cs_source.html b/_instance_manager_8cs_source.html index f495566c8b..1088fab355 100644 --- a/_instance_manager_8cs_source.html +++ b/_instance_manager_8cs_source.html @@ -91,14 +91,14 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
InstanceManager.cs
-Go to the documentation of this file.
1 using Microsoft.EntityFrameworkCore;
2 using Microsoft.Extensions.Hosting;
3 using Microsoft.Extensions.Logging;
4 using System;
5 using System.Collections.Generic;
6 using System.Linq;
7 using System.Threading;
8 using System.Threading.Tasks;
10 using Tgstation.Server.Host.IO;
11 
12 namespace Tgstation.Server.Host.Components
13 {
16  {
21 
26 
31 
36 
41 
45  readonly ILogger<InstanceManager> logger;
46 
50  readonly Dictionary<long, IInstance> instances;
51 
55  readonly List<Task> shutdownTasks;
56 
60  readonly CancellationTokenSource shutdownCancellationTokenSource;
61 
72  public InstanceManager(IInstanceFactory instanceFactory, IIOManager ioManager, IDatabaseContextFactory databaseContextFactory, IApplication application, IJobManager jobManager, IServerControl serverControl, ILogger<InstanceManager> logger)
73  {
74  this.instanceFactory = instanceFactory ?? throw new ArgumentNullException(nameof(instanceFactory));
75  this.ioManager = ioManager ?? throw new ArgumentNullException(nameof(ioManager));
76  this.databaseContextFactory = databaseContextFactory ?? throw new ArgumentNullException(nameof(databaseContextFactory));
77  this.application = application ?? throw new ArgumentNullException(nameof(application));
78  this.jobManager = jobManager ?? throw new ArgumentNullException(nameof(jobManager));
79 
80  if (serverControl == null)
81  throw new ArgumentNullException(nameof(serverControl));
82 
83  shutdownCancellationTokenSource = new CancellationTokenSource();
84  var cancellationToken = shutdownCancellationTokenSource.Token;
85  serverControl.RegisterForRestart(() =>
86  {
87  lock (this)
88  shutdownTasks.AddRange(instances.Select(x => x.Value.Chat.SendBroadcast("TGS: Restart requested...", cancellationToken)));
89  });
90 
91  this.logger = logger ?? throw new ArgumentNullException(nameof(logger));
92 
93  instances = new Dictionary<long, IInstance>();
94  shutdownTasks = new List<Task>();
95  }
96 
98  public void Dispose()
99  {
100  foreach (var I in instances)
101  I.Value.Dispose();
102  shutdownCancellationTokenSource.Dispose();
103  }
104 
106  public IInstance GetInstance(Models.Instance metadata)
107  {
108  if (metadata == null)
109  throw new ArgumentNullException(nameof(metadata));
110  lock (this)
111  {
112  if (!instances.TryGetValue(metadata.Id, out IInstance instance))
113  throw new InvalidOperationException("Instance not online!");
114  return instance;
115  }
116  }
117 
119  public async Task MoveInstance(Models.Instance instance, string newPath, CancellationToken cancellationToken)
120  {
121  if (newPath == null)
122  throw new ArgumentNullException(nameof(newPath));
123  if (instance.Online.Value)
124  throw new InvalidOperationException("Cannot move an online instance!");
125  var oldPath = instance.Path;
126  await ioManager.CopyDirectory(oldPath, newPath, null, cancellationToken).ConfigureAwait(false);
127  instance.Path = ioManager.ResolvePath(newPath);
128  await ioManager.DeleteDirectory(oldPath, cancellationToken).ConfigureAwait(false);
129  }
130 
132  public async Task OfflineInstance(Models.Instance metadata, CancellationToken cancellationToken)
133  {
134  if (metadata == null)
135  throw new ArgumentNullException(nameof(metadata));
136  logger.LogInformation("Offlining instance ID {0}", metadata.Id);
137  IInstance instance;
138  lock (this)
139  {
140  if (!instances.TryGetValue(metadata.Id, out instance))
141  throw new InvalidOperationException("Instance not online!");
142  instances.Remove(metadata.Id);
143  }
144  try
145  {
146  await instance.StopAsync(cancellationToken).ConfigureAwait(false);
147  }
148  finally
149  {
150  instance.Dispose();
151  }
152  }
153 
155  public async Task OnlineInstance(Models.Instance metadata, CancellationToken cancellationToken)
156  {
157  if (metadata == null)
158  throw new ArgumentNullException(nameof(metadata));
159  logger.LogInformation("Onlining instance ID {0} ({1}) at {2}", metadata.Id, metadata.Name, metadata.Path);
160  var instance = instanceFactory.CreateInstance(metadata);
161  try
162  {
163  lock (this)
164  {
165  if (instances.ContainsKey(metadata.Id))
166  throw new InvalidOperationException("Instance already online!");
167  instances.Add(metadata.Id, instance);
168  }
169  }
170  catch
171  {
172  instance.Dispose();
173  throw;
174  }
175  await instance.StartAsync(cancellationToken).ConfigureAwait(false);
176  }
177 
179  public Task StartAsync(CancellationToken cancellationToken) => databaseContextFactory.UseContext(async databaseContext =>
180  {
181  try
182  {
183  var factoryStartup = instanceFactory.StartAsync(cancellationToken);
184  await databaseContext.Initialize(cancellationToken).ConfigureAwait(false);
185  await jobManager.StartAsync(cancellationToken).ConfigureAwait(false);
186  var dbInstances = databaseContext.Instances.Where(x => x.Online.Value)
187  .Include(x => x.RepositorySettings)
188  .Include(x => x.ChatSettings)
189  .ThenInclude(x => x.Channels)
190  .Include(x => x.DreamDaemonSettings)
191  .ToAsyncEnumerable();
192  var tasks = new List<Task>();
193  await factoryStartup.ConfigureAwait(false);
194  await dbInstances.ForEachAsync(metadata => tasks.Add(metadata.Online.Value ? OnlineInstance(metadata, cancellationToken) : Task.CompletedTask), cancellationToken).ConfigureAwait(false);
195  await Task.WhenAll(tasks).ConfigureAwait(false);
196  logger.LogInformation("Instance manager ready!");
197  application.Ready(null);
198  }
199  catch (OperationCanceledException)
200  {
201  logger.LogInformation("Cancelled instance manager initialization!");
202  }
203  catch (Exception e)
204  {
205  logger.LogCritical("Instance manager startup error! Exception: {0}", e);
206  application.Ready(e);
207  }
208  });
209 
211  public async Task StopAsync(CancellationToken cancellationToken)
212  {
213  await jobManager.StopAsync(cancellationToken).ConfigureAwait(false);
214 
215  using (cancellationToken.Register(() => shutdownCancellationTokenSource.Cancel()))
216  await Task.WhenAll(shutdownTasks).ConfigureAwait(false);
217  await Task.WhenAll(instances.Select(x => x.Value.StopAsync(cancellationToken))).ConfigureAwait(false);
218 
219  await instanceFactory.StopAsync(cancellationToken).ConfigureAwait(false);
220  }
221  }
222 }
-
Manages the runtime of Jobs
Definition: IJobManager.cs:13
+Go to the documentation of this file.
1 using Microsoft.EntityFrameworkCore;
2 using Microsoft.Extensions.Hosting;
3 using Microsoft.Extensions.Logging;
4 using System;
5 using System.Collections.Generic;
6 using System.Linq;
7 using System.Threading;
8 using System.Threading.Tasks;
10 using Tgstation.Server.Host.IO;
11 
12 namespace Tgstation.Server.Host.Components
13 {
16  {
21 
26 
31 
36 
41 
45  readonly ILogger<InstanceManager> logger;
46 
50  readonly Dictionary<long, IInstance> instances;
51 
55  readonly List<Task> shutdownTasks;
56 
60  readonly CancellationTokenSource shutdownCancellationTokenSource;
61 
72  public InstanceManager(IInstanceFactory instanceFactory, IIOManager ioManager, IDatabaseContextFactory databaseContextFactory, IApplication application, IJobManager jobManager, IServerControl serverControl, ILogger<InstanceManager> logger)
73  {
74  this.instanceFactory = instanceFactory ?? throw new ArgumentNullException(nameof(instanceFactory));
75  this.ioManager = ioManager ?? throw new ArgumentNullException(nameof(ioManager));
76  this.databaseContextFactory = databaseContextFactory ?? throw new ArgumentNullException(nameof(databaseContextFactory));
77  this.application = application ?? throw new ArgumentNullException(nameof(application));
78  this.jobManager = jobManager ?? throw new ArgumentNullException(nameof(jobManager));
79 
80  if (serverControl == null)
81  throw new ArgumentNullException(nameof(serverControl));
82 
83  shutdownCancellationTokenSource = new CancellationTokenSource();
84  var cancellationToken = shutdownCancellationTokenSource.Token;
85  serverControl.RegisterForRestart(() =>
86  {
87  lock (this)
88  shutdownTasks.AddRange(instances.Select(x => x.Value.Chat.SendBroadcast("TGS: Restart requested...", cancellationToken)));
89  });
90 
91  this.logger = logger ?? throw new ArgumentNullException(nameof(logger));
92 
93  instances = new Dictionary<long, IInstance>();
94  shutdownTasks = new List<Task>();
95  }
96 
98  public void Dispose()
99  {
100  foreach (var I in instances)
101  I.Value.Dispose();
102  shutdownCancellationTokenSource.Dispose();
103  }
104 
106  public IInstance GetInstance(Models.Instance metadata)
107  {
108  if (metadata == null)
109  throw new ArgumentNullException(nameof(metadata));
110  lock (this)
111  {
112  if (!instances.TryGetValue(metadata.Id, out IInstance instance))
113  throw new InvalidOperationException("Instance not online!");
114  return instance;
115  }
116  }
117 
119  public async Task MoveInstance(Models.Instance instance, string newPath, CancellationToken cancellationToken)
120  {
121  if (newPath == null)
122  throw new ArgumentNullException(nameof(newPath));
123  if (instance.Online.Value)
124  throw new InvalidOperationException("Cannot move an online instance!");
125  var oldPath = instance.Path;
126  await ioManager.CopyDirectory(oldPath, newPath, null, cancellationToken).ConfigureAwait(false);
127  await databaseContextFactory.UseContext(db =>
128  {
129  var targetInstance = new Models.Instance
130  {
131  Id = instance.Id
132  };
133  db.Instances.Attach(targetInstance);
134  targetInstance.Path = newPath;
135  return db.Save(cancellationToken);
136  }).ConfigureAwait(false);
137  await ioManager.DeleteDirectory(oldPath, cancellationToken).ConfigureAwait(false);
138  }
139 
141  public async Task OfflineInstance(Models.Instance metadata, Models.User user, CancellationToken cancellationToken)
142  {
143  if (metadata == null)
144  throw new ArgumentNullException(nameof(metadata));
145  logger.LogInformation("Offlining instance ID {0}", metadata.Id);
146  IInstance instance;
147  lock (this)
148  {
149  if (!instances.TryGetValue(metadata.Id, out instance))
150  throw new InvalidOperationException("Instance not online!");
151  instances.Remove(metadata.Id);
152  }
153  try
154  {
155  //we are the one responsible for cancelling his jobs
156  var tasks = new List<Task>();
157  await databaseContextFactory.UseContext(async db =>
158  {
159  var jobs = db.Jobs.Where(x => x.Instance.Id == metadata.Id).Select(x => new Models.Job
160  {
161  Id = x.Id
162  }).ToAsyncEnumerable();
163  await jobs.ForEachAsync(job =>
164  {
165  lock (tasks)
166  tasks.Add(jobManager.CancelJob(job, user, true, cancellationToken));
167  }, cancellationToken).ConfigureAwait(false);
168  }).ConfigureAwait(false);
169 
170  await Task.WhenAll(tasks).ConfigureAwait(false);
171 
172  await instance.StopAsync(cancellationToken).ConfigureAwait(false);
173  }
174  finally
175  {
176  instance.Dispose();
177  }
178  }
179 
181  public async Task OnlineInstance(Models.Instance metadata, CancellationToken cancellationToken)
182  {
183  if (metadata == null)
184  throw new ArgumentNullException(nameof(metadata));
185  logger.LogInformation("Onlining instance ID {0} ({1}) at {2}", metadata.Id, metadata.Name, metadata.Path);
186  var instance = instanceFactory.CreateInstance(metadata);
187  try
188  {
189  lock (this)
190  {
191  if (instances.ContainsKey(metadata.Id))
192  throw new InvalidOperationException("Instance already online!");
193  instances.Add(metadata.Id, instance);
194  }
195  }
196  catch
197  {
198  instance.Dispose();
199  throw;
200  }
201  await instance.StartAsync(cancellationToken).ConfigureAwait(false);
202  }
203 
205  public Task StartAsync(CancellationToken cancellationToken) => databaseContextFactory.UseContext(async databaseContext =>
206  {
207  try
208  {
209  var factoryStartup = instanceFactory.StartAsync(cancellationToken);
210  await databaseContext.Initialize(cancellationToken).ConfigureAwait(false);
211  await jobManager.StartAsync(cancellationToken).ConfigureAwait(false);
212  var dbInstances = databaseContext.Instances.Where(x => x.Online.Value)
213  .Include(x => x.RepositorySettings)
214  .Include(x => x.ChatSettings)
215  .ThenInclude(x => x.Channels)
216  .Include(x => x.DreamDaemonSettings)
217  .ToAsyncEnumerable();
218  var tasks = new List<Task>();
219  await factoryStartup.ConfigureAwait(false);
220  await dbInstances.ForEachAsync(metadata => tasks.Add(metadata.Online.Value ? OnlineInstance(metadata, cancellationToken) : Task.CompletedTask), cancellationToken).ConfigureAwait(false);
221  await Task.WhenAll(tasks).ConfigureAwait(false);
222  logger.LogInformation("Instance manager ready!");
223  application.Ready(null);
224  }
225  catch (OperationCanceledException)
226  {
227  logger.LogInformation("Cancelled instance manager initialization!");
228  }
229  catch (Exception e)
230  {
231  logger.LogCritical("Instance manager startup error! Exception: {0}", e);
232  application.Ready(e);
233  }
234  });
235 
237  public async Task StopAsync(CancellationToken cancellationToken)
238  {
239  await jobManager.StopAsync(cancellationToken).ConfigureAwait(false);
240 
241  using (cancellationToken.Register(() => shutdownCancellationTokenSource.Cancel()))
242  await Task.WhenAll(shutdownTasks).ConfigureAwait(false);
243  await Task.WhenAll(instances.Select(x => x.Value.StopAsync(cancellationToken))).ConfigureAwait(false);
244 
245  await instanceFactory.StopAsync(cancellationToken).ConfigureAwait(false);
246  }
247  }
248 }
+
Manages the runtime of Jobs
Definition: IJobManager.cs:12
IInstance GetInstance(Models.Instance metadata)
Get the IInstance associated with given metadata
Configures the ASP.NET Core web application
Definition: IApplication.cs:8
-
async Task OnlineInstance(Models.Instance metadata, CancellationToken cancellationToken)
Online an IInstance
+
async Task OnlineInstance(Models.Instance metadata, CancellationToken cancellationToken)
Online an IInstance
Factory for scoping usage of IDatabaseContexts. Meant for use by Components
-
async Task OfflineInstance(Models.Instance metadata, CancellationToken cancellationToken)
Offline an IInstance
+
async Task OfflineInstance(Models.Instance metadata, Models.User user, CancellationToken cancellationToken)
readonly IDatabaseContextFactory databaseContextFactory
The IDatabaseContextFactory for the InstanceManager
@@ -107,7 +107,7 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
readonly List< Task > shutdownTasks
List<T> of Tasks to finish in StopAsync(CancellationToken)
-
async Task StopAsync(CancellationToken cancellationToken)
+
async Task StopAsync(CancellationToken cancellationToken)
readonly ILogger< InstanceManager > logger
The ILogger for the InstanceManager
diff --git a/_instance_manager_client_8cs_source.html b/_instance_manager_client_8cs_source.html index 728c3606c4..aa768157f0 100644 --- a/_instance_manager_client_8cs_source.html +++ b/_instance_manager_client_8cs_source.html @@ -91,7 +91,7 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
InstanceManagerClient.cs
-Go to the documentation of this file.
1 using System;
2 using System.Collections.Generic;
3 using System.Threading;
4 using System.Threading.Tasks;
5 using Tgstation.Server.Api;
8 
9 namespace Tgstation.Server.Client
10 {
13  {
18 
22  readonly Dictionary<long, IInstanceClient> cachedClients;
23 
29  {
30  this.apiClient = apiClient ?? throw new ArgumentNullException(nameof(apiClient));
31 
32  cachedClients = new Dictionary<long, IInstanceClient>();
33  }
34 
36  public Task<Instance> Create(Instance instance, CancellationToken cancellationToken) => apiClient.Create<Instance, Instance>(Routes.InstanceManager, instance ?? throw new ArgumentNullException(nameof(instance)), cancellationToken);
37 
39  public Task Delete(Instance instance, CancellationToken cancellationToken) => apiClient.Delete(Routes.SetID(Routes.InstanceManager, instance?.Id ?? throw new ArgumentNullException(nameof(instance))), cancellationToken);
40 
42  public Task<IReadOnlyList<Instance>> List(CancellationToken cancellationToken) => apiClient.Read<IReadOnlyList<Instance>>(Routes.List(Routes.InstanceManager), cancellationToken);
43 
45  public Task<Instance> Update(Instance instance, CancellationToken cancellationToken) => apiClient.Update<Instance, Instance>(Routes.InstanceManager, instance ?? throw new ArgumentNullException(nameof(instance)), cancellationToken);
46 
48  public Task<Instance> GetId(Instance instance, CancellationToken cancellationToken) => apiClient.Read<Instance>(Routes.SetID(Routes.InstanceManager, instance?.Id ?? throw new ArgumentNullException(nameof(instance))), cancellationToken);
49 
52  {
53  if (!cachedClients.TryGetValue(instance?.Id ?? throw new ArgumentNullException(nameof(instance)), out var client))
54  {
55  client = new InstanceClient(apiClient, instance);
56  cachedClients.Add(instance.Id, client);
57  }
58  return client;
59  }
60  }
61 }
+Go to the documentation of this file.
1 using System;
2 using System.Collections.Generic;
3 using System.Threading;
4 using System.Threading.Tasks;
5 using Tgstation.Server.Api;
8 
9 namespace Tgstation.Server.Client
10 {
13  {
18 
22  readonly Dictionary<long, IInstanceClient> cachedClients;
23 
29  {
30  this.apiClient = apiClient ?? throw new ArgumentNullException(nameof(apiClient));
31 
32  cachedClients = new Dictionary<long, IInstanceClient>();
33  }
34 
36  public Task<Instance> CreateOrAttach(Instance instance, CancellationToken cancellationToken) => apiClient.Create<Instance, Instance>(Routes.InstanceManager, instance ?? throw new ArgumentNullException(nameof(instance)), cancellationToken);
37 
39  public Task Detach(Instance instance, CancellationToken cancellationToken) => apiClient.Delete(Routes.SetID(Routes.InstanceManager, instance?.Id ?? throw new ArgumentNullException(nameof(instance))), cancellationToken);
40 
42  public Task<IReadOnlyList<Instance>> List(CancellationToken cancellationToken) => apiClient.Read<IReadOnlyList<Instance>>(Routes.List(Routes.InstanceManager), cancellationToken);
43 
45  public Task<Instance> Update(Instance instance, CancellationToken cancellationToken) => apiClient.Update<Instance, Instance>(Routes.InstanceManager, instance ?? throw new ArgumentNullException(nameof(instance)), cancellationToken);
46 
48  public Task<Instance> GetId(Instance instance, CancellationToken cancellationToken) => apiClient.Read<Instance>(Routes.SetID(Routes.InstanceManager, instance?.Id ?? throw new ArgumentNullException(nameof(instance))), cancellationToken);
49 
52  {
53  if (!cachedClients.TryGetValue(instance?.Id ?? throw new ArgumentNullException(nameof(instance)), out var client))
54  {
55  client = new InstanceClient(apiClient, instance);
56  cachedClients.Add(instance.Id, client);
57  }
58  return client;
59  }
60  }
61 }
diff --git a/_instance_manager_test_8cs.html b/_instance_manager_test_8cs.html new file mode 100644 index 0000000000..4077a9d916 --- /dev/null +++ b/_instance_manager_test_8cs.html @@ -0,0 +1,118 @@ + + + + + + +tgstation-server: tests/Tgstation.Server.Tests/InstanceManagerTest.cs File Reference + + + + + + + + + + +
+
+ + + + + + + +
+
tgstation-server +
+
The /tg/station 13 server suite
+
+
+ + + + + + +
+
+ + +
+ +
+ + +
+
+ +
+
InstanceManagerTest.cs File Reference
+
+
+ +

Go to the source code of this file.

+ + + + +

+Classes

class  Tgstation.Server.Tests.InstanceManagerTest
 
+ + + +

+Namespaces

namespace  Tgstation.Server.Tests
 
+
+ + + + diff --git a/_instance_manager_test_8cs_source.html b/_instance_manager_test_8cs_source.html new file mode 100644 index 0000000000..e4bac54815 --- /dev/null +++ b/_instance_manager_test_8cs_source.html @@ -0,0 +1,125 @@ + + + + + + +tgstation-server: tests/Tgstation.Server.Tests/InstanceManagerTest.cs Source File + + + + + + + + + + +
+
+ + + + + + + +
+
tgstation-server +
+
The /tg/station 13 server suite
+
+
+ + + + + + +
+
+ + +
+ +
+ + +
+
+
+
InstanceManagerTest.cs
+
+
+Go to the documentation of this file.
1 using Microsoft.VisualStudio.TestTools.UnitTesting;
2 using System;
3 using System.IO;
4 using System.Reflection;
5 using System.Threading;
6 using System.Threading.Tasks;
11 
12 namespace Tgstation.Server.Tests
13 {
14  sealed class InstanceManagerTest
15  {
17  readonly string testRootPath;
18 
19  long counter;
20 
21  public InstanceManagerTest(IInstanceManagerClient instanceManagerClient, string testRootPath)
22  {
23  this.instanceManagerClient = instanceManagerClient ?? throw new ArgumentNullException(nameof(instanceManagerClient));
24  this.testRootPath = testRootPath ?? throw new ArgumentNullException(nameof(testRootPath));
25 
26  counter = 0;
27  }
28 
29  Task<Api.Models.Instance> CreateTestInstance(CancellationToken cancellationToken) => instanceManagerClient.CreateOrAttach(new Api.Models.Instance
30  {
31  Name = "TestInstance-" + ++counter,
32  Path = Path.Combine(testRootPath, Guid.NewGuid().ToString()),
33  Online = true
34  }, cancellationToken);
35 
36  public async Task Run(CancellationToken cancellationToken)
37  {
38  var firstTest = await CreateTestInstance(cancellationToken).ConfigureAwait(false);
39  //instances always start offline
40  Assert.AreEqual(false, firstTest.Online);
41  //check it exists
42  Assert.IsTrue(Directory.Exists(firstTest.Path));
43 
44  //cant create instances in existent directories
45  var testNonEmpty = Path.Combine(testRootPath, Guid.NewGuid().ToString());
46  Directory.CreateDirectory(testNonEmpty);
47  await Assert.ThrowsExceptionAsync<ConflictException>(() => instanceManagerClient.CreateOrAttach(new Api.Models.Instance
48  {
49  Path = testNonEmpty,
50  Name = "NonEmptyTest"
51  }, cancellationToken)).ConfigureAwait(false);
52  await Assert.ThrowsExceptionAsync<ConflictException>(() => instanceManagerClient.CreateOrAttach(firstTest, cancellationToken)).ConfigureAwait(false);
53 
54  //can't move to existent directories
55  await Assert.ThrowsExceptionAsync<ConflictException>(() => instanceManagerClient.Update(new Api.Models.Instance
56  {
57  Id = firstTest.Id,
58  Path = testNonEmpty
59  }, cancellationToken)).ConfigureAwait(false);
60 
61  //test basic move
62  Directory.Delete(testNonEmpty);
63  var initialPath = firstTest.Path;
64  firstTest = await instanceManagerClient.Update(new Api.Models.Instance
65  {
66  Id = firstTest.Id,
67  Path = testNonEmpty
68  }, cancellationToken).ConfigureAwait(false);
69 
70  Assert.IsNotNull(firstTest.MoveJob);
71 
72  do
73  {
74  firstTest = await instanceManagerClient.GetId(firstTest, cancellationToken).ConfigureAwait(false);
75  await Task.Delay(TimeSpan.FromSeconds(1), cancellationToken).ConfigureAwait(false);
76  } while (firstTest.MoveJob != null);
77 
78 
79  //online it for real for component tests
80  firstTest.Online = true;
81  firstTest.ConfigurationType = ConfigurationType.HostWrite;
82  firstTest = await instanceManagerClient.Update(firstTest, cancellationToken).ConfigureAwait(false);
83  Assert.AreEqual(true, firstTest.Online);
84  Assert.AreEqual(ConfigurationType.HostWrite, firstTest.ConfigurationType);
85 
86  //can't move online instance
87  await Assert.ThrowsExceptionAsync<ConflictException>(() => instanceManagerClient.Update(new Api.Models.Instance
88  {
89  Id = firstTest.Id,
90  Path = initialPath
91  }, cancellationToken)).ConfigureAwait(false);
92 
93  var testSuite1 = new InstanceTest(instanceManagerClient.CreateClient(firstTest));
94  await testSuite1.RunTests(cancellationToken).ConfigureAwait(false);
95 
96  //can't detach online instance
97  await Assert.ThrowsExceptionAsync<ConflictException>(() => instanceManagerClient.Detach(firstTest, cancellationToken)).ConfigureAwait(false);
98 
99  firstTest.Online = false;
100  firstTest = await instanceManagerClient.Update(firstTest, cancellationToken).ConfigureAwait(false);
101  await instanceManagerClient.Detach(firstTest, cancellationToken).ConfigureAwait(false);
102 
103  var instanceAttachFileName = (string)typeof(InstanceController).GetField("InstanceAttachFileName", BindingFlags.NonPublic | BindingFlags.Static).GetValue(null);
104  var attachPath = Path.Combine(firstTest.Path, instanceAttachFileName);
105  Assert.IsTrue(File.Exists(attachPath));
106 
107  //can recreate detached instance
108  firstTest = await instanceManagerClient.CreateOrAttach(firstTest, cancellationToken).ConfigureAwait(false);
109 
110  //but only if the attach file exists
111  await instanceManagerClient.Detach(firstTest, cancellationToken).ConfigureAwait(false);
112  File.Delete(attachPath);
113  await Assert.ThrowsExceptionAsync<ConflictException>(() => instanceManagerClient.CreateOrAttach(firstTest, cancellationToken)).ConfigureAwait(false);
114  }
115  }
116 }
+
async Task RunTests(CancellationToken cancellationToken)
Definition: InstanceTest.cs:19
+ + +
readonly IInstanceManagerClient instanceManagerClient
+ + + +
ConfigurationType
The type of configuration allowed on an Instance
+
Controller for managing Components.Instances
+ +
async Task Run(CancellationToken cancellationToken)
+ + + + + +
Occurs when the client performs an action that would result in data conflict
+ +
InstanceManagerTest(IInstanceManagerClient instanceManagerClient, string testRootPath)
+ + + +
+ + + + diff --git a/_instance_test_8cs.html b/_instance_test_8cs.html new file mode 100644 index 0000000000..7f6504cd0b --- /dev/null +++ b/_instance_test_8cs.html @@ -0,0 +1,118 @@ + + + + + + +tgstation-server: tests/Tgstation.Server.Tests/Instance/InstanceTest.cs File Reference + + + + + + + + + + +
+
+ + + + + + + +
+
tgstation-server +
+
The /tg/station 13 server suite
+
+
+ + + + + + +
+
+ + +
+ +
+ + +
+
+ +
+
InstanceTest.cs File Reference
+
+ + + + + diff --git a/_instance_test_8cs_source.html b/_instance_test_8cs_source.html new file mode 100644 index 0000000000..d300d02618 --- /dev/null +++ b/_instance_test_8cs_source.html @@ -0,0 +1,114 @@ + + + + + + +tgstation-server: tests/Tgstation.Server.Tests/Instance/InstanceTest.cs Source File + + + + + + + + + + +
+
+ + + + + + + +
+
tgstation-server +
+
The /tg/station 13 server suite
+
+
+ + + + + + +
+
+ + +
+ +
+ + +
+
+
+
InstanceTest.cs
+
+
+Go to the documentation of this file.
1 using System;
2 using System.Collections.Generic;
3 using System.Text;
4 using System.Threading;
5 using System.Threading.Tasks;
7 
8 namespace Tgstation.Server.Tests.Instance
9 {
10  sealed class InstanceTest
11  {
13 
14  public InstanceTest(IInstanceClient instanceClient)
15  {
16  this.instanceClient = instanceClient ?? throw new ArgumentNullException(nameof(instanceClient));
17  }
18 
19  public async Task RunTests(CancellationToken cancellationToken)
20  {
21  var configTests = new ConfigurationTest(instanceClient.Configuration, instanceClient.Metadata);
22  await configTests.Run(cancellationToken).ConfigureAwait(false);
23  }
24  }
25 }
+
async Task RunTests(CancellationToken cancellationToken)
Definition: InstanceTest.cs:19
+ + + + + + +
InstanceTest(IInstanceClient instanceClient)
Definition: InstanceTest.cs:14
+ + + +
+ + + + diff --git a/_integration_test_8cs_source.html b/_integration_test_8cs_source.html index ca68e5d037..94e073cd56 100644 --- a/_integration_test_8cs_source.html +++ b/_integration_test_8cs_source.html @@ -91,24 +91,31 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
IntegrationTest.cs
-Go to the documentation of this file.
1 using Microsoft.VisualStudio.TestTools.UnitTesting;
2 using System;
3 using System.Net.Http.Headers;
4 using System.Reflection;
5 using System.Threading;
6 using System.Threading.Tasks;
7 using Tgstation.Server.Api;
10 using Tgstation.Server.Host;
11 
12 namespace Tgstation.Server.Tests
13 {
14  [TestClass]
15  public sealed class IntegrationTest
16  {
17  readonly IServerClientFactory clientFactory = new ServerClientFactory(new ProductHeaderValue(Assembly.GetExecutingAssembly().GetName().Name, Assembly.GetExecutingAssembly().GetName().Version.ToString()));
18 
19  [TestMethod]
20  public async Task FullMonty()
21  {
22  using (var server = new TestingServer())
23  using (var serverCts = new CancellationTokenSource())
24  {
25  var serverTask = server.RunAsync(serverCts.Token);
26  try
27  {
28  using (var adminClient = await clientFactory.CreateServerClient(server.Url, User.AdminName, User.DefaultAdminPassword).ConfigureAwait(false))
29  {
30  var serverInfo = await adminClient.Version(default).ConfigureAwait(false);
31 
32  Assert.AreEqual(ApiHeaders.Version, serverInfo.ApiVersion);
33  Assert.AreEqual(typeof(IServer).Assembly.GetName().Version, serverInfo.Version);
34 
35  await new AdministrationTest(adminClient.Administration).Run().ConfigureAwait(false);
36  }
37  }
38  finally
39  {
40  serverCts.Cancel();
41  try
42  {
43  await serverTask.ConfigureAwait(false);
44  }
45  catch (OperationCanceledException) { }
46  }
47  }
48  }
49  }
50 }
+Go to the documentation of this file.
1 using Microsoft.VisualStudio.TestTools.UnitTesting;
2 using System;
3 using System.IO;
4 using System.Net.Http.Headers;
5 using System.Reflection;
6 using System.Threading;
7 using System.Threading.Tasks;
8 using Tgstation.Server.Api;
10 using Tgstation.Server.Client;
11 using Tgstation.Server.Host;
12 
13 namespace Tgstation.Server.Tests
14 {
15  [TestClass]
16  public sealed class IntegrationTest
17  {
18  readonly IServerClientFactory clientFactory = new ServerClientFactory(new ProductHeaderValue(Assembly.GetExecutingAssembly().GetName().Name, Assembly.GetExecutingAssembly().GetName().Version.ToString()));
19 
20  [TestMethod]
21  public async Task FullMonty()
22  {
23  using (var server = new TestingServer())
24  using (var serverCts = new CancellationTokenSource())
25  {
26  var cancellationToken = serverCts.Token;
27  var serverTask = server.RunAsync(cancellationToken);
28  try
29  {
30  IServerClient adminClient;
31 
32  var giveUpAt = DateTimeOffset.Now.AddSeconds(60);
33  do
34  {
35  try
36  {
37  adminClient = await clientFactory.CreateServerClient(server.Url, User.AdminName, User.DefaultAdminPassword).ConfigureAwait(false);
38  break;
39  }
41  {
42  //migrating, to be expected
43  if (DateTimeOffset.Now > giveUpAt)
44  throw;
45  await Task.Delay(TimeSpan.FromSeconds(1), cancellationToken);
46  }
47  } while (true);
48 
49  using (adminClient)
50  {
51  var serverInfo = await adminClient.Version(default).ConfigureAwait(false);
52 
53  Assert.AreEqual(ApiHeaders.Version, serverInfo.ApiVersion);
54  Assert.AreEqual(typeof(IServer).Assembly.GetName().Version, serverInfo.Version);
55 
56  await new AdministrationTest(adminClient.Administration).Run(cancellationToken).ConfigureAwait(false);
57  await new InstanceManagerTest(adminClient.Instances, server.Directory).Run(cancellationToken).ConfigureAwait(false);
58  }
59  }
60  finally
61  {
62  serverCts.Cancel();
63  try
64  {
65  await serverTask.ConfigureAwait(false);
66  }
67  catch (OperationCanceledException) { }
68  }
69  }
70  }
71  }
72 }
async Task Run(CancellationToken cancellationToken)
+ - - +
Task< ServerInformation > Version(CancellationToken cancellationToken)
The ServerInformation of the IServerClient
+ + - +
const string AdminName
The name of the default admin user
Definition: User.cs:9
+
async Task Run(CancellationToken cancellationToken)
Represents the header that must be present for every server request
Definition: ApiHeaders.cs:16
+
Occurs when the client makes a request while the server is starting or stopping
+
Main client for communicating with a server
Represents the host
Definition: IServer.cs:10
- +
Factory for creating IServerClients
const string DefaultAdminPassword
The default admin password
Definition: User.cs:14
+ - +
IInstanceManagerClient Instances
Access the IInstanceManagerClient
+
IAdministrationClient Administration
Access the IAdministrationClient
static Version Version
Get the version of the Api the caller is using
Definition: ApiHeaders.cs:56
diff --git a/_job_controller_8cs_source.html b/_job_controller_8cs_source.html index 9031f7bed5..bec5842e06 100644 --- a/_job_controller_8cs_source.html +++ b/_job_controller_8cs_source.html @@ -91,8 +91,8 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
JobController.cs
-Go to the documentation of this file.
1 using Microsoft.AspNetCore.Mvc;
2 using Microsoft.EntityFrameworkCore;
3 using Microsoft.Extensions.Logging;
4 using System;
5 using System.Linq;
6 using System.Net;
7 using System.Threading;
8 using System.Threading.Tasks;
9 using Tgstation.Server.Api;
13 
14 namespace Tgstation.Server.Host.Controllers
15 {
19  [Route(Routes.Jobs)]
20  public sealed class JobController : ModelController<Api.Models.Job>
21  {
26 
34  public JobController(IDatabaseContext databaseContext, IAuthenticationContextFactory authenticationContextFactory, IJobManager jobManager, ILogger<JobController> logger) : base(databaseContext, authenticationContextFactory, logger, true)
35  {
36  this.jobManager = jobManager ?? throw new ArgumentNullException(nameof(jobManager));
37  }
38 
40  [TgsAuthorize]
41  public override async Task<IActionResult> Read(CancellationToken cancellationToken)
42  {
43  var result = await DatabaseContext.Jobs.Where(x => x.Instance.Id == Instance.Id && !x.StoppedAt.HasValue).OrderByDescending(x => x.StartedAt).ToListAsync(cancellationToken).ConfigureAwait(false);
44  return Json(result.Select(x => x.ToApi()));
45  }
46 
48  [TgsAuthorize]
49  public override async Task<IActionResult> List(CancellationToken cancellationToken)
50  {
51  //you KNOW this will need pagination eventually right?
52  var jobs = await DatabaseContext.Jobs.Where(x => x.Instance.Id == Instance.Id).OrderByDescending(x => x.StartedAt).Select(x => new Api.Models.Job
53  {
54  Id = x.Id
55  }).ToListAsync(cancellationToken).ConfigureAwait(false);
56  return Json(jobs);
57  }
58 
60  [TgsAuthorize]
61  public override async Task<IActionResult> Delete(long id, CancellationToken cancellationToken)
62  {
63  //don't care if an instance post or not at this point
64  var job = await DatabaseContext.Jobs.Where(x => x.Id == id).FirstOrDefaultAsync(cancellationToken).ConfigureAwait(false);
65  if (job == default(Job))
66  return NotFound();
67 
68  if (job.StoppedAt != null)
69  return StatusCode((int)HttpStatusCode.Gone);
70 
71  if (job.CancelRight.HasValue && job.CancelRightsType.HasValue && (AuthenticationContext.GetRight(job.CancelRightsType.Value) & job.CancelRight.Value) == 0)
72  return Forbid();
73 
74  await jobManager.CancelJob(job, AuthenticationContext.User, cancellationToken).ConfigureAwait(false);
75  return Ok();
76  }
77 
79  [TgsAuthorize]
80  public override async Task<IActionResult> GetId(long id, CancellationToken cancellationToken)
81  {
82  var job = await DatabaseContext.Jobs.Where(x => x.Id == id).Include(x => x.StartedBy).FirstOrDefaultAsync(cancellationToken).ConfigureAwait(false);
83  if (job == default(Job))
84  return NotFound();
85  var api = job.ToApi();
86  api.Progress = jobManager.JobProgress(job);
87  return Json(api);
88  }
89  }
90 }
-
Manages the runtime of Jobs
Definition: IJobManager.cs:13
+Go to the documentation of this file.
1 using Microsoft.AspNetCore.Mvc;
2 using Microsoft.EntityFrameworkCore;
3 using Microsoft.Extensions.Logging;
4 using System;
5 using System.Linq;
6 using System.Net;
7 using System.Threading;
8 using System.Threading.Tasks;
9 using Tgstation.Server.Api;
13 
14 namespace Tgstation.Server.Host.Controllers
15 {
19  [Route(Routes.Jobs)]
20  public sealed class JobController : ModelController<Api.Models.Job>
21  {
26 
34  public JobController(IDatabaseContext databaseContext, IAuthenticationContextFactory authenticationContextFactory, IJobManager jobManager, ILogger<JobController> logger) : base(databaseContext, authenticationContextFactory, logger, true)
35  {
36  this.jobManager = jobManager ?? throw new ArgumentNullException(nameof(jobManager));
37  }
38 
40  [TgsAuthorize]
41  public override async Task<IActionResult> Read(CancellationToken cancellationToken)
42  {
43  var result = await DatabaseContext.Jobs.Where(x => x.Instance.Id == Instance.Id && !x.StoppedAt.HasValue).OrderByDescending(x => x.StartedAt).ToListAsync(cancellationToken).ConfigureAwait(false);
44  return Json(result.Select(x => x.ToApi()));
45  }
46 
48  [TgsAuthorize]
49  public override async Task<IActionResult> List(CancellationToken cancellationToken)
50  {
51  //you KNOW this will need pagination eventually right?
52  var jobs = await DatabaseContext.Jobs.Where(x => x.Instance.Id == Instance.Id).OrderByDescending(x => x.StartedAt).Select(x => new Api.Models.Job
53  {
54  Id = x.Id
55  }).ToListAsync(cancellationToken).ConfigureAwait(false);
56  return Json(jobs);
57  }
58 
60  [TgsAuthorize]
61  public override async Task<IActionResult> Delete(long id, CancellationToken cancellationToken)
62  {
63  //don't care if an instance post or not at this point
64  var job = await DatabaseContext.Jobs.Where(x => x.Id == id).FirstOrDefaultAsync(cancellationToken).ConfigureAwait(false);
65  if (job == default(Job))
66  return NotFound();
67 
68  if (job.StoppedAt != null)
69  return StatusCode((int)HttpStatusCode.Gone);
70 
71  if (job.CancelRight.HasValue && job.CancelRightsType.HasValue && (AuthenticationContext.GetRight(job.CancelRightsType.Value) & job.CancelRight.Value) == 0)
72  return Forbid();
73 
74  var cancelled = await jobManager.CancelJob(job, AuthenticationContext.User, false, cancellationToken).ConfigureAwait(false);
75  return cancelled ? (IActionResult)Accepted() : StatusCode((int)HttpStatusCode.Gone);
76  }
77 
79  [TgsAuthorize]
80  public override async Task<IActionResult> GetId(long id, CancellationToken cancellationToken)
81  {
82  var job = await DatabaseContext.Jobs.Where(x => x.Id == id).Include(x => x.StartedBy).FirstOrDefaultAsync(cancellationToken).ConfigureAwait(false);
83  if (job == default(Job))
84  return NotFound();
85  var api = job.ToApi();
86  api.Progress = jobManager.JobProgress(job);
87  return Json(api);
88  }
89  }
90 }
+
Manages the runtime of Jobs
Definition: IJobManager.cs:12
override async Task< IActionResult > List(CancellationToken cancellationToken)
Attempt to list entries of the TModel
diff --git a/_job_manager_8cs_source.html b/_job_manager_8cs_source.html index ca657bfbcb..8420723e13 100644 --- a/_job_manager_8cs_source.html +++ b/_job_manager_8cs_source.html @@ -91,13 +91,13 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
JobManager.cs
-Go to the documentation of this file.
1 using Microsoft.EntityFrameworkCore;
2 using Microsoft.Extensions.DependencyInjection;
3 using Microsoft.Extensions.Logging;
4 using System;
5 using System.Collections.Generic;
6 using System.Linq;
7 using System.Threading;
8 using System.Threading.Tasks;
10 
11 namespace Tgstation.Server.Host.Core
12 {
15  {
19  readonly IServiceProvider serviceProvider;
20 
24  readonly ILogger<JobManager> logger;
25 
29  readonly Dictionary<long, JobHandler> jobs;
30 
36  public JobManager(IServiceProvider serviceProvider, ILogger<JobManager> logger)
37  {
38  this.serviceProvider = serviceProvider ?? throw new ArgumentNullException(nameof(serviceProvider));
39  this.logger = logger ?? throw new ArgumentNullException(nameof(logger));
40  jobs = new Dictionary<long, JobHandler>();
41  }
42 
44  public void Dispose()
45  {
46  foreach (var job in jobs)
47  job.Value.Dispose();
48  }
49 
56  {
57  lock (this)
58  {
59  if (!jobs.TryGetValue(job.Id, out JobHandler jobHandler))
60  throw new InvalidOperationException("Job not running!");
61  return jobHandler;
62  }
63  }
64 
72  async Task RunJob(Job job, Func<Job, IServiceProvider, CancellationToken, Task> operation, CancellationToken cancellationToken)
73  {
74  try
75  {
76  using (var scope = serviceProvider.CreateScope())
77  {
78  IDatabaseContext databaseContext = null;
79  try
80  {
81  var oldJob = job;
82  job = new Job { Id = oldJob.Id };
83  databaseContext = scope.ServiceProvider.GetRequiredService<IDatabaseContext>();
84  databaseContext.Jobs.Attach(job);
85 
86  await operation(job, scope.ServiceProvider, cancellationToken).ConfigureAwait(false);
87 
88  logger.LogDebug("Job {0} completed!", job.Id);
89  }
90  catch (OperationCanceledException)
91  {
92  logger.LogDebug("Job {0} cancelled!", job.Id);
93  job.Cancelled = true;
94  }
95  catch (Exception e)
96  {
97  job.ExceptionDetails = e is JobException ? e.Message : e.ToString();
98  logger.LogDebug("Job {0} exited with error! Exception: {1}", job.Id, job.ExceptionDetails);
99  }
100  job.StoppedAt = DateTimeOffset.Now;
101  await databaseContext.Save(default).ConfigureAwait(false);
102  }
103  }
104  finally
105  {
106  lock (this)
107  {
108  var handler = jobs[job.Id];
109  jobs.Remove(job.Id);
110  handler.Dispose();
111  }
112  }
113  }
114 
116  public async Task RegisterOperation(Job job, Func<Job, IServiceProvider, Action<int>, CancellationToken, Task> operation, CancellationToken cancellationToken)
117  {
118  using (var scope = serviceProvider.CreateScope())
119  {
120  var databaseContext = scope.ServiceProvider.GetRequiredService<IDatabaseContext>();
121  job.StartedAt = DateTimeOffset.Now;
122  job.Cancelled = false;
123  job.Instance = new Instance
124  {
125  Id = job.Instance.Id
126  };
127  databaseContext.Instances.Attach(job.Instance);
128  if (job.StartedBy != null)
129  {
130  job.StartedBy = new User
131  {
132  Id = job.StartedBy.Id
133  };
134  databaseContext.Users.Attach(job.StartedBy);
135  }
136  databaseContext.Jobs.Add(job);
137  await databaseContext.Save(cancellationToken).ConfigureAwait(false);
138  logger.LogDebug("Starting job {0}: {1}...", job.Id, job.Description);
139  var jobHandler = JobHandler.Create(x => RunJob(job, (jobParam, serviceProvider, ct) =>
140  operation(jobParam, serviceProvider, y =>
141  {
142  lock (this)
143  if (jobs.TryGetValue(job.Id, out var handler))
144  handler.Progress = y;
145  }, ct),
146  x));
147  lock (this)
148  jobs.Add(job.Id, jobHandler);
149  }
150  }
151 
153  public async Task StartAsync(CancellationToken cancellationToken)
154  {
155  logger.LogTrace("Starting job manager...");
156  using (var scope = serviceProvider.CreateScope())
157  {
158  var databaseContext = scope.ServiceProvider.GetRequiredService<IDatabaseContext>();
159 
160  //mark all jobs as cancelled
161  var badJobs = await databaseContext.Jobs.Where(y => !y.StoppedAt.HasValue).Select(y => y.Id).ToListAsync(cancellationToken).ConfigureAwait(false);
162  if (badJobs.Count > 0)
163  {
164  logger.LogTrace("Cleaning {0} unfinished jobs...", badJobs.Count);
165  foreach (var I in badJobs)
166  {
167  var job = new Job { Id = I };
168  databaseContext.Jobs.Attach(job);
169  job.Cancelled = true;
170  job.StoppedAt = DateTimeOffset.Now;
171  }
172  await databaseContext.Save(cancellationToken).ConfigureAwait(false);
173  }
174  }
175  logger.LogDebug("Job manager started!");
176  }
177 
179  public async Task StopAsync(CancellationToken cancellationToken)
180  {
181  var joinTasks = jobs.Select(x =>
182  {
183  x.Value.Cancel();
184  return x.Value.Wait(cancellationToken);
185  });
186  await Task.WhenAll(joinTasks).ConfigureAwait(false);
187  }
188 
190  public async Task CancelJob(Job job, User user, CancellationToken cancellationToken)
191  {
192  if (job == null)
193  throw new ArgumentNullException(nameof(job));
194  if (user == null)
195  throw new ArgumentNullException(nameof(user));
196  CheckGetJob(job).Cancel(); //this will ensure the db update is only done once
197  using (var scope = serviceProvider.CreateScope())
198  {
199  var databaseContext = scope.ServiceProvider.GetRequiredService<IDatabaseContext>();
200  job = new Job { Id = job.Id };
201  databaseContext.Jobs.Attach(job);
202  user = new User { Id = user.Id };
203  databaseContext.Users.Attach(user);
204  job.CancelledBy = user;
205  //let either startup or cancellation set job.cancelled
206  await databaseContext.Save(cancellationToken).ConfigureAwait(false);
207  }
208  }
209 
211  public int? JobProgress(Job job)
212  {
213  lock (this)
214  {
215  if (!jobs.TryGetValue(job.Id, out var handler))
216  return null;
217  return handler.Progress;
218  }
219  }
220  }
221 }
+Go to the documentation of this file.
1 using Microsoft.EntityFrameworkCore;
2 using Microsoft.Extensions.DependencyInjection;
3 using Microsoft.Extensions.Logging;
4 using System;
5 using System.Collections.Generic;
6 using System.Linq;
7 using System.Threading;
8 using System.Threading.Tasks;
10 
11 namespace Tgstation.Server.Host.Core
12 {
15  {
19  readonly IServiceProvider serviceProvider;
20 
24  readonly ILogger<JobManager> logger;
25 
29  readonly Dictionary<long, JobHandler> jobs;
30 
36  public JobManager(IServiceProvider serviceProvider, ILogger<JobManager> logger)
37  {
38  this.serviceProvider = serviceProvider ?? throw new ArgumentNullException(nameof(serviceProvider));
39  this.logger = logger ?? throw new ArgumentNullException(nameof(logger));
40  jobs = new Dictionary<long, JobHandler>();
41  }
42 
44  public void Dispose()
45  {
46  foreach (var job in jobs)
47  job.Value.Dispose();
48  }
49 
56  {
57  lock (this)
58  {
59  if (!jobs.TryGetValue(job.Id, out JobHandler jobHandler))
60  throw new InvalidOperationException("Job not running!");
61  return jobHandler;
62  }
63  }
64 
72  async Task RunJob(Job job, Func<Job, IServiceProvider, CancellationToken, Task> operation, CancellationToken cancellationToken)
73  {
74  try
75  {
76  using (var scope = serviceProvider.CreateScope())
77  {
78  IDatabaseContext databaseContext = null;
79  try
80  {
81  var oldJob = job;
82  job = new Job { Id = oldJob.Id };
83  databaseContext = scope.ServiceProvider.GetRequiredService<IDatabaseContext>();
84  databaseContext.Jobs.Attach(job);
85 
86  await operation(job, scope.ServiceProvider, cancellationToken).ConfigureAwait(false);
87 
88  logger.LogDebug("Job {0} completed!", job.Id);
89  }
90  catch (OperationCanceledException)
91  {
92  logger.LogDebug("Job {0} cancelled!", job.Id);
93  job.Cancelled = true;
94  }
95  catch (Exception e)
96  {
97  job.ExceptionDetails = e is JobException ? e.Message : e.ToString();
98  logger.LogDebug("Job {0} exited with error! Exception: {1}", job.Id, job.ExceptionDetails);
99  }
100  job.StoppedAt = DateTimeOffset.Now;
101  await databaseContext.Save(default).ConfigureAwait(false);
102  }
103  }
104  finally
105  {
106  lock (this)
107  {
108  var handler = jobs[job.Id];
109  jobs.Remove(job.Id);
110  handler.Dispose();
111  }
112  }
113  }
114 
116  public async Task RegisterOperation(Job job, Func<Job, IServiceProvider, Action<int>, CancellationToken, Task> operation, CancellationToken cancellationToken)
117  {
118  using (var scope = serviceProvider.CreateScope())
119  {
120  var databaseContext = scope.ServiceProvider.GetRequiredService<IDatabaseContext>();
121  job.StartedAt = DateTimeOffset.Now;
122  job.Cancelled = false;
123  job.Instance = new Instance
124  {
125  Id = job.Instance.Id
126  };
127  databaseContext.Instances.Attach(job.Instance);
128  if (job.StartedBy != null)
129  {
130  job.StartedBy = new User
131  {
132  Id = job.StartedBy.Id
133  };
134  databaseContext.Users.Attach(job.StartedBy);
135  }
136  databaseContext.Jobs.Add(job);
137  await databaseContext.Save(cancellationToken).ConfigureAwait(false);
138  logger.LogDebug("Starting job {0}: {1}...", job.Id, job.Description);
139  var jobHandler = JobHandler.Create(x => RunJob(job, (jobParam, serviceProvider, ct) =>
140  operation(jobParam, serviceProvider, y =>
141  {
142  lock (this)
143  if (jobs.TryGetValue(job.Id, out var handler))
144  handler.Progress = y;
145  }, ct),
146  x));
147  lock (this)
148  jobs.Add(job.Id, jobHandler);
149  }
150  }
151 
153  public async Task StartAsync(CancellationToken cancellationToken)
154  {
155  logger.LogTrace("Starting job manager...");
156  using (var scope = serviceProvider.CreateScope())
157  {
158  var databaseContext = scope.ServiceProvider.GetRequiredService<IDatabaseContext>();
159 
160  //mark all jobs as cancelled
161  var badJobs = await databaseContext.Jobs.Where(y => !y.StoppedAt.HasValue).Select(y => y.Id).ToListAsync(cancellationToken).ConfigureAwait(false);
162  if (badJobs.Count > 0)
163  {
164  logger.LogTrace("Cleaning {0} unfinished jobs...", badJobs.Count);
165  foreach (var I in badJobs)
166  {
167  var job = new Job { Id = I };
168  databaseContext.Jobs.Attach(job);
169  job.Cancelled = true;
170  job.StoppedAt = DateTimeOffset.Now;
171  }
172  await databaseContext.Save(cancellationToken).ConfigureAwait(false);
173  }
174  }
175  logger.LogDebug("Job manager started!");
176  }
177 
179  public async Task StopAsync(CancellationToken cancellationToken)
180  {
181  var joinTasks = jobs.Select(x =>
182  {
183  x.Value.Cancel();
184  return x.Value.Wait(cancellationToken);
185  });
186  await Task.WhenAll(joinTasks).ConfigureAwait(false);
187  }
188 
190  public async Task<bool> CancelJob(Job job, User user, bool blocking, CancellationToken cancellationToken)
191  {
192  if (job == null)
193  throw new ArgumentNullException(nameof(job));
194  if (user == null)
195  throw new ArgumentNullException(nameof(user));
196  JobHandler handler;
197  try
198  {
199  handler = CheckGetJob(job);
200  }
201  catch (InvalidOperationException)
202  {
203  //this is fine
204  return false;
205  }
206  handler.Cancel(); //this will ensure the db update is only done once
207  using (var scope = serviceProvider.CreateScope())
208  {
209  var databaseContext = scope.ServiceProvider.GetRequiredService<IDatabaseContext>();
210  job = new Job { Id = job.Id };
211  databaseContext.Jobs.Attach(job);
212  user = new User { Id = user.Id };
213  databaseContext.Users.Attach(user);
214  job.CancelledBy = user;
215  //let either startup or cancellation set job.cancelled
216  await databaseContext.Save(cancellationToken).ConfigureAwait(false);
217  }
218  if (blocking)
219  await handler.Wait(cancellationToken).ConfigureAwait(false);
220  return true;
221  }
222 
224  public int? JobProgress(Job job)
225  {
226  lock (this)
227  {
228  if (!jobs.TryGetValue(job.Id, out var handler))
229  return null;
230  return handler.Progress;
231  }
232  }
233  }
234 }
-
Manages the runtime of Jobs
Definition: IJobManager.cs:13
+
Manages the runtime of Jobs
Definition: IJobManager.cs:12
+
JobHandler CheckGetJob(Job job)
Gets the JobHandler for a given job if it exists
Definition: JobManager.cs:55
async Task StartAsync(CancellationToken cancellationToken)
Definition: JobManager.cs:153
Operation exceptions thrown from the context of a Models.Job
Definition: JobException.cs:8
-
async Task CancelJob(Job job, User user, CancellationToken cancellationToken)
Cancels a give job
Definition: JobManager.cs:190
User StartedBy
See Api.Models.Job.StartedBy
Definition: Job.cs:12
readonly Dictionary< long, JobHandler > jobs
Dictionary<TKey, TValue> of Api.Models.Internal.Job.Id to running JobHandlers
Definition: JobManager.cs:29
@@ -105,6 +105,8 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
JobManager(IServiceProvider serviceProvider, ILogger< JobManager > logger)
Construct a JobManager
Definition: JobManager.cs:36
DateTimeOffset StoppedAt
When the Job stopped
Definition: Job.cs:37
+
async Task< bool > CancelJob(Job job, User user, bool blocking, CancellationToken cancellationToken)
Cancels a give job
Definition: JobManager.cs:190
+
async Task Wait(CancellationToken cancellationToken)
Wait for task to complete
Definition: JobHandler.cs:45
DbSet< Job > Jobs
The Jobs in the IDatabaseContext
DateTimeOffset StartedAt
When the Job was started
Definition: Job.cs:32
string Description
English description of the Job
Definition: Job.cs:21
@@ -122,7 +124,7 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
Represents an Api.Models.Instance in the database
Definition: Instance.cs:8
long Id
The id of the Instance. Not modifiable
Definition: Instance.cs:14
-
int JobProgress(Job job)
Get the Api.Models.Job.Progress for a job
Definition: JobManager.cs:211
+
int JobProgress(Job job)
Get the Api.Models.Job.Progress for a job
Definition: JobManager.cs:224
long Id
The ID of the User
Definition: User.cs:15
diff --git a/_my_sql_database_context_8cs_source.html b/_my_sql_database_context_8cs_source.html index f5c3ffb4c5..690d0e1ad0 100644 --- a/_my_sql_database_context_8cs_source.html +++ b/_my_sql_database_context_8cs_source.html @@ -91,15 +91,21 @@ 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;
5 
6 namespace Tgstation.Server.Host.Models
7 {
11  sealed class MySqlDatabaseContext : DatabaseContext<MySqlDatabaseContext>
12  {
20  public MySqlDatabaseContext(DbContextOptions<MySqlDatabaseContext> dbContextOptions, IOptions<DatabaseConfiguration> databaseConfiguration, IDatabaseSeeder databaseSeeder, ILogger<MySqlDatabaseContext> logger) : base(dbContextOptions, databaseConfiguration, databaseSeeder, logger)
21  { }
22 
24  protected override void OnConfiguring(DbContextOptionsBuilder options)
25  {
26  base.OnConfiguring(options);
27  options.UseMySQL(ConnectionString);
28  }
29  }
30 }
MySqlDatabaseContext(DbContextOptions< MySqlDatabaseContext > dbContextOptions, IOptions< DatabaseConfiguration > databaseConfiguration, IDatabaseSeeder databaseSeeder, ILogger< MySqlDatabaseContext > logger)
Construct a MySqlDatabaseContext
-
override void OnConfiguring(DbContextOptionsBuilder options)
+Go to the documentation of this file.
1 using Microsoft.EntityFrameworkCore;
2 using Microsoft.Extensions.Logging;
3 using Microsoft.Extensions.Options;
4 using Pomelo.EntityFrameworkCore.MySql.Infrastructure;
5 using System;
7 
8 namespace Tgstation.Server.Host.Models
9 {
13  sealed class MySqlDatabaseContext : DatabaseContext<MySqlDatabaseContext>
14  {
22  public MySqlDatabaseContext(DbContextOptions<MySqlDatabaseContext> dbContextOptions, IOptions<DatabaseConfiguration> databaseConfiguration, IDatabaseSeeder databaseSeeder, ILogger<MySqlDatabaseContext> logger) : base(dbContextOptions, databaseConfiguration, databaseSeeder, logger)
23  { }
24 
26  protected override void OnConfiguring(DbContextOptionsBuilder options)
27  {
28  base.OnConfiguring(options);
29  if (!String.IsNullOrEmpty(DatabaseConfiguration.MySqlServerVersion))
30  options.UseMySql(DatabaseConfiguration.ConnectionString, mySqlOptions => mySqlOptions.ServerVersion(Version.Parse(DatabaseConfiguration.MySqlServerVersion), DatabaseConfiguration.DatabaseType == DatabaseType.MariaDB ? ServerType.MariaDb : ServerType.MySql));
31  else
32  options.UseMySql(DatabaseConfiguration.ConnectionString);
33  }
34  }
35 }
string ConnectionString
The connection string for the database
+
MySqlDatabaseContext(DbContextOptions< MySqlDatabaseContext > dbContextOptions, IOptions< DatabaseConfiguration > databaseConfiguration, IDatabaseSeeder databaseSeeder, ILogger< MySqlDatabaseContext > logger)
Construct a MySqlDatabaseContext
+
override void OnConfiguring(DbContextOptionsBuilder options)
+
For initially seeding a database
+
DatabaseType DatabaseType
The Configuration.DatabaseType to create
+
string MySqlServerVersion
The string form of the System.Version of a target MySQL/MariaDB server
+
Configuration options for the Models.DatabaseContext<TParentContext>
+
DatabaseType
Type of database to user
Definition: DatabaseType.cs:6
-
DatabaseContext<TParentContext> for MySQL
+
DatabaseContext<TParentContext> for MySQL
diff --git a/_sqlite_design_time_db_context_factory_8cs.html b/_my_sql_database_context_model_snapshot_8cs.html similarity index 92% rename from _sqlite_design_time_db_context_factory_8cs.html rename to _my_sql_database_context_model_snapshot_8cs.html index 2ff4b74308..44273a66a9 100644 --- a/_sqlite_design_time_db_context_factory_8cs.html +++ b/_my_sql_database_context_model_snapshot_8cs.html @@ -4,7 +4,7 @@ -tgstation-server: src/Tgstation.Server.Host/Models/Migrations/SqliteDesignTimeDbContextFactory.cs File Reference +tgstation-server: src/Tgstation.Server.Host/Models/Migrations/MySqlDatabaseContextModelSnapshot.cs File Reference @@ -91,15 +91,15 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search'); Classes | Namespaces
-
SqliteDesignTimeDbContextFactory.cs File Reference
+
MySqlDatabaseContextModelSnapshot.cs File Reference
-

Go to the source code of this file.

+

Go to the source code of this file.

- +

Classes

class  Tgstation.Server.Host.Models.Migrations.SqliteDesignTimeDbContextFactory
class  Tgstation.Server.Host.Models.Migrations.MySqlDatabaseContextModelSnapshot
 
- - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + @@ -337,9 +335,12 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search'); - - - + + + + + + @@ -358,12 +359,11 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search'); - - - - - - + + + + + @@ -404,9 +404,13 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search'); - - - + + + + + + + @@ -417,7 +421,9 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search'); - + + +

diff --git a/_my_sql_database_context_model_snapshot_8cs_source.html b/_my_sql_database_context_model_snapshot_8cs_source.html new file mode 100644 index 0000000000..eb844589b3 --- /dev/null +++ b/_my_sql_database_context_model_snapshot_8cs_source.html @@ -0,0 +1,109 @@ + + + + + + +tgstation-server: src/Tgstation.Server.Host/Models/Migrations/MySqlDatabaseContextModelSnapshot.cs Source File + + + + + + + + + + +
+
+ + + + + + + +
+
tgstation-server +
+
The /tg/station 13 server suite
+
+
+ + + + + + +
+
+ + +
+ +
+ + +
+
+
+
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 using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
6 
7 namespace Tgstation.Server.Host.Models.Migrations
8 {
9  [DbContext(typeof(MySqlDatabaseContext))]
11  {
12  protected override void BuildModel(ModelBuilder modelBuilder)
13  {
14 #pragma warning disable 612, 618
15  modelBuilder
16  .HasAnnotation("ProductVersion", "2.1.2-rtm-30932")
17  .HasAnnotation("Relational:MaxIdentifierLength", 64);
18 
19  modelBuilder.Entity("Tgstation.Server.Host.Models.ChatBot", b =>
20  {
21  b.Property<long>("Id")
22  .ValueGeneratedOnAdd();
23 
24  b.Property<string>("ConnectionString")
25  .IsRequired();
26 
27  b.Property<bool?>("Enabled");
28 
29  b.Property<long>("InstanceId");
30 
31  b.Property<string>("Name")
32  .IsRequired();
33 
34  b.Property<int?>("Provider");
35 
36  b.HasKey("Id");
37 
38  b.HasIndex("InstanceId");
39 
40  b.HasIndex("Name")
41  .IsUnique();
42 
43  b.ToTable("ChatBots");
44  });
45 
46  modelBuilder.Entity("Tgstation.Server.Host.Models.ChatChannel", b =>
47  {
48  b.Property<long>("Id")
49  .ValueGeneratedOnAdd();
50 
51  b.Property<long>("ChatSettingsId");
52 
53  b.Property<ulong?>("DiscordChannelId");
54 
55  b.Property<string>("IrcChannel");
56 
57  b.Property<bool?>("IsAdminChannel")
58  .IsRequired();
59 
60  b.Property<bool?>("IsUpdatesChannel")
61  .IsRequired();
62 
63  b.Property<bool?>("IsWatchdogChannel")
64  .IsRequired();
65 
66  b.Property<string>("Tag");
67 
68  b.HasKey("Id");
69 
70  b.HasIndex("ChatSettingsId", "DiscordChannelId")
71  .IsUnique();
72 
73  b.HasIndex("ChatSettingsId", "IrcChannel")
74  .IsUnique();
75 
76  b.ToTable("ChatChannels");
77  });
78 
79  modelBuilder.Entity("Tgstation.Server.Host.Models.CompileJob", b =>
80  {
81  b.Property<long>("Id")
82  .ValueGeneratedOnAdd();
83 
84  b.Property<string>("ByondVersion")
85  .IsRequired();
86 
87  b.Property<Guid?>("DirectoryName");
88 
89  b.Property<string>("DmeName");
90 
91  b.Property<long?>("JobId");
92 
93  b.Property<string>("Output");
94 
95  b.Property<long>("RevisionInformationId");
96 
97  b.HasKey("Id");
98 
99  b.HasIndex("DirectoryName");
100 
101  b.HasIndex("JobId");
102 
103  b.HasIndex("RevisionInformationId");
104 
105  b.ToTable("CompileJobs");
106  });
107 
108  modelBuilder.Entity("Tgstation.Server.Host.Models.DreamDaemonSettings", b =>
109  {
110  b.Property<long>("Id")
111  .ValueGeneratedOnAdd();
112 
113  b.Property<string>("AccessToken");
114 
115  b.Property<bool?>("AllowWebClient")
116  .IsRequired();
117 
118  b.Property<bool?>("AutoStart")
119  .IsRequired();
120 
121  b.Property<long>("InstanceId");
122 
123  b.Property<ushort?>("PrimaryPort")
124  .IsRequired();
125 
126  b.Property<int?>("ProcessId");
127 
128  b.Property<ushort?>("SecondaryPort")
129  .IsRequired();
130 
131  b.Property<int>("SecurityLevel");
132 
133  b.Property<bool?>("SoftRestart")
134  .IsRequired();
135 
136  b.Property<bool?>("SoftShutdown")
137  .IsRequired();
138 
139  b.Property<uint?>("StartupTimeout")
140  .IsRequired();
141 
142  b.HasKey("Id");
143 
144  b.HasIndex("InstanceId")
145  .IsUnique();
146 
147  b.ToTable("DreamDaemonSettings");
148  });
149 
150  modelBuilder.Entity("Tgstation.Server.Host.Models.DreamMakerSettings", b =>
151  {
152  b.Property<long>("Id")
153  .ValueGeneratedOnAdd();
154 
155  b.Property<ushort?>("ApiValidationPort")
156  .IsRequired();
157 
158  b.Property<long>("InstanceId");
159 
160  b.Property<string>("ProjectName");
161 
162  b.HasKey("Id");
163 
164  b.HasIndex("InstanceId")
165  .IsUnique();
166 
167  b.ToTable("DreamMakerSettings");
168  });
169 
170  modelBuilder.Entity("Tgstation.Server.Host.Models.Instance", b =>
171  {
172  b.Property<long>("Id")
173  .ValueGeneratedOnAdd();
174 
175  b.Property<uint?>("AutoUpdateInterval")
176  .IsRequired();
177 
178  b.Property<int>("ConfigurationType");
179 
180  b.Property<string>("Name")
181  .IsRequired();
182 
183  b.Property<bool?>("Online")
184  .IsRequired();
185 
186  b.Property<string>("Path")
187  .IsRequired();
188 
189  b.Property<long?>("WatchdogReattachInformationId");
190 
191  b.HasKey("Id");
192 
193  b.HasIndex("Path")
194  .IsUnique();
195 
196  b.HasIndex("WatchdogReattachInformationId");
197 
198  b.ToTable("Instances");
199  });
200 
201  modelBuilder.Entity("Tgstation.Server.Host.Models.InstanceUser", b =>
202  {
203  b.Property<long>("Id")
204  .ValueGeneratedOnAdd();
205 
206  b.Property<ulong>("ByondRights");
207 
208  b.Property<ulong>("ChatBotRights");
209 
210  b.Property<ulong>("ConfigurationRights");
211 
212  b.Property<ulong>("DreamDaemonRights");
213 
214  b.Property<ulong>("DreamMakerRights");
215 
216  b.Property<long>("InstanceId");
217 
218  b.Property<ulong>("InstanceUserRights");
219 
220  b.Property<ulong>("RepositoryRights");
221 
222  b.Property<long?>("UserId")
223  .IsRequired();
224 
225  b.HasKey("Id");
226 
227  b.HasIndex("InstanceId");
228 
229  b.HasIndex("UserId", "InstanceId")
230  .IsUnique();
231 
232  b.ToTable("InstanceUsers");
233  });
234 
235  modelBuilder.Entity("Tgstation.Server.Host.Models.Job", b =>
236  {
237  b.Property<long>("Id")
238  .ValueGeneratedOnAdd();
239 
240  b.Property<ulong?>("CancelRight");
241 
242  b.Property<ulong?>("CancelRightsType");
243 
244  b.Property<bool?>("Cancelled")
245  .IsRequired();
246 
247  b.Property<long?>("CancelledById");
248 
249  b.Property<string>("Description")
250  .IsRequired();
251 
252  b.Property<string>("ExceptionDetails");
253 
254  b.Property<long>("InstanceId");
255 
256  b.Property<DateTimeOffset?>("StartedAt")
257  .IsRequired();
258 
259  b.Property<long>("StartedById");
260 
261  b.Property<DateTimeOffset?>("StoppedAt");
262 
263  b.HasKey("Id");
264 
265  b.HasIndex("CancelledById");
266 
267  b.HasIndex("InstanceId");
268 
269  b.HasIndex("StartedById");
270 
271  b.ToTable("Jobs");
272  });
273 
274  modelBuilder.Entity("Tgstation.Server.Host.Models.ReattachInformation", b =>
275  {
276  b.Property<long>("Id")
277  .ValueGeneratedOnAdd();
278 
279  b.Property<string>("AccessIdentifier")
280  .IsRequired();
281 
282  b.Property<string>("ChatChannelsJson")
283  .IsRequired();
284 
285  b.Property<string>("ChatCommandsJson")
286  .IsRequired();
287 
288  b.Property<long?>("CompileJobId");
289 
290  b.Property<bool>("IsPrimary");
291 
292  b.Property<ushort>("Port");
293 
294  b.Property<int>("ProcessId");
295 
296  b.Property<int>("RebootState");
297 
298  b.Property<string>("ServerCommandsJson")
299  .IsRequired();
300 
301  b.HasKey("Id");
302 
303  b.HasIndex("CompileJobId");
304 
305  b.ToTable("ReattachInformations");
306  });
307 
308  modelBuilder.Entity("Tgstation.Server.Host.Models.RepositorySettings", b =>
309  {
310  b.Property<long>("Id")
311  .ValueGeneratedOnAdd();
312 
313  b.Property<string>("AccessToken");
314 
315  b.Property<string>("AccessUser");
316 
317  b.Property<bool?>("AutoUpdatesKeepTestMerges")
318  .IsRequired();
319 
320  b.Property<bool?>("AutoUpdatesSynchronize")
321  .IsRequired();
322 
323  b.Property<string>("CommitterEmail")
324  .IsRequired();
325 
326  b.Property<string>("CommitterName")
327  .IsRequired();
328 
329  b.Property<long>("InstanceId");
330 
331  b.Property<bool?>("PushTestMergeCommits")
332  .IsRequired();
333 
334  b.Property<bool?>("ShowTestMergeCommitters")
335  .IsRequired();
336 
337  b.HasKey("Id");
338 
339  b.HasIndex("InstanceId")
340  .IsUnique();
341 
342  b.ToTable("RepositorySettings");
343  });
344 
345  modelBuilder.Entity("Tgstation.Server.Host.Models.RevInfoTestMerge", b =>
346  {
347  b.Property<long>("Id")
348  .ValueGeneratedOnAdd();
349 
350  b.Property<long>("RevisionInformationId");
351 
352  b.Property<long>("TestMergeId");
353 
354  b.HasKey("Id");
355 
356  b.HasIndex("RevisionInformationId");
357 
358  b.HasIndex("TestMergeId");
359 
360  b.ToTable("RevInfoTestMerges");
361  });
362 
363  modelBuilder.Entity("Tgstation.Server.Host.Models.RevisionInformation", b =>
364  {
365  b.Property<long>("Id")
366  .ValueGeneratedOnAdd();
367 
368  b.Property<string>("CommitSha")
369  .IsRequired()
370  .HasMaxLength(40);
371 
372  b.Property<long>("InstanceId");
373 
374  b.Property<string>("OriginCommitSha")
375  .IsRequired()
376  .HasMaxLength(40);
377 
378  b.HasKey("Id");
379 
380  b.HasIndex("CommitSha")
381  .IsUnique();
382 
383  b.HasIndex("InstanceId");
384 
385  b.ToTable("RevisionInformations");
386  });
387 
388  modelBuilder.Entity("Tgstation.Server.Host.Models.TestMerge", b =>
389  {
390  b.Property<long>("Id")
391  .ValueGeneratedOnAdd();
392 
393  b.Property<string>("Author")
394  .IsRequired();
395 
396  b.Property<string>("BodyAtMerge")
397  .IsRequired();
398 
399  b.Property<string>("Comment");
400 
401  b.Property<DateTimeOffset>("MergedAt");
402 
403  b.Property<long>("MergedById");
404 
405  b.Property<int?>("Number")
406  .IsRequired();
407 
408  b.Property<long?>("PrimaryRevisionInformationId");
409 
410  b.Property<string>("PullRequestRevision")
411  .IsRequired();
412 
413  b.Property<string>("TitleAtMerge")
414  .IsRequired();
415 
416  b.Property<string>("Url")
417  .IsRequired();
418 
419  b.HasKey("Id");
420 
421  b.HasIndex("MergedById");
422 
423  b.HasIndex("PrimaryRevisionInformationId")
424  .IsUnique();
425 
426  b.ToTable("TestMerges");
427  });
428 
429  modelBuilder.Entity("Tgstation.Server.Host.Models.User", b =>
430  {
431  b.Property<long>("Id")
432  .ValueGeneratedOnAdd();
433 
434  b.Property<ulong>("AdministrationRights");
435 
436  b.Property<string>("CanonicalName")
437  .IsRequired();
438 
439  b.Property<DateTimeOffset?>("CreatedAt")
440  .IsRequired();
441 
442  b.Property<long?>("CreatedById");
443 
444  b.Property<bool?>("Enabled")
445  .IsRequired();
446 
447  b.Property<ulong>("InstanceManagerRights");
448 
449  b.Property<DateTimeOffset?>("LastPasswordUpdate");
450 
451  b.Property<string>("Name")
452  .IsRequired();
453 
454  b.Property<string>("PasswordHash");
455 
456  b.Property<string>("SystemIdentifier");
457 
458  b.HasKey("Id");
459 
460  b.HasIndex("CanonicalName")
461  .IsUnique();
462 
463  b.HasIndex("CreatedById");
464 
465  b.ToTable("Users");
466  });
467 
468  modelBuilder.Entity("Tgstation.Server.Host.Models.WatchdogReattachInformation", b =>
469  {
470  b.Property<long>("Id")
471  .ValueGeneratedOnAdd();
472 
473  b.Property<long?>("AlphaId");
474 
475  b.Property<bool>("AlphaIsActive");
476 
477  b.Property<long?>("BravoId");
478 
479  b.HasKey("Id");
480 
481  b.HasIndex("AlphaId");
482 
483  b.HasIndex("BravoId");
484 
485  b.ToTable("WatchdogReattachInformations");
486  });
487 
488  modelBuilder.Entity("Tgstation.Server.Host.Models.ChatBot", b =>
489  {
490  b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
491  .WithMany("ChatSettings")
492  .HasForeignKey("InstanceId")
493  .OnDelete(DeleteBehavior.Cascade);
494  });
495 
496  modelBuilder.Entity("Tgstation.Server.Host.Models.ChatChannel", b =>
497  {
498  b.HasOne("Tgstation.Server.Host.Models.ChatBot", "ChatSettings")
499  .WithMany("Channels")
500  .HasForeignKey("ChatSettingsId")
501  .OnDelete(DeleteBehavior.Cascade);
502  });
503 
504  modelBuilder.Entity("Tgstation.Server.Host.Models.CompileJob", b =>
505  {
506  b.HasOne("Tgstation.Server.Host.Models.Job", "Job")
507  .WithMany()
508  .HasForeignKey("JobId");
509 
510  b.HasOne("Tgstation.Server.Host.Models.RevisionInformation", "RevisionInformation")
511  .WithMany("CompileJobs")
512  .HasForeignKey("RevisionInformationId")
513  .OnDelete(DeleteBehavior.Cascade);
514  });
515 
516  modelBuilder.Entity("Tgstation.Server.Host.Models.DreamDaemonSettings", b =>
517  {
518  b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
519  .WithOne("DreamDaemonSettings")
520  .HasForeignKey("Tgstation.Server.Host.Models.DreamDaemonSettings", "InstanceId")
521  .OnDelete(DeleteBehavior.Cascade);
522  });
523 
524  modelBuilder.Entity("Tgstation.Server.Host.Models.DreamMakerSettings", b =>
525  {
526  b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
527  .WithOne("DreamMakerSettings")
528  .HasForeignKey("Tgstation.Server.Host.Models.DreamMakerSettings", "InstanceId")
529  .OnDelete(DeleteBehavior.Cascade);
530  });
531 
532  modelBuilder.Entity("Tgstation.Server.Host.Models.Instance", b =>
533  {
534  b.HasOne("Tgstation.Server.Host.Models.WatchdogReattachInformation", "WatchdogReattachInformation")
535  .WithMany()
536  .HasForeignKey("WatchdogReattachInformationId");
537  });
538 
539  modelBuilder.Entity("Tgstation.Server.Host.Models.InstanceUser", b =>
540  {
541  b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
542  .WithMany("InstanceUsers")
543  .HasForeignKey("InstanceId")
544  .OnDelete(DeleteBehavior.Cascade);
545 
546  b.HasOne("Tgstation.Server.Host.Models.User")
547  .WithMany("InstanceUsers")
548  .HasForeignKey("UserId")
549  .OnDelete(DeleteBehavior.Cascade);
550  });
551 
552  modelBuilder.Entity("Tgstation.Server.Host.Models.Job", b =>
553  {
554  b.HasOne("Tgstation.Server.Host.Models.User", "CancelledBy")
555  .WithMany()
556  .HasForeignKey("CancelledById");
557 
558  b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
559  .WithMany("Jobs")
560  .HasForeignKey("InstanceId")
561  .OnDelete(DeleteBehavior.Cascade);
562 
563  b.HasOne("Tgstation.Server.Host.Models.User", "StartedBy")
564  .WithMany()
565  .HasForeignKey("StartedById")
566  .OnDelete(DeleteBehavior.Cascade);
567  });
568 
569  modelBuilder.Entity("Tgstation.Server.Host.Models.ReattachInformation", b =>
570  {
571  b.HasOne("Tgstation.Server.Host.Models.CompileJob", "CompileJob")
572  .WithMany()
573  .HasForeignKey("CompileJobId");
574  });
575 
576  modelBuilder.Entity("Tgstation.Server.Host.Models.RepositorySettings", b =>
577  {
578  b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
579  .WithOne("RepositorySettings")
580  .HasForeignKey("Tgstation.Server.Host.Models.RepositorySettings", "InstanceId")
581  .OnDelete(DeleteBehavior.Cascade);
582  });
583 
584  modelBuilder.Entity("Tgstation.Server.Host.Models.RevInfoTestMerge", b =>
585  {
586  b.HasOne("Tgstation.Server.Host.Models.RevisionInformation", "RevisionInformation")
587  .WithMany("ActiveTestMerges")
588  .HasForeignKey("RevisionInformationId")
589  .OnDelete(DeleteBehavior.Cascade);
590 
591  b.HasOne("Tgstation.Server.Host.Models.TestMerge", "TestMerge")
592  .WithMany("RevisonInformations")
593  .HasForeignKey("TestMergeId")
594  .OnDelete(DeleteBehavior.Cascade);
595  });
596 
597  modelBuilder.Entity("Tgstation.Server.Host.Models.RevisionInformation", b =>
598  {
599  b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
600  .WithMany("RevisionInformations")
601  .HasForeignKey("InstanceId")
602  .OnDelete(DeleteBehavior.Cascade);
603  });
604 
605  modelBuilder.Entity("Tgstation.Server.Host.Models.TestMerge", b =>
606  {
607  b.HasOne("Tgstation.Server.Host.Models.User", "MergedBy")
608  .WithMany("TestMerges")
609  .HasForeignKey("MergedById")
610  .OnDelete(DeleteBehavior.Restrict);
611 
612  b.HasOne("Tgstation.Server.Host.Models.RevisionInformation", "PrimaryRevisionInformation")
613  .WithOne("PrimaryTestMerge")
614  .HasForeignKey("Tgstation.Server.Host.Models.TestMerge", "PrimaryRevisionInformationId")
615  .OnDelete(DeleteBehavior.SetNull);
616  });
617 
618  modelBuilder.Entity("Tgstation.Server.Host.Models.User", b =>
619  {
620  b.HasOne("Tgstation.Server.Host.Models.User", "CreatedBy")
621  .WithMany("CreatedUsers")
622  .HasForeignKey("CreatedById");
623  });
624 
625  modelBuilder.Entity("Tgstation.Server.Host.Models.WatchdogReattachInformation", b =>
626  {
627  b.HasOne("Tgstation.Server.Host.Models.ReattachInformation", "Alpha")
628  .WithMany()
629  .HasForeignKey("AlphaId");
630 
631  b.HasOne("Tgstation.Server.Host.Models.ReattachInformation", "Bravo")
632  .WithMany()
633  .HasForeignKey("BravoId");
634  });
635 #pragma warning restore 612, 618
636  }
637  }
638 }
+ + + + + + +
+ + + + diff --git a/_my_sql_design_time_db_context_factory_8cs_source.html b/_my_sql_design_time_db_context_factory_8cs_source.html index 8c3e674a5f..a49e992894 100644 --- a/_my_sql_design_time_db_context_factory_8cs_source.html +++ b/_my_sql_design_time_db_context_factory_8cs_source.html @@ -91,7 +91,7 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
MySqlDesignTimeDbContextFactory.cs
-Go to the documentation of this file.
1 using Microsoft.AspNetCore.Identity;
2 using Microsoft.EntityFrameworkCore;
3 using Microsoft.EntityFrameworkCore.Design;
4 using Microsoft.Extensions.Logging;
6 
7 namespace Tgstation.Server.Host.Models.Migrations
8 {
10  sealed class MySqlDesignTimeDbContextFactory : IDesignTimeDbContextFactory<MySqlDatabaseContext>
11  {
13  public MySqlDatabaseContext CreateDbContext(string[] args) => new MySqlDatabaseContext(new DbContextOptions<MySqlDatabaseContext>(), DesignTimeDbContextFactoryHelpers.GetDbContextOptions(), new DatabaseSeeder(new CryptographySuite(new PasswordHasher<User>())), new LoggerFactory().CreateLogger<MySqlDatabaseContext>());
14  }
15 }
+Go to the documentation of this file.
1 using Microsoft.AspNetCore.Identity;
2 using Microsoft.EntityFrameworkCore;
3 using Microsoft.EntityFrameworkCore.Design;
4 using Microsoft.Extensions.Logging;
6 
7 namespace Tgstation.Server.Host.Models.Migrations
8 {
10  sealed class MySqlDesignTimeDbContextFactory : IDesignTimeDbContextFactory<MySqlDatabaseContext>
11  {
13  public MySqlDatabaseContext CreateDbContext(string[] args) => new MySqlDatabaseContext(new DbContextOptions<MySqlDatabaseContext>(), DesignTimeDbContextFactoryHelpers.GetDbContextOptions(), new DatabaseSeeder(new CryptographySuite(new PasswordHasher<User>())), new LoggerFactory().CreateLogger<MySqlDatabaseContext>());
14  }
15 }
Contains helpers for creating design time Models.DatabaseContext<TParentContext>s ...
@@ -100,7 +100,7 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search'); -
DatabaseContext<TParentContext> for MySQL
+
DatabaseContext<TParentContext> for MySQL
diff --git a/_r_e_a_d_m_e_8md_source.html b/_r_e_a_d_m_e_8md_source.html index 2da87a05b8..76784c539b 100644 --- a/_r_e_a_d_m_e_8md_source.html +++ b/_r_e_a_d_m_e_8md_source.html @@ -87,7 +87,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) [![Waffle.io - Columns and their card count](https://badge.waffle.io/tgstation/tgstation-server.png?columns=all)](https://waffle.io/tgstation/tgstation-server?utm_source=badge)
4 
5 [![GitHub license](https://img.shields.io/github/license/tgstation/tgstation-server.svg)](https://github.com/tgstation/tgstation-server/blob/master/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 
12 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 fixing errors and merging GitHub Pull Requests locally.
13 
14 Generally, updates force a live tracking of the configured git repo, resetting local modifications. If you plan to make modifications, set up a new git repo to store your version of the code in, and point this script to that in the config (explained below). This can be on GitHub or a local repo using file:/// urls.
15 
16 ### Legacy Servers
17 * Older server versions can be found in the V# branches of this repository
18 
19 ## Setup
20 
21 ### Installation
22 
23 1. Download and install the [.NET Core Runtime (>= v2.1)](https://www.microsoft.com/net/download) 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.1 (Download can be found on same page). Enusre that the `dotnet` executable file is in your system's `PATH` variable (or the user's that will be running the server).
24 2. [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.
25 3. Extract the .zip file to where you want the server to run from. Note the account running the server must have write access to the `lib` subdirectory.
26 4. If using the ServerService package, run `Tgstation.Server.Host.Service.exe`. It should prompt you to install the service. Click `Yes` and accept a potential UAC elevation prompt. You should now be able to control the service using the Windows service control commandlet.
27 
28 #### Docker
29 
30 tgstation-server supports running in a docker container on linux systems and is the preferred deployment method to avoid [native dependency hell with libgit2](https://github.com/libgit2/libgit2sharp/issues/1533). The official image repository is located at https://hub.docker.com/r/tgstation/server it can be build locally, however, by running `docker build . -f build/Dockerfile` in the repository root.
31 
32 To create a container run `docker create --restart=always -p <public port>:80 -v /path/to/your/appsettings.Production.json:/config_data -v path/to/your/log/folder:/tgs_logs tgstation/server` with any additional options you desire (i.e. You'll have to expose more ports in order to actually host servers, add a volume to create instances on, and create a volume for the SQLite database if that is what you're using).
33 
34 Note that due to the nature of docker. If the container restarts, you will be sent back to the version of TGS you installed with the initial command. Server updates will have to be reapplied. For this reason it is NOT RECOMMENDED to use the live update feature with a docker host.
35 
36 ### Configuring
37 
38 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:
39  - `General:LogFileDirectory`: Override the default directory where server logs are stored. Default is C:/ProgramData/tgstation-server/logs on Windows, /usr/share/tgstation-server/logs otherwise
40  - `General:MinimumPasswordLength`: Minimum password length requirement for database users
41  - `Logging:LogLevel:Default`: 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.
42  - `Database:DatabaseType`: Can be one of `SqlServer`, `MySql`, or `Sqlite`. Note that, at the time of this writing, there is a [blocking bug with the MySQL DBAL provider](https://bugs.mysql.com/bug.php?id=89855) which prevents its usage
43  - `Database:ConnectionString`: Connection string for your database. For SQLite this should be in the form `Data Source=/path/to/database.ext`. Otherwise, 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.
44 
45 ### Database Configuration
46 
47 If you are using an SQLite database just ensure the specified database file doesn't exist for first time setup and you may skip this section.
48 
49 For dedicated SQL servers, the user created for the application will need the privilege to create databases for the first run. Once the initial set of migrations is run, the create right may be revoked.
50 
51 ### Starting
52 
53 For the Windows service version start the `tgstation-server-4` service
54 
55 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
56 
57 ### Stopping
58 
59 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.
60 
61 For the Windows service version stop the `tgstation-server-4` service
62 
63 For the console version press `Ctrl+C` or send a SIGQUIT to the ORIGINAL dotnet process
64 
65 ## Integrating
66 
67 A breaking change from V3: tgstation-server 4 now 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.
68 
69 1. Copy the [DMAPI] files 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
70 2. Modify your `.dme`(s) to include the `tgs.dm` and `tgs/includes.dm` files (ORDER OF APPEARANCE IS MANDATORY)
71 3. Follow the instructions in `tgs.dm` to integrate the API with your codebase.
72 
73 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 new version
74 
75 ## Usage
76 
77 tgstation-sever 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.
78 
79 ### Users
80 
81 All actions apart from logging in must be taken by a user. TGS installs with one default user whose credentials can be found [here](https://github.com/tgstation/tgstation-server/blob/master/src/Tgstation.Server.Api/Models/User.cs). If access to all users is lost, the default user can be reset using the `Database:ResetAdminPassword` configuration setting. 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.
82 
83 ### Instances
84 
85 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.
86 
87 An instance is stored in a single folder anywhere on a system and is made up of several components: The source code git repository, BYOND, the compiler, the watchdog, chat bots, and static file management systems.
88 
89 ##### Instance Users
90 
91 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).
92 
93 #### Repository
94 
95 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).
96 
97 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.
98 
99 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.
100 
101 #### Byond
102 
103 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 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.
104 
105 #### Compiler
106 
107 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).
108 
109 #### Watchdog
110 
111 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.
112 
113 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.
114 
115 #### Chat Bots
116 
117 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
118 
119 - Internet Relay Chat (IRC)
120 - Discord
121 
122 More can be added by providing a new implementation of the [IProvider](https://github.com/tgstation/tgstation-server/blob/master/src/Tgstation.Server.Host/Components/Chat/Providers/IProvider.cs) interface
123 
124 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.
125 
126 #### Static Files
127 
128 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.
129 
130 The `StaticFiles` folder contains 3 root folders
131  - `CodeModifications`
132  - `EventScripts`
133  - `GameStaticFiles`
134 
135 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.
136 
137 This folder may be freely modified manually just beware this may cause deployments to error if done simulatenously on Windows systems.
138 
139 #### CodeModifications
140 
141 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.
142 
143 If `<target dme>.dm` is present, that .dme will be used instead of the repository's `.dme`
144 
145 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
146 
147 #### EventScripts
148 
149 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](https://github.com/tgstation/tgstation-server/blob/master/src/Tgstation.Server.Host/Components/StaticFiles/Configuration.cs#L28) (subject to expansion) list of event parameters can be found [here](https://github.com/tgstation/tgstation-server/blob/master/src/Tgstation.Server.Host/Components/EventType.cs)
150 
151 #### GameStaticFiles
152 
153 Any files and folders contained in 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.
154 
155 ### Updating
156 
157 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.
158 
159 ### Clients
160 
161 Here are some tools for interacting with the TGS 4 JSON API
162 
163 - [Tgstation.Server.ControlPanel](https://github.com/tgstation/Tgstation.Server.ControlPanel): Official client. A cross platform GUI for using tgstation-server
164 - [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
165 - [Tgstation.Server.Api](https://www.nuget.org/packages/Tgstation.Server.Api): A nuget .NET Standard 2.0 library containing API definitions for tgstation-server
166 - [Postman](https://www.getpostman.com/): This repository contains [TGS.postman_collection.json](https://github.com/tgstation/tgstation-server/blob/master/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)
167 
168 Contact project maintainers to get your client added to this list
169 
170 ## Troubleshooting
171 
172 Feel free to ask for help at the coderbus discord: https://discord.gg/Vh8TJp9. Cyberboss#8246 can answer most questions.
173 
174 ## Contributing
175 
176 * See [CONTRIBUTING.md](https://github.com/tgstation/tgstation-server/blob/master/.github/CONTRIBUTING.md)
177 
178 ## Licensing
179 
180 * The DM API for the project is licensed under the MIT license.
181 * The /tg/station 13 icon is licensed under [Creative Commons 3.0 BY-SA](http://creativecommons.org/licenses/by-sa/3.0/).
182 * The remainder of the project is licensed under [GNU AGPL v3](http://www.gnu.org/licenses/agpl-3.0.html)
183 
184 See 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) [![Waffle.io - Columns and their card count](https://badge.waffle.io/tgstation/tgstation-server.png?columns=all)](https://waffle.io/tgstation/tgstation-server?utm_source=badge)
4 
5 [![GitHub license](https://img.shields.io/github/license/tgstation/tgstation-server.svg)](https://github.com/tgstation/tgstation-server/blob/master/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 
12 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 fixing errors and merging GitHub Pull Requests locally.
13 
14 Generally, updates force a live tracking of the configured git repo, resetting local modifications. If you plan to make modifications, set up a new git repo to store your version of the code in, and point this script to that in the config (explained below). This can be on GitHub or a local repo using file:/// urls.
15 
16 ### Legacy Servers
17 * Older server versions can be found in the V# branches of this repository
18 
19 ## Setup
20 
21 ### Installation
22 
23 1. Download and install the [.NET Core Runtime (>= v2.1)](https://www.microsoft.com/net/download) 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.1 (Download can be found on same page). Enusre that the `dotnet` executable file is in your system's `PATH` variable (or the user's that will be running the server).
24 2. [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.
25 3. Extract the .zip file to where you want the server to run from. Note the account running the server must have write access to the `lib` subdirectory.
26 4. If using the ServerService package, run `Tgstation.Server.Host.Service.exe`. It should prompt you to install the service. Click `Yes` and accept a potential UAC elevation prompt. You should now be able to control the service using the Windows service control commandlet.
27 
28 #### Docker
29 
30 tgstation-server supports running in a docker container on linux systems and is the preferred deployment method to avoid [native dependency hell with libgit2](https://github.com/libgit2/libgit2sharp/issues/1533). The official image repository is located at https://hub.docker.com/r/tgstation/server it can be built locally, however, by running `docker build . -f build/Dockerfile` in the repository root.
31 
32 To create a container run `docker create --restart=always -p <public port>:80 -v /path/to/your/appsettings.Production.json:/config_data -v path/to/your/log/folder:/tgs_logs tgstation/server` with any additional options you desire (i.e. You'll have to expose more ports in order to actually host servers, add a volume to create instances on, and create a volume for the SQLite database if that is what you're using).
33 
34 Note that due to the nature of docker. If the container restarts, you will be sent back to the version of TGS you installed with the initial command. Server updates will have to be reapplied. For this reason it is NOT RECOMMENDED to use the live update feature with a docker host.
35 
36 ### Configuring
37 
38 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:
39 
40 - `General:LogFileDirectory`: Override the default directory where server logs are stored. Default is C:/ProgramData/tgstation-server/logs on Windows, /usr/share/tgstation-server/logs otherwise
41 
42 - `General:MinimumPasswordLength`: Minimum password length requirement for database users
43 
44 - `Logging:LogLevel:Default`: 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.
45 
46 - `Database:DatabaseType`: Can be one of `SqlServer`, `MariaDB`, or `MySql`
47 
48 - `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.1.1/src/EFCore.MySql/Storage/Internal/ServerVersion.cs) ignore at your own risk. A string in the form `<major>.<minor>.<patch>`
49 
50 - `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.
51 
52 ### Database Configuration
53 
54 If using MySQL, our provider 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.
55 
56 The user created for the application will need the privilege to create databases on the first run. 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
57 
58 ### Starting
59 
60 For the Windows service version start the `tgstation-server-4` service
61 
62 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
63 
64 ### Stopping
65 
66 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.
67 
68 For the Windows service version stop the `tgstation-server-4` service
69 
70 For the console version press `Ctrl+C` or send a SIGQUIT to the ORIGINAL dotnet process
71 
72 ## Integrating
73 
74 A breaking change from V3: tgstation-server 4 now 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.
75 
76 1. Copy the [DMAPI] files 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
77 2. Modify your `.dme`(s) to include the `tgs.dm` and `tgs/includes.dm` files (ORDER OF APPEARANCE IS MANDATORY)
78 3. Follow the instructions in `tgs.dm` to integrate the API with your codebase.
79 
80 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 new version
81 
82 ## Usage
83 
84 tgstation-sever 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.
85 
86 ### Users
87 
88 All actions apart from logging in must be taken by a user. TGS installs with one default user whose credentials can be found [here](https://github.com/tgstation/tgstation-server/blob/master/src/Tgstation.Server.Api/Models/User.cs). If access to all users is lost, the default user can be reset using the `Database:ResetAdminPassword` configuration setting. 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.
89 
90 ### Instances
91 
92 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.
93 
94 An instance is stored in a single folder anywhere on a system and is made up of several components: The source code git repository, BYOND, the compiler, the watchdog, chat bots, and static file management systems.
95 
96 ##### Instance Users
97 
98 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).
99 
100 #### Repository
101 
102 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).
103 
104 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.
105 
106 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.
107 
108 #### Byond
109 
110 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 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.
111 
112 #### Compiler
113 
114 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).
115 
116 #### Watchdog
117 
118 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.
119 
120 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.
121 
122 #### Chat Bots
123 
124 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
125 
126 - Internet Relay Chat (IRC)
127 - Discord
128 
129 More can be added by providing a new implementation of the [IProvider](https://github.com/tgstation/tgstation-server/blob/master/src/Tgstation.Server.Host/Components/Chat/Providers/IProvider.cs) interface
130 
131 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.
132 
133 #### Static Files
134 
135 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.
136 
137 The `StaticFiles` folder contains 3 root folders
138  - `CodeModifications`
139  - `EventScripts`
140  - `GameStaticFiles`
141 
142 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.
143 
144 This folder may be freely modified manually just beware this may cause deployments to error if done simulatenously on Windows systems.
145 
146 #### CodeModifications
147 
148 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.
149 
150 If `<target dme>.dm` is present, that .dme will be used instead of the repository's `.dme`
151 
152 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
153 
154 #### EventScripts
155 
156 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](https://github.com/tgstation/tgstation-server/blob/master/src/Tgstation.Server.Host/Components/StaticFiles/Configuration.cs#L28) (subject to expansion) list of event parameters can be found [here](https://github.com/tgstation/tgstation-server/blob/master/src/Tgstation.Server.Host/Components/EventType.cs)
157 
158 #### GameStaticFiles
159 
160 Any files and folders contained in 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.
161 
162 ### Updating
163 
164 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.
165 
166 ### Clients
167 
168 Here are some tools for interacting with the TGS 4 JSON API
169 
170 - [Tgstation.Server.ControlPanel](https://github.com/tgstation/Tgstation.Server.ControlPanel): Official client. A cross platform GUI for using tgstation-server
171 - [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
172 - [Tgstation.Server.Api](https://www.nuget.org/packages/Tgstation.Server.Api): A nuget .NET Standard 2.0 library containing API definitions for tgstation-server
173 - [Postman](https://www.getpostman.com/): This repository contains [TGS.postman_collection.json](https://github.com/tgstation/tgstation-server/blob/master/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)
174 
175 Contact project maintainers to get your client added to this list
176 
177 ## Troubleshooting
178 
179 Feel free to ask for help at the coderbus discord: https://discord.gg/Vh8TJp9. Cyberboss#8246 can answer most questions.
180 
181 ## Contributing
182 
183 * See [CONTRIBUTING.md](https://github.com/tgstation/tgstation-server/blob/master/.github/CONTRIBUTING.md)
184 
185 ## Licensing
186 
187 * The DM API for the project is licensed under the MIT license.
188 * The /tg/station 13 icon is licensed under [Creative Commons 3.0 BY-SA](http://creativecommons.org/licenses/by-sa/3.0/).
189 * The remainder of the project is licensed under [GNU AGPL v3](http://www.gnu.org/licenses/agpl-3.0.html)
190 
191 See the /src/DMAPI tree for the MIT license

 NCore
 NIO
 NModels
 NSecurity
 CServer
 CServerFactory
 NTests
 CAdministrationTest
 CIntegrationTest
 CTestingServer
 NInstance
 CAdministrationTest
 CInstanceManagerTest
 CIntegrationTest
 CTestingServer
 CAuthorizeAttribute
 CAutoGeneratedProgram
 CController
 CIEventLog
 CIHostedService
 CJsonConverter
 CServiceBase
 CMigration
 CModelSnapshot
 CServiceBase
diff --git a/api.html b/api.html index 49b8fc64bc..4605b157fc 100644 --- a/api.html +++ b/api.html @@ -247,6 +247,7 @@ Instances

Jobs

Some requests return Tgstation.Server.Api.Models.Job objects. These are long running tasks the server will perform asyncronously and can be polled for status.

+

Note that the job representing instance move operations must be queried and cancelled differently than others. See the documentation of Tgstation.Server.Api.Models.Instance.MoveJob for details

To list all jobs in an Instance use the following request

I GET "/Job/List" => Array of Tgstation.Server.Api.Models.Job

Note that the response for this request will only have the Tgstation.Server.Api.Models.Job.Id field populated

@@ -329,9 +330,12 @@ Static Files

410 will be returned if the path doesn't exist. Tgstation.Server.Api.Models.ConfigurationFile.Content will only be populated if the path is a file the user has access to, otherwise the other fields will be populated appropriately.

To create, write, and delete files use the following request

I POST "/Config" Tgstation.Server.Api.Models.ConfigurationFile => Tgstation.Server.Api.Models.ConfigurationFile

-

When creating a file, only Tgstation.Server.Api.Models.ConfigurationFile.Path and Tgstation.Server.Api.Models.ConfigurationFile.Content should be specified. Any necessary preceeding directories will be created if possible.

+

When creating a file, only Tgstation.Server.Api.Models.ConfigurationFile.Path and Tgstation.Server.Api.Models.ConfigurationFile.Content should be specified

If the file already exists, the Tgstation.Server.Api.Models.ConfigurationFile.LastReadHash field must also be present with the last version recieved from the server for that file. If this does not match at the time of the request, 409 will be returned, indicating the file has changed since it was last viewed by the client.

-

To delete a file set Tgstation.Server.Api.Models.ConfigurationFile.Content to null in the request. If deleting a file leaves a directory empty, they too will be deleted.

+

To delete a file set Tgstation.Server.Api.Models.ConfigurationFile.Content to null in the request.

+

To delete an empty directory use the following request

+

I DELETE "/Config" Tgstation.Server.Api.Models.ConfigurationFile => OK

+

Where the request Tgstation.Server.Api.Models.ConfigurationFile.Path is set to the directory to delete

Watchdog

To read watchdog status use the following request:

diff --git a/class_auto_generated_program.html b/class_auto_generated_program.html index 0f6b04ebfb..0602ab05d6 100644 --- a/class_auto_generated_program.html +++ b/class_auto_generated_program.html @@ -294,12 +294,12 @@ Static Private Member Functions

The documentation for this class was generated from the following files:
diff --git a/class_i_design_time_db_context_factory.html b/class_i_design_time_db_context_factory.html index 01a4b27f39..bca5bed2f7 100644 --- a/class_i_design_time_db_context_factory.html +++ b/class_i_design_time_db_context_factory.html @@ -97,8 +97,7 @@ Inheritance diagram for IDesignTimeDbContextFactory:
Tgstation.Server.Host.Models.Migrations.MySqlDesignTimeDbContextFactory -Tgstation.Server.Host.Models.Migrations.SqliteDesignTimeDbContextFactory -Tgstation.Server.Host.Models.Migrations.SqlServerDesignTimeDbContextFactory +Tgstation.Server.Host.Models.Migrations.SqlServerDesignTimeDbContextFactory

The documentation for this class was generated from the following file:
    diff --git a/class_i_design_time_db_context_factory.png b/class_i_design_time_db_context_factory.png index a26a3444a1..3191e8cb11 100644 Binary files a/class_i_design_time_db_context_factory.png and b/class_i_design_time_db_context_factory.png differ diff --git a/class_migration.html b/class_migration.html new file mode 100644 index 0000000000..c60d4dd879 --- /dev/null +++ b/class_migration.html @@ -0,0 +1,114 @@ + + + + + + +tgstation-server: Migration Class Reference + + + + + + + + + + +
    +
    + + + + + + + +
    +
    tgstation-server +
    +
    The /tg/station 13 server suite
    +
    +
    + + + + + + +
    +
    + + +
    + +
    + +
    +
    +
    +
    Migration Class Reference
    +
    +
    +
    +Inheritance diagram for Migration:
    +
    +
    + + +Tgstation.Server.Host.Models.Migrations.MSInitialCreate +Tgstation.Server.Host.Models.Migrations.MYInitialCreate + +
    +
    The documentation for this class was generated from the following file: +
    + + + + diff --git a/class_migration.png b/class_migration.png new file mode 100644 index 0000000000..5f417d541d Binary files /dev/null and b/class_migration.png differ diff --git a/class_model_snapshot.html b/class_model_snapshot.html new file mode 100644 index 0000000000..fe7e81ae9d --- /dev/null +++ b/class_model_snapshot.html @@ -0,0 +1,114 @@ + + + + + + +tgstation-server: ModelSnapshot Class Reference + + + + + + + + + + +
    +
    + + + + + + + +
    +
    tgstation-server +
    +
    The /tg/station 13 server suite
    +
    +
    + + + + + + +
    +
    + + +
    + +
    + +
    +
    +
    +
    ModelSnapshot Class Reference
    +
    +
    +
    +Inheritance diagram for ModelSnapshot:
    +
    +
    + + +Tgstation.Server.Host.Models.Migrations.MySqlDatabaseContextModelSnapshot +Tgstation.Server.Host.Models.Migrations.SqlServerDatabaseContextModelSnapshot + +
    +
    The documentation for this class was generated from the following file: +
    + + + + diff --git a/class_model_snapshot.png b/class_model_snapshot.png new file mode 100644 index 0000000000..b073fdb633 Binary files /dev/null and b/class_model_snapshot.png differ diff --git a/class_tgstation_1_1_server_1_1_api_1_1_api_headers.html b/class_tgstation_1_1_server_1_1_api_1_1_api_headers.html index 596c946f88..4787e49328 100644 --- a/class_tgstation_1_1_server_1_1_api_1_1_api_headers.html +++ b/class_tgstation_1_1_server_1_1_api_1_1_api_headers.html @@ -697,7 +697,7 @@ Static Private Attributes

    Definition at line 56 of file ApiHeaders.cs.

    -

    Referenced by Tgstation.Server.Tests.IntegrationTest.FullMonty().

    +

    Referenced by Tgstation.Server.Tests.IntegrationTest.FullMonty().

diff --git a/class_tgstation_1_1_server_1_1_api_1_1_models_1_1_configuration_file.html b/class_tgstation_1_1_server_1_1_api_1_1_models_1_1_configuration_file.html index 76f75cb955..2a13c92d4c 100644 --- a/class_tgstation_1_1_server_1_1_api_1_1_models_1_1_configuration_file.html +++ b/class_tgstation_1_1_server_1_1_api_1_1_models_1_1_configuration_file.html @@ -146,7 +146,7 @@ Properties

Definition at line 16 of file ConfigurationFile.cs.

-

Referenced by Tgstation.Server.Host.Components.StaticFiles.Configuration.Read(), and Tgstation.Server.Host.Components.StaticFiles.Configuration.Write().

+

Referenced by Tgstation.Server.Host.Components.StaticFiles.Configuration.Read(), and Tgstation.Server.Host.Components.StaticFiles.Configuration.Write().

@@ -196,7 +196,7 @@ Properties

Definition at line 21 of file ConfigurationFile.cs.

-

Referenced by Tgstation.Server.Host.Components.StaticFiles.Configuration.Read(), and Tgstation.Server.Host.Components.StaticFiles.Configuration.Write().

+

Referenced by Tgstation.Server.Host.Components.StaticFiles.Configuration.Read(), and Tgstation.Server.Host.Components.StaticFiles.Configuration.Write().

@@ -246,7 +246,7 @@ Properties

Definition at line 11 of file ConfigurationFile.cs.

-

Referenced by Tgstation.Server.Client.Components.ConfigurationClient.Read().

+

Referenced by Tgstation.Server.Tests.Instance.ConfigurationTest.FileExists(), Tgstation.Server.Client.Components.ConfigurationClient.Read(), and Tgstation.Server.Tests.Instance.ConfigurationTest.TestDeleteDirectory().

diff --git a/class_tgstation_1_1_server_1_1_api_1_1_models_1_1_error_message.html b/class_tgstation_1_1_server_1_1_api_1_1_models_1_1_error_message.html index c5ab67fae3..097e6f53c0 100644 --- a/class_tgstation_1_1_server_1_1_api_1_1_models_1_1_error_message.html +++ b/class_tgstation_1_1_server_1_1_api_1_1_models_1_1_error_message.html @@ -137,7 +137,7 @@ Properties

Definition at line 13 of file ErrorMessage.cs.

-

Referenced by Tgstation.Server.Host.Controllers.ChatController.Create(), Tgstation.Server.Host.Controllers.InstanceController.Create(), Tgstation.Server.Host.Controllers.ConfigurationController.File(), Tgstation.Server.Host.Controllers.ApiController.OnActionExecutionAsync(), and Tgstation.Server.Host.Controllers.ConfigurationController.Update().

+

Referenced by Tgstation.Server.Host.Controllers.ChatController.Create(), Tgstation.Server.Host.Controllers.InstanceController.Create(), Tgstation.Server.Host.Controllers.ConfigurationController.File(), Tgstation.Server.Host.Controllers.ApiController.OnActionExecutionAsync(), and Tgstation.Server.Host.Controllers.ConfigurationController.Update().

diff --git a/class_tgstation_1_1_server_1_1_api_1_1_models_1_1_instance.html b/class_tgstation_1_1_server_1_1_api_1_1_models_1_1_instance.html index 9f7975ffd9..a62a43e8e7 100644 --- a/class_tgstation_1_1_server_1_1_api_1_1_models_1_1_instance.html +++ b/class_tgstation_1_1_server_1_1_api_1_1_models_1_1_instance.html @@ -209,7 +209,7 @@ Properties

Definition at line 38 of file Instance.cs.

-

Referenced by Tgstation.Server.Host.Controllers.ConfigurationController.ConfigurationController().

+

Referenced by Tgstation.Server.Host.Controllers.ConfigurationController.ConfigurationController().

@@ -235,7 +235,7 @@ Properties

Definition at line 14 of file Instance.cs.

-

Referenced by Tgstation.Server.Host.Controllers.InstanceUserController.Create(), Tgstation.Server.Host.Controllers.ChatController.Create(), Tgstation.Server.Host.Controllers.RepositoryController.Create(), Tgstation.Server.Client.InstanceManagerClient.CreateClient(), Tgstation.Server.Host.Controllers.InstanceUserController.Delete(), Tgstation.Server.Host.Controllers.RepositoryController.Delete(), Tgstation.Server.Client.Components.DreamMakerClient.DreamMakerClient(), Tgstation.Server.Host.Controllers.InstanceUserController.GetId(), Tgstation.Server.Client.InstanceManagerClient.InstanceManagerClient(), Tgstation.Server.Host.Controllers.JobController.List(), Tgstation.Server.Host.Controllers.InstanceUserController.List(), Tgstation.Server.Host.Controllers.ChatController.List(), Tgstation.Server.Host.Controllers.JobController.Read(), Tgstation.Server.Host.Controllers.RepositoryController.Read(), Tgstation.Server.Host.Controllers.DreamDaemonController.ReadImpl(), Tgstation.Server.Host.Core.JobManager.RegisterOperation(), Tgstation.Server.Host.Controllers.ByondController.Update(), Tgstation.Server.Host.Controllers.InstanceUserController.Update(), Tgstation.Server.Host.Controllers.DreamDaemonController.Update(), Tgstation.Server.Host.Controllers.ChatController.Update(), Tgstation.Server.Host.Controllers.InstanceController.Update(), and Tgstation.Server.Host.Controllers.RepositoryController.Update().

+

Referenced by Tgstation.Server.Host.Controllers.InstanceUserController.Create(), Tgstation.Server.Host.Controllers.ChatController.Create(), Tgstation.Server.Host.Controllers.RepositoryController.Create(), Tgstation.Server.Client.InstanceManagerClient.CreateClient(), Tgstation.Server.Host.Controllers.InstanceUserController.Delete(), Tgstation.Server.Host.Controllers.RepositoryController.Delete(), Tgstation.Server.Client.Components.DreamMakerClient.DreamMakerClient(), Tgstation.Server.Host.Controllers.InstanceUserController.GetId(), Tgstation.Server.Client.InstanceManagerClient.InstanceManagerClient(), Tgstation.Server.Host.Controllers.JobController.List(), Tgstation.Server.Host.Controllers.InstanceUserController.List(), Tgstation.Server.Host.Controllers.ChatController.List(), Tgstation.Server.Host.Controllers.JobController.Read(), Tgstation.Server.Host.Controllers.RepositoryController.Read(), Tgstation.Server.Host.Controllers.DreamDaemonController.ReadImpl(), Tgstation.Server.Host.Core.JobManager.RegisterOperation(), Tgstation.Server.Host.Controllers.ByondController.Update(), Tgstation.Server.Host.Controllers.InstanceUserController.Update(), Tgstation.Server.Host.Controllers.DreamDaemonController.Update(), Tgstation.Server.Host.Controllers.ChatController.Update(), and Tgstation.Server.Host.Controllers.RepositoryController.Update().

@@ -258,8 +258,9 @@ Properties

The Job representing a change of Path

+

Due to how Jobs are children of Instances but moving one requires the Instance to be offline, interactions with this Job are performed in a non-standard fashion. The Job is read by querying the Instance again (either via list or ID lookup) and cancelled by making any sort of update to the Instance. Once the Instance comes back Online it can be queried like a normal job

-

Definition at line 50 of file Instance.cs.

+

Definition at line 51 of file Instance.cs.

@@ -333,8 +334,6 @@ Properties

Definition at line 26 of file Instance.cs.

-

Referenced by Tgstation.Server.Host.Controllers.InstanceController.Update().

-
The documentation for this class was generated from the following file:
-
Release Directory Reference
+
Debug Directory Reference
- +

Directories

directory  netcoreapp2.0
directory  netcoreapp2.0
 
diff --git a/dir_53c056aa6a96f034cca75f793964be6e.html b/dir_0b6a380fe7fc1e1d640d1ac854fc3fb6.html similarity index 92% rename from dir_53c056aa6a96f034cca75f793964be6e.html rename to dir_0b6a380fe7fc1e1d640d1ac854fc3fb6.html index 6155db1604..b0f9600a96 100644 --- a/dir_53c056aa6a96f034cca75f793964be6e.html +++ b/dir_0b6a380fe7fc1e1d640d1ac854fc3fb6.html @@ -4,7 +4,7 @@ -tgstation-server: tests/Tgstation.Server.Host.Tests/obj/Release Directory Reference +tgstation-server: tests/Tgstation.Server.Host.Tests/obj/Debug Directory Reference @@ -83,18 +83,18 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
-
Release Directory Reference
+
Debug Directory Reference
- +

Directories

directory  netcoreapp2.0
directory  netcoreapp2.0
 
diff --git a/dir_0c9add60ab05c04ed8367ecaedf67c01.html b/dir_0c9add60ab05c04ed8367ecaedf67c01.html index acec7234dd..2de244232c 100644 --- a/dir_0c9add60ab05c04ed8367ecaedf67c01.html +++ b/dir_0c9add60ab05c04ed8367ecaedf67c01.html @@ -94,11 +94,21 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search'); + + + + + + + + + + - + diff --git a/dir_103b33cf1ab7043de6f87dfa61afa23f.html b/dir_103b33cf1ab7043de6f87dfa61afa23f.html index f8c2b3de11..9ed508fe22 100644 --- a/dir_103b33cf1ab7043de6f87dfa61afa23f.html +++ b/dir_103b33cf1ab7043de6f87dfa61afa23f.html @@ -94,7 +94,7 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');

Files

file  20180906135553_MSInitialCreate.cs [code]
 
file  20180906135553_MSInitialCreate.Designer.cs [code]
 
file  20180906143029_MYInitialCreate.cs [code]
 
file  20180906143029_MYInitialCreate.Designer.cs [code]
 
file  DesignTimeDbContextFactoryHelpers.cs [code]
 
file  MySqlDatabaseContextModelSnapshot.cs [code]
 
file  MySqlDesignTimeDbContextFactory.cs [code]
 
file  SqliteDesignTimeDbContextFactory.cs [code]
file  SqlServerDatabaseContextModelSnapshot.cs [code]
 
file  SqlServerDesignTimeDbContextFactory.cs [code]
 
- +

Directories

directory  Release
directory  Debug
 
diff --git a/dir_177c182c6d1f29bec543c3fdad3072d5.html b/dir_177c182c6d1f29bec543c3fdad3072d5.html index b2fb8372f0..a10549bc27 100644 --- a/dir_177c182c6d1f29bec543c3fdad3072d5.html +++ b/dir_177c182c6d1f29bec543c3fdad3072d5.html @@ -94,7 +94,7 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search'); - +

Directories

directory  Release
directory  Debug
 
diff --git a/dir_90400719279795a0c5ea912516a4c874.html b/dir_2b9d136d9ffdab429a63a74fca28b300.html similarity index 92% rename from dir_90400719279795a0c5ea912516a4c874.html rename to dir_2b9d136d9ffdab429a63a74fca28b300.html index b6b5a8712a..2952795a88 100644 --- a/dir_90400719279795a0c5ea912516a4c874.html +++ b/dir_2b9d136d9ffdab429a63a74fca28b300.html @@ -4,7 +4,7 @@ -tgstation-server: src/Tgstation.Server.Host/obj/Release Directory Reference +tgstation-server: src/Tgstation.Server.Host/obj/Debug Directory Reference @@ -83,18 +83,18 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
-
Release Directory Reference
+
Debug Directory Reference
- +

Directories

directory  netcoreapp2.0
directory  netcoreapp2.0
 
diff --git a/dir_7ae0efc0613c36c848fa310214e0aee3.html b/dir_2e67631cb23401d696bcfdbe81f86f0f.html similarity index 92% rename from dir_7ae0efc0613c36c848fa310214e0aee3.html rename to dir_2e67631cb23401d696bcfdbe81f86f0f.html index 3927676178..27b202e0f8 100644 --- a/dir_7ae0efc0613c36c848fa310214e0aee3.html +++ b/dir_2e67631cb23401d696bcfdbe81f86f0f.html @@ -4,7 +4,7 @@ -tgstation-server: src/Tgstation.Server.Api/obj/Release Directory Reference +tgstation-server: src/Tgstation.Server.Api/obj/Debug Directory Reference @@ -83,18 +83,18 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
-
Release Directory Reference
+
Debug Directory Reference
- +

Directories

directory  netstandard2.0
directory  netstandard2.0
 
diff --git a/dir_446bdfc1cd19b69c11765b5665ebfd45.html b/dir_446bdfc1cd19b69c11765b5665ebfd45.html index e5c1157e62..b39278d097 100644 --- a/dir_446bdfc1cd19b69c11765b5665ebfd45.html +++ b/dir_446bdfc1cd19b69c11765b5665ebfd45.html @@ -94,7 +94,7 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search'); - +

Directories

directory  Release
directory  Debug
 
diff --git a/dir_4122b363a9d577bed19bef337e757ff3.html b/dir_48968f849f7ddb061f7130e079fe66a6.html similarity index 92% rename from dir_4122b363a9d577bed19bef337e757ff3.html rename to dir_48968f849f7ddb061f7130e079fe66a6.html index 5ed1f42d3b..581324c065 100644 --- a/dir_4122b363a9d577bed19bef337e757ff3.html +++ b/dir_48968f849f7ddb061f7130e079fe66a6.html @@ -4,7 +4,7 @@ -tgstation-server: src/Tgstation.Server.Client/obj/Release Directory Reference +tgstation-server: src/Tgstation.Server.Client/obj/Debug Directory Reference @@ -83,18 +83,18 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
-
Release Directory Reference
+
Debug Directory Reference
- +

Directories

directory  netstandard2.0
directory  netstandard2.0
 
diff --git a/dir_5069f282fe9d0323372a5085da5fee49.html b/dir_5af7de1413352a4fe88a28f48c07cbc2.html similarity index 95% rename from dir_5069f282fe9d0323372a5085da5fee49.html rename to dir_5af7de1413352a4fe88a28f48c07cbc2.html index 0e9785e4fe..50e94a6310 100644 --- a/dir_5069f282fe9d0323372a5085da5fee49.html +++ b/dir_5af7de1413352a4fe88a28f48c07cbc2.html @@ -4,7 +4,7 @@ -tgstation-server: tests/Tgstation.Server.Client.Tests/obj/Release/netcoreapp2.0 Directory Reference +tgstation-server: tests/Tgstation.Server.Client.Tests/obj/Debug/netcoreapp2.0 Directory Reference @@ -83,7 +83,7 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
diff --git a/dir_685b5aa2d8a661ab5bc3f68b331887b0.html b/dir_685b5aa2d8a661ab5bc3f68b331887b0.html index bcc564585d..8a5a32744f 100644 --- a/dir_685b5aa2d8a661ab5bc3f68b331887b0.html +++ b/dir_685b5aa2d8a661ab5bc3f68b331887b0.html @@ -94,7 +94,7 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search'); - +

Directories

directory  Release
directory  Debug
 
diff --git a/dir_689f0584ee97ae9ca308532531d9c5d2.html b/dir_689f0584ee97ae9ca308532531d9c5d2.html index 7232bcac7a..0f32ae339c 100644 --- a/dir_689f0584ee97ae9ca308532531d9c5d2.html +++ b/dir_689f0584ee97ae9ca308532531d9c5d2.html @@ -94,7 +94,7 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search'); - +

Directories

directory  Release
directory  Debug
 
diff --git a/dir_74017a19412e2f90cc6ad11fcac82495.html b/dir_74017a19412e2f90cc6ad11fcac82495.html index 14da4b1975..9617799fa6 100644 --- a/dir_74017a19412e2f90cc6ad11fcac82495.html +++ b/dir_74017a19412e2f90cc6ad11fcac82495.html @@ -94,7 +94,7 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search'); - +

Directories

directory  Release
directory  Debug
 
diff --git a/dir_0e792d76844bf2d7c79795a56a50ebec.html b/dir_87039fa7b841d848ca75981d64237477.html similarity index 92% rename from dir_0e792d76844bf2d7c79795a56a50ebec.html rename to dir_87039fa7b841d848ca75981d64237477.html index 9f64c7ceca..7fd2744d90 100644 --- a/dir_0e792d76844bf2d7c79795a56a50ebec.html +++ b/dir_87039fa7b841d848ca75981d64237477.html @@ -4,7 +4,7 @@ -tgstation-server: tests/Tgstation.Server.Tests/obj/Release Directory Reference +tgstation-server: tests/Tgstation.Server.Tests/obj/Debug Directory Reference @@ -83,18 +83,18 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
-
Release Directory Reference
+
Debug Directory Reference
- +

Directories

directory  netcoreapp2.0
directory  netcoreapp2.0
 
diff --git a/dir_87a4f2035ed18e30422f4b60aebc4381.html b/dir_87a4f2035ed18e30422f4b60aebc4381.html index ffcb15b5a1..09acbb5221 100644 --- a/dir_87a4f2035ed18e30422f4b60aebc4381.html +++ b/dir_87a4f2035ed18e30422f4b60aebc4381.html @@ -94,7 +94,7 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search'); - +

Directories

directory  Release
directory  Debug
 
diff --git a/dir_1619c6f669609cae8ce384dc7b32dfde.html b/dir_8f136bd1d72979caeb62a1146c97f173.html similarity index 94% rename from dir_1619c6f669609cae8ce384dc7b32dfde.html rename to dir_8f136bd1d72979caeb62a1146c97f173.html index 4cf545e1ea..0a7c34316b 100644 --- a/dir_1619c6f669609cae8ce384dc7b32dfde.html +++ b/dir_8f136bd1d72979caeb62a1146c97f173.html @@ -4,7 +4,7 @@ -tgstation-server: tests/Tgstation.Server.Tests/obj/Release/netcoreapp2.0 Directory Reference +tgstation-server: tests/Tgstation.Server.Tests/obj/Debug/netcoreapp2.0 Directory Reference @@ -83,7 +83,7 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
diff --git a/dir_78d37ae488054b1d483e4fd9348761bd.html b/dir_9001df4c867c54e409a37c0d680ebe27.html similarity index 94% rename from dir_78d37ae488054b1d483e4fd9348761bd.html rename to dir_9001df4c867c54e409a37c0d680ebe27.html index 152d28e3f0..affc17aca9 100644 --- a/dir_78d37ae488054b1d483e4fd9348761bd.html +++ b/dir_9001df4c867c54e409a37c0d680ebe27.html @@ -4,7 +4,7 @@ -tgstation-server: src/Tgstation.Server.Host/obj/Release/netcoreapp2.0 Directory Reference +tgstation-server: src/Tgstation.Server.Host/obj/Debug/netcoreapp2.0 Directory Reference @@ -83,7 +83,7 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
diff --git a/dir_6deca23b0c5a69ee313e728b24a2ba8c.html b/dir_9aedc62a69ee0ed357488302c2a7ddc7.html similarity index 93% rename from dir_6deca23b0c5a69ee313e728b24a2ba8c.html rename to dir_9aedc62a69ee0ed357488302c2a7ddc7.html index 92d7a6cf4e..3e36407f0f 100644 --- a/dir_6deca23b0c5a69ee313e728b24a2ba8c.html +++ b/dir_9aedc62a69ee0ed357488302c2a7ddc7.html @@ -4,7 +4,7 @@ -tgstation-server: src/Tgstation.Server.Host.Console/obj/Release/netcoreapp2.0 Directory Reference +tgstation-server: src/Tgstation.Server.Host.Console/obj/Debug/netcoreapp2.0 Directory Reference @@ -83,7 +83,7 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
diff --git a/dir_13bee0ed20ecc83b9bcd4b869b726d42.html b/dir_a3714ab037275c284988fc90b4d045a5.html similarity index 95% rename from dir_13bee0ed20ecc83b9bcd4b869b726d42.html rename to dir_a3714ab037275c284988fc90b4d045a5.html index 871455c8e3..2d3c3d0d26 100644 --- a/dir_13bee0ed20ecc83b9bcd4b869b726d42.html +++ b/dir_a3714ab037275c284988fc90b4d045a5.html @@ -4,7 +4,7 @@ -tgstation-server: src/Tgstation.Server.Host.Watchdog/obj/Release/netstandard2.0 Directory Reference +tgstation-server: src/Tgstation.Server.Host.Watchdog/obj/Debug/netstandard2.0 Directory Reference @@ -83,7 +83,7 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
diff --git a/dir_b524b00ec2259c3c83b44909fd47f6e3.html b/dir_b524b00ec2259c3c83b44909fd47f6e3.html index 452d0abee5..319aa44920 100644 --- a/dir_b524b00ec2259c3c83b44909fd47f6e3.html +++ b/dir_b524b00ec2259c3c83b44909fd47f6e3.html @@ -94,7 +94,7 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search'); - +

Directories

directory  Release
directory  Debug
 
diff --git a/dir_40138fd4849573e743490d583f6d06d6.html b/dir_b92573f47b933d7a3661c26d1af2098b.html similarity index 93% rename from dir_40138fd4849573e743490d583f6d06d6.html rename to dir_b92573f47b933d7a3661c26d1af2098b.html index 4525c69083..36108a8022 100644 --- a/dir_40138fd4849573e743490d583f6d06d6.html +++ b/dir_b92573f47b933d7a3661c26d1af2098b.html @@ -4,7 +4,7 @@ -tgstation-server: tests/Tgstation.Server.Host.Console.Tests/obj/Release Directory Reference +tgstation-server: tests/Tgstation.Server.Host.Console.Tests/obj/Debug Directory Reference @@ -83,18 +83,18 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
-
Release Directory Reference
+
Debug Directory Reference
- +

Directories

directory  netcoreapp2.0
directory  netcoreapp2.0
 
diff --git a/dir_18a5e30c14613227f95d12bd9fddc614.html b/dir_be9f3ed3485a880e3213506a311823d1.html similarity index 92% rename from dir_18a5e30c14613227f95d12bd9fddc614.html rename to dir_be9f3ed3485a880e3213506a311823d1.html index 75dd24b00a..7c4c163948 100644 --- a/dir_18a5e30c14613227f95d12bd9fddc614.html +++ b/dir_be9f3ed3485a880e3213506a311823d1.html @@ -4,7 +4,7 @@ -tgstation-server: tests/Tgstation.Server.Api.Tests/obj/Release Directory Reference +tgstation-server: tests/Tgstation.Server.Api.Tests/obj/Debug Directory Reference @@ -83,18 +83,18 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
-
Release Directory Reference
+
Debug Directory Reference
- +

Directories

directory  netcoreapp2.0
directory  netcoreapp2.0
 
diff --git a/dir_c2c4d4ee47e55233c6ea5db8b96422c6.html b/dir_c2c4d4ee47e55233c6ea5db8b96422c6.html index d475bdf48f..5192198596 100644 --- a/dir_c2c4d4ee47e55233c6ea5db8b96422c6.html +++ b/dir_c2c4d4ee47e55233c6ea5db8b96422c6.html @@ -135,8 +135,6 @@ Files   file  RevisionInformation.cs [code]   -file  SqliteDatabaseContext.cs [code] -  file  SqlServerDatabaseContext.cs [code]   file  TestMerge.cs [code] diff --git a/dir_85962924731471f7a9836641cd31e82a.html b/dir_c3ccbc778e7dc2ad3f7649c567722ab9.html similarity index 95% rename from dir_85962924731471f7a9836641cd31e82a.html rename to dir_c3ccbc778e7dc2ad3f7649c567722ab9.html index b622a96a45..575a73c989 100644 --- a/dir_85962924731471f7a9836641cd31e82a.html +++ b/dir_c3ccbc778e7dc2ad3f7649c567722ab9.html @@ -4,7 +4,7 @@ -tgstation-server: tests/Tgstation.Server.Host.Console.Tests/obj/Release/netcoreapp2.0 Directory Reference +tgstation-server: tests/Tgstation.Server.Host.Console.Tests/obj/Debug/netcoreapp2.0 Directory Reference @@ -83,7 +83,7 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
diff --git a/dir_1302441b003a340c6a16c9bc1b51c94e.html b/dir_c9d0608eee53bfffcc33d03463bbe2db.html similarity index 80% rename from dir_1302441b003a340c6a16c9bc1b51c94e.html rename to dir_c9d0608eee53bfffcc33d03463bbe2db.html index 1d4de10efb..52c4c99e69 100644 --- a/dir_1302441b003a340c6a16c9bc1b51c94e.html +++ b/dir_c9d0608eee53bfffcc33d03463bbe2db.html @@ -4,7 +4,7 @@ -tgstation-server: tests/Tgstation.Server.Host.Tests/Core Directory Reference +tgstation-server: tests/Tgstation.Server.Tests/Instance Directory Reference @@ -83,18 +83,20 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
-
Core Directory Reference
+
Instance Directory Reference
diff --git a/dir_83a6f600c846cd444f33e44bb9b4f9cb.html b/dir_d8a279f7113e0073f6cd65dd050c1475.html similarity index 94% rename from dir_83a6f600c846cd444f33e44bb9b4f9cb.html rename to dir_d8a279f7113e0073f6cd65dd050c1475.html index 0257f8ff81..d0d041faeb 100644 --- a/dir_83a6f600c846cd444f33e44bb9b4f9cb.html +++ b/dir_d8a279f7113e0073f6cd65dd050c1475.html @@ -4,7 +4,7 @@ -tgstation-server: src/Tgstation.Server.Api/obj/Release/netstandard2.0 Directory Reference +tgstation-server: src/Tgstation.Server.Api/obj/Debug/netstandard2.0 Directory Reference @@ -83,7 +83,7 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
diff --git a/dir_6824e70542450d573273f8d3973ed576.html b/dir_daeb063f9bb170acf64b90bbd817fcde.html similarity index 94% rename from dir_6824e70542450d573273f8d3973ed576.html rename to dir_daeb063f9bb170acf64b90bbd817fcde.html index 28a760aef5..e4b57c88f4 100644 --- a/dir_6824e70542450d573273f8d3973ed576.html +++ b/dir_daeb063f9bb170acf64b90bbd817fcde.html @@ -4,7 +4,7 @@ -tgstation-server: tests/Tgstation.Server.Api.Tests/obj/Release/netcoreapp2.0 Directory Reference +tgstation-server: tests/Tgstation.Server.Api.Tests/obj/Debug/netcoreapp2.0 Directory Reference @@ -83,7 +83,7 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
diff --git a/dir_e0c1bccb03249d93bc42ac0968ce43fd.html b/dir_e0c1bccb03249d93bc42ac0968ce43fd.html index 21e7dbc09a..f61d62e55a 100644 --- a/dir_e0c1bccb03249d93bc42ac0968ce43fd.html +++ b/dir_e0c1bccb03249d93bc42ac0968ce43fd.html @@ -94,7 +94,7 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search'); - +

Directories

directory  Release
directory  Debug
 
diff --git a/dir_c65d377d3c73fb4a07b3637f9c0caf4d.html b/dir_e97d380c3eebaf0856a4ec180d749620.html similarity index 93% rename from dir_c65d377d3c73fb4a07b3637f9c0caf4d.html rename to dir_e97d380c3eebaf0856a4ec180d749620.html index 76db3f0b24..4ea24688cc 100644 --- a/dir_c65d377d3c73fb4a07b3637f9c0caf4d.html +++ b/dir_e97d380c3eebaf0856a4ec180d749620.html @@ -4,7 +4,7 @@ -tgstation-server: tests/Tgstation.Server.Host.Watchdog.Tests/obj/Release Directory Reference +tgstation-server: tests/Tgstation.Server.Host.Watchdog.Tests/obj/Debug Directory Reference @@ -83,18 +83,18 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
-
Release Directory Reference
+
Debug Directory Reference
- +

Directories

directory  netcoreapp2.0
directory  netcoreapp2.0
 
diff --git a/dir_6c8d8f20501e8e412458f542d0998195.html b/dir_ea36b5ab01ad6e8b607438a02ae91964.html similarity index 93% rename from dir_6c8d8f20501e8e412458f542d0998195.html rename to dir_ea36b5ab01ad6e8b607438a02ae91964.html index 43b815b991..93aec1543b 100644 --- a/dir_6c8d8f20501e8e412458f542d0998195.html +++ b/dir_ea36b5ab01ad6e8b607438a02ae91964.html @@ -4,7 +4,7 @@ -tgstation-server: src/Tgstation.Server.Host.Watchdog/obj/Release Directory Reference +tgstation-server: src/Tgstation.Server.Host.Watchdog/obj/Debug Directory Reference @@ -83,18 +83,18 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
-
Release Directory Reference
+
Debug Directory Reference
- +

Directories

directory  netstandard2.0
directory  netstandard2.0
 
diff --git a/dir_eb7c70823ae63176c38851687b13349d.html b/dir_eb7c70823ae63176c38851687b13349d.html index 3a5e1e6c4d..d36a330b9e 100644 --- a/dir_eb7c70823ae63176c38851687b13349d.html +++ b/dir_eb7c70823ae63176c38851687b13349d.html @@ -94,6 +94,8 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search'); + +

Directories

directory  Instance
 
directory  obj
 
@@ -101,6 +103,8 @@ Directories Files + + diff --git a/dir_56301f73c488544f78326004b3ba69a6.html b/dir_eca6d9a7be6772490ed1cc9215fd08cf.html similarity index 95% rename from dir_56301f73c488544f78326004b3ba69a6.html rename to dir_eca6d9a7be6772490ed1cc9215fd08cf.html index 5ef32f6f15..596c9e65d9 100644 --- a/dir_56301f73c488544f78326004b3ba69a6.html +++ b/dir_eca6d9a7be6772490ed1cc9215fd08cf.html @@ -4,7 +4,7 @@ -tgstation-server: tests/Tgstation.Server.Host.Watchdog.Tests/obj/Release/netcoreapp2.0 Directory Reference +tgstation-server: tests/Tgstation.Server.Host.Watchdog.Tests/obj/Debug/netcoreapp2.0 Directory Reference @@ -83,7 +83,7 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
diff --git a/dir_ed78ff3b0fc4a7f5234cdb0b34617476.html b/dir_ed78ff3b0fc4a7f5234cdb0b34617476.html index c3a21cc2f9..9f1e7a7686 100644 --- a/dir_ed78ff3b0fc4a7f5234cdb0b34617476.html +++ b/dir_ed78ff3b0fc4a7f5234cdb0b34617476.html @@ -94,8 +94,6 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
file  AdministrationTest.cs [code]
 
file  InstanceManagerTest.cs [code]
 
file  IntegrationTest.cs [code]
 
file  TestingServer.cs [code]
- - diff --git a/dir_f0581cdd528a911f791df57d0f438bcb.html b/dir_f0581cdd528a911f791df57d0f438bcb.html index c3b8e31cc7..bfa467f450 100644 --- a/dir_f0581cdd528a911f791df57d0f438bcb.html +++ b/dir_f0581cdd528a911f791df57d0f438bcb.html @@ -94,7 +94,7 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');

Directories

directory  Core
 
directory  obj
 
directory  Security
- +

Directories

directory  Release
directory  Debug
 
diff --git a/dir_383ca23c40357ede4cca827b4a365c76.html b/dir_f88948f9bde0555374e8a59a663bdfc9.html similarity index 93% rename from dir_383ca23c40357ede4cca827b4a365c76.html rename to dir_f88948f9bde0555374e8a59a663bdfc9.html index 9a22cede05..4fad47ae6e 100644 --- a/dir_383ca23c40357ede4cca827b4a365c76.html +++ b/dir_f88948f9bde0555374e8a59a663bdfc9.html @@ -4,7 +4,7 @@ -tgstation-server: src/Tgstation.Server.Client/obj/Release/netstandard2.0 Directory Reference +tgstation-server: src/Tgstation.Server.Client/obj/Debug/netstandard2.0 Directory Reference @@ -83,7 +83,7 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
diff --git a/dir_4a67386b25ed7510b9ff12136421e5e0.html b/dir_fa19fb0671b7c8d7e52fd04aee5fcddb.html similarity index 94% rename from dir_4a67386b25ed7510b9ff12136421e5e0.html rename to dir_fa19fb0671b7c8d7e52fd04aee5fcddb.html index 56f8074893..d93e977058 100644 --- a/dir_4a67386b25ed7510b9ff12136421e5e0.html +++ b/dir_fa19fb0671b7c8d7e52fd04aee5fcddb.html @@ -4,7 +4,7 @@ -tgstation-server: tests/Tgstation.Server.Host.Tests/obj/Release/netcoreapp2.0 Directory Reference +tgstation-server: tests/Tgstation.Server.Host.Tests/obj/Debug/netcoreapp2.0 Directory Reference @@ -83,7 +83,7 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
diff --git a/dir_7a9d035dcfcd6d9c7aa1efef4b6b58a4.html b/dir_fab1362e86740a2c573ef875dd474a67.html similarity index 92% rename from dir_7a9d035dcfcd6d9c7aa1efef4b6b58a4.html rename to dir_fab1362e86740a2c573ef875dd474a67.html index a2418e9312..b9af2dad4d 100644 --- a/dir_7a9d035dcfcd6d9c7aa1efef4b6b58a4.html +++ b/dir_fab1362e86740a2c573ef875dd474a67.html @@ -4,7 +4,7 @@ -tgstation-server: src/Tgstation.Server.Host.Console/obj/Release Directory Reference +tgstation-server: src/Tgstation.Server.Host.Console/obj/Debug Directory Reference @@ -83,18 +83,18 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
-
Release Directory Reference
+
Debug Directory Reference
- +

Directories

directory  netcoreapp2.0
directory  netcoreapp2.0
 
diff --git a/dir_fb12273b841438c2f2c9b5bc5398c636.html b/dir_fb12273b841438c2f2c9b5bc5398c636.html index ccac41e608..d3db42b66b 100644 --- a/dir_fb12273b841438c2f2c9b5bc5398c636.html +++ b/dir_fb12273b841438c2f2c9b5bc5398c636.html @@ -94,7 +94,7 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search'); - +

Directories

directory  Release
directory  Debug
 
diff --git a/files.html b/files.html index bc9886bc7a..3b5bb276c7 100644 --- a/files.html +++ b/files.html @@ -130,8 +130,8 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');  User.cs  UserUpdate.cs   obj -  Release -  netstandard2.0 +  Debug +  netstandard2.0  Tgstation.Server.Api.AssemblyInfo.cs   Rights  AdministrationRights.cs @@ -168,8 +168,8 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');  JobsClient.cs  RepositoryClient.cs   obj -  Release -  netstandard2.0 +  Debug +  netstandard2.0  Tgstation.Server.Client.AssemblyInfo.cs  AdministrationClient.cs  ApiClient.cs @@ -350,10 +350,15 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');  WindowsSymlinkFactory.cs   Models   Migrations - DesignTimeDbContextFactoryHelpers.cs - MySqlDesignTimeDbContextFactory.cs - SqliteDesignTimeDbContextFactory.cs - SqlServerDesignTimeDbContextFactory.cs + 20180906135553_MSInitialCreate.cs + 20180906135553_MSInitialCreate.Designer.cs + 20180906143029_MYInitialCreate.cs + 20180906143029_MYInitialCreate.Designer.cs + DesignTimeDbContextFactoryHelpers.cs + MySqlDatabaseContextModelSnapshot.cs + MySqlDesignTimeDbContextFactory.cs + SqlServerDatabaseContextModelSnapshot.cs + SqlServerDesignTimeDbContextFactory.cs  ChatBot.cs  ChatChannel.cs  CompileJob.cs @@ -372,15 +377,14 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');  RepositorySettings.cs  RevInfoTestMerge.cs  RevisionInformation.cs - SqliteDatabaseContext.cs - SqlServerDatabaseContext.cs - TestMerge.cs - User.cs - WatchdogReattachInformation.cs - WatchdogReattachInformationBase.cs + SqlServerDatabaseContext.cs + TestMerge.cs + User.cs + WatchdogReattachInformation.cs + WatchdogReattachInformationBase.cs   obj -  Release -  netcoreapp2.0 +  Debug +  netcoreapp2.0  Tgstation.Server.Host.AssemblyInfo.cs  Tgstation.Server.Host.RazorAssemblyInfo.cs   Security @@ -409,8 +413,8 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');  ServerFactory.cs   Tgstation.Server.Host.Console   obj -  Release -  netcoreapp2.0 +  Debug +  netcoreapp2.0  Tgstation.Server.Host.Console.AssemblyInfo.cs  AssemblyInfo.cs  Program.cs @@ -422,8 +426,8 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');  ServerService.cs   Tgstation.Server.Host.Watchdog   obj -  Release -  netstandard2.0 +  Debug +  netstandard2.0  Tgstation.Server.Host.Watchdog.AssemblyInfo.cs  AssemblyInfo.cs  IWatchdog.cs @@ -433,8 +437,8 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');   tests   Tgstation.Server.Api.Tests   obj -  Release -  netcoreapp2.0 +  Debug +  netcoreapp2.0  Tgstation.Server.Api.Tests.AssemblyInfo.cs  Tgstation.Server.Api.Tests.Program.cs   Rights @@ -442,15 +446,15 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');  TestApiHeaders.cs   Tgstation.Server.Client.Tests   obj -  Release -  netcoreapp2.0 +  Debug +  netcoreapp2.0  Tgstation.Server.Client.Tests.AssemblyInfo.cs  Tgstation.Server.Client.Tests.Program.cs  TestServerClientFactory.cs   Tgstation.Server.Host.Console.Tests   obj -  Release -  netcoreapp2.0 +  Debug +  netcoreapp2.0  Tgstation.Server.Host.Console.Tests.AssemblyInfo.cs  Tgstation.Server.Host.Console.Tests.Program.cs  TestProgram.cs @@ -459,32 +463,34 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');  AssemblyInfo.cs  TestServerService.cs   Tgstation.Server.Host.Tests -  Core - TestApplication.cs -  obj -  Release -  netcoreapp2.0 - Tgstation.Server.Host.Tests.AssemblyInfo.cs - Tgstation.Server.Host.Tests.Program.cs -  Security - TestAuthenticationContext.cs -  Tgstation.Server.Host.Watchdog.Tests -  obj -  Release -  netcoreapp2.0 - Tgstation.Server.Host.Watchdog.Tests.AssemblyInfo.cs - Tgstation.Server.Host.Watchdog.Tests.Program.cs - TestWatchdog.cs - TestWatchdogFactory.cs -  Tgstation.Server.Tests -  obj -  Release -  netcoreapp2.0 - Tgstation.Server.Tests.AssemblyInfo.cs - Tgstation.Server.Tests.Program.cs - AdministrationTest.cs - IntegrationTest.cs - TestingServer.cs +  obj +  Debug +  netcoreapp2.0 + Tgstation.Server.Host.Tests.AssemblyInfo.cs + Tgstation.Server.Host.Tests.Program.cs +  Security + TestAuthenticationContext.cs +  Tgstation.Server.Host.Watchdog.Tests +  obj +  Debug +  netcoreapp2.0 + Tgstation.Server.Host.Watchdog.Tests.AssemblyInfo.cs + Tgstation.Server.Host.Watchdog.Tests.Program.cs + TestWatchdog.cs + TestWatchdogFactory.cs +  Tgstation.Server.Tests +  Instance + ConfigurationTest.cs + InstanceTest.cs +  obj +  Debug +  netcoreapp2.0 + Tgstation.Server.Tests.AssemblyInfo.cs + Tgstation.Server.Tests.Program.cs + AdministrationTest.cs + InstanceManagerTest.cs + IntegrationTest.cs + TestingServer.cs diff --git a/functions.html b/functions.html index 1f3fbf7afd..6470b77467 100644 --- a/functions.html +++ b/functions.html @@ -320,7 +320,6 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
  • ApplyUpdate() : Tgstation.Server.Host.Core.IServerControl -, Tgstation.Server.Host.Core.Tests.TestApplication , Tgstation.Server.Host.Server
  • assemblyName @@ -331,7 +330,7 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
  • AuthenticationContext : Tgstation.Server.Host.Controllers.ApiController -, Tgstation.Server.Host.Security.AuthenticationContext +, Tgstation.Server.Host.Security.AuthenticationContext
  • AuthenticationContextFactory() : Tgstation.Server.Host.Security.AuthenticationContextFactory diff --git a/functions_b.html b/functions_b.html index 110cde57f5..ffee69f4c0 100644 --- a/functions_b.html +++ b/functions_b.html @@ -155,6 +155,14 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
  • bravoServer : Tgstation.Server.Host.Components.Watchdog.Watchdog
  • +
  • BuildModel() +: Tgstation.Server.Host.Models.Migrations.MySqlDatabaseContextModelSnapshot +, Tgstation.Server.Host.Models.Migrations.SqlServerDatabaseContextModelSnapshot +
  • +
  • BuildTargetModel() +: Tgstation.Server.Host.Models.Migrations.MSInitialCreate +, Tgstation.Server.Host.Models.Migrations.MYInitialCreate +
  • builtinCommands : Tgstation.Server.Host.Components.Chat.Chat
  • diff --git a/functions_c.html b/functions_c.html index 0eeadd6389..273552f522 100644 --- a/functions_c.html +++ b/functions_c.html @@ -142,8 +142,8 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search'); , Tgstation.Server.Host.Core.JobHandler
  • CancelJob() -: Tgstation.Server.Host.Core.IJobManager -, Tgstation.Server.Host.Core.JobManager +: Tgstation.Server.Host.Core.IJobManager +, Tgstation.Server.Host.Core.JobManager
  • cancellationToken : Tgstation.Server.Host.Components.Interop.CommContext @@ -451,8 +451,11 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
  • ConfigurationClient() : Tgstation.Server.Client.Components.ConfigurationClient
  • +
  • configurationClient +: Tgstation.Server.Tests.Instance.ConfigurationTest +
  • ConfigurationController() -: Tgstation.Server.Host.Controllers.ConfigurationController +: Tgstation.Server.Host.Controllers.ConfigurationController
  • ConfigurationFile() : Tgstation.Server.Client.Components.ConfigurationClient @@ -460,6 +463,9 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
  • ConfigurationRights : Tgstation.Server.Api.Models.InstanceUser
  • +
  • ConfigurationTest() +: Tgstation.Server.Tests.Instance.ConfigurationTest +
  • ConfigurationType : Tgstation.Server.Api.Models.Instance
  • @@ -492,7 +498,6 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
  • ConnectionString : Tgstation.Server.Api.Models.Internal.ChatBot , Tgstation.Server.Host.Configuration.DatabaseConfiguration -, Tgstation.Server.Host.Models.DatabaseContext< TParentContext >
  • connectionsUpdated : Tgstation.Server.Host.Components.Chat.Chat @@ -523,13 +528,14 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search'); : Tgstation.Server.Host.Components.Repository.IRepository , Tgstation.Server.Host.Components.Repository.Repository
  • +
  • counter +: Tgstation.Server.Tests.InstanceManagerTest +
  • Create() : Tgstation.Server.Client.Components.ChatBotsClient , Tgstation.Server.Client.Components.IChatBotsClient , Tgstation.Server.Client.Components.IInstanceUserClient , Tgstation.Server.Client.Components.InstanceUserClient -, Tgstation.Server.Client.IInstanceManagerClient -, Tgstation.Server.Client.InstanceManagerClient , Tgstation.Server.Client.IUsersClient , Tgstation.Server.Client.UsersClient , Tgstation.Server.Host.Controllers.ChatController @@ -545,10 +551,10 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
  • Create< TBody, TResult >() : Tgstation.Server.Client.ApiClient -, Tgstation.Server.Client.IApiClient +, Tgstation.Server.Client.IApiClient
  • Create< TResult >() -: Tgstation.Server.Client.ApiClient +: Tgstation.Server.Client.ApiClient , Tgstation.Server.Client.IApiClient
  • CreateApiClient() @@ -577,7 +583,6 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
  • CreateDbContext() : Tgstation.Server.Host.Models.Migrations.MySqlDesignTimeDbContextFactory -, Tgstation.Server.Host.Models.Migrations.SqliteDesignTimeDbContextFactory , Tgstation.Server.Host.Models.Migrations.SqlServerDesignTimeDbContextFactory
  • CreatedBy @@ -599,6 +604,10 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search'); : Tgstation.Server.Host.Components.IInstanceFactory , Tgstation.Server.Host.Components.InstanceFactory
  • +
  • CreateOrAttach() +: Tgstation.Server.Client.IInstanceManagerClient +, Tgstation.Server.Client.InstanceManagerClient +
  • CreateProvider() : Tgstation.Server.Host.Components.Chat.IProviderFactory , Tgstation.Server.Host.Components.Chat.ProviderFactory @@ -609,7 +618,7 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
  • CreateServerClient() : Tgstation.Server.Client.IServerClientFactory -, Tgstation.Server.Client.ServerClientFactory +, Tgstation.Server.Client.ServerClientFactory
  • CreateSymbolicLink() : Tgstation.Server.Host.IO.ISymlinkFactory @@ -625,6 +634,9 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search'); , Tgstation.Server.Host.Security.PosixSystemIdentityFactory , Tgstation.Server.Host.Security.WindowsSystemIdentityFactory
  • +
  • CreateTestInstance() +: Tgstation.Server.Tests.InstanceManagerTest +
  • CreateToken() : Tgstation.Server.Host.Controllers.HomeController , Tgstation.Server.Host.Security.ITokenFactory diff --git a/functions_d.html b/functions_d.html index bdc3426189..3b29e02710 100644 --- a/functions_d.html +++ b/functions_d.html @@ -126,8 +126,8 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
    Here is a list of all class members with links to the classes they belong to:

    - d -

    diff --git a/functions_f.html b/functions_f.html index 3c47b1f893..191aa496e0 100644 --- a/functions_f.html +++ b/functions_f.html @@ -136,12 +136,13 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
  • FileExists() : Tgstation.Server.Host.IO.DefaultIOManager , Tgstation.Server.Host.IO.IIOManager +, Tgstation.Server.Tests.Instance.ConfigurationTest
  • fileSystemWatcher : Tgstation.Server.Host.Components.Interop.CommContext
  • ForbidDueToModeConflicts() -: Tgstation.Server.Host.Controllers.ConfigurationController +: Tgstation.Server.Host.Controllers.ConfigurationController
  • FriendlyName : Tgstation.Server.Host.Components.Chat.Channel diff --git a/functions_func.html b/functions_func.html index 2dc668ee4a..af456e9ff5 100644 --- a/functions_func.html +++ b/functions_func.html @@ -167,14 +167,13 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
  • ApplyUpdate() : Tgstation.Server.Host.Core.IServerControl -, Tgstation.Server.Host.Core.Tests.TestApplication , Tgstation.Server.Host.Server
  • AttachExitHandler() : Tgstation.Server.Host.Core.ProcessExecutor
  • AuthenticationContext() -: Tgstation.Server.Host.Security.AuthenticationContext +: Tgstation.Server.Host.Security.AuthenticationContext
  • AuthenticationContextFactory() : Tgstation.Server.Host.Security.AuthenticationContextFactory diff --git a/functions_func_b.html b/functions_func_b.html index 3d5761fa9e..14c321037f 100644 --- a/functions_func_b.html +++ b/functions_func_b.html @@ -125,6 +125,14 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');  

    - b -

      +
    • BuildModel() +: Tgstation.Server.Host.Models.Migrations.MySqlDatabaseContextModelSnapshot +, Tgstation.Server.Host.Models.Migrations.SqlServerDatabaseContextModelSnapshot +
    • +
    • BuildTargetModel() +: Tgstation.Server.Host.Models.Migrations.MSInitialCreate +, Tgstation.Server.Host.Models.Migrations.MYInitialCreate +
    • Byond() : Tgstation.Server.Client.Components.ByondClient
    • diff --git a/functions_func_c.html b/functions_func_c.html index f01c7c1983..18e86a4cfb 100644 --- a/functions_func_c.html +++ b/functions_func_c.html @@ -135,8 +135,8 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search'); , Tgstation.Server.Host.Core.JobHandler
    • CancelJob() -: Tgstation.Server.Host.Core.IJobManager -, Tgstation.Server.Host.Core.JobManager +: Tgstation.Server.Host.Core.IJobManager +, Tgstation.Server.Host.Core.JobManager
    • CanConvert() : Tgstation.Server.Host.Components.Chat.Commands.BoolConverter @@ -252,11 +252,14 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search'); : Tgstation.Server.Client.Components.ConfigurationClient
    • ConfigurationController() -: Tgstation.Server.Host.Controllers.ConfigurationController +: Tgstation.Server.Host.Controllers.ConfigurationController
    • ConfigurationFile() : Tgstation.Server.Client.Components.ConfigurationClient
    • +
    • ConfigurationTest() +: Tgstation.Server.Tests.Instance.ConfigurationTest +
    • Configure() : Tgstation.Server.Host.Core.Application
    • @@ -303,8 +306,6 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search'); , Tgstation.Server.Client.Components.IChatBotsClient , Tgstation.Server.Client.Components.IInstanceUserClient , Tgstation.Server.Client.Components.InstanceUserClient -, Tgstation.Server.Client.IInstanceManagerClient -, Tgstation.Server.Client.InstanceManagerClient , Tgstation.Server.Client.IUsersClient , Tgstation.Server.Client.UsersClient , Tgstation.Server.Host.Controllers.ChatController @@ -320,7 +321,7 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
    • Create< TBody, TResult >() : Tgstation.Server.Client.ApiClient -, Tgstation.Server.Client.IApiClient +, Tgstation.Server.Client.IApiClient
    • Create< TResult >() : Tgstation.Server.Client.ApiClient @@ -344,12 +345,11 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
    • CreateClient() : Tgstation.Server.Client.IInstanceManagerClient , Tgstation.Server.Client.InstanceManagerClient -, Tgstation.Server.Host.Core.GitHubClientFactory -, Tgstation.Server.Host.Core.IGitHubClientFactory +, Tgstation.Server.Host.Core.GitHubClientFactory +, Tgstation.Server.Host.Core.IGitHubClientFactory
    • CreateDbContext() : Tgstation.Server.Host.Models.Migrations.MySqlDesignTimeDbContextFactory -, Tgstation.Server.Host.Models.Migrations.SqliteDesignTimeDbContextFactory , Tgstation.Server.Host.Models.Migrations.SqlServerDesignTimeDbContextFactory
    • CreateDirectory() @@ -364,6 +364,10 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search'); : Tgstation.Server.Host.Components.IInstanceFactory , Tgstation.Server.Host.Components.InstanceFactory
    • +
    • CreateOrAttach() +: Tgstation.Server.Client.IInstanceManagerClient +, Tgstation.Server.Client.InstanceManagerClient +
    • CreateProvider() : Tgstation.Server.Host.Components.Chat.IProviderFactory , Tgstation.Server.Host.Components.Chat.ProviderFactory @@ -386,9 +390,12 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search'); : Tgstation.Server.Host.IO.DefaultIOManager
    • CreateSystemIdentity() -: Tgstation.Server.Host.Security.ISystemIdentityFactory +: Tgstation.Server.Host.Security.ISystemIdentityFactory , Tgstation.Server.Host.Security.PosixSystemIdentityFactory -, Tgstation.Server.Host.Security.WindowsSystemIdentityFactory +, Tgstation.Server.Host.Security.WindowsSystemIdentityFactory +
    • +
    • CreateTestInstance() +: Tgstation.Server.Tests.InstanceManagerTest
    • CreateToken() : Tgstation.Server.Host.Controllers.HomeController diff --git a/functions_func_d.html b/functions_func_d.html index c586b8924a..a59a3a9479 100644 --- a/functions_func_d.html +++ b/functions_func_d.html @@ -143,10 +143,9 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search'); , Tgstation.Server.Client.Components.IRepositoryClient , Tgstation.Server.Client.Components.RepositoryClient , Tgstation.Server.Client.IApiClient -, Tgstation.Server.Client.IInstanceManagerClient -, Tgstation.Server.Client.InstanceManagerClient , Tgstation.Server.Host.Controllers.AdministrationController , Tgstation.Server.Host.Controllers.ChatController +, Tgstation.Server.Host.Controllers.ConfigurationController , Tgstation.Server.Host.Controllers.DreamDaemonController , Tgstation.Server.Host.Controllers.InstanceController , Tgstation.Server.Host.Controllers.InstanceUserController @@ -154,6 +153,10 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search'); , Tgstation.Server.Host.Controllers.ModelController< TModel > , Tgstation.Server.Host.Controllers.RepositoryController
    • +
    • Delete< TBody >() +: Tgstation.Server.Client.ApiClient +, Tgstation.Server.Client.IApiClient +
    • Delete< TResult >() : Tgstation.Server.Client.ApiClient , Tgstation.Server.Client.IApiClient @@ -163,8 +166,16 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search'); , Tgstation.Server.Host.Components.Chat.IChat
    • DeleteDirectory() -: Tgstation.Server.Host.IO.DefaultIOManager +: Tgstation.Server.Host.Components.StaticFiles.Configuration +, Tgstation.Server.Host.Components.StaticFiles.IConfiguration +, Tgstation.Server.Host.IO.DefaultIOManager , Tgstation.Server.Host.IO.IIOManager +, Tgstation.Server.Host.IO.ISynchronousIOManager +, Tgstation.Server.Host.IO.SynchronousIOManager +
    • +
    • DeleteEmptyDirectory() +: Tgstation.Server.Client.Components.ConfigurationClient +, Tgstation.Server.Client.Components.IConfigurationClient
    • DeleteFile() : Tgstation.Server.Host.IO.DefaultIOManager @@ -174,6 +185,10 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search'); : Tgstation.Server.Host.Components.Repository.IRepositoryManager , Tgstation.Server.Host.Components.Repository.RepositoryManager
    • +
    • Detach() +: Tgstation.Server.Client.IInstanceManagerClient +, Tgstation.Server.Client.InstanceManagerClient +
    • Directory() : Tgstation.Server.Host.Controllers.ConfigurationController
    • @@ -210,7 +225,7 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search'); , Tgstation.Server.Host.Components.Repository.Repository , Tgstation.Server.Host.Components.Repository.RepositoryManager , Tgstation.Server.Host.Components.StaticFiles.Configuration -, Tgstation.Server.Host.Components.Watchdog.SessionController +, Tgstation.Server.Host.Components.Watchdog.SessionController , Tgstation.Server.Host.Components.Watchdog.Watchdog , Tgstation.Server.Host.Core.JobHandler , Tgstation.Server.Host.Core.JobManager @@ -237,6 +252,10 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search'); : Tgstation.Server.Host.Components.Byond.ByondExecutableLock , Tgstation.Server.Host.Components.Byond.IByondExecutableLock +
    • Down() +: Tgstation.Server.Host.Models.Migrations.MSInitialCreate +, Tgstation.Server.Host.Models.Migrations.MYInitialCreate +
    • DownloadFile() : Tgstation.Server.Host.IO.DefaultIOManager , Tgstation.Server.Host.IO.IIOManager diff --git a/functions_func_f.html b/functions_func_f.html index 5010c775f5..876ad0f47a 100644 --- a/functions_func_f.html +++ b/functions_func_f.html @@ -135,9 +135,10 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
    • FileExists() : Tgstation.Server.Host.IO.DefaultIOManager , Tgstation.Server.Host.IO.IIOManager +, Tgstation.Server.Tests.Instance.ConfigurationTest
    • ForbidDueToModeConflicts() -: Tgstation.Server.Host.Controllers.ConfigurationController +: Tgstation.Server.Host.Controllers.ConfigurationController
    • FromCompileJob() : Tgstation.Server.Host.Components.Compiler.DmbFactory diff --git a/functions_func_i.html b/functions_func_i.html index b528ec1ed6..030415fafb 100644 --- a/functions_func_i.html +++ b/functions_func_i.html @@ -165,6 +165,12 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
    • InstanceManagerClient() : Tgstation.Server.Client.InstanceManagerClient
    • +
    • InstanceManagerTest() +: Tgstation.Server.Tests.InstanceManagerTest +
    • +
    • InstanceTest() +: Tgstation.Server.Tests.Instance.InstanceTest +
    • InstanceUser() : Tgstation.Server.Client.Components.InstanceUserClient
    • diff --git a/functions_func_o.html b/functions_func_o.html index e29c097d91..8da29cd205 100644 --- a/functions_func_o.html +++ b/functions_func_o.html @@ -126,15 +126,14 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');

      - o -

      diff --git a/functions_s.html b/functions_s.html index 912f05dedc..adeed1712f 100644 --- a/functions_s.html +++ b/functions_s.html @@ -126,6 +126,9 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
      Here is a list of all class members with links to the classes they belong to:

      - s -

      diff --git a/functions_vars_t.html b/functions_vars_t.html index c694dec853..e2cc68141a 100644 --- a/functions_vars_t.html +++ b/functions_vars_t.html @@ -128,6 +128,9 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search'); : Tgstation.Server.Host.Core.JobHandler , Tgstation.Server.Host.Security.IdentityCacheObject +
    • testRootPath +: Tgstation.Server.Tests.InstanceManagerTest +
    • TimeoutSeconds : Tgstation.Server.Host.Components.Chat.Providers.IrcProvider
    • diff --git a/hierarchy.html b/hierarchy.html index c8509c2852..7fdeffe3d8 100644 --- a/hierarchy.html +++ b/hierarchy.html @@ -113,16 +113,15 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');  CTgstation.Server.Api.Models.CompileJob  CTgstation.Server.Host.Models.CompileJob  CTgstation.Server.Api.Models.ConfigurationFileRepresents a game configuration file. Create and delete actions uncerimonuously overwrite/delete files - CTgstation.Server.Host.Components.Interop.Constants - CController - CTgstation.Server.Host.Controllers.ApiControllerA Controller for API functions - CTgstation.Server.Host.Controllers.HomeControllerMain ApiController for the Application - CTgstation.Server.Host.Controllers.ModelController< TModel >An ApiController representing a TModel - CTgstation.Server.Host.Configuration.DatabaseConfigurationConfiguration options for the Models.DatabaseContext<TParentContext> - CTgstation.Server.Host.Models.DatabaseContext< MySqlDatabaseContext > - CTgstation.Server.Host.Models.MySqlDatabaseContextDatabaseContext<TParentContext> for MySQL - CTgstation.Server.Host.Models.DatabaseContext< SqliteDatabaseContext > - CTgstation.Server.Host.Models.SqliteDatabaseContextDatabaseContext<TParentContext> for Sqlite + CTgstation.Server.Tests.Instance.ConfigurationTest + CTgstation.Server.Host.Components.Interop.Constants + CController + CTgstation.Server.Host.Controllers.ApiControllerA Controller for API functions + CTgstation.Server.Host.Controllers.HomeControllerMain ApiController for the Application + CTgstation.Server.Host.Controllers.ModelController< TModel >An ApiController representing a TModel + CTgstation.Server.Host.Configuration.DatabaseConfigurationConfiguration options for the Models.DatabaseContext<TParentContext> + CTgstation.Server.Host.Models.DatabaseContext< MySqlDatabaseContext > + CTgstation.Server.Host.Models.MySqlDatabaseContextDatabaseContext<TParentContext> for MySQL  CTgstation.Server.Host.Models.DatabaseContext< SqlServerDatabaseContext >  CTgstation.Server.Host.Models.SqlServerDatabaseContextDatabaseContext<TParentContext> for Sqlserver  CDbContext @@ -193,8 +192,7 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');  CTgstation.Server.Host.Models.DatabaseSeeder  CIDesignTimeDbContextFactory  CTgstation.Server.Host.Models.Migrations.MySqlDesignTimeDbContextFactory - CTgstation.Server.Host.Models.Migrations.SqliteDesignTimeDbContextFactory - CTgstation.Server.Host.Models.Migrations.SqlServerDesignTimeDbContextFactory + CTgstation.Server.Host.Models.Migrations.SqlServerDesignTimeDbContextFactory  CIDisposable  CTgstation.Server.Client.IApiClientWeb interface for the API  CTgstation.Server.Client.ApiClient @@ -293,126 +291,133 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');  CTgstation.Server.Client.Components.JobsClient  CTgstation.Server.Api.Models.InstanceMetadata about a server instance  CTgstation.Server.Host.Models.InstanceRepresents an Api.Models.Instance in the database - CTgstation.Server.Api.Models.InstanceUserRepresents a Users permissions in an Instance - CTgstation.Server.Host.Models.InstanceUser - CTgstation.Server.Tests.IntegrationTest - CTgstation.Server.Host.Components.StaticFiles.IPostWriteHandler - CTgstation.Server.Host.Components.StaticFiles.PosixPostWriteHandlerIPostWriteHandler for POSIX systems - CTgstation.Server.Host.Components.StaticFiles.WindowsPostWriteHandlerIPostWriteHandler for Windows systems - CTgstation.Server.Host.Core.IProcessExecutorFor launching IProcess' - CTgstation.Server.Host.Core.ProcessExecutor - CTgstation.Server.Host.Components.Chat.IProviderFactoryFactory for IProviders - CTgstation.Server.Host.Components.Chat.ProviderFactory - CTgstation.Server.Host.Components.Watchdog.IReattachInfoHandlerHandles saving and loading WatchdogReattachInformation - CTgstation.Server.Host.Components.ReattachInfoHandler - CTgstation.Server.Client.Components.IRepositoryClientFor managing the Repository - CTgstation.Server.Client.Components.RepositoryClient - CTgstation.Server.Client.IRequestLoggerFor logging HTTP requests and responses - CTgstation.Server.Client.IServerClientFactoryFactory for creating IServerClients - CTgstation.Server.Client.ServerClientFactory - CTgstation.Server.Host.Core.IServerControlRepresents a service that may take an updated Host assembly and run it, stopping the current assembly in the process - CTgstation.Server.Host.Core.Tests.TestApplication - CTgstation.Server.Host.Server - CTgstation.Server.Host.IServerFactoryFor creating IServers - CTgstation.Server.Host.ServerFactory - CTgstation.Server.Host.Components.Watchdog.ISessionControllerFactoryFactory for ISessionControllers - CTgstation.Server.Host.Components.Watchdog.SessionControllerFactory - CTgstation.Server.Host.IO.ISymlinkFactoryFor creating filesystem symbolic links - CTgstation.Server.Host.IO.PosixSymlinkFactoryISymlinkFactory for posix systems - CTgstation.Server.Host.IO.WindowsSymlinkFactoryISymlinkFactory for windows systems - CTgstation.Server.Host.IO.ISynchronousIOManagerFor accessing the disk in a synchronous manner - CTgstation.Server.Host.IO.SynchronousIOManager - CTgstation.Server.Host.Security.ISystemIdentityFactoryFactory for ISystemIdentitys - CTgstation.Server.Host.Security.PosixSystemIdentityFactoryISystemIdentityFactory for posix systems - CTgstation.Server.Host.Security.WindowsSystemIdentityFactoryISystemIdentityFactory for windows systems. Uses long running tasks due to potential networked domains - CTgstation.Server.Host.Security.ITokenFactoryFor creating Tokens - CTgstation.Server.Host.Security.TokenFactory - CTgstation.Server.Client.IUsersClientFor managing Users - CTgstation.Server.Client.UsersClient - CTgstation.Server.Host.Watchdog.IWatchdogThe watchdog for a Host - CTgstation.Server.Host.Watchdog.Watchdog - CTgstation.Server.Host.Components.Watchdog.IWatchdogFactoryFor creating IWatchdogs - CTgstation.Server.Host.Components.Watchdog.WatchdogFactory - CTgstation.Server.Host.Watchdog.IWatchdogFactoryFactory for creating IWatchdogs - CTgstation.Server.Host.Watchdog.WatchdogFactory - CTgstation.Server.Api.Models.Internal.JobRepresents a long running job - CTgstation.Server.Api.Models.JobRepresents a long running job on the server. Model is read-only, updates attempt to cancel the job - CTgstation.Server.Host.Models.Job - CJsonConverter - CTgstation.Server.Host.Components.Chat.Commands.BoolConverterJsonConverter for decoding bools returned by BYOND - CTgstation.Server.Host.Components.Interop.JsonFileRepresentation of the initial json passed to DreamDaemon - CTgstation.Server.Host.Components.Interop.JsonSubFileListInformation used in for reattaching and interop - CTgstation.Server.Host.Models.ReattachInformationBaseBase class for ReattachInformation - CTgstation.Server.Host.Components.Watchdog.ReattachInformationParameters necessary for duplicating a ISessionController session - CTgstation.Server.Host.Models.ReattachInformationDatabase representation of Components.Watchdog.ReattachInformation - CTgstation.Server.Host.Components.Watchdog.LaunchResultRepresents the result of trying to start a DD process - CTgstation.Server.Host.Components.Chat.Providers.MessageRepresents a message recieved by a IProvider - CTgstation.Server.Host.Controllers.ModelController< Administration > - CTgstation.Server.Host.Controllers.AdministrationControllerModelController<TModel> for Administration - CTgstation.Server.Host.Controllers.ModelController< Api.Models.Byond > - CTgstation.Server.Host.Controllers.ByondControllerController for managing Api.Models.Byond.Versions - CTgstation.Server.Host.Controllers.ModelController< Api.Models.ChatBot > - CTgstation.Server.Host.Controllers.ChatControllerModelController<TModel> for managing Api.Models.ChatBots - CTgstation.Server.Host.Controllers.ModelController< Api.Models.DreamMaker > - CTgstation.Server.Host.Controllers.DreamMakerControllerController for managing the compiler - CTgstation.Server.Host.Controllers.ModelController< Api.Models.Instance > - CTgstation.Server.Host.Controllers.InstanceControllerController for managing Components.Instances - CTgstation.Server.Host.Controllers.ModelController< Api.Models.InstanceUser > - CTgstation.Server.Host.Controllers.InstanceUserControllerFor managing Users - CTgstation.Server.Host.Controllers.ModelController< Api.Models.Job > - CTgstation.Server.Host.Controllers.JobControllerModelController<TModel> for Api.Models.Jobs - CTgstation.Server.Host.Controllers.ModelController< ConfigurationFile > - CTgstation.Server.Host.Controllers.ConfigurationControllerThe ModelController<TModel> for ConfigurationFiles - CTgstation.Server.Host.Controllers.ModelController< DreamDaemon > - CTgstation.Server.Host.Controllers.DreamDaemonControllerModelController<TModel> for managing the DreamDaemon - CTgstation.Server.Host.Controllers.ModelController< Repository > - CTgstation.Server.Host.Controllers.RepositoryControllerController for managing the Repositorys - CTgstation.Server.Host.Controllers.ModelController< UserUpdate > - CTgstation.Server.Host.Controllers.UserControllerFor managing Users - CTgstation.Server.Host.Components.Watchdog.MonitorStateThe (absolute) state of the Watchdog - CTgstation.Server.Host.NativeMethodsNative methods used by the code - CTgstation.Server.Host.Service.ProgramContains the entrypoint for the application - CTgstation.Server.Host.ProgramEntrypoint for the Process - CTgstation.Server.Host.Console.ProgramContains the entrypoint for the application - CTgstation.Server.Api.Models.Internal.RepositorySettingsRepresents configurable settings for a Repository - CTgstation.Server.Api.Models.RepositoryRepresents a git repository - CTgstation.Server.Host.Models.RepositorySettings - CTgstation.Server.Host.Components.Chat.ResponseRepresents a chat message requested by DD - CTgstation.Server.Host.Models.RevInfoTestMergeMany to many relationship for Models.RevisionInformation and Models.TestMerge - CTgstation.Server.Api.Models.Internal.RevisionInformationRepresents information about a current git revison - CTgstation.Server.Api.Models.RevisionInformation - CTgstation.Server.Host.Models.RevisionInformation - CTgstation.Server.Api.Rights.RightsHelperHelper for RightsTypes - CTgstation.Server.Api.RoutesRoutes to a server actions - CTgstation.Server.Api.Models.ServerInformationRepresents basic server information - CTgstation.Server.Host.Components.StaticFiles.ServerSideModificationsRepresents code modifications via configuration - CServiceBase - CTgstation.Server.Host.Service.ServerServiceRepresents a IWatchdog as a ServiceBase - CTgstation.Server.Api.Tests.TestApiHeadersTests for ApiHeaders - CTgstation.Server.Host.Security.Tests.TestAuthenticationContextTests for AuthenticationContext - CTgstation.Server.Api.Models.TestMergeParametersParameters for creating a TestMerge - CTgstation.Server.Api.Models.Internal.TestMergeBaseLayer of test merge data required internally - CTgstation.Server.Api.Models.Internal.TestMergeRepresents a merge of a GitHub pull request - CTgstation.Server.Api.Models.TestMerge - CTgstation.Server.Host.Models.TestMerge - CTgstation.Server.Host.Components.Interop.TestMergeThis model mirrors /datum/tgs_revision_information/test_merge - CTgstation.Server.Host.Console.Tests.TestProgram - CTgstation.Server.Api.Rights.Tests.TestRightsTests for Rights - CTgstation.Server.Client.Tests.TestServerClientFactory - CTgstation.Server.Host.Service.Tests.TestServerServiceTests for ServerService - CTgstation.Server.Host.Watchdog.Tests.TestWatchdog - CTgstation.Server.Host.Watchdog.Tests.TestWatchdogFactoryTests for WatchdogFactory - CTgstation.Server.Api.Models.TokenRepresents a JWT returned by the API - CTgstation.Server.Host.Configuration.UpdatesConfigurationConfiguration for the automatic update system - CTgstation.Server.Host.Components.Chat.UserRepresents a tgs_chat_user datum - CTgstation.Server.Api.Models.Internal.UserRepresents a server User - CTgstation.Server.Api.Models.User - CTgstation.Server.Api.Models.UserUpdateFor editing a given User. Will never be returned by the API - CTgstation.Server.Host.Models.User - CTgstation.Server.Host.Components.Watchdog.WatchdogLaunchResultLaunch results for a IWatchdog - CTgstation.Server.Host.Models.WatchdogReattachInformationBaseBase class for WatchdogReattachInformation - CTgstation.Server.Host.Components.Watchdog.WatchdogReattachInformationReattach information for a IWatchdog - CTgstation.Server.Host.Models.WatchdogReattachInformationDatabase representation of Components.Watchdog.WatchdogReattachInformation + CTgstation.Server.Tests.InstanceManagerTest + CTgstation.Server.Tests.Instance.InstanceTest + CTgstation.Server.Api.Models.InstanceUserRepresents a Users permissions in an Instance + CTgstation.Server.Host.Models.InstanceUser + CTgstation.Server.Tests.IntegrationTest + CTgstation.Server.Host.Components.StaticFiles.IPostWriteHandler + CTgstation.Server.Host.Components.StaticFiles.PosixPostWriteHandlerIPostWriteHandler for POSIX systems + CTgstation.Server.Host.Components.StaticFiles.WindowsPostWriteHandlerIPostWriteHandler for Windows systems + CTgstation.Server.Host.Core.IProcessExecutorFor launching IProcess' + CTgstation.Server.Host.Core.ProcessExecutor + CTgstation.Server.Host.Components.Chat.IProviderFactoryFactory for IProviders + CTgstation.Server.Host.Components.Chat.ProviderFactory + CTgstation.Server.Host.Components.Watchdog.IReattachInfoHandlerHandles saving and loading WatchdogReattachInformation + CTgstation.Server.Host.Components.ReattachInfoHandler + CTgstation.Server.Client.Components.IRepositoryClientFor managing the Repository + CTgstation.Server.Client.Components.RepositoryClient + CTgstation.Server.Client.IRequestLoggerFor logging HTTP requests and responses + CTgstation.Server.Client.IServerClientFactoryFactory for creating IServerClients + CTgstation.Server.Client.ServerClientFactory + CTgstation.Server.Host.Core.IServerControlRepresents a service that may take an updated Host assembly and run it, stopping the current assembly in the process + CTgstation.Server.Host.Server + CTgstation.Server.Host.IServerFactoryFor creating IServers + CTgstation.Server.Host.ServerFactory + CTgstation.Server.Host.Components.Watchdog.ISessionControllerFactoryFactory for ISessionControllers + CTgstation.Server.Host.Components.Watchdog.SessionControllerFactory + CTgstation.Server.Host.IO.ISymlinkFactoryFor creating filesystem symbolic links + CTgstation.Server.Host.IO.PosixSymlinkFactoryISymlinkFactory for posix systems + CTgstation.Server.Host.IO.WindowsSymlinkFactoryISymlinkFactory for windows systems + CTgstation.Server.Host.IO.ISynchronousIOManagerFor accessing the disk in a synchronous manner + CTgstation.Server.Host.IO.SynchronousIOManager + CTgstation.Server.Host.Security.ISystemIdentityFactoryFactory for ISystemIdentitys + CTgstation.Server.Host.Security.PosixSystemIdentityFactoryISystemIdentityFactory for posix systems + CTgstation.Server.Host.Security.WindowsSystemIdentityFactoryISystemIdentityFactory for windows systems. Uses long running tasks due to potential networked domains + CTgstation.Server.Host.Security.ITokenFactoryFor creating Tokens + CTgstation.Server.Host.Security.TokenFactory + CTgstation.Server.Client.IUsersClientFor managing Users + CTgstation.Server.Client.UsersClient + CTgstation.Server.Host.Watchdog.IWatchdogThe watchdog for a Host + CTgstation.Server.Host.Watchdog.Watchdog + CTgstation.Server.Host.Watchdog.IWatchdogFactoryFactory for creating IWatchdogs + CTgstation.Server.Host.Watchdog.WatchdogFactory + CTgstation.Server.Host.Components.Watchdog.IWatchdogFactoryFor creating IWatchdogs + CTgstation.Server.Host.Components.Watchdog.WatchdogFactory + CTgstation.Server.Api.Models.Internal.JobRepresents a long running job + CTgstation.Server.Api.Models.JobRepresents a long running job on the server. Model is read-only, updates attempt to cancel the job + CTgstation.Server.Host.Models.Job + CJsonConverter + CTgstation.Server.Host.Components.Chat.Commands.BoolConverterJsonConverter for decoding bools returned by BYOND + CTgstation.Server.Host.Components.Interop.JsonFileRepresentation of the initial json passed to DreamDaemon + CTgstation.Server.Host.Components.Interop.JsonSubFileListInformation used in for reattaching and interop + CTgstation.Server.Host.Models.ReattachInformationBaseBase class for ReattachInformation + CTgstation.Server.Host.Components.Watchdog.ReattachInformationParameters necessary for duplicating a ISessionController session + CTgstation.Server.Host.Models.ReattachInformationDatabase representation of Components.Watchdog.ReattachInformation + CTgstation.Server.Host.Components.Watchdog.LaunchResultRepresents the result of trying to start a DD process + CTgstation.Server.Host.Components.Chat.Providers.MessageRepresents a message recieved by a IProvider + CMigration + CTgstation.Server.Host.Models.Migrations.MSInitialCreateThe initial database migration for MSSQL + CTgstation.Server.Host.Models.Migrations.MYInitialCreateThe initial database migration for MySQL/MariaDB + CTgstation.Server.Host.Controllers.ModelController< Administration > + CTgstation.Server.Host.Controllers.AdministrationControllerModelController<TModel> for Administration + CTgstation.Server.Host.Controllers.ModelController< Api.Models.Byond > + CTgstation.Server.Host.Controllers.ByondControllerController for managing Api.Models.Byond.Versions + CTgstation.Server.Host.Controllers.ModelController< Api.Models.ChatBot > + CTgstation.Server.Host.Controllers.ChatControllerModelController<TModel> for managing Api.Models.ChatBots + CTgstation.Server.Host.Controllers.ModelController< Api.Models.DreamMaker > + CTgstation.Server.Host.Controllers.DreamMakerControllerController for managing the compiler + CTgstation.Server.Host.Controllers.ModelController< Api.Models.Instance > + CTgstation.Server.Host.Controllers.InstanceControllerController for managing Components.Instances + CTgstation.Server.Host.Controllers.ModelController< Api.Models.InstanceUser > + CTgstation.Server.Host.Controllers.InstanceUserControllerFor managing Users + CTgstation.Server.Host.Controllers.ModelController< Api.Models.Job > + CTgstation.Server.Host.Controllers.JobControllerModelController<TModel> for Api.Models.Jobs + CTgstation.Server.Host.Controllers.ModelController< ConfigurationFile > + CTgstation.Server.Host.Controllers.ConfigurationControllerThe ModelController<TModel> for ConfigurationFiles + CTgstation.Server.Host.Controllers.ModelController< DreamDaemon > + CTgstation.Server.Host.Controllers.DreamDaemonControllerModelController<TModel> for managing the DreamDaemon + CTgstation.Server.Host.Controllers.ModelController< Repository > + CTgstation.Server.Host.Controllers.RepositoryControllerController for managing the Repositorys + CTgstation.Server.Host.Controllers.ModelController< UserUpdate > + CTgstation.Server.Host.Controllers.UserControllerFor managing Users + CModelSnapshot + CTgstation.Server.Host.Models.Migrations.MySqlDatabaseContextModelSnapshot + CTgstation.Server.Host.Models.Migrations.SqlServerDatabaseContextModelSnapshot + CTgstation.Server.Host.Components.Watchdog.MonitorStateThe (absolute) state of the Watchdog + CTgstation.Server.Host.NativeMethodsNative methods used by the code + CTgstation.Server.Host.Console.ProgramContains the entrypoint for the application + CTgstation.Server.Host.Service.ProgramContains the entrypoint for the application + CTgstation.Server.Host.ProgramEntrypoint for the Process + CTgstation.Server.Api.Models.Internal.RepositorySettingsRepresents configurable settings for a Repository + CTgstation.Server.Api.Models.RepositoryRepresents a git repository + CTgstation.Server.Host.Models.RepositorySettings + CTgstation.Server.Host.Components.Chat.ResponseRepresents a chat message requested by DD + CTgstation.Server.Host.Models.RevInfoTestMergeMany to many relationship for Models.RevisionInformation and Models.TestMerge + CTgstation.Server.Api.Models.Internal.RevisionInformationRepresents information about a current git revison + CTgstation.Server.Api.Models.RevisionInformation + CTgstation.Server.Host.Models.RevisionInformation + CTgstation.Server.Api.Rights.RightsHelperHelper for RightsTypes + CTgstation.Server.Api.RoutesRoutes to a server actions + CTgstation.Server.Api.Models.ServerInformationRepresents basic server information + CTgstation.Server.Host.Components.StaticFiles.ServerSideModificationsRepresents code modifications via configuration + CServiceBase + CTgstation.Server.Host.Service.ServerServiceRepresents a IWatchdog as a ServiceBase + CTgstation.Server.Api.Tests.TestApiHeadersTests for ApiHeaders + CTgstation.Server.Host.Security.Tests.TestAuthenticationContextTests for AuthenticationContext + CTgstation.Server.Api.Models.TestMergeParametersParameters for creating a TestMerge + CTgstation.Server.Api.Models.Internal.TestMergeBaseLayer of test merge data required internally + CTgstation.Server.Api.Models.Internal.TestMergeRepresents a merge of a GitHub pull request + CTgstation.Server.Api.Models.TestMerge + CTgstation.Server.Host.Models.TestMerge + CTgstation.Server.Host.Components.Interop.TestMergeThis model mirrors /datum/tgs_revision_information/test_merge + CTgstation.Server.Host.Console.Tests.TestProgram + CTgstation.Server.Api.Rights.Tests.TestRightsTests for Rights + CTgstation.Server.Client.Tests.TestServerClientFactory + CTgstation.Server.Host.Service.Tests.TestServerServiceTests for ServerService + CTgstation.Server.Host.Watchdog.Tests.TestWatchdog + CTgstation.Server.Host.Watchdog.Tests.TestWatchdogFactoryTests for WatchdogFactory + CTgstation.Server.Api.Models.TokenRepresents a JWT returned by the API + CTgstation.Server.Host.Configuration.UpdatesConfigurationConfiguration for the automatic update system + CTgstation.Server.Host.Components.Chat.UserRepresents a tgs_chat_user datum + CTgstation.Server.Api.Models.Internal.UserRepresents a server User + CTgstation.Server.Api.Models.User + CTgstation.Server.Api.Models.UserUpdateFor editing a given User. Will never be returned by the API + CTgstation.Server.Host.Models.User + CTgstation.Server.Host.Components.Watchdog.WatchdogLaunchResultLaunch results for a IWatchdog + CTgstation.Server.Host.Models.WatchdogReattachInformationBaseBase class for WatchdogReattachInformation + CTgstation.Server.Host.Components.Watchdog.WatchdogReattachInformationReattach information for a IWatchdog + CTgstation.Server.Host.Models.WatchdogReattachInformationDatabase representation of Components.Watchdog.WatchdogReattachInformation diff --git a/index.html b/index.html index 9391eb3bdb..14e333cd54 100644 --- a/index.html +++ b/index.html @@ -101,20 +101,22 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
    • If using the ServerService package, run Tgstation.Server.Host.Service.exe. It should prompt you to install the service. Click Yes and accept a potential UAC elevation prompt. You should now be able to control the service using the Windows service control commandlet.
    • Docker

      -

      tgstation-server supports running in a docker container on linux systems and is the preferred deployment method to avoid native dependency hell with libgit2. The official image repository is located at https://hub.docker.com/r/tgstation/server it can be build locally, however, by running docker build . -f build/Dockerfile in the repository root.

      +

      tgstation-server supports running in a docker container on linux systems and is the preferred deployment method to avoid native dependency hell with libgit2. The official image repository is located at https://hub.docker.com/r/tgstation/server it can be built locally, however, by running docker build . -f build/Dockerfile in the repository root.

      To create a container run docker create --restart=always -p <public port>:80 -v /path/to/your/appsettings.Production.json:/config_data -v path/to/your/log/folder:/tgs_logs tgstation/server with any additional options you desire (i.e. You'll have to expose more ports in order to actually host servers, add a volume to create instances on, and create a volume for the SQLite database if that is what you're using).

      Note that due to the nature of docker. If the container restarts, you will be sent back to the version of TGS you installed with the initial command. Server updates will have to be reapplied. For this reason it is NOT RECOMMENDED to use the live update feature with a docker host.

      Configuring

      -

      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:

        +

        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:

        +
        • General:LogFileDirectory: Override the default directory where server logs are stored. Default is C:/ProgramData/tgstation-server/logs on Windows, /usr/share/tgstation-server/logs otherwise
        • General:MinimumPasswordLength: Minimum password length requirement for database users
        • Logging:LogLevel:Default: 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.
        • -
        • Database:DatabaseType: Can be one of SqlServer, MySql, or Sqlite. Note that, at the time of this writing, there is a blocking bug with the MySQL DBAL provider which prevents its usage
        • -
        • Database:ConnectionString: Connection string for your database. For SQLite this should be in the form Data Source=/path/to/database.ext. Otherwise, click here for an SQL Server generator or see here for a MySQL guide.
        • +
        • Database:DatabaseType: Can be one of SqlServer, MariaDB, or MySql
        • +
        • 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 ignore at your own risk. A string in the form <major>.<minor>.<patch>
        • +
        • Database:ConnectionString: Connection string for your database. Click here for an SQL Server generator or see here for a MySQL guide.

        Database Configuration

        -

        If you are using an SQLite database just ensure the specified database file doesn't exist for first time setup and you may skip this section.

        -

        For dedicated SQL servers, the user created for the application will need the privilege to create databases for the first run. Once the initial set of migrations is run, the create right may be revoked.

        +

        If using MySQL, our provider library recommends you set 'utf8mb4' as your default charset disregard at your own risk.

        +

        The user created for the application will need the privilege to create databases on the first run. 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

        Starting

        For the Windows service version start the tgstation-server-4 service

        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

        diff --git a/interface_tgstation_1_1_server_1_1_client_1_1_components_1_1_i_configuration_client-members.html b/interface_tgstation_1_1_server_1_1_client_1_1_components_1_1_i_configuration_client-members.html index 20f6a962b9..01abb5e4e3 100644 --- a/interface_tgstation_1_1_server_1_1_client_1_1_components_1_1_i_configuration_client-members.html +++ b/interface_tgstation_1_1_server_1_1_client_1_1_components_1_1_i_configuration_client-members.html @@ -97,9 +97,10 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');

        This is the complete list of members for Tgstation.Server.Client.Components.IConfigurationClient, including all inherited members.

        - - - + + + +
        List(string directory, CancellationToken cancellationToken)Tgstation.Server.Client.Components.IConfigurationClient
        Read(ConfigurationFile file, CancellationToken cancellationToken)Tgstation.Server.Client.Components.IConfigurationClient
        Write(ConfigurationFile file, CancellationToken cancellationToken)Tgstation.Server.Client.Components.IConfigurationClient
        DeleteEmptyDirectory(ConfigurationFile directory, CancellationToken cancellationToken)Tgstation.Server.Client.Components.IConfigurationClient
        List(string directory, CancellationToken cancellationToken)Tgstation.Server.Client.Components.IConfigurationClient
        Read(ConfigurationFile file, CancellationToken cancellationToken)Tgstation.Server.Client.Components.IConfigurationClient
        Write(ConfigurationFile file, CancellationToken cancellationToken)Tgstation.Server.Client.Components.IConfigurationClient