diff --git a/exercise.wwwapi/.dockerignore b/exercise.wwwapi/.dockerignore new file mode 100644 index 0000000..6f6a8af --- /dev/null +++ b/exercise.wwwapi/.dockerignore @@ -0,0 +1,37 @@ +# Ignore build outputs +bin/ +obj/ + +# User secrets, local settings +*.user +*.swp +*.suo +*.userosscache +*.sln.docstates + +# IDE directories +.vs/ +.vscode/ +.idea/ + +# Logs and temp +*.log +**/logs/ +**/LogFiles/ + +# Dotnet artifacts +artifacts/ +TestResults/ +.nuget/ + +# Git +.git/ +.gitignore +.gitattributes + +# Misc +Dockerfile.* +**/*.db +**/*.sqlite +*.DS_Store +Thumbs.db diff --git a/exercise.wwwapi/Configuration/ConfigurationSettings.cs b/exercise.wwwapi/Configuration/ConfigurationSettings.cs index 54ba065..be43794 100644 --- a/exercise.wwwapi/Configuration/ConfigurationSettings.cs +++ b/exercise.wwwapi/Configuration/ConfigurationSettings.cs @@ -6,7 +6,11 @@ public class ConfigurationSettings : IConfigurationSettings public ConfigurationSettings() { - _configuration = new ConfigurationBuilder().AddJsonFile("appsettings.json").AddUserSecrets().Build(); + _configuration = new ConfigurationBuilder() + .AddJsonFile("appsettings.json", optional: true) + .AddUserSecrets(optional: true) + .AddEnvironmentVariables() + .Build(); } public string GetValue(string key) diff --git a/exercise.wwwapi/Data/DataContext.cs b/exercise.wwwapi/Data/DataContext.cs index 97c20cc..383219e 100644 --- a/exercise.wwwapi/Data/DataContext.cs +++ b/exercise.wwwapi/Data/DataContext.cs @@ -1,4 +1,5 @@ using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Diagnostics; namespace exercise.wwwapi.Data; @@ -9,6 +10,12 @@ public DataContext(DbContextOptions options) : base(options) Database.EnsureCreated(); } + protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) + { + optionsBuilder.ConfigureWarnings(w => w.Ignore(RelationalEventId.PendingModelChangesWarning)); + base.OnConfiguring(optionsBuilder); + } + protected override void OnModelCreating(ModelBuilder modelBuilder) { base.OnModelCreating(modelBuilder); diff --git a/exercise.wwwapi/Data/ModelSeeder.cs b/exercise.wwwapi/Data/ModelSeeder.cs index 3425e09..2900534 100644 --- a/exercise.wwwapi/Data/ModelSeeder.cs +++ b/exercise.wwwapi/Data/ModelSeeder.cs @@ -10,6 +10,7 @@ public class ModelSeeder { private static readonly DateTime _seedTime = new DateTime(2025, 01, 01, 0, 0, 0, DateTimeKind.Utc); + private readonly Random _random = new(123456); private static string[] _passwordHashes = [ "$2a$11$NlNrSkH2Uop6Nl90BHeF9udj/s5N79m9j94htBwtiwPMzoJ5EXozW", // Test1test1% @@ -177,7 +178,7 @@ public class ModelSeeder private List _modules = new List(); private List _courseModules = new List(); private List _userExercises = new List(); - + public void Seed(ModelBuilder modelBuilder) @@ -265,9 +266,8 @@ public void Seed(ModelBuilder modelBuilder) for (int i = 6; i < 50; i++) { - Random userRandom = new Random(); - var firstname = _firstnames[userRandom.Next(_firstnames.Count)]; - var lastname = _lastnames[userRandom.Next(_lastnames.Count)]; + var firstname = _firstnames[_random.Next(_firstnames.Count)]; + var lastname = _lastnames[_random.Next(_lastnames.Count)]; var username = $"{firstname}{lastname}{i}"; User user = new User() { @@ -275,10 +275,10 @@ public void Seed(ModelBuilder modelBuilder) FirstName = firstname, LastName = lastname, Username = username, - Email = $"{username}@{_domain[userRandom.Next(_domain.Count)]}", + Email = $"{username}@{_domain[_random.Next(_domain.Count)]}", PasswordHash = _passwordHashes[5], - Role = _roles[userRandom.Next(_roles.Count)], - Mobile = userRandom.Next(12345678, 23456789).ToString(), + Role = _roles[_random.Next(_roles.Count)], + Mobile = _random.Next(12345678, 23456789).ToString(), Github = $"{username}git", Bio = $"{_firstword[userRandom.Next(_firstword.Count)]}{_secondword[userRandom.Next(_secondword.Count)]}{_thirdword[userRandom.Next(_thirdword.Count)]}", PhotoUrl = "" @@ -329,12 +329,11 @@ public void Seed(ModelBuilder modelBuilder) for (int i = 6; i < 20; i++) { - Random postRandom = new Random(); Post p = new Post() { Id = i, - AuthorId = postRandom.Next(_users.Count), - Body = $"{_firstPart[postRandom.Next(_firstPart.Count)]} {_lastPart[postRandom.Next(_lastPart.Count)]}", + AuthorId = _random.Next(_users.Count), + Body = $"{_firstPart[_random.Next(_firstPart.Count)]} {_lastPart[_random.Next(_lastPart.Count)]}", CreatedAt = _seedTime }; _posts.Add(p); @@ -392,15 +391,14 @@ public void Seed(ModelBuilder modelBuilder) for (int i = 6; i < 50; i++) { - Random commentRandom = new Random(); - int postId = _posts[commentRandom.Next(_posts.Count)].Id; - int userId = _users[commentRandom.Next(_users.Count)].Id; + int postId = _posts[_random.Next(_posts.Count)].Id; + int userId = _users[_random.Next(_users.Count)].Id; Comment c = new Comment { Id = i, PostId = postId, UserId = userId, - Body = _commentText[commentRandom.Next(_commentText.Count)], + Body = _commentText[_random.Next(_commentText.Count)], CreatedAt = _seedTime, }; _comments.Add(c); @@ -528,9 +526,8 @@ public void Seed(ModelBuilder modelBuilder) for (int i = 4; i <= _users.Count; i++) { - Random userCCRandom = new Random(); var userId = i; - var ccId = _cohortCourses[userCCRandom.Next(_cohortCourses.Count)].Id; + var ccId = _cohortCourses[_random.Next(_cohortCourses.Count)].Id; UserCC ucc = new UserCC { @@ -761,13 +758,12 @@ public void Seed(ModelBuilder modelBuilder) var user = _users[i]; if (user.Role == Role.Student) { - Random noteRandom = new Random(); Note n = new Note { Id = noteId++, UserId = user.Id, Title = $"Note for {user.FirstName}", - Content = _commentText[noteRandom.Next(_commentText.Count)], + Content = _commentText[_random.Next(_commentText.Count)], CreatedAt = _seedTime, UpdatedAt = _seedTime }; diff --git a/exercise.wwwapi/Dockerfile b/exercise.wwwapi/Dockerfile new file mode 100644 index 0000000..72d3801 --- /dev/null +++ b/exercise.wwwapi/Dockerfile @@ -0,0 +1,22 @@ +# syntax=docker/dockerfile:1 +FROM mcr.microsoft.com/dotnet/sdk:9.0 AS build +WORKDIR /src +ARG BUILD_CONFIGURATION=Release +COPY ["exercise.wwwapi.csproj", "./"] +RUN dotnet restore "./exercise.wwwapi.csproj" +COPY . . +RUN dotnet publish "./exercise.wwwapi.csproj" \ + -c $BUILD_CONFIGURATION \ + -o /app/publish \ + --no-restore \ + /p:UseAppHost=false +FROM mcr.microsoft.com/dotnet/aspnet:9.0 AS final +WORKDIR /app +ENV ASPNETCORE_URLS=http://+:8080 +ENV ASPNETCORE_ENVIRONMENT=Production +ENV DOTNET_EnableDiagnostics=0 +EXPOSE 8080 +COPY --from=build --chown=app:app /app/publish . +USER app +ENTRYPOINT ["dotnet", "exercise.wwwapi.dll"] + diff --git a/exercise.wwwapi/Program.cs b/exercise.wwwapi/Program.cs index 6da481b..9337e93 100644 --- a/exercise.wwwapi/Program.cs +++ b/exercise.wwwapi/Program.cs @@ -75,23 +75,13 @@ } else { - var host = builder.Configuration["Neon:Host"]; - var database = builder.Configuration["Neon:Database"]; - var username = builder.Configuration["Neon:Username"]; - var password = builder.Configuration["Neon:Password"]; - const string defaultConnectionName = "DefaultConnection"; var connectionString = builder.Configuration.GetConnectionString(defaultConnectionName); - if (connectionString == null) + if (string.IsNullOrWhiteSpace(connectionString)) { throw new Exception("Could not find connection string with name: " + defaultConnectionName); } - connectionString = connectionString.Replace("${Neon:Host}", host); - connectionString = connectionString.Replace("${Neon:Database}", database); - connectionString = connectionString.Replace("${Neon:Username}", username); - connectionString = connectionString.Replace("${Neon:Password}", password); - options.UseNpgsql(connectionString); options.LogTo(message => Debug.WriteLine(message)); }