parent
cca72cf225
commit
38aaf947c3
@ -0,0 +1,11 @@ |
|||||||
|
<?xml version="1.0" encoding="UTF-8"?> |
||||||
|
<project version="4"> |
||||||
|
<component name="EfCoreCommonOptions"> |
||||||
|
<option name="solutionLevelOptions"> |
||||||
|
<map> |
||||||
|
<entry key="migrationsProject" value="7ef668eb-6d79-4a51-a161-f4e99dccd789" /> |
||||||
|
<entry key="startupProject" value="7ef668eb-6d79-4a51-a161-f4e99dccd789" /> |
||||||
|
</map> |
||||||
|
</option> |
||||||
|
</component> |
||||||
|
</project> |
@ -0,0 +1,114 @@ |
|||||||
|
using DreamsCaster.Models; |
||||||
|
using Microsoft.AspNetCore.Authorization; |
||||||
|
using Microsoft.AspNetCore.Mvc; |
||||||
|
using Microsoft.EntityFrameworkCore; |
||||||
|
using Console = System.Console; |
||||||
|
|
||||||
|
namespace DreamsCaster.Controllers; |
||||||
|
|
||||||
|
[ApiController] |
||||||
|
[Route("[controller]")]
|
||||||
|
public class DeveloperController : ControllerBase |
||||||
|
{ |
||||||
|
public AppDbContext Database { get; set; } |
||||||
|
|
||||||
|
public DeveloperController(AppDbContext dbContext) |
||||||
|
{ |
||||||
|
Database = dbContext; |
||||||
|
} |
||||||
|
|
||||||
|
[HttpGet("{developerId:guid}")] |
||||||
|
public async Task<IActionResult> GetDeveloperAsync([FromRoute] Guid developerId) |
||||||
|
{ |
||||||
|
try |
||||||
|
{ |
||||||
|
if (developerId == null) |
||||||
|
{ |
||||||
|
return Ok(); |
||||||
|
} |
||||||
|
|
||||||
|
if (!Database.Developers.Include(x=>x.Data).TryGet(developerId, out var val)) return NotFound(); |
||||||
|
|
||||||
|
return Ok(val); |
||||||
|
} |
||||||
|
catch (Exception ex) |
||||||
|
{ |
||||||
|
Console.WriteLine(ex); |
||||||
|
return StatusCode(StatusCodes.Status500InternalServerError); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
[HttpPost] |
||||||
|
//[Authorize] |
||||||
|
public async Task<IActionResult> PostDeveloperAsync([FromBody] PostBase publisher) |
||||||
|
{ |
||||||
|
try |
||||||
|
{ |
||||||
|
var pub = Database.Developers.Add(new Developer() |
||||||
|
{ |
||||||
|
Name = publisher.Name, |
||||||
|
Data = new MetaData<Developer>() |
||||||
|
{ |
||||||
|
Description = publisher.Description, |
||||||
|
CoverPhoto = publisher.CoverPhoto |
||||||
|
} |
||||||
|
}); |
||||||
|
|
||||||
|
await Database.SaveChangesAsync(); |
||||||
|
|
||||||
|
return Ok(pub.Entity); |
||||||
|
} |
||||||
|
catch (Exception ex) |
||||||
|
{ |
||||||
|
Console.WriteLine(ex); |
||||||
|
return StatusCode(StatusCodes.Status500InternalServerError); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
[HttpPut("{developerId:guid}")] |
||||||
|
//[Authorize] |
||||||
|
public async Task<IActionResult> PatchDeveloperAsync([FromRoute] Guid developerId, [FromBody] PatchInfo patchData) |
||||||
|
{ |
||||||
|
if (!Database.Developers.TryGet(developerId, out var pub)) return NotFound(); |
||||||
|
|
||||||
|
try |
||||||
|
{ |
||||||
|
if (patchData.Name.HasValue) pub.Name = patchData.Name; |
||||||
|
if (patchData.Description.HasValue) pub.Data.Description = patchData.Description; |
||||||
|
if (patchData.CoverPhoto.HasValue) pub.Data.CoverPhoto = patchData.CoverPhoto; |
||||||
|
|
||||||
|
await Database.SaveChangesAsync(); |
||||||
|
|
||||||
|
return Ok(); |
||||||
|
} |
||||||
|
catch (Exception ex) |
||||||
|
{ |
||||||
|
Console.WriteLine(ex); |
||||||
|
return StatusCode(StatusCodes.Status500InternalServerError); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
[HttpDelete("{developerId:guid}")] |
||||||
|
//[Authorize] |
||||||
|
public async Task<IActionResult> DeleteDeveloperAsync([FromRoute] Guid developerId) |
||||||
|
{ |
||||||
|
if (!Database.Developers.TryGet(developerId, out var dev )) return NotFound(); |
||||||
|
|
||||||
|
try |
||||||
|
{ |
||||||
|
foreach (var game in Database.Games.Where(x=>x.Developers.Contains(dev))) |
||||||
|
{ |
||||||
|
game.Developers.Remove(dev); |
||||||
|
} |
||||||
|
|
||||||
|
Database.Developers.Remove(dev); |
||||||
|
await Database.SaveChangesAsync(); |
||||||
|
return Ok(); |
||||||
|
} |
||||||
|
catch (Exception ex) |
||||||
|
{ |
||||||
|
Console.WriteLine(ex); |
||||||
|
return StatusCode(StatusCodes.Status500InternalServerError); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,108 @@ |
|||||||
|
using DreamsCaster.Models; |
||||||
|
using Microsoft.AspNetCore.Authorization; |
||||||
|
using Microsoft.AspNetCore.Mvc; |
||||||
|
using Console = System.Console; |
||||||
|
|
||||||
|
namespace DreamsCaster.Controllers; |
||||||
|
|
||||||
|
[ApiController] |
||||||
|
[Route("[controller]")]
|
||||||
|
public class ManufacturerController : ControllerBase |
||||||
|
{ |
||||||
|
public AppDbContext Database { get; set; } |
||||||
|
|
||||||
|
public ManufacturerController(AppDbContext dbContext) |
||||||
|
{ |
||||||
|
Database = dbContext; |
||||||
|
} |
||||||
|
|
||||||
|
[HttpGet("{manufacturerId:guid}")] |
||||||
|
public async Task<IActionResult> GetManufacturerAsync([FromRoute] Guid manufacturerId) |
||||||
|
{ |
||||||
|
try |
||||||
|
{ |
||||||
|
if (manufacturerId == null) |
||||||
|
{ |
||||||
|
return Ok(); |
||||||
|
} |
||||||
|
|
||||||
|
if (!Database.Manufacturers.TryGet(manufacturerId, out var val)) return NotFound(); |
||||||
|
|
||||||
|
return Ok(val); |
||||||
|
} |
||||||
|
catch (Exception ex) |
||||||
|
{ |
||||||
|
Console.WriteLine(ex); |
||||||
|
return StatusCode(StatusCodes.Status500InternalServerError); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
[HttpPost] |
||||||
|
//[Authorize] |
||||||
|
public async Task<IActionResult> PostManufacturerAsync([FromBody] PostBase manufacturer) |
||||||
|
{ |
||||||
|
try |
||||||
|
{ |
||||||
|
var pub = Database.Manufacturers.Add(new Manufacturer() |
||||||
|
{ |
||||||
|
Name = manufacturer.Name, |
||||||
|
Data = new MetaData<Manufacturer>() |
||||||
|
{ |
||||||
|
Description = manufacturer.Description, |
||||||
|
CoverPhoto = manufacturer.CoverPhoto |
||||||
|
} |
||||||
|
}); |
||||||
|
|
||||||
|
await Database.SaveChangesAsync(); |
||||||
|
|
||||||
|
return Ok(pub.Entity); |
||||||
|
} |
||||||
|
catch (Exception ex) |
||||||
|
{ |
||||||
|
Console.WriteLine(ex); |
||||||
|
return StatusCode(StatusCodes.Status500InternalServerError); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
[HttpPut("{manufacturerId:guid}")] |
||||||
|
//[Authorize] |
||||||
|
public async Task<IActionResult> PatchManufacturerAsync([FromRoute] Guid manufacturerId, [FromBody] PatchInfo patchData) |
||||||
|
{ |
||||||
|
if (!Database.Manufacturers.TryGet(manufacturerId, out var manufacturer)) return NotFound(); |
||||||
|
|
||||||
|
try |
||||||
|
{ |
||||||
|
if (patchData.Name.HasValue) manufacturer.Name = patchData.Name; |
||||||
|
if (patchData.Description.HasValue) manufacturer.Data.Description = patchData.Description; |
||||||
|
if (patchData.CoverPhoto.HasValue) manufacturer.Data.CoverPhoto = patchData.CoverPhoto; |
||||||
|
|
||||||
|
await Database.SaveChangesAsync(); |
||||||
|
|
||||||
|
return Ok(); |
||||||
|
} |
||||||
|
catch (Exception ex) |
||||||
|
{ |
||||||
|
Console.WriteLine(ex); |
||||||
|
return StatusCode(StatusCodes.Status500InternalServerError); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
[HttpDelete("{manufacturerId:guid}")] |
||||||
|
//[Authorize] |
||||||
|
public async Task<IActionResult> DeleteManufacturerAsync([FromRoute] Guid manufacturerId) |
||||||
|
{ |
||||||
|
if (!Database.Manufacturers.TryGet(manufacturerId, out var manufacturer)) return NotFound(); |
||||||
|
|
||||||
|
try |
||||||
|
{ |
||||||
|
Database.Manufacturers.Remove(manufacturer); |
||||||
|
await Database.SaveChangesAsync(); |
||||||
|
return Ok(); |
||||||
|
} |
||||||
|
catch (Exception ex) |
||||||
|
{ |
||||||
|
Console.WriteLine(ex); |
||||||
|
return StatusCode(StatusCodes.Status500InternalServerError); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,108 @@ |
|||||||
|
using DreamsCaster.Models; |
||||||
|
using Microsoft.AspNetCore.Authorization; |
||||||
|
using Microsoft.AspNetCore.Mvc; |
||||||
|
using Console = System.Console; |
||||||
|
|
||||||
|
namespace DreamsCaster.Controllers; |
||||||
|
|
||||||
|
[ApiController] |
||||||
|
[Route("[controller]")]
|
||||||
|
public class PublisherController : ControllerBase |
||||||
|
{ |
||||||
|
public AppDbContext Database { get; set; } |
||||||
|
|
||||||
|
public PublisherController(AppDbContext dbContext) |
||||||
|
{ |
||||||
|
Database = dbContext; |
||||||
|
} |
||||||
|
|
||||||
|
[HttpGet("{publisherId:guid}")] |
||||||
|
public async Task<IActionResult> GetPublisherAsync([FromRoute] Guid publisherId) |
||||||
|
{ |
||||||
|
try |
||||||
|
{ |
||||||
|
if (publisherId == null) |
||||||
|
{ |
||||||
|
return Ok(); |
||||||
|
} |
||||||
|
|
||||||
|
if (!Database.Publishers.TryGet(publisherId, out var val)) return NotFound(); |
||||||
|
|
||||||
|
return Ok(val); |
||||||
|
} |
||||||
|
catch (Exception ex) |
||||||
|
{ |
||||||
|
Console.WriteLine(ex); |
||||||
|
return StatusCode(StatusCodes.Status500InternalServerError); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
[HttpPost] |
||||||
|
//[Authorize] |
||||||
|
public async Task<IActionResult> PostPublisherAsync([FromBody] PostBase publisher) |
||||||
|
{ |
||||||
|
try |
||||||
|
{ |
||||||
|
var pub = Database.Publishers.Add(new Publisher() |
||||||
|
{ |
||||||
|
Name = publisher.Name, |
||||||
|
Data = new MetaData<Publisher>() |
||||||
|
{ |
||||||
|
Description = publisher.Description, |
||||||
|
CoverPhoto = publisher.CoverPhoto |
||||||
|
} |
||||||
|
}); |
||||||
|
|
||||||
|
await Database.SaveChangesAsync(); |
||||||
|
|
||||||
|
return Ok(pub.Entity); |
||||||
|
} |
||||||
|
catch (Exception ex) |
||||||
|
{ |
||||||
|
Console.WriteLine(ex); |
||||||
|
return StatusCode(StatusCodes.Status500InternalServerError); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
[HttpPut("{publisherId:guid}")] |
||||||
|
//[Authorize] |
||||||
|
public async Task<IActionResult> PatchPublisherAsync([FromRoute] Guid publisherId, [FromBody] PatchInfo patchData) |
||||||
|
{ |
||||||
|
if (!Database.Publishers.TryGet(publisherId, out var pub)) return NotFound(); |
||||||
|
|
||||||
|
try |
||||||
|
{ |
||||||
|
if (patchData.Name.HasValue) pub.Name = patchData.Name; |
||||||
|
if (patchData.Description.HasValue) pub.Data.Description = patchData.Description; |
||||||
|
if (patchData.CoverPhoto.HasValue) pub.Data.CoverPhoto = patchData.CoverPhoto; |
||||||
|
|
||||||
|
await Database.SaveChangesAsync(); |
||||||
|
|
||||||
|
return Ok(); |
||||||
|
} |
||||||
|
catch (Exception ex) |
||||||
|
{ |
||||||
|
Console.WriteLine(ex); |
||||||
|
return StatusCode(StatusCodes.Status500InternalServerError); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
[HttpDelete("{publisherId:guid}")] |
||||||
|
//[Authorize] |
||||||
|
public async Task<IActionResult> DeletePublisherAsync([FromRoute] Guid publisherId) |
||||||
|
{ |
||||||
|
if (!Database.Publishers.TryGet(publisherId, out Publisher publisher)) return NotFound(); |
||||||
|
|
||||||
|
try |
||||||
|
{ |
||||||
|
Database.Publishers.Remove(publisher); |
||||||
|
await Database.SaveChangesAsync(); |
||||||
|
return Ok(); |
||||||
|
} |
||||||
|
catch (Exception ex) |
||||||
|
{ |
||||||
|
Console.WriteLine(ex); |
||||||
|
return StatusCode(StatusCodes.Status500InternalServerError); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -1,17 +1,67 @@ |
|||||||
using System.ComponentModel.DataAnnotations; |
using System.ComponentModel.DataAnnotations; |
||||||
|
using System.ComponentModel.DataAnnotations.Schema; |
||||||
|
using DreamsCaster.Helpers; |
||||||
|
|
||||||
namespace DreamsCaster.Models; |
namespace DreamsCaster.Models; |
||||||
|
|
||||||
public class IdEntity |
public class IdEntity |
||||||
{ |
{ |
||||||
public Guid Id; |
[DatabaseGenerated(DatabaseGeneratedOption.Identity)] |
||||||
|
[Key] |
||||||
|
public Guid Id { get; set; } |
||||||
} |
} |
||||||
|
|
||||||
public class MetaData<T> : IdEntity |
public class MetaData<T> : IdEntity |
||||||
{ |
{ |
||||||
public string Description; |
public string? Description { get; set; } |
||||||
public string CoverPhoto; |
public string? CoverPhoto { get; set; } |
||||||
|
|
||||||
|
[Required] |
||||||
|
public virtual Guid OwnerId { get; set; } |
||||||
|
|
||||||
[Required] |
[Required] |
||||||
public virtual T Owner { get; set; } |
public virtual T Owner { get; set; } |
||||||
} |
} |
||||||
|
|
||||||
|
public class MetaDataContainer<T> : IdEntity |
||||||
|
{ |
||||||
|
public string Name { get; set; } |
||||||
|
|
||||||
|
public virtual MetaData<T> Data { get; set; } |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
[System.Serializable] |
||||||
|
public class PatchInfo |
||||||
|
{ |
||||||
|
public Optional<string> Name { get; set; } = new(); |
||||||
|
public Optional<string> Description { get; set; } = new(); |
||||||
|
public Optional<string> CoverPhoto { get; set; } = new(); |
||||||
|
} |
||||||
|
|
||||||
|
[System.Serializable] |
||||||
|
public class PostBase |
||||||
|
{ |
||||||
|
[Required] public string Name { get; set; } |
||||||
|
public string? Description { get; set; } = null; |
||||||
|
public string? CoverPhoto { get; set; } = null; |
||||||
|
} |
||||||
|
|
||||||
|
[System.Serializable] |
||||||
|
public class PostRelease : PostBase |
||||||
|
{ |
||||||
|
public DateTime Release { get; set; } |
||||||
|
public Guid ConsoleId { get; set; } |
||||||
|
} |
||||||
|
|
||||||
|
[System.Serializable] |
||||||
|
public class PostGame : PostBase |
||||||
|
{ |
||||||
|
public List<PostRelease> Releases { get; set; } |
||||||
|
} |
||||||
|
|
||||||
|
[System.Serializable] |
||||||
|
public class PostConsole : PostBase |
||||||
|
{ |
||||||
|
public Guid ManufacturerId { get; set; } |
||||||
|
} |
@ -1,10 +1,11 @@ |
|||||||
|
using Microsoft.EntityFrameworkCore; |
||||||
|
|
||||||
namespace DreamsCaster.Models; |
namespace DreamsCaster.Models; |
||||||
|
|
||||||
public class Console : IdEntity |
public class Console : MetaDataContainer<Console> |
||||||
{ |
{ |
||||||
public string Name; |
|
||||||
|
|
||||||
public virtual ICollection<Game> Games { get; set; } |
public virtual ICollection<Game> Games { get; set; } |
||||||
|
|
||||||
public virtual MetaData<Console> Data { get; set; } |
public virtual Manufacturer Manufacturer { get; set; } |
||||||
} |
} |
@ -0,0 +1,6 @@ |
|||||||
|
namespace DreamsCaster.Models; |
||||||
|
|
||||||
|
public class Developer : MetaDataContainer<Developer> |
||||||
|
{ |
||||||
|
public virtual ICollection<Game> Games { get; set; } |
||||||
|
} |
@ -1,10 +1,11 @@ |
|||||||
namespace DreamsCaster.Models; |
namespace DreamsCaster.Models; |
||||||
|
|
||||||
public class Game : IdEntity |
public class Game : MetaDataContainer<Game> |
||||||
{ |
{ |
||||||
public string Name; |
public virtual ICollection<Release> Releases { get; set; } |
||||||
|
|
||||||
public ICollection<Release> Releases; |
public virtual ICollection<Developer> Developers { get; set; } |
||||||
|
public virtual ICollection<Publisher> Publishers { get; set; } |
||||||
|
|
||||||
public virtual MetaData<Game> Data { get; set; } |
public virtual MetaData<Game> Data { get; set; } |
||||||
} |
} |
@ -0,0 +1,6 @@ |
|||||||
|
namespace DreamsCaster.Models; |
||||||
|
|
||||||
|
public class Manufacturer : MetaDataContainer<Manufacturer> |
||||||
|
{ |
||||||
|
public virtual ICollection<Console> Consoles { get; set; } |
||||||
|
} |
@ -0,0 +1,6 @@ |
|||||||
|
namespace DreamsCaster.Models; |
||||||
|
|
||||||
|
public class Publisher : MetaDataContainer<Publisher> |
||||||
|
{ |
||||||
|
public virtual ICollection<Game> Games { get; set; } |
||||||
|
} |
@ -0,0 +1,108 @@ |
|||||||
|
{ |
||||||
|
"Logging": { |
||||||
|
"LogLevel": { |
||||||
|
"Default": "Debug" |
||||||
|
} |
||||||
|
}, |
||||||
|
"AllowedHosts": "*", |
||||||
|
"NLog": { |
||||||
|
"extensions": [ |
||||||
|
{ "assembly": "Sentry.NLog" } |
||||||
|
], |
||||||
|
"targets": { |
||||||
|
"sentry": { |
||||||
|
"type": "Sentry", |
||||||
|
"layout": "${message}", |
||||||
|
"environment": "Development", |
||||||
|
"breadcrumbLayout": "${message}", |
||||||
|
"minimumBreadcrumbLevel": "Debug", |
||||||
|
"minimumEventLevel": "Error", |
||||||
|
"options": { |
||||||
|
"sendDefaultPii": true, |
||||||
|
"attachStacktrace": true, |
||||||
|
"shutdownTimeoutSeconds": 5, |
||||||
|
"debug": false, |
||||||
|
"includeEventDataOnBreadcrumbs": true |
||||||
|
} |
||||||
|
}, |
||||||
|
"coloredConsole": { |
||||||
|
"type": "ColoredConsole", |
||||||
|
"layout": "${longdate}|${pad:padding=5:inner=${level:uppercase=true}}|${callsite}|${message}", |
||||||
|
"rowHighlightingRules": [ |
||||||
|
{ |
||||||
|
"condition": "level == LogLevel.Debug", |
||||||
|
"foregroundColor": "White" |
||||||
|
}, |
||||||
|
{ |
||||||
|
"condition": "level == LogLevel.Info", |
||||||
|
"foregroundColor": "Blue" |
||||||
|
}, |
||||||
|
{ |
||||||
|
"condition": "level == LogLevel.Warn", |
||||||
|
"foregroundColor": "Yellow" |
||||||
|
}, |
||||||
|
{ |
||||||
|
"condition": "level == LogLevel.Error", |
||||||
|
"foregroundColor": "Red" |
||||||
|
}, |
||||||
|
{ |
||||||
|
"condition": "level == LogLevel.Fatal", |
||||||
|
"foregroundColor": "Red", |
||||||
|
"backgroundColor": "White" |
||||||
|
} |
||||||
|
] |
||||||
|
}, |
||||||
|
"infoFile": { |
||||||
|
"type": "File", |
||||||
|
"layout": "${longdate} ${pad:padding=5:inner=${level:uppercase=true}} ${logger} ${message}", |
||||||
|
"fileName": "${basedir}/logs/info.log", |
||||||
|
"keepFileOpen": false, |
||||||
|
"encoding": "iso-8859-2" |
||||||
|
}, |
||||||
|
"errorFile": { |
||||||
|
"type": "File", |
||||||
|
"layout": "${longdate} ${pad:padding=5:inner=${level:uppercase=true}} ${logger} ${message}${newline}${exception:format=ToString,StackTrace}", |
||||||
|
"fileName": "${basedir}/logs/error.log", |
||||||
|
"keepFileOpen": false, |
||||||
|
"encoding": "iso-8859-2" |
||||||
|
}, |
||||||
|
"allFile": { |
||||||
|
"type": "File", |
||||||
|
"layout": "${longdate} ${pad:padding=5:inner=${level:uppercase=true}} ${logger} ${message}${newline}${exception:format=ToString,StackTrace}", |
||||||
|
"fileName": "${basedir}/logs/all.log", |
||||||
|
"keepFileOpen": false, |
||||||
|
"encoding": "iso-8859-2" |
||||||
|
} |
||||||
|
}, |
||||||
|
"rules": [ |
||||||
|
{ |
||||||
|
"logger": "Microsoft.*", |
||||||
|
"maxLevel": "Error", |
||||||
|
"final": true |
||||||
|
}, |
||||||
|
{ |
||||||
|
"logger": "*", |
||||||
|
"minLevel": "Trace", |
||||||
|
"writeTo": "allFile,coloredConsole" |
||||||
|
}, |
||||||
|
{ |
||||||
|
"logger": "*", |
||||||
|
"minLevel": "Warn", |
||||||
|
"maxLevel": "Fatal", |
||||||
|
"writeTo": "errorFile,sentry" |
||||||
|
} |
||||||
|
] |
||||||
|
}, |
||||||
|
"Sentry": { |
||||||
|
"Dsn": "", |
||||||
|
"MaxRequestBodySize": "Always", |
||||||
|
"MinimumBreadcrumbLevel": "Debug", |
||||||
|
"MinimumEventLevel": "Warning", |
||||||
|
"AttachStackTrace": true, |
||||||
|
"DiagnosticsLevel": "Error", |
||||||
|
"TracesSampleRate": 1.0 |
||||||
|
}, |
||||||
|
"ConnectionStrings": { |
||||||
|
"Database": "" |
||||||
|
} |
||||||
|
} |
Loading…
Reference in new issue