From 23c50de468995e4bedf0d1c8d62dd39bd2ad7150 Mon Sep 17 00:00:00 2001 From: Kun Deng Date: Sat, 15 Jun 2024 12:17:12 -0400 Subject: [PATCH] Net8 (#89) * .NET 8 migration * Fixed bugs with song management * More cleanup and making some constants * Updated yml * Clean up Removing comments, some cleanup, and moving the startup code into Program.cs * Removing comments * Fixed song deletion issue * Added functionality to delete song directories * Updated README --- .github/workflows/dotnet.yml | 2 +- Certs/Signing.cs | 13 +- Constants/DirectoryPaths.cs | 3 +- Constants/FileExtensions.cs | 13 ++ Controllers/Managers/CoverArtManager.cs | 1 + Controllers/Managers/SongManager.cs | 22 ++- Controllers/Managers/TokenManager.cs | 191 +++-------------------- Controllers/Utilities/SongCompression.cs | 2 +- Controllers/v1/BaseController.cs | 11 -- Controllers/v1/SongDataController.cs | 2 +- Controllers/v1/SongStreamController.cs | 7 +- Database/Contexts/SongContext.cs | 32 ---- Database/Contexts/UserContext.cs | 5 - Icarus.csproj | 22 +-- Models/Album.cs | 6 - Models/Artist.cs | 5 - Models/CoverArt.cs | 13 +- Models/Genre.cs | 5 - Models/Song.cs | 12 +- Program.cs | 134 +++++++++++----- README.md | 10 +- Startup.cs | 125 --------------- 22 files changed, 182 insertions(+), 454 deletions(-) create mode 100644 Constants/FileExtensions.cs delete mode 100644 Startup.cs diff --git a/.github/workflows/dotnet.yml b/.github/workflows/dotnet.yml index e5c95d6..f909f00 100644 --- a/.github/workflows/dotnet.yml +++ b/.github/workflows/dotnet.yml @@ -16,7 +16,7 @@ jobs: - name: Setup .NET uses: actions/setup-dotnet@v2 with: - dotnet-version: 6.0.x + dotnet-version: 8.0.x - name: Restore dependencies run: dotnet restore - name: Build diff --git a/Certs/Signing.cs b/Certs/Signing.cs index 80347fb..9185892 100644 --- a/Certs/Signing.cs +++ b/Certs/Signing.cs @@ -1,5 +1,4 @@ using System; -using System.Security.Cryptography; using Microsoft.IdentityModel.Tokens; using Org.BouncyCastle.Crypto.Parameters; @@ -10,11 +9,11 @@ namespace Icarus.Certs; public class SigningIssuerCertificate : IDisposable { - private readonly RSACryptoServiceProvider _rsa; + private readonly System.Security.Cryptography.RSACryptoServiceProvider _rsa; public SigningIssuerCertificate() { - _rsa = new RSACryptoServiceProvider(); + _rsa = new System.Security.Cryptography.RSACryptoServiceProvider(); } public RsaSecurityKey GetIssuerSigningKey(string publicKeyPath) @@ -26,7 +25,7 @@ public class SigningIssuerCertificate : IDisposable { var pem = new PemReader(reader); var o = (RsaKeyParameters)pem.ReadObject(); - var parameters = new RSAParameters(); + var parameters = new System.Security.Cryptography.RSAParameters(); parameters.Modulus = o.Modulus.ToByteArray(); parameters.Exponent = o.Exponent.ToByteArray(); _rsa.ImportParameters(parameters); @@ -45,11 +44,11 @@ public class SigningIssuerCertificate : IDisposable public class SigningAudienceCertificate : IDisposable { - private readonly RSACryptoServiceProvider _rsa; + private readonly System.Security.Cryptography.RSACryptoServiceProvider _rsa; public SigningAudienceCertificate() { - _rsa = new RSACryptoServiceProvider(); + _rsa = new System.Security.Cryptography.RSACryptoServiceProvider(); } public SigningCredentials GetAudienceSigningKey(string keyPath) @@ -61,7 +60,7 @@ public class SigningAudienceCertificate : IDisposable { var pem = new PemReader(reader); var o = (RsaKeyParameters)pem.ReadObject(); - var parameters = new RSAParameters(); + var parameters = new System.Security.Cryptography.RSAParameters(); parameters.Modulus = o.Modulus.ToByteArray(); parameters.Exponent = o.Exponent.ToByteArray(); _rsa.ImportParameters(parameters); diff --git a/Constants/DirectoryPaths.cs b/Constants/DirectoryPaths.cs index be323e5..8aab732 100644 --- a/Constants/DirectoryPaths.cs +++ b/Constants/DirectoryPaths.cs @@ -1,4 +1,3 @@ -using System; using System.IO; namespace Icarus.Constants; @@ -7,4 +6,6 @@ public class DirectoryPaths { public static string CoverArtPath => Directory.GetCurrentDirectory() + "/Images/Stock/CoverArt.png"; + public static string FILENAME_CHARACTERS = "ABCDEF0123456789"; + public static int FILENAME_LENGTH = 25; } diff --git a/Constants/FileExtensions.cs b/Constants/FileExtensions.cs new file mode 100644 index 0000000..281526e --- /dev/null +++ b/Constants/FileExtensions.cs @@ -0,0 +1,13 @@ +namespace Icarus.Constants; + +public class FileExtensions +{ + // Contains file extension with period at the beginning + public static string MP3_EXTENSION = ".mp3"; + + // Contains file extension with period at the beginning + public static string WAV_EXTENSION = ".wav"; + + // Contains file extension with period at the beginning + public static string JPG_EXTENSION = ".jpg"; +} diff --git a/Controllers/Managers/CoverArtManager.cs b/Controllers/Managers/CoverArtManager.cs index a4e2441..33e093d 100644 --- a/Controllers/Managers/CoverArtManager.cs +++ b/Controllers/Managers/CoverArtManager.cs @@ -47,6 +47,7 @@ public class CoverArtManager : BaseManager { _logger.Info("Attempting to delete cover art from the database"); + _coverArtContext.Attach(coverArt); _coverArtContext.Remove(coverArt); _coverArtContext.SaveChanges(); } diff --git a/Controllers/Managers/SongManager.cs b/Controllers/Managers/SongManager.cs index 34d4bfd..3fb3f52 100644 --- a/Controllers/Managers/SongManager.cs +++ b/Controllers/Managers/SongManager.cs @@ -145,7 +145,7 @@ public class SongManager : BaseManager { try { - if (DeleteSongFromFilesystem(song)) + if (DeleteSongFromFilesystem(song, true)) { _logger.Error("Failed to delete the song"); @@ -158,8 +158,8 @@ public class SongManager : BaseManager var coverArt = coverMgr.GetCoverArt(song); coverMgr.DeleteCoverArt(coverArt); - coverMgr.DeleteCoverArtFromDatabase(coverArt); DeleteSongFromDatabase(song); + coverMgr.DeleteCoverArtFromDatabase(coverArt); } catch (Exception ex) { @@ -241,13 +241,7 @@ public class SongManager : BaseManager } var coverMgr = new CoverArtManager(_config); - var meta = new MetadataRetriever(); var coverArt = coverMgr.SaveCoverArt(coverArtData, song); - meta.UpdateCoverArt(song, coverArt); - song.Duration = meta.RetrieveSongDuration(song.SongPath()); - - meta.UpdateMetadata(song, song); - DirectoryManager dirMgr = new DirectoryManager(_config, song); dirMgr.CreateDirectory(); @@ -406,7 +400,7 @@ public class SongManager : BaseManager } - private bool DeleteSongFromFilesystem(Song song) + private bool DeleteSongFromFilesystem(Song song, bool deleteDirectory = false) { var songPath = song.SongPath(); @@ -415,6 +409,8 @@ public class SongManager : BaseManager try { System.IO.File.Delete(songPath); + + DeleteEmptyDirectories(ref song, ref song); } catch(Exception ex) { @@ -425,6 +421,7 @@ public class SongManager : BaseManager return DoesSongExistOnFilesystem(song); } + private bool DoesSongExistOnFilesystem(Song song) { if (!System.IO.File.Exists(song.SongPath())) @@ -494,13 +491,12 @@ public class SongManager : BaseManager var albumMgr = new AlbumManager(_config); var artistMgr = new ArtistManager(_config); var genreMgr = new GenreManager(_config); - albumMgr.DeleteAlbumFromDatabase(song); - artistMgr.DeleteArtistFromDatabase(song); - genreMgr.DeleteGenreFromDatabase(song); - var sngContext = new SongContext(_connectionString); sngContext.Songs.Remove(song); sngContext.SaveChanges(); + artistMgr.DeleteArtistFromDatabase(song); + albumMgr.DeleteAlbumFromDatabase(song); + genreMgr.DeleteGenreFromDatabase(song); } #endregion } diff --git a/Controllers/Managers/TokenManager.cs b/Controllers/Managers/TokenManager.cs index 7d16f4a..ece55fc 100644 --- a/Controllers/Managers/TokenManager.cs +++ b/Controllers/Managers/TokenManager.cs @@ -2,20 +2,13 @@ using System; using System.Collections.Generic; using System.Linq; using System.Security.Claims; -using System.Security.Cryptography; using System.Threading.Tasks; using System.IdentityModel.Tokens.Jwt; using System.Text; -using JWT; -using JWT.Serializers; using Microsoft.Extensions.Configuration; using Microsoft.IdentityModel.Tokens; using Newtonsoft.Json; -using Org.BouncyCastle.Crypto; -using Org.BouncyCastle.Crypto.Parameters; -using Org.BouncyCastle.OpenSsl; -using Org.BouncyCastle.Security; using RestSharp; using Icarus.Models; @@ -87,35 +80,6 @@ public class TokenManager : BaseManager } - [Obsolete("Asymmetric key signing for tokens have been deprecated")] - public LoginResult LogIn(User user) - { - var tokenResult = new TokenTierOne(); - tokenResult.TokenType = "Jwt"; - - var privateKey = ReadKeyContent(_privateKeyPath).Result; - var publicKey = ReadKeyContent(_publicKeyPath).Result; - - var payload = Payload(); - - var token = CreateToken(payload, privateKey); - tokenResult.AccessToken = token; - - var expClaim = payload.FirstOrDefault(cl => - { - return cl.Type.Equals("exp"); - }); - - tokenResult.Expiration = System.Convert.ToInt32(expClaim.Value); - - return new LoginResult - { - UserID = user.UserID, Username = user.Username, Token = tokenResult.AccessToken, - TokenType = tokenResult.TokenType, Expiration = tokenResult.Expiration, - Message = "Successfully retrieved token" - }; - } - public LoginResult LoginSymmetric(User user) { var tokenResult = new TokenTierOne(); @@ -124,16 +88,25 @@ public class TokenManager : BaseManager var payload = Payload(); payload.Add(new System.Security.Claims.Claim("user_id", user.UserID.ToString(), ClaimValueTypes.Integer)); - var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_config["JWT:Secret"])); - var signIn = new SigningCredentials(key, SecurityAlgorithms.HmacSha256); - var token = new JwtSecurityToken( - _config["JWT:Issuer"], - _config["JWT:Audience"], - payload, - expires: DateTime.UtcNow.AddMinutes(30), - signingCredentials: signIn); + var tokenHandler = new JwtSecurityTokenHandler(); + var key = Encoding.ASCII.GetBytes(_config["JWT:Secret"]); + var tokenDescriptor = new SecurityTokenDescriptor + { + Subject = new ClaimsIdentity(new Claim[] + { + new Claim("user_id", user.UserID.ToString(), ClaimValueTypes.Integer) + // Add more claims as needed + }), + Expires = DateTime.UtcNow.AddHours(1), + SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature), + Issuer = _config["Jwt:Issuer"], // Add this line + Audience = _config["Jwt:Audience"] + }; + + var token = tokenHandler.CreateToken(tokenDescriptor); - tokenResult.AccessToken = new JwtSecurityTokenHandler().WriteToken(token); + + tokenResult.AccessToken = tokenHandler.WriteToken(token); var expClaim = payload.FirstOrDefault(cl => { @@ -152,59 +125,6 @@ public class TokenManager : BaseManager }; } - [Obsolete("Asymmetric key signing for tokens have been deprecated")] - public bool IsTokenValid(string scope, string accessToken) - { - var result = false; - var token = DecodeToken(accessToken); - - if (token == null || token.Erroneous()) - { - return result; - } - - result = (!token.TokenExpired() && token.ContainsScope(scope)) ? true : false; - - // What would make a token valid? - // 1. The expiration date must be before the current date - // 2. The desired scope must be part of the scopes within the access token - // 3. Must be able to be decoded - - return result; - } - - [Obsolete("Asymmetric key signing for tokens have been deprecated")] - public Token DecodeToken(string accessToken) - { - var rsaParams = GetRSAPublic(_publicKey); - Token tok = null; - - try - { - using (var rsa = new RSACryptoServiceProvider()) - { - rsa.ImportParameters(rsaParams); - - IJsonSerializer serializer = new JsonNetSerializer(); - IDateTimeProvider provider = new UtcDateTimeProvider(); - IJwtValidator validator = new JwtValidator(serializer, provider); - IBase64UrlEncoder urlEncoder = new JwtBase64UrlEncoder(); - var algorithm = new JWT.Algorithms.RS256Algorithm(rsa); - IJwtDecoder decoder = new JwtDecoder(serializer, validator, urlEncoder, algorithm); - - var json = decoder.Decode(accessToken); - tok = JsonConvert.DeserializeObject(json); - } - } - catch (Exception ex) - { - _logger.Error("An error occurred: {0}", ex.Message); - } - - - return tok; - } - private string AllScopes() { @@ -266,81 +186,6 @@ public class TokenManager : BaseManager return claim; } - [Obsolete("Asymmetric key signing for tokens have been deprecated")] - private string CreateToken(List claims, string privateKey) - { - var token = string.Empty; - - if (string.IsNullOrEmpty(privateKey)) - { - privateKey = ReadKeyContent(_privateKeyPath).Result; - } - - RSAParameters rsaParams; - using (var tr = new System.IO.StringReader(privateKey)) - { - var pemReader = new PemReader(tr); - var keyPair = pemReader.ReadObject() as AsymmetricCipherKeyPair; - if (keyPair == null) - { - throw new Exception("Could not read RSA private key"); - } - var privateRsaParams = keyPair.Private as RsaPrivateCrtKeyParameters; - rsaParams = DotNetUtilities.ToRSAParameters(privateRsaParams); - } - - using (RSACryptoServiceProvider rsa = new RSACryptoServiceProvider()) - { - var rsaParamsPublic = GetRSAPublic(ReadKeyContent(_publicKeyPath).Result); - var rsaPublic = new RSACryptoServiceProvider(); - - rsa.ImportParameters(rsaParams); - rsaPublic.ImportParameters(rsaParamsPublic); - - Dictionary payload = new Dictionary(); - - foreach (var claim in claims) - { - var type = claim.Type; - var val = Int32.TryParse(claim.Value, out _); - - if (val) - { - payload.Add(type, Convert.ToInt32(claim.Value)); - } - else - { - payload.Add(type, claim.Value); - } - - } - - var algorithm = new JWT.Algorithms.RS256Algorithm(rsaPublic, rsa); - IJsonSerializer serializer = new JsonNetSerializer(); - IBase64UrlEncoder urlEncoder = new JwtBase64UrlEncoder(); - IJwtEncoder encoder = new JwtEncoder(algorithm, serializer, urlEncoder); - - token = encoder.Encode(payload, privateKey); - } - - return token; - } - - [Obsolete("Asymmetric key signing for tokens have been deprecated")] - private RSAParameters GetRSAPublic(string publicKey) - { - using (var tr = new System.IO.StringReader(publicKey)) - { - var pemReader = new PemReader(tr); - var publicKeyParams = pemReader.ReadObject() as RsaKeyParameters; - if (publicKeyParams == null) - { - throw new Exception("Could not read RSA public key"); - } - return DotNetUtilities.ToRSAParameters(publicKeyParams); - } - } - private async Task ReadKeyContent(string filepath) { return await System.IO.File.ReadAllTextAsync(filepath); diff --git a/Controllers/Utilities/SongCompression.cs b/Controllers/Utilities/SongCompression.cs index 469c128..1b4dca4 100644 --- a/Controllers/Utilities/SongCompression.cs +++ b/Controllers/Utilities/SongCompression.cs @@ -83,7 +83,7 @@ public class SongCompression Console.WriteLine(exMsg); } - if (songDetails.Filename.Contains(".mp3")) + if (songDetails.Filename.Contains(Constants.FileExtensions.WAV_EXTENSION)) { _compressedSongFilename = StripMP3Extension(songDetails.Filename); } diff --git a/Controllers/v1/BaseController.cs b/Controllers/v1/BaseController.cs index 40765c7..093b470 100644 --- a/Controllers/v1/BaseController.cs +++ b/Controllers/v1/BaseController.cs @@ -4,8 +4,6 @@ using System.Linq; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Configuration; -using Icarus.Controllers.Managers; - namespace Icarus.Controllers.V1; @@ -39,14 +37,5 @@ public class BaseController : ControllerBase return token; } - [ApiExplorerSettings(IgnoreApi = true)] - [Obsolete("Asymmetric key signing for tokens have been deprecated")] - protected bool IsTokenValid(string scope) - { - var token = ParseBearerTokenFromHeader(); - var tokMgr = new TokenManager(_config); - - return tokMgr.IsTokenValid(scope, token); - } #endregion } diff --git a/Controllers/v1/SongDataController.cs b/Controllers/v1/SongDataController.cs index 6c24b94..5ae5529 100644 --- a/Controllers/v1/SongDataController.cs +++ b/Controllers/v1/SongDataController.cs @@ -148,7 +148,7 @@ public class SongDataController : BaseController _songMgr.DeleteSong(songMetaData); - return Ok(); + return Ok(songMetaData); } } diff --git a/Controllers/v1/SongStreamController.cs b/Controllers/v1/SongStreamController.cs index 957a152..7e5aa07 100644 --- a/Controllers/v1/SongStreamController.cs +++ b/Controllers/v1/SongStreamController.cs @@ -53,7 +53,12 @@ public class SongStreamController : BaseController var stream = new FileStream(song.SongPath(), FileMode.Open, FileAccess.Read); stream.Position = 0; - var filename = $"{song.Title}.mp3"; + var filename = song.Filename; + + if (string.IsNullOrEmpty(song.Filename)) + { + filename = song.GenerateFilename(); + } _logger.LogInformation("Starting to stream song...>"); Console.WriteLine("Starting to streamsong..."); diff --git a/Database/Contexts/SongContext.cs b/Database/Contexts/SongContext.cs index 3b49a05..ff7ed98 100644 --- a/Database/Contexts/SongContext.cs +++ b/Database/Contexts/SongContext.cs @@ -25,38 +25,6 @@ public class SongContext : DbContext modelBuilder.Entity() .ToTable("Song"); - /** - modelBuilder.Entity() - .HasOne(s => s.Album) - .WithMany(al => al.Songs) - .HasForeignKey(s => s.AlbumID) - .OnDelete(DeleteBehavior.SetNull); - - modelBuilder.Entity() - .HasOne(sa => sa.SongArtist) - .WithMany(ar => ar.Songs) - .HasForeignKey(s => s.ArtistID) - .OnDelete(DeleteBehavior.SetNull); - - modelBuilder.Entity() - .HasOne(s => s.SongGenre) - .WithMany(gnr => gnr.Songs) - .HasForeignKey(s => s.GenreID) - .OnDelete(DeleteBehavior.SetNull); - - modelBuilder.Entity() - .HasOne(s => s.SongYear) - .WithMany(yr => yr.Songs) - .HasForeignKey(s => s.YearID) - .OnDelete(DeleteBehavior.SetNull); - - modelBuilder.Entity() - .HasOne(s => s.SongCoverArt) - .WithMany(ca => ca.Songs) - .HasForeignKey(s => s.CoverArtID) - .OnDelete(DeleteBehavior.SetNull); - */ - modelBuilder.Entity() .Property(s => s.Year) .IsRequired(false); diff --git a/Database/Contexts/UserContext.cs b/Database/Contexts/UserContext.cs index 2ec1249..d83287e 100644 --- a/Database/Contexts/UserContext.cs +++ b/Database/Contexts/UserContext.cs @@ -2,11 +2,6 @@ using System; using System.Collections.Generic; using System.Linq; -// using MySql.Data.EntityFrameworkCore; -// using MySql.Data; -// using MySql.Data.EntityFrameworkCore.Extensions; -// using MySql.Data.Entity; -// using MySql.Data.MySqlClient; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; diff --git a/Icarus.csproj b/Icarus.csproj index b4164ab..b5fada8 100644 --- a/Icarus.csproj +++ b/Icarus.csproj @@ -1,32 +1,34 @@ - net6 + net8.0 + enable InProcess 0.1.10 + - - - - - + + + + + runtime; build; native; contentfiles; analyzers all - - + + - - + + diff --git a/Models/Album.cs b/Models/Album.cs index 857aacf..7fe3d64 100644 --- a/Models/Album.cs +++ b/Models/Album.cs @@ -1,5 +1,3 @@ -using System; -using System.Collections.Generic; using System.ComponentModel.DataAnnotations.Schema; using Newtonsoft.Json; @@ -20,8 +18,4 @@ public class Album public int SongCount { get; set; } [JsonProperty("year")] public int Year { get; set; } - - [JsonIgnore] - [NotMapped] - public List Songs { get; set; } } diff --git a/Models/Artist.cs b/Models/Artist.cs index b4a8a19..ca66412 100644 --- a/Models/Artist.cs +++ b/Models/Artist.cs @@ -1,5 +1,3 @@ -using System; -using System.Collections.Generic; using System.ComponentModel.DataAnnotations.Schema; using Newtonsoft.Json; @@ -16,7 +14,4 @@ public class Artist [JsonProperty("song_count")] [NotMapped] public int SongCount { get; set; } - [JsonIgnore] - [NotMapped] - public List Songs { get; set; } } diff --git a/Models/CoverArt.cs b/Models/CoverArt.cs index 8b3cc1a..7b22816 100644 --- a/Models/CoverArt.cs +++ b/Models/CoverArt.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations.Schema; using System.Linq; +using System.Reflection.Metadata; using System.Text; using Newtonsoft.Json; @@ -17,24 +18,18 @@ public class CoverArt public string SongTitle { get; set; } [JsonIgnore] public string ImagePath { get; set; } - [JsonIgnore] - [NotMapped] - public int SongID { get; set; } - [JsonIgnore] - [NotMapped] - public List Songs { get; set; } #endregion #region Methods public string GenerateFilename(int flag) { - const int length = 25; - const string chars = "ABCDEF0123456789"; + int length = Constants.DirectoryPaths.FILENAME_LENGTH; + string chars = Constants.DirectoryPaths.FILENAME_CHARACTERS; var random = new Random(); var filename = new string(Enumerable.Repeat(chars, length).Select(s => s[random.Next(s.Length)]).ToArray()); - var extension = ".mp3"; + var extension = Constants.FileExtensions.JPG_EXTENSION; return (flag == 0) ? filename : $"{filename}{extension}"; } diff --git a/Models/Genre.cs b/Models/Genre.cs index b3ad87a..b157a5f 100644 --- a/Models/Genre.cs +++ b/Models/Genre.cs @@ -1,5 +1,3 @@ -using System; -using System.Collections.Generic; using System.ComponentModel.DataAnnotations.Schema; using Newtonsoft.Json; @@ -16,7 +14,4 @@ public class Genre [JsonProperty("song_count")] [NotMapped] public int SongCount { get; set; } - [JsonIgnore] - [NotMapped] - public List Songs { get; set; } } diff --git a/Models/Song.cs b/Models/Song.cs index 1fab172..828005e 100644 --- a/Models/Song.cs +++ b/Models/Song.cs @@ -73,20 +73,20 @@ public class Song public string GenerateFilename(int flag = 0) { - const int length = 25; - const string chars = "ABCDEF0123456789"; + int length = Constants.DirectoryPaths.FILENAME_LENGTH; + string chars = Constants.DirectoryPaths.FILENAME_CHARACTERS; var random = new Random(); var filename = new string(Enumerable.Repeat(chars, length).Select(s => s[random.Next(s.Length)]).ToArray()); - var extension = ".mp3"; + var extension = Icarus.Constants.FileExtensions.WAV_EXTENSION; return flag == 0 ? filename : $"{filename}{extension}"; } public async Task GenerateFilenameAsync(int flag = 0) { - const int length = 25; - const string chars = "ABCDEF0123456789"; - var extension = ".mp3"; + int length = Constants.DirectoryPaths.FILENAME_LENGTH; + string chars = Constants.DirectoryPaths.FILENAME_CHARACTERS; + var extension = Icarus.Constants.FileExtensions.WAV_EXTENSION; var random = new Random(); var filename = await Task.Run(() => { diff --git a/Program.cs b/Program.cs index 2fa72b3..2b5db35 100644 --- a/Program.cs +++ b/Program.cs @@ -1,45 +1,105 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Threading.Tasks; +using Microsoft.AspNetCore.Authentication.JwtBearer; +using Microsoft.EntityFrameworkCore; +using Microsoft.IdentityModel.Tokens; +using Microsoft.OpenApi.Models; -using Microsoft.AspNetCore; -using Microsoft.AspNetCore.Hosting; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.Hosting; -using Microsoft.Extensions.Logging; -using NLog.Web; +using Icarus.Database.Contexts; -namespace Icarus; -public class Program +var builder = WebApplication.CreateBuilder(args); + +var MAX_REQUEST_BODY_SIZE = 51200000000; + + +builder.Services.AddEndpointsApiExplorer(); +builder.Services.AddSwaggerGen(c => { - public static void Main(string[] args) + c.ResolveConflictingActions(apiDescriptions => apiDescriptions.First()); + + c.SwaggerDoc("v1", new OpenApiInfo { Title = "Icarus", Version = "v1" }); + c.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme() + { + Name = "Authorization", + Scheme = "Bearer", + BearerFormat = "JWT", + Type = SecuritySchemeType.ApiKey, + In = ParameterLocation.Header, + Description = "Bearer *Auth Token*", + }); + c.AddSecurityRequirement(new OpenApiSecurityRequirement { - var logger = NLog.Web.NLogBuilder.ConfigureNLog("nlog.config").GetCurrentClassLogger(); - - try { - logger.Debug("init main"); - - CreateHostBuilder(args).Build().Run(); - } - catch (Exception ex) - { - logger.Error(ex, "An error occurred"); - throw; - } - finally - { - NLog.LogManager.Shutdown(); - } - } - - public static IHostBuilder CreateHostBuilder(string[] args) => - Host.CreateDefaultBuilder(args) - .ConfigureWebHostDefaults(webBuilder => + new OpenApiSecurityScheme { - webBuilder.UseStartup(); - }); + Reference = new OpenApiReference + { + Type = ReferenceType.SecurityScheme, + Id = "Bearer" + } + }, + new string[] {} + } + }); +}); + +builder.Services.AddControllers(); +builder.WebHost.UseKestrel(option => +{ + option.Limits.MaxRequestBodySize = MAX_REQUEST_BODY_SIZE; +}); + +var Configuration = builder.Configuration; + +var connString = Configuration.GetConnectionString("DefaultConnection"); + +builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme).AddJwtBearer(options => +{ + options.RequireHttpsMetadata = false; + options.SaveToken = true; + options.TokenValidationParameters = new TokenValidationParameters() + { + ValidateIssuer = true, + ValidateAudience = true, + ValidateIssuerSigningKey = true, + ValidateLifetime = true, + ValidAudience = Configuration["JWT:Audience"], + ValidIssuer = Configuration["JWT:Issuer"], + IssuerSigningKey = new SymmetricSecurityKey(System.Text.Encoding.UTF8.GetBytes(Configuration["JWT:Secret"])) + }; +}); + +builder.Services.AddDbContext(options => options.UseMySQL(connString)); +builder.Services.AddDbContext(options => options.UseMySQL(connString)); +builder.Services.AddDbContext(options => options.UseMySQL(connString)); +builder.Services.AddDbContext(options => options.UseMySQL(connString)); +builder.Services.AddDbContext(options => options.UseMySQL(connString)); +builder.Services.AddDbContext(options => options.UseMySQL(connString)); + +builder.Services.AddControllers() + .AddNewtonsoftJson(); + +builder.Services.AddEndpointsApiExplorer(); + + +var app = builder.Build(); + +// Configure the HTTP request pipeline. +if (app.Environment.IsDevelopment()) +{ + app.UseSwagger(); + app.UseSwaggerUI(); } + +// NOTE: This should be enabled at some point +// app.UseHttpsRedirection(); + +app.UseRouting(); +app.UseAuthentication(); +app.UseAuthorization(); +app.UseEndpoints(endpoints => +{ + endpoints.MapControllers(); +}); + + +app.Run(); diff --git a/README.md b/README.md index c3bb084..5e5362c 100644 --- a/README.md +++ b/README.md @@ -1,18 +1,18 @@ # Icarus -Icarus is a music streaming API Server that interacts with [Mear](https://github.com/amazing-username/mear). +Icarus is a music streaming API Server that interacts with [Mear](https://github.com/kdeng00/mear). ### Interfacing With Icarus One can interface with Icarus the music server either by: -* [Mear](https://github.com/amazing-username/mear) - Partially implemented (under development) -* [IcarusDownloadManager](https://github.com/amazing-username/IcarusDownloadManager) +* [Mear](https://github.com/kdeng00/mear) - Partially implemented (under development) +* [IcarusDownloadManager](https://github.com/kdeng00/IcarusDownloadManager) ## Built With -* C# [.NET](https://dotnet.microsoft.com/) 6 +* C# [.NET](https://dotnet.microsoft.com/) 8 * [MySql](https://www.nuget.org/packages/MySql.Data/) * OpenSSL * BCrypt.Net-Next @@ -120,7 +120,7 @@ Prior to starting the API, the Migrations must be applied. There are 6 tables wi * Year * Genre -There is a script for Linux systems to apply these migrations, it can be found in the [Scripts/Migrations/Linux](https://github.com/amazing-username/Icarus/blob/master/Scripts/Migrations/Linux/AddUpdate.sh) directory. Just merely execute: +There is a script for Linux systems to apply these migrations, it can be found in the [Scripts/Migrations/Linux](https://github.com/kdeng00/Icarus/blob/master/Scripts/Migrations/Linux/AddUpdate.sh) directory. Just merely execute: ```shell scripts/Migrations/Linux/AddUpdate.sh ``` diff --git a/Startup.cs b/Startup.cs deleted file mode 100644 index 0da5769..0000000 --- a/Startup.cs +++ /dev/null @@ -1,125 +0,0 @@ -using System; -using System.Text; -using System.Linq; - -using Microsoft.AspNetCore.Authentication.JwtBearer; -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Hosting; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Hosting; -using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Options; -using Microsoft.EntityFrameworkCore; -using Microsoft.IdentityModel.Tokens; -using Microsoft.OpenApi.Models; -using Newtonsoft.Json; -using NLog; -using NLog.Web; -using NLog.Web.AspNetCore; - -using Icarus.Database.Contexts; - -namespace Icarus; -public class Startup -{ - #region Constructors - public Startup(IConfiguration configuration) - { - Configuration = configuration; - } - #endregion - - #region Properties - public IConfiguration Configuration { get; } - #endregion - - - #region Methods - // This method gets called by the runtime. Use this method to add services to the container. - public void ConfigureServices(IServiceCollection services) - { - services.AddControllers(); - - var auth_id = Configuration["Auth0:Domain"]; - var domain = $"https://{auth_id}/"; - var audience = Configuration["Auth0:ApiIdentifier"]; - - var connString = Configuration.GetConnectionString("DefaultConnection"); - - services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme).AddJwtBearer(options => - { - options.RequireHttpsMetadata = false; - options.SaveToken = true; - options.TokenValidationParameters = new TokenValidationParameters() - { - ValidateIssuer = true, - ValidateAudience = true, - ValidAudience = Configuration["JWT:Audience"], - ValidIssuer = Configuration["JWT:Issuer"], - IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Configuration["JWT:Secret"])) - }; - }); - - services.AddDbContext(options => options.UseMySQL(connString)); - services.AddDbContext(options => options.UseMySQL(connString)); - services.AddDbContext(options => options.UseMySQL(connString)); - services.AddDbContext(options => options.UseMySQL(connString)); - services.AddDbContext(options => options.UseMySQL(connString)); - services.AddDbContext(options => options.UseMySQL(connString)); - - services.AddControllers() - .AddNewtonsoftJson(); - - services.AddEndpointsApiExplorer(); - services.AddSwaggerGen(c => - { - c.ResolveConflictingActions(apiDescriptions => apiDescriptions.First()); - - c.SwaggerDoc("v1", new OpenApiInfo { Title = "Icarus", Version = "v1" }); - c.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme() - { - Name = "Authorization", - Scheme = "Bearer", - BearerFormat = "JWT", - Type = SecuritySchemeType.ApiKey, - In = ParameterLocation.Header, - Description = "Bearer *Auth Token*", - }); - c.AddSecurityRequirement(new OpenApiSecurityRequirement - { - { - new OpenApiSecurityScheme - { - Reference = new OpenApiReference - { - Type = ReferenceType.SecurityScheme, - Id = "Bearer" - } - }, - new string[] {} - } - }); - }); - } - - // Called by the runtime. Use this method to configure the HTTP request pipeline. - public void Configure(IApplicationBuilder app, IWebHostEnvironment env) - { - // NOTE: Dev-related configuration can be done when env.IsDevelopment() evaluated to true - if (env.IsDevelopment()) - { - app.UseSwagger(); - app.UseSwaggerUI(); - } - - app.UseRouting(); - app.UseAuthentication(); - app.UseAuthorization(); - app.UseEndpoints(endpoints => - { - endpoints.MapControllers(); - }); - } - #endregion -}