Start migration to asp .net core + entity framework core
This commit is contained in:
parent
aea09c2c8b
commit
f7761f200f
21
Application/Application.csproj
Normal file
21
Application/Application.csproj
Normal file
@ -0,0 +1,21 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Domain\Domain.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="MediatR" Version="11.1.0" />
|
||||
<PackageReference Include="MediatR.Extensions.Microsoft.DependencyInjection" Version="11.0.0" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="7.0.2" />
|
||||
<PackageReference Include="Microsoft.Extensions.Options" Version="7.0.0" />
|
||||
<PackageReference Include="Riok.Mapperly" Version="2.7.0-next.2" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
15
Application/DependencyInjection.cs
Normal file
15
Application/DependencyInjection.cs
Normal file
@ -0,0 +1,15 @@
|
||||
using System.Reflection;
|
||||
using MediatR;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace Application;
|
||||
|
||||
public static class DependencyInjection
|
||||
{
|
||||
public static IServiceCollection AddApplication(this IServiceCollection services)
|
||||
{
|
||||
services.AddMediatR(Assembly.GetExecutingAssembly());
|
||||
|
||||
return services;
|
||||
}
|
||||
}
|
33
Application/Dto/CardDto.cs
Normal file
33
Application/Dto/CardDto.cs
Normal file
@ -0,0 +1,33 @@
|
||||
using System.Xml.Serialization;
|
||||
|
||||
namespace Application.Dto;
|
||||
|
||||
public class CardDto
|
||||
{
|
||||
[XmlElement(ElementName = "card_id")]
|
||||
public long CardId { get; set; }
|
||||
|
||||
[XmlElement(ElementName = "player_name")]
|
||||
public string PlayerName { get; set; } = string.Empty;
|
||||
|
||||
[XmlElement("score_i1")]
|
||||
public long ScoreI1 { get; set; }
|
||||
|
||||
[XmlElement("fcol1")]
|
||||
public long Fcol1 { get; set; }
|
||||
|
||||
[XmlElement("fcol2")]
|
||||
public long Fcol2 { get; set; }
|
||||
|
||||
[XmlElement("fcol3")]
|
||||
public long Fcol3 { get; set; }
|
||||
|
||||
[XmlElement("achieve_status")]
|
||||
public string AchieveStatus { get; set; } = string.Empty;
|
||||
|
||||
[XmlElement("created")]
|
||||
public string Created { get; set; } = string.Empty;
|
||||
|
||||
[XmlElement("modified")]
|
||||
public string Modified { get; set; } = string.Empty;
|
||||
}
|
62
Application/Game/Option/PlayCountQuery.cs
Normal file
62
Application/Game/Option/PlayCountQuery.cs
Normal file
@ -0,0 +1,62 @@
|
||||
using Application.Interfaces;
|
||||
using MediatR;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Application.Game.Option;
|
||||
|
||||
public record PlayCountQuery(long CardId) : IRequest<long>;
|
||||
|
||||
public class PlayCountQueryHandler : IRequestHandler<PlayCountQuery, long>
|
||||
{
|
||||
private readonly ICardDbContext context;
|
||||
|
||||
private readonly ILogger<PlayCountQueryHandler> logger;
|
||||
|
||||
public PlayCountQueryHandler(ICardDbContext context, ILogger<PlayCountQueryHandler> logger)
|
||||
{
|
||||
this.context = context;
|
||||
this.logger = logger;
|
||||
}
|
||||
|
||||
public async Task<long> Handle(PlayCountQuery request, CancellationToken cancellationToken)
|
||||
{
|
||||
return await GetPlayCount(request.CardId);
|
||||
}
|
||||
|
||||
private async Task<long> GetPlayCount(long cardId)
|
||||
{
|
||||
var record = await context.CardPlayCounts.FirstOrDefaultAsync(count => count.CardId == cardId);
|
||||
if (record is null)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
var now = DateTime.Now;
|
||||
var lastPlayedTime = record.LastPlayedTime;
|
||||
|
||||
if (now <= lastPlayedTime)
|
||||
{
|
||||
logger.LogWarning("Clock skew detected! " +
|
||||
"Current time: {Now}," +
|
||||
"Last Play Time: {Last}", now, lastPlayedTime);
|
||||
return 0;
|
||||
}
|
||||
|
||||
DateTime start;
|
||||
DateTime end;
|
||||
if (now.Hour >= 8)
|
||||
{
|
||||
start = DateTime.Today.AddHours(8);
|
||||
end = start.AddHours(24);
|
||||
}
|
||||
else
|
||||
{
|
||||
end = DateTime.Today.AddHours(8);
|
||||
start = end.AddHours(-24);
|
||||
}
|
||||
|
||||
var inBetween = lastPlayedTime >= start && lastPlayedTime <= end;
|
||||
return inBetween ? record.PlayCount : 0;
|
||||
}
|
||||
}
|
100
Application/Game/Server/CertifyCommand.cs
Normal file
100
Application/Game/Server/CertifyCommand.cs
Normal file
@ -0,0 +1,100 @@
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using Application.Interfaces;
|
||||
using Domain.Config;
|
||||
using MediatR;
|
||||
using Microsoft.Extensions.Options;
|
||||
|
||||
namespace Application.Game.Server;
|
||||
|
||||
public record CertifyCommand(string? Gid, string? Mac, string? Random, string? Md5, string Host) : IRequest<string>;
|
||||
|
||||
public partial class CertifyCommandHandler : IRequestHandler<CertifyCommand, string>
|
||||
{
|
||||
private readonly RelayConfig relayConfig;
|
||||
|
||||
|
||||
public CertifyCommandHandler(IOptions<RelayConfig> relayOptions)
|
||||
{
|
||||
relayConfig = relayOptions.Value;
|
||||
}
|
||||
|
||||
public Task<string> Handle(CertifyCommand request, CancellationToken cancellationToken)
|
||||
{
|
||||
if (request.Gid == null)
|
||||
{
|
||||
return Task.FromResult(QuitWithError(ErrorCode.ErrorNoGid));
|
||||
}
|
||||
|
||||
if (request.Mac == null)
|
||||
{
|
||||
return Task.FromResult(QuitWithError(ErrorCode.ErrorNoMac));
|
||||
}
|
||||
|
||||
if (request.Random == null)
|
||||
{
|
||||
return Task.FromResult(QuitWithError(ErrorCode.ErrorNoRandom));
|
||||
}
|
||||
|
||||
if (request.Md5 == null)
|
||||
{
|
||||
return Task.FromResult(QuitWithError(ErrorCode.ErrorNoHash));
|
||||
}
|
||||
|
||||
if (!MacValid(request.Mac))
|
||||
{
|
||||
return Task.FromResult(QuitWithError(ErrorCode.ErrorInvalidMac));
|
||||
}
|
||||
|
||||
if (!Md5Valid(request.Md5))
|
||||
{
|
||||
return Task.FromResult(QuitWithError(ErrorCode.ErrorInvalidHash));
|
||||
}
|
||||
|
||||
var ticket = string.Join(string.Empty,
|
||||
MD5.HashData(Encoding.UTF8.GetBytes(request.Gid)).Select(b => b.ToString("x2")));
|
||||
|
||||
var response = $"host=card_id=7020392000147361,relay_addr={relayConfig.RelayServer},relay_port={relayConfig.RelayPort}\n" +
|
||||
"no=1337\n" +
|
||||
"name=123\n" +
|
||||
"pref=nesys\n" +
|
||||
"addr=nesys@home\n" +
|
||||
"x-next-time=15\n" +
|
||||
$"x-img=http://{request.Host}/news.png\n" +
|
||||
$"x-ranking=http://{request.Host}/ranking/ranking.php\n" +
|
||||
$"ticket={ticket}";
|
||||
|
||||
return Task.FromResult(response);
|
||||
}
|
||||
|
||||
private static bool MacValid(string mac)
|
||||
{
|
||||
return MacRegex().IsMatch(mac);
|
||||
}
|
||||
|
||||
private static bool Md5Valid(string md5)
|
||||
{
|
||||
return Md5Regex().IsMatch(md5);
|
||||
}
|
||||
|
||||
private static string QuitWithError(ErrorCode errorCode)
|
||||
{
|
||||
return $"error={(int)errorCode}";
|
||||
}
|
||||
|
||||
private enum ErrorCode
|
||||
{
|
||||
ErrorNoGid,
|
||||
ErrorNoMac,
|
||||
ErrorNoRandom,
|
||||
ErrorNoHash,
|
||||
ErrorInvalidMac,
|
||||
ErrorInvalidHash
|
||||
}
|
||||
|
||||
[GeneratedRegex("^[a-fA-F0-9]{12}$")]
|
||||
private static partial Regex MacRegex();
|
||||
[GeneratedRegex("^[a-fA-F0-9]{32}$")]
|
||||
private static partial Regex Md5Regex();
|
||||
}
|
48
Application/Game/Server/GetDataQuery.cs
Normal file
48
Application/Game/Server/GetDataQuery.cs
Normal file
@ -0,0 +1,48 @@
|
||||
using System.Text;
|
||||
using Application.Interfaces;
|
||||
using MediatR;
|
||||
|
||||
namespace Application.Game.Server;
|
||||
|
||||
public record GetDataQuery(string Host) : IRequest<string>;
|
||||
|
||||
public class GetDataQueryHandler : IRequestHandler<GetDataQuery, string>
|
||||
{
|
||||
private readonly IEventManagerService eventManagerService;
|
||||
|
||||
public GetDataQueryHandler(IEventManagerService eventManagerService)
|
||||
{
|
||||
this.eventManagerService = eventManagerService;
|
||||
}
|
||||
|
||||
public Task<string> Handle(GetDataQuery request, CancellationToken cancellationToken)
|
||||
{
|
||||
var response = "count=0\n" +
|
||||
"nexttime=0\n";
|
||||
if (!eventManagerService.UseEvents())
|
||||
{
|
||||
return Task.FromResult(response);
|
||||
}
|
||||
|
||||
var host = request.Host;
|
||||
var urlBase = $"http://{host}/events/";
|
||||
var dataString = new StringBuilder();
|
||||
var events = eventManagerService.GetEvents();
|
||||
var count = 0;
|
||||
foreach (var pair in events.Select((@event, i) => new {Value = @event, Index = i}))
|
||||
{
|
||||
var value = pair.Value;
|
||||
var index = pair.Index;
|
||||
var fileUrl = $"{urlBase}{value.Name}";
|
||||
var eventString = $"{index},{fileUrl},{value.NotBefore},{value.NotAfter},{value.Md5},{value.Index}";
|
||||
dataString.Append(eventString).Append('\n');
|
||||
count++;
|
||||
}
|
||||
|
||||
response = $"count={count}\n" +
|
||||
"nexttime=1\n" +
|
||||
$"{dataString}";
|
||||
|
||||
return Task.FromResult(response);
|
||||
}
|
||||
}
|
19
Application/Interfaces/ICardDbContext.cs
Normal file
19
Application/Interfaces/ICardDbContext.cs
Normal file
@ -0,0 +1,19 @@
|
||||
using Domain.Entities;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace Application.Interfaces;
|
||||
|
||||
public interface ICardDbContext
|
||||
{
|
||||
public DbSet<CardBdatum> CardBdata { get; set; }
|
||||
|
||||
public DbSet<CardDetail> CardDetails { get; set; }
|
||||
|
||||
public DbSet<CardMain> CardMains { get; set; }
|
||||
|
||||
public DbSet<CardPlayCount> CardPlayCounts { get; set; }
|
||||
|
||||
public Task<int> SaveChangesAsync(CancellationToken cancellationToken);
|
||||
|
||||
|
||||
}
|
12
Application/Interfaces/IEventManagerService.cs
Normal file
12
Application/Interfaces/IEventManagerService.cs
Normal file
@ -0,0 +1,12 @@
|
||||
using Domain.Models;
|
||||
|
||||
namespace Application.Interfaces;
|
||||
|
||||
public interface IEventManagerService
|
||||
{
|
||||
public void InitializeEvents();
|
||||
|
||||
public bool UseEvents();
|
||||
|
||||
public IEnumerable<Event> GetEvents();
|
||||
}
|
13
Application/Interfaces/IMusicDbContext.cs
Normal file
13
Application/Interfaces/IMusicDbContext.cs
Normal file
@ -0,0 +1,13 @@
|
||||
using Domain.Entities;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace Application.Interfaces;
|
||||
|
||||
public interface IMusicDbContext
|
||||
{
|
||||
public DbSet<MusicAou> MusicAous { get; set; }
|
||||
|
||||
public DbSet<MusicExtra> MusicExtras { get; set; }
|
||||
|
||||
public DbSet<MusicUnlock> MusicUnlocks { get; set; }
|
||||
}
|
11
Application/Mappers/CardMapper.cs
Normal file
11
Application/Mappers/CardMapper.cs
Normal file
@ -0,0 +1,11 @@
|
||||
using Application.Dto;
|
||||
using Domain.Entities;
|
||||
using Riok.Mapperly.Abstractions;
|
||||
|
||||
namespace Application.Mappers;
|
||||
|
||||
[Mapper]
|
||||
public static partial class CardMapper
|
||||
{
|
||||
public static partial CardDto CardMainToCardDto(this CardMain cardMain);
|
||||
}
|
17
Domain/Config/EventConfig.cs
Normal file
17
Domain/Config/EventConfig.cs
Normal file
@ -0,0 +1,17 @@
|
||||
namespace Domain.Config;
|
||||
|
||||
public class EventConfig
|
||||
{
|
||||
public const string EVENT_SECTION = "Events";
|
||||
|
||||
public bool UseEvents { get; set; }
|
||||
|
||||
public List<EventFile> EventFiles { get; set; } = new();
|
||||
}
|
||||
|
||||
public class EventFile
|
||||
{
|
||||
public string FileName { get; set; } = string.Empty;
|
||||
|
||||
public int Index { get; set; }
|
||||
}
|
10
Domain/Config/RelayConfig.cs
Normal file
10
Domain/Config/RelayConfig.cs
Normal file
@ -0,0 +1,10 @@
|
||||
namespace Domain.Config;
|
||||
|
||||
public class RelayConfig
|
||||
{
|
||||
public const string RELAY_SECTION = "Relay";
|
||||
|
||||
public string RelayServer { get; set; } = "127.0.0.1";
|
||||
|
||||
public int RelayPort { get; set; } = 3333;
|
||||
}
|
9
Domain/Domain.csproj
Normal file
9
Domain/Domain.csproj
Normal file
@ -0,0 +1,9 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
</Project>
|
10
Domain/Entities/CardBdatum.cs
Normal file
10
Domain/Entities/CardBdatum.cs
Normal file
@ -0,0 +1,10 @@
|
||||
namespace Domain.Entities;
|
||||
|
||||
public partial class CardBdatum
|
||||
{
|
||||
public long CardId { get; set; }
|
||||
|
||||
public string? Bdata { get; set; }
|
||||
|
||||
public long BdataSize { get; set; }
|
||||
}
|
38
Domain/Entities/CardDetail.cs
Normal file
38
Domain/Entities/CardDetail.cs
Normal file
@ -0,0 +1,38 @@
|
||||
namespace Domain.Entities;
|
||||
|
||||
public partial class CardDetail
|
||||
{
|
||||
public long CardId { get; set; }
|
||||
|
||||
public long Pcol1 { get; set; }
|
||||
|
||||
public long Pcol2 { get; set; }
|
||||
|
||||
public long Pcol3 { get; set; }
|
||||
|
||||
public long ScoreI1 { get; set; }
|
||||
|
||||
public long ScoreUi1 { get; set; }
|
||||
|
||||
public long ScoreUi2 { get; set; }
|
||||
|
||||
public long ScoreUi3 { get; set; }
|
||||
|
||||
public long ScoreUi4 { get; set; }
|
||||
|
||||
public long ScoreUi5 { get; set; }
|
||||
|
||||
public long ScoreUi6 { get; set; }
|
||||
|
||||
public long ScoreBi1 { get; set; }
|
||||
|
||||
public string? LastPlayTenpoId { get; set; } = string.Empty;
|
||||
|
||||
public long Fcol1 { get; set; }
|
||||
|
||||
public long Fcol2 { get; set; }
|
||||
|
||||
public long Fcol3 { get; set; }
|
||||
|
||||
public DateTime LastPlayTime { get; set; }
|
||||
}
|
22
Domain/Entities/CardMain.cs
Normal file
22
Domain/Entities/CardMain.cs
Normal file
@ -0,0 +1,22 @@
|
||||
namespace Domain.Entities;
|
||||
|
||||
public partial class CardMain
|
||||
{
|
||||
public long CardId { get; set; }
|
||||
|
||||
public string PlayerName { get; set; } = string.Empty;
|
||||
|
||||
public long ScoreI1 { get; set; }
|
||||
|
||||
public long Fcol1 { get; set; }
|
||||
|
||||
public long Fcol2 { get; set; }
|
||||
|
||||
public long Fcol3 { get; set; }
|
||||
|
||||
public string AchieveStatus { get; set; } = string.Empty;
|
||||
|
||||
public string? Created { get; set; } = string.Empty;
|
||||
|
||||
public string? Modified { get; set; } = string.Empty;
|
||||
}
|
10
Domain/Entities/CardPlayCount.cs
Normal file
10
Domain/Entities/CardPlayCount.cs
Normal file
@ -0,0 +1,10 @@
|
||||
namespace Domain.Entities;
|
||||
|
||||
public partial class CardPlayCount
|
||||
{
|
||||
public long CardId { get; set; }
|
||||
|
||||
public long PlayCount { get; set; }
|
||||
|
||||
public DateTime LastPlayedTime { get; set; }
|
||||
}
|
8
Domain/Entities/MusicAou.cs
Normal file
8
Domain/Entities/MusicAou.cs
Normal file
@ -0,0 +1,8 @@
|
||||
namespace Domain.Entities;
|
||||
|
||||
public class MusicAou
|
||||
{
|
||||
public long MusicId { get; set; }
|
||||
|
||||
public bool UseFlag { get; set; }
|
||||
}
|
8
Domain/Entities/MusicExtra.cs
Normal file
8
Domain/Entities/MusicExtra.cs
Normal file
@ -0,0 +1,8 @@
|
||||
namespace Domain.Entities;
|
||||
|
||||
public class MusicExtra
|
||||
{
|
||||
public long MusicId { get; set; }
|
||||
|
||||
public bool UseFlag { get; set; }
|
||||
}
|
18
Domain/Entities/MusicUnlock.cs
Normal file
18
Domain/Entities/MusicUnlock.cs
Normal file
@ -0,0 +1,18 @@
|
||||
namespace Domain.Entities;
|
||||
|
||||
public partial class MusicUnlock
|
||||
{
|
||||
public long MusicId { get; set; }
|
||||
|
||||
public string Title { get; set; } = string.Empty;
|
||||
|
||||
public string Artist { get; set; } = string.Empty;
|
||||
|
||||
public DateTime ReleaseDate { get; set; }
|
||||
|
||||
public DateTime EndDate { get; set; }
|
||||
|
||||
public bool NewFlag { get; set; }
|
||||
|
||||
public bool UseFlag { get; set; }
|
||||
}
|
14
Domain/Models/Event.cs
Normal file
14
Domain/Models/Event.cs
Normal file
@ -0,0 +1,14 @@
|
||||
namespace Domain.Models;
|
||||
|
||||
public class Event
|
||||
{
|
||||
public string Name { get; set; } = string.Empty;
|
||||
|
||||
public string Md5 { get; set; } = string.Empty;
|
||||
|
||||
public int Index { get; set; }
|
||||
|
||||
public string NotBefore { get; set; } = string.Empty;
|
||||
|
||||
public string NotAfter { get; set; } = string.Empty;
|
||||
}
|
@ -11,6 +11,16 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MudAdmin", "MudAdmin\MudAdm
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GCRelayServer", "GCRelayServer\GCRelayServer.csproj", "{268178DF-6345-4D9E-A389-9E809CBF39C9}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MatchServer", "MatchServer\MatchServer.csproj", "{CB91C3D3-ED69-4DC6-A205-B262A08BEE49}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MainServer", "MainServer\MainServer.csproj", "{B8C6CA7E-5E58-43BC-8E03-84306916DE39}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Application", "Application\Application.csproj", "{B0691233-0D7E-4694-8923-646E7A3BDBF4}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Domain", "Domain\Domain.csproj", "{CB95F4B7-7627-4CCA-A2B6-2D6FD48446C1}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Infrastructure", "Infrastructure\Infrastructure.csproj", "{2666D734-0E81-431E-A22D-216218FC9023}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
@ -33,6 +43,26 @@ Global
|
||||
{268178DF-6345-4D9E-A389-9E809CBF39C9}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{268178DF-6345-4D9E-A389-9E809CBF39C9}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{268178DF-6345-4D9E-A389-9E809CBF39C9}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{CB91C3D3-ED69-4DC6-A205-B262A08BEE49}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{CB91C3D3-ED69-4DC6-A205-B262A08BEE49}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{CB91C3D3-ED69-4DC6-A205-B262A08BEE49}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{CB91C3D3-ED69-4DC6-A205-B262A08BEE49}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{B8C6CA7E-5E58-43BC-8E03-84306916DE39}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{B8C6CA7E-5E58-43BC-8E03-84306916DE39}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{B8C6CA7E-5E58-43BC-8E03-84306916DE39}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{B8C6CA7E-5E58-43BC-8E03-84306916DE39}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{B0691233-0D7E-4694-8923-646E7A3BDBF4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{B0691233-0D7E-4694-8923-646E7A3BDBF4}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{B0691233-0D7E-4694-8923-646E7A3BDBF4}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{B0691233-0D7E-4694-8923-646E7A3BDBF4}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{CB95F4B7-7627-4CCA-A2B6-2D6FD48446C1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{CB95F4B7-7627-4CCA-A2B6-2D6FD48446C1}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{CB95F4B7-7627-4CCA-A2B6-2D6FD48446C1}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{CB95F4B7-7627-4CCA-A2B6-2D6FD48446C1}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{2666D734-0E81-431E-A22D-216218FC9023}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{2666D734-0E81-431E-A22D-216218FC9023}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{2666D734-0E81-431E-A22D-216218FC9023}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{2666D734-0E81-431E-A22D-216218FC9023}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
@ -1,4 +1,5 @@
|
||||
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
|
||||
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=ADDR/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=BDATA/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=Fcol/@EntryIndexedValue">True</s:Boolean>
|
||||
|
306
Infrastructure/Common/CertificateService.cs
Normal file
306
Infrastructure/Common/CertificateService.cs
Normal file
@ -0,0 +1,306 @@
|
||||
using System.Security.Cryptography;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
using CertificateManager;
|
||||
using CertificateManager.Models;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Infrastructure.Common;
|
||||
|
||||
public class CertificateService
|
||||
{
|
||||
private const X509KeyUsageFlags ROOT_CA_X509_KEY_USAGE_FLAGS = X509KeyUsageFlags.KeyCertSign |
|
||||
X509KeyUsageFlags.DataEncipherment |
|
||||
X509KeyUsageFlags.KeyEncipherment |
|
||||
X509KeyUsageFlags.DigitalSignature;
|
||||
|
||||
private const X509KeyStorageFlags X509_KEY_STORAGE_FLAGS_MACHINE = X509KeyStorageFlags.PersistKeySet |
|
||||
X509KeyStorageFlags.MachineKeySet |
|
||||
X509KeyStorageFlags.Exportable;
|
||||
|
||||
private const X509KeyUsageFlags CERT_X509_KEY_USAGE_FLAGS = X509KeyUsageFlags.DataEncipherment |
|
||||
X509KeyUsageFlags.KeyEncipherment |
|
||||
X509KeyUsageFlags.DigitalSignature;
|
||||
|
||||
private const string ROOT_CA_CN = "Taito Arcade Machine CA";
|
||||
private const string CERT_CN = "GC local server";
|
||||
private const string CERT_DIR = "Certificates";
|
||||
private const string CERT_FILE_NAME = "cert.pfx";
|
||||
private const string ROOT_CERT_FILE_NAME = "root.pfx";
|
||||
private static readonly string CERT_PATH = Path.Combine(CERT_DIR, CERT_FILE_NAME);
|
||||
private static readonly string ROOT_CERT_PATH = Path.Combine(CERT_DIR, ROOT_CERT_FILE_NAME);
|
||||
|
||||
private ILogger logger;
|
||||
|
||||
private static readonly DistinguishedName ROOT_CA_DISTINGUISHED_NAME = new()
|
||||
{
|
||||
CommonName = ROOT_CA_CN
|
||||
};
|
||||
|
||||
private static readonly DistinguishedName CERT_DISTINGUISHED_NAME = new()
|
||||
{
|
||||
CommonName = CERT_CN
|
||||
};
|
||||
|
||||
private static readonly BasicConstraints ROOT_CA_BASIC_CONSTRAINTS = new()
|
||||
{
|
||||
CertificateAuthority = true,
|
||||
HasPathLengthConstraint = true,
|
||||
PathLengthConstraint = 3,
|
||||
Critical = true
|
||||
};
|
||||
|
||||
public static readonly BasicConstraints CERT_BASIC_CONSTRAINTS = new()
|
||||
{
|
||||
CertificateAuthority = false,
|
||||
HasPathLengthConstraint = false,
|
||||
PathLengthConstraint = 0,
|
||||
Critical = true,
|
||||
};
|
||||
|
||||
private readonly SubjectAlternativeName subjectAlternativeName;
|
||||
|
||||
private static readonly ValidityPeriod VALIDITY_PERIOD = new()
|
||||
{
|
||||
ValidFrom = DateTime.UtcNow,
|
||||
ValidTo = DateTime.UtcNow.AddYears(3)
|
||||
};
|
||||
|
||||
private static readonly OidCollection OID_COLLECTION = new()
|
||||
{
|
||||
OidLookup.ServerAuthentication,
|
||||
OidLookup.AnyPurpose
|
||||
};
|
||||
|
||||
public CertificateService(string serverIp, ILogger logger)
|
||||
{
|
||||
this.logger = logger;
|
||||
subjectAlternativeName = new SubjectAlternativeName
|
||||
{
|
||||
DnsName = new List<string>
|
||||
{
|
||||
"localhost",
|
||||
"cert.nesys.jp",
|
||||
"nesys.taito.co.jp",
|
||||
"fjm170920zero.nesica.net"
|
||||
},
|
||||
IpAddress = System.Net.IPAddress.Parse(serverIp)
|
||||
};
|
||||
}
|
||||
|
||||
public X509Certificate2 InitializeCertificate()
|
||||
{
|
||||
return Environment.OSVersion.Platform == PlatformID.Win32NT
|
||||
? InitializeCertificateWindows()
|
||||
: InitializeCertificateOthers();
|
||||
}
|
||||
|
||||
private X509Certificate2 InitializeCertificateOthers()
|
||||
{
|
||||
if (CertificateExists())
|
||||
{
|
||||
return new X509Certificate2(CERT_PATH);
|
||||
}
|
||||
|
||||
logger.LogInformation("Existing certs not found! Removing old certificates and genrate new ones...");
|
||||
|
||||
File.Delete(CERT_PATH);
|
||||
File.Delete(ROOT_CERT_PATH);
|
||||
|
||||
return GenerateCertificate();
|
||||
}
|
||||
|
||||
private X509Certificate2 InitializeCertificateWindows()
|
||||
{
|
||||
if (CertificateExists())
|
||||
{
|
||||
var existingCert = GetCertificate(StoreName.My, StoreLocation.LocalMachine, CERT_CN);
|
||||
|
||||
if (existingCert != null)
|
||||
{
|
||||
return existingCert;
|
||||
}
|
||||
|
||||
logger.LogInformation("Existing CN not found! Removing old certificates and genrate new ones...");
|
||||
}
|
||||
|
||||
RemovePreviousCert(StoreName.My, StoreLocation.LocalMachine);
|
||||
RemovePreviousCert(StoreName.Root, StoreLocation.LocalMachine);
|
||||
|
||||
return GenerateCertificate();
|
||||
}
|
||||
|
||||
private X509Certificate2 GenerateCertificate()
|
||||
{
|
||||
var serviceProvider = new ServiceCollection()
|
||||
.AddCertificateManager().BuildServiceProvider();
|
||||
|
||||
var createCertificates = serviceProvider.GetService<CreateCertificates>();
|
||||
|
||||
if (createCertificates == null)
|
||||
{
|
||||
logger.LogError("Cannot initialize CreateCertificates service!");
|
||||
throw new Exception();
|
||||
}
|
||||
|
||||
var rootCa = createCertificates.NewRsaSelfSignedCertificate(
|
||||
ROOT_CA_DISTINGUISHED_NAME,
|
||||
ROOT_CA_BASIC_CONSTRAINTS,
|
||||
VALIDITY_PERIOD,
|
||||
new SubjectAlternativeName(),
|
||||
OID_COLLECTION,
|
||||
ROOT_CA_X509_KEY_USAGE_FLAGS,
|
||||
new RsaConfiguration()
|
||||
);
|
||||
|
||||
var cert = createCertificates.NewRsaChainedCertificate(
|
||||
CERT_DISTINGUISHED_NAME,
|
||||
CERT_BASIC_CONSTRAINTS,
|
||||
VALIDITY_PERIOD,
|
||||
subjectAlternativeName,
|
||||
rootCa,
|
||||
OID_COLLECTION,
|
||||
CERT_X509_KEY_USAGE_FLAGS,
|
||||
new RsaConfiguration()
|
||||
);
|
||||
|
||||
var exportService = serviceProvider.GetService<ImportExportCertificate>();
|
||||
|
||||
if (exportService == null)
|
||||
{
|
||||
logger.LogError("Cannot initialize ImportExportCertificate service!");
|
||||
throw new Exception();
|
||||
}
|
||||
|
||||
var rootCaPfxBytes = exportService.ExportRootPfx(null, rootCa);
|
||||
var certPfxBytes = exportService.ExportChainedCertificatePfx(null, cert, rootCa);
|
||||
|
||||
var rootCaWithPrivateKey = new X509Certificate2(rootCaPfxBytes, (string)null!,
|
||||
X509_KEY_STORAGE_FLAGS_MACHINE);
|
||||
|
||||
var certWithPrivateKey = new X509Certificate2(certPfxBytes, (string)null!,
|
||||
X509_KEY_STORAGE_FLAGS_MACHINE);
|
||||
|
||||
|
||||
if (Environment.OSVersion.Platform == PlatformID.Win32NT)
|
||||
{
|
||||
AddCertToStore(rootCaWithPrivateKey, StoreName.My, StoreLocation.LocalMachine);
|
||||
AddCertToStore(rootCaWithPrivateKey, StoreName.Root, StoreLocation.LocalMachine);
|
||||
AddCertToStore(certWithPrivateKey, StoreName.My, StoreLocation.LocalMachine);
|
||||
}
|
||||
|
||||
Directory.CreateDirectory(CERT_DIR);
|
||||
|
||||
File.WriteAllBytes(ROOT_CERT_PATH, rootCaWithPrivateKey.Export(X509ContentType.Pfx));
|
||||
File.WriteAllBytes(CERT_PATH, certWithPrivateKey.Export(X509ContentType.Pfx));
|
||||
|
||||
return certWithPrivateKey;
|
||||
}
|
||||
|
||||
private void AddCertToStore(X509Certificate2 cert, StoreName storeName, StoreLocation storeLocation)
|
||||
{
|
||||
try
|
||||
{
|
||||
var store = new X509Store(storeName, storeLocation);
|
||||
store.Open(OpenFlags.ReadWrite);
|
||||
store.Add(cert);
|
||||
|
||||
store.Close();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
logger.LogError(e, "An exception occurs when adding certificate");
|
||||
}
|
||||
}
|
||||
|
||||
private void RemovePreviousCert(StoreName storeName, StoreLocation storeLocation)
|
||||
{
|
||||
try
|
||||
{
|
||||
var store = new X509Store(storeName, storeLocation);
|
||||
store.Open(OpenFlags.ReadWrite);
|
||||
var result = store.Certificates.Find(X509FindType.FindByIssuerName, ROOT_CA_CN, true);
|
||||
|
||||
if (result.Any())
|
||||
{
|
||||
store.RemoveRange(result);
|
||||
logger.LogInformation("Removed previous certificates!");
|
||||
}
|
||||
|
||||
store.Close();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
logger.LogError(e, "An exception occurs when removing previous certificates");
|
||||
}
|
||||
}
|
||||
|
||||
private bool CertificateExists()
|
||||
{
|
||||
bool certificateExists;
|
||||
if (Environment.OSVersion.Platform != PlatformID.Win32NT)
|
||||
{
|
||||
// Non windows just use generated certificate file
|
||||
certificateExists = Path.Exists(Path.Combine(CERT_DIR, "cert.pfx")) &&
|
||||
Path.Exists(Path.Combine(CERT_DIR, "root.pfx"));
|
||||
}
|
||||
else
|
||||
{
|
||||
try
|
||||
{
|
||||
var store = new X509Store(StoreName.My, StoreLocation.LocalMachine);
|
||||
store.Open(OpenFlags.ReadOnly);
|
||||
var result = store.Certificates.Find(X509FindType.FindByIssuerName, ROOT_CA_CN, true);
|
||||
|
||||
certificateExists = result.Count == 2;
|
||||
|
||||
store.Close();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
logger.LogError(e, "An exception occurs when checking certificates");
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (certificateExists)
|
||||
{
|
||||
logger.LogInformation("Certificate exists!");
|
||||
}
|
||||
else
|
||||
{
|
||||
logger.LogInformation("Certificate not found! Will generate new certs...");
|
||||
}
|
||||
|
||||
return certificateExists;
|
||||
}
|
||||
|
||||
private X509Certificate2? GetCertificate(StoreName storeName, StoreLocation storeLocation, string commonName)
|
||||
{
|
||||
try
|
||||
{
|
||||
var store = new X509Store(storeName, storeLocation);
|
||||
store.Open(OpenFlags.ReadWrite);
|
||||
var result = store.Certificates.Find(X509FindType.FindBySubjectDistinguishedName,
|
||||
$"CN={commonName}", true);
|
||||
|
||||
if (result.Any())
|
||||
{
|
||||
logger.LogInformation("Certificate CN={CommonName} found!", commonName);
|
||||
|
||||
return result.First();
|
||||
}
|
||||
|
||||
store.Close();
|
||||
|
||||
return null;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
logger.LogError(e, "An exception occurs when getting certificate {CommonName}", commonName);
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
31
Infrastructure/Common/PathHelper.cs
Normal file
31
Infrastructure/Common/PathHelper.cs
Normal file
@ -0,0 +1,31 @@
|
||||
using System.Diagnostics;
|
||||
using Validation;
|
||||
|
||||
namespace Infrastructure.Common;
|
||||
|
||||
public static class PathHelper
|
||||
{
|
||||
public static string DatabasePath = Path.Combine(BasePath, "Database");
|
||||
|
||||
public static string ConfigurationPath = Path.Combine(BasePath, "Configurations");
|
||||
|
||||
public static string BasePath
|
||||
{
|
||||
get
|
||||
{
|
||||
var assemblyPath = Environment.ProcessPath;
|
||||
Assumes.NotNull(assemblyPath);
|
||||
|
||||
#if DEBUG
|
||||
var parentFullName = Directory.GetParent(assemblyPath)?.Parent?.Parent?.Parent?.FullName;
|
||||
|
||||
return parentFullName ?? "";
|
||||
|
||||
#else
|
||||
var parent = Directory.GetParent(assemblyPath);
|
||||
Assumes.NotNull(parent);
|
||||
return parent.ToString();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
46
Infrastructure/DependencyInjection.cs
Normal file
46
Infrastructure/DependencyInjection.cs
Normal file
@ -0,0 +1,46 @@
|
||||
using Application.Interfaces;
|
||||
using Infrastructure.Common;
|
||||
using Infrastructure.Persistence;
|
||||
using Infrastructure.Services;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace Infrastructure;
|
||||
|
||||
public static class DependencyInjection
|
||||
{
|
||||
public static IServiceCollection AddInfrastructure(this IServiceCollection services, IConfiguration configuration)
|
||||
{
|
||||
services.AddSingleton<IEventManagerService, EventManagerService>();
|
||||
|
||||
services.AddDbContext<MusicDbContext>(option =>
|
||||
{
|
||||
var dbName = configuration["MusicDbName"];
|
||||
if (string.IsNullOrEmpty(dbName))
|
||||
{
|
||||
dbName = "music471omni.db3";
|
||||
}
|
||||
|
||||
var path = Path.Combine(PathHelper.DatabasePath, dbName);
|
||||
option.UseSqlite($"Data Source={path}");
|
||||
});
|
||||
|
||||
services.AddDbContext<CardDbContext>(option =>
|
||||
{
|
||||
var dbName = configuration["CardDbName"];
|
||||
if (string.IsNullOrEmpty(dbName))
|
||||
{
|
||||
dbName = "card.db3";
|
||||
}
|
||||
|
||||
var path = Path.Combine(PathHelper.DatabasePath, dbName);
|
||||
option.UseSqlite($"Data Source={path}");
|
||||
});
|
||||
|
||||
services.AddScoped<ICardDbContext>(provider => provider.GetService<CardDbContext>() ?? throw new InvalidOperationException());
|
||||
services.AddScoped<IMusicDbContext>(provider => provider.GetService<MusicDbContext>() ?? throw new InvalidOperationException());
|
||||
|
||||
return services;
|
||||
}
|
||||
}
|
9
Infrastructure/Exceptions/EventExceptions.cs
Normal file
9
Infrastructure/Exceptions/EventExceptions.cs
Normal file
@ -0,0 +1,9 @@
|
||||
namespace Infrastructure.Exceptions;
|
||||
|
||||
public class EventFileNotFoundException : Exception
|
||||
{
|
||||
}
|
||||
|
||||
public class EventFileTypeUnknownException : Exception
|
||||
{
|
||||
}
|
29
Infrastructure/Infrastructure.csproj
Normal file
29
Infrastructure/Infrastructure.csproj
Normal file
@ -0,0 +1,29 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="CertificateManager" Version="1.0.8" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="7.0.2">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="7.0.2" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration" Version="7.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.FileExtensions" Version="7.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="7.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="7.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="7.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Options" Version="7.0.0" />
|
||||
<PackageReference Include="Validation" Version="2.6.13-beta" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Application\Application.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
182
Infrastructure/Migrations/20230208132952_Initial.Designer.cs
generated
Normal file
182
Infrastructure/Migrations/20230208132952_Initial.Designer.cs
generated
Normal file
@ -0,0 +1,182 @@
|
||||
// <auto-generated />
|
||||
using Infrastructure.Persistence;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Infrastructure.Migrations
|
||||
{
|
||||
[DbContext(typeof(CardDbContext))]
|
||||
[Migration("20230208132952_Initial")]
|
||||
partial class Initial
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder.HasAnnotation("ProductVersion", "7.0.2");
|
||||
|
||||
modelBuilder.Entity("Domain.Entities.CardBdatum", b =>
|
||||
{
|
||||
b.Property<long>("CardId")
|
||||
.HasColumnType("INTEGER")
|
||||
.HasColumnName("card_id");
|
||||
|
||||
b.Property<string>("Bdata")
|
||||
.HasColumnType("TEXT")
|
||||
.HasColumnName("bdata");
|
||||
|
||||
b.Property<long>("BdataSize")
|
||||
.HasColumnType("INTEGER")
|
||||
.HasColumnName("bdata_size");
|
||||
|
||||
b.HasKey("CardId");
|
||||
|
||||
b.ToTable("card_bdata", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Domain.Entities.CardDetail", b =>
|
||||
{
|
||||
b.Property<long>("CardId")
|
||||
.HasColumnType("INTEGER")
|
||||
.HasColumnName("card_id");
|
||||
|
||||
b.Property<long>("Pcol1")
|
||||
.HasColumnType("INTEGER")
|
||||
.HasColumnName("pcol1");
|
||||
|
||||
b.Property<long>("Pcol2")
|
||||
.HasColumnType("INTEGER")
|
||||
.HasColumnName("pcol2");
|
||||
|
||||
b.Property<long>("Pcol3")
|
||||
.HasColumnType("INTEGER")
|
||||
.HasColumnName("pcol3");
|
||||
|
||||
b.Property<long>("Fcol1")
|
||||
.HasColumnType("INTEGER")
|
||||
.HasColumnName("fcol1");
|
||||
|
||||
b.Property<long>("Fcol2")
|
||||
.HasColumnType("INTEGER")
|
||||
.HasColumnName("fcol2");
|
||||
|
||||
b.Property<long>("Fcol3")
|
||||
.HasColumnType("INTEGER")
|
||||
.HasColumnName("fcol3");
|
||||
|
||||
b.Property<string>("LastPlayTenpoId")
|
||||
.HasColumnType("TEXT")
|
||||
.HasColumnName("last_play_tenpo_id");
|
||||
|
||||
b.Property<long>("LastPlayTime")
|
||||
.HasColumnType("INTEGER")
|
||||
.HasColumnName("last_play_time");
|
||||
|
||||
b.Property<long>("ScoreBi1")
|
||||
.HasColumnType("INTEGER")
|
||||
.HasColumnName("score_bi1");
|
||||
|
||||
b.Property<long>("ScoreI1")
|
||||
.HasColumnType("INTEGER")
|
||||
.HasColumnName("score_i1");
|
||||
|
||||
b.Property<long>("ScoreUi1")
|
||||
.HasColumnType("INTEGER")
|
||||
.HasColumnName("score_ui1");
|
||||
|
||||
b.Property<long>("ScoreUi2")
|
||||
.HasColumnType("INTEGER")
|
||||
.HasColumnName("score_ui2");
|
||||
|
||||
b.Property<long>("ScoreUi3")
|
||||
.HasColumnType("INTEGER")
|
||||
.HasColumnName("score_ui3");
|
||||
|
||||
b.Property<long>("ScoreUi4")
|
||||
.HasColumnType("INTEGER")
|
||||
.HasColumnName("score_ui4");
|
||||
|
||||
b.Property<long>("ScoreUi5")
|
||||
.HasColumnType("INTEGER")
|
||||
.HasColumnName("score_ui5");
|
||||
|
||||
b.Property<long>("ScoreUi6")
|
||||
.HasColumnType("INTEGER")
|
||||
.HasColumnName("score_ui6");
|
||||
|
||||
b.HasKey("CardId", "Pcol1", "Pcol2", "Pcol3");
|
||||
|
||||
b.ToTable("card_detail", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Domain.Entities.CardMain", b =>
|
||||
{
|
||||
b.Property<long>("CardId")
|
||||
.HasColumnType("INTEGER")
|
||||
.HasColumnName("card_id");
|
||||
|
||||
b.Property<string>("AchieveStatus")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT")
|
||||
.HasColumnName("achieve_status");
|
||||
|
||||
b.Property<string>("Created")
|
||||
.HasColumnType("TEXT")
|
||||
.HasColumnName("created");
|
||||
|
||||
b.Property<long>("Fcol1")
|
||||
.HasColumnType("INTEGER")
|
||||
.HasColumnName("fcol1");
|
||||
|
||||
b.Property<long>("Fcol2")
|
||||
.HasColumnType("INTEGER")
|
||||
.HasColumnName("fcol2");
|
||||
|
||||
b.Property<long>("Fcol3")
|
||||
.HasColumnType("INTEGER")
|
||||
.HasColumnName("fcol3");
|
||||
|
||||
b.Property<string>("Modified")
|
||||
.HasColumnType("TEXT")
|
||||
.HasColumnName("modified");
|
||||
|
||||
b.Property<string>("PlayerName")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT")
|
||||
.HasColumnName("player_name");
|
||||
|
||||
b.Property<long>("ScoreI1")
|
||||
.HasColumnType("INTEGER")
|
||||
.HasColumnName("score_i1");
|
||||
|
||||
b.HasKey("CardId");
|
||||
|
||||
b.ToTable("card_main", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Domain.Entities.CardPlayCount", b =>
|
||||
{
|
||||
b.Property<long>("CardId")
|
||||
.HasColumnType("INTEGER")
|
||||
.HasColumnName("card_id");
|
||||
|
||||
b.Property<long>("LastPlayedTime")
|
||||
.HasColumnType("INTEGER")
|
||||
.HasColumnName("last_played_time");
|
||||
|
||||
b.Property<long>("PlayCount")
|
||||
.HasColumnType("INTEGER")
|
||||
.HasColumnName("play_count");
|
||||
|
||||
b.HasKey("CardId");
|
||||
|
||||
b.ToTable("CardPlayCount", (string)null);
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
102
Infrastructure/Migrations/20230208132952_Initial.cs
Normal file
102
Infrastructure/Migrations/20230208132952_Initial.cs
Normal file
@ -0,0 +1,102 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Infrastructure.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class Initial : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.CreateTable(
|
||||
name: "card_bdata",
|
||||
columns: table => new
|
||||
{
|
||||
cardid = table.Column<long>(name: "card_id", type: "INTEGER", nullable: false),
|
||||
bdata = table.Column<string>(type: "TEXT", nullable: true),
|
||||
bdatasize = table.Column<long>(name: "bdata_size", type: "INTEGER", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_card_bdata", x => x.cardid);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "card_detail",
|
||||
columns: table => new
|
||||
{
|
||||
cardid = table.Column<long>(name: "card_id", type: "INTEGER", nullable: false),
|
||||
pcol1 = table.Column<long>(type: "INTEGER", nullable: false),
|
||||
pcol2 = table.Column<long>(type: "INTEGER", nullable: false),
|
||||
pcol3 = table.Column<long>(type: "INTEGER", nullable: false),
|
||||
scorei1 = table.Column<long>(name: "score_i1", type: "INTEGER", nullable: false),
|
||||
scoreui1 = table.Column<long>(name: "score_ui1", type: "INTEGER", nullable: false),
|
||||
scoreui2 = table.Column<long>(name: "score_ui2", type: "INTEGER", nullable: false),
|
||||
scoreui3 = table.Column<long>(name: "score_ui3", type: "INTEGER", nullable: false),
|
||||
scoreui4 = table.Column<long>(name: "score_ui4", type: "INTEGER", nullable: false),
|
||||
scoreui5 = table.Column<long>(name: "score_ui5", type: "INTEGER", nullable: false),
|
||||
scoreui6 = table.Column<long>(name: "score_ui6", type: "INTEGER", nullable: false),
|
||||
scorebi1 = table.Column<long>(name: "score_bi1", type: "INTEGER", nullable: false),
|
||||
lastplaytenpoid = table.Column<string>(name: "last_play_tenpo_id", type: "TEXT", nullable: true),
|
||||
fcol1 = table.Column<long>(type: "INTEGER", nullable: false),
|
||||
fcol2 = table.Column<long>(type: "INTEGER", nullable: false),
|
||||
fcol3 = table.Column<long>(type: "INTEGER", nullable: false),
|
||||
lastplaytime = table.Column<long>(name: "last_play_time", type: "INTEGER", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_card_detail", x => new { x.cardid, x.pcol1, x.pcol2, x.pcol3 });
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "card_main",
|
||||
columns: table => new
|
||||
{
|
||||
cardid = table.Column<long>(name: "card_id", type: "INTEGER", nullable: false),
|
||||
playername = table.Column<string>(name: "player_name", type: "TEXT", nullable: false),
|
||||
scorei1 = table.Column<long>(name: "score_i1", type: "INTEGER", nullable: false),
|
||||
fcol1 = table.Column<long>(type: "INTEGER", nullable: false),
|
||||
fcol2 = table.Column<long>(type: "INTEGER", nullable: false),
|
||||
fcol3 = table.Column<long>(type: "INTEGER", nullable: false),
|
||||
achievestatus = table.Column<string>(name: "achieve_status", type: "TEXT", nullable: false),
|
||||
created = table.Column<string>(type: "TEXT", nullable: true),
|
||||
modified = table.Column<string>(type: "TEXT", nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_card_main", x => x.cardid);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "CardPlayCount",
|
||||
columns: table => new
|
||||
{
|
||||
cardid = table.Column<long>(name: "card_id", type: "INTEGER", nullable: false),
|
||||
playcount = table.Column<long>(name: "play_count", type: "INTEGER", nullable: false),
|
||||
lastplayedtime = table.Column<long>(name: "last_played_time", type: "INTEGER", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_CardPlayCount", x => x.cardid);
|
||||
});
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropTable(
|
||||
name: "card_bdata");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "card_detail");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "card_main");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "CardPlayCount");
|
||||
}
|
||||
}
|
||||
}
|
179
Infrastructure/Migrations/CardDbContextModelSnapshot.cs
Normal file
179
Infrastructure/Migrations/CardDbContextModelSnapshot.cs
Normal file
@ -0,0 +1,179 @@
|
||||
// <auto-generated />
|
||||
using Infrastructure.Persistence;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Infrastructure.Migrations
|
||||
{
|
||||
[DbContext(typeof(CardDbContext))]
|
||||
partial class CardDbContextModelSnapshot : ModelSnapshot
|
||||
{
|
||||
protected override void BuildModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder.HasAnnotation("ProductVersion", "7.0.2");
|
||||
|
||||
modelBuilder.Entity("Domain.Entities.CardBdatum", b =>
|
||||
{
|
||||
b.Property<long>("CardId")
|
||||
.HasColumnType("INTEGER")
|
||||
.HasColumnName("card_id");
|
||||
|
||||
b.Property<string>("Bdata")
|
||||
.HasColumnType("TEXT")
|
||||
.HasColumnName("bdata");
|
||||
|
||||
b.Property<long>("BdataSize")
|
||||
.HasColumnType("INTEGER")
|
||||
.HasColumnName("bdata_size");
|
||||
|
||||
b.HasKey("CardId");
|
||||
|
||||
b.ToTable("card_bdata", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Domain.Entities.CardDetail", b =>
|
||||
{
|
||||
b.Property<long>("CardId")
|
||||
.HasColumnType("INTEGER")
|
||||
.HasColumnName("card_id");
|
||||
|
||||
b.Property<long>("Pcol1")
|
||||
.HasColumnType("INTEGER")
|
||||
.HasColumnName("pcol1");
|
||||
|
||||
b.Property<long>("Pcol2")
|
||||
.HasColumnType("INTEGER")
|
||||
.HasColumnName("pcol2");
|
||||
|
||||
b.Property<long>("Pcol3")
|
||||
.HasColumnType("INTEGER")
|
||||
.HasColumnName("pcol3");
|
||||
|
||||
b.Property<long>("Fcol1")
|
||||
.HasColumnType("INTEGER")
|
||||
.HasColumnName("fcol1");
|
||||
|
||||
b.Property<long>("Fcol2")
|
||||
.HasColumnType("INTEGER")
|
||||
.HasColumnName("fcol2");
|
||||
|
||||
b.Property<long>("Fcol3")
|
||||
.HasColumnType("INTEGER")
|
||||
.HasColumnName("fcol3");
|
||||
|
||||
b.Property<string>("LastPlayTenpoId")
|
||||
.HasColumnType("TEXT")
|
||||
.HasColumnName("last_play_tenpo_id");
|
||||
|
||||
b.Property<long>("LastPlayTime")
|
||||
.HasColumnType("INTEGER")
|
||||
.HasColumnName("last_play_time");
|
||||
|
||||
b.Property<long>("ScoreBi1")
|
||||
.HasColumnType("INTEGER")
|
||||
.HasColumnName("score_bi1");
|
||||
|
||||
b.Property<long>("ScoreI1")
|
||||
.HasColumnType("INTEGER")
|
||||
.HasColumnName("score_i1");
|
||||
|
||||
b.Property<long>("ScoreUi1")
|
||||
.HasColumnType("INTEGER")
|
||||
.HasColumnName("score_ui1");
|
||||
|
||||
b.Property<long>("ScoreUi2")
|
||||
.HasColumnType("INTEGER")
|
||||
.HasColumnName("score_ui2");
|
||||
|
||||
b.Property<long>("ScoreUi3")
|
||||
.HasColumnType("INTEGER")
|
||||
.HasColumnName("score_ui3");
|
||||
|
||||
b.Property<long>("ScoreUi4")
|
||||
.HasColumnType("INTEGER")
|
||||
.HasColumnName("score_ui4");
|
||||
|
||||
b.Property<long>("ScoreUi5")
|
||||
.HasColumnType("INTEGER")
|
||||
.HasColumnName("score_ui5");
|
||||
|
||||
b.Property<long>("ScoreUi6")
|
||||
.HasColumnType("INTEGER")
|
||||
.HasColumnName("score_ui6");
|
||||
|
||||
b.HasKey("CardId", "Pcol1", "Pcol2", "Pcol3");
|
||||
|
||||
b.ToTable("card_detail", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Domain.Entities.CardMain", b =>
|
||||
{
|
||||
b.Property<long>("CardId")
|
||||
.HasColumnType("INTEGER")
|
||||
.HasColumnName("card_id");
|
||||
|
||||
b.Property<string>("AchieveStatus")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT")
|
||||
.HasColumnName("achieve_status");
|
||||
|
||||
b.Property<string>("Created")
|
||||
.HasColumnType("TEXT")
|
||||
.HasColumnName("created");
|
||||
|
||||
b.Property<long>("Fcol1")
|
||||
.HasColumnType("INTEGER")
|
||||
.HasColumnName("fcol1");
|
||||
|
||||
b.Property<long>("Fcol2")
|
||||
.HasColumnType("INTEGER")
|
||||
.HasColumnName("fcol2");
|
||||
|
||||
b.Property<long>("Fcol3")
|
||||
.HasColumnType("INTEGER")
|
||||
.HasColumnName("fcol3");
|
||||
|
||||
b.Property<string>("Modified")
|
||||
.HasColumnType("TEXT")
|
||||
.HasColumnName("modified");
|
||||
|
||||
b.Property<string>("PlayerName")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT")
|
||||
.HasColumnName("player_name");
|
||||
|
||||
b.Property<long>("ScoreI1")
|
||||
.HasColumnType("INTEGER")
|
||||
.HasColumnName("score_i1");
|
||||
|
||||
b.HasKey("CardId");
|
||||
|
||||
b.ToTable("card_main", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Domain.Entities.CardPlayCount", b =>
|
||||
{
|
||||
b.Property<long>("CardId")
|
||||
.HasColumnType("INTEGER")
|
||||
.HasColumnName("card_id");
|
||||
|
||||
b.Property<long>("LastPlayedTime")
|
||||
.HasColumnType("INTEGER")
|
||||
.HasColumnName("last_played_time");
|
||||
|
||||
b.Property<long>("PlayCount")
|
||||
.HasColumnType("INTEGER")
|
||||
.HasColumnName("play_count");
|
||||
|
||||
b.HasKey("CardId");
|
||||
|
||||
b.ToTable("CardPlayCount", (string)null);
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
34
Infrastructure/Migrations/MigrationHelper.cs
Normal file
34
Infrastructure/Migrations/MigrationHelper.cs
Normal file
@ -0,0 +1,34 @@
|
||||
using System.Data;
|
||||
using Infrastructure.Common;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
|
||||
namespace Infrastructure.Migrations;
|
||||
|
||||
public static class MigrationHelper
|
||||
{
|
||||
public static bool Exists(string tableName)
|
||||
{
|
||||
var options = new DbContextOptionsBuilder().UseSqlite($"Data Source={GetConnectionString()}");
|
||||
using var context = new DbContext(options.Options);
|
||||
using var command = context.Database.GetDbConnection().CreateCommand();
|
||||
command.CommandText = $"SELECT count(*) FROM sqlite_master WHERE type='table' AND name='{tableName}';";
|
||||
command.CommandType = CommandType.Text;
|
||||
context.Database.OpenConnection();
|
||||
|
||||
using var reader = command.ExecuteReader();
|
||||
|
||||
return reader.Read()? (long)reader[0] == 1 : false;
|
||||
}
|
||||
|
||||
public static string GetConnectionString()
|
||||
{
|
||||
var builder = new ConfigurationBuilder()
|
||||
.SetBasePath(PathHelper.ConfigurationPath)
|
||||
.AddJsonFile("database.json", optional: false, reloadOnChange: false);
|
||||
|
||||
var cardDbName = builder.Build()["CardDbName"];
|
||||
var cardDbPath = Path.Combine(PathHelper.DatabasePath, cardDbName);
|
||||
return cardDbPath;
|
||||
}
|
||||
}
|
118
Infrastructure/Persistence/CardDbContext.cs
Normal file
118
Infrastructure/Persistence/CardDbContext.cs
Normal file
@ -0,0 +1,118 @@
|
||||
using Application.Interfaces;
|
||||
using Domain.Entities;
|
||||
using Infrastructure.Common;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
|
||||
namespace Infrastructure.Persistence;
|
||||
|
||||
public partial class CardDbContext : DbContext, ICardDbContext
|
||||
{
|
||||
public CardDbContext()
|
||||
{
|
||||
}
|
||||
|
||||
public CardDbContext(DbContextOptions<CardDbContext> options)
|
||||
: base(options)
|
||||
{
|
||||
}
|
||||
|
||||
public virtual DbSet<CardBdatum> CardBdata { get; set; } = null!;
|
||||
|
||||
public virtual DbSet<CardDetail> CardDetails { get; set; } = null!;
|
||||
|
||||
public virtual DbSet<CardMain> CardMains { get; set; } = null!;
|
||||
|
||||
public virtual DbSet<CardPlayCount> CardPlayCounts { get; set; } = null!;
|
||||
|
||||
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
|
||||
{
|
||||
if (optionsBuilder.IsConfigured)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var defaultDb = Path.Combine(PathHelper.DatabasePath, "card.db3");
|
||||
optionsBuilder.UseSqlite($"Data Source={defaultDb}");
|
||||
}
|
||||
|
||||
|
||||
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
||||
{
|
||||
modelBuilder.Entity<CardBdatum>(entity =>
|
||||
{
|
||||
entity.HasKey(e => e.CardId);
|
||||
|
||||
entity.ToTable("card_bdata");
|
||||
|
||||
entity.Property(e => e.CardId)
|
||||
.ValueGeneratedNever()
|
||||
.HasColumnName("card_id");
|
||||
entity.Property(e => e.Bdata).HasColumnName("bdata");
|
||||
entity.Property(e => e.BdataSize).HasColumnName("bdata_size");
|
||||
});
|
||||
|
||||
modelBuilder.Entity<CardDetail>(entity =>
|
||||
{
|
||||
entity.HasKey(e => new { e.CardId, e.Pcol1, e.Pcol2, e.Pcol3 });
|
||||
|
||||
entity.ToTable("card_detail");
|
||||
|
||||
entity.Property(e => e.CardId).HasColumnName("card_id");
|
||||
entity.Property(e => e.Pcol1).HasColumnName("pcol1");
|
||||
entity.Property(e => e.Pcol2).HasColumnName("pcol2");
|
||||
entity.Property(e => e.Pcol3).HasColumnName("pcol3");
|
||||
entity.Property(e => e.Fcol1).HasColumnName("fcol1");
|
||||
entity.Property(e => e.Fcol2).HasColumnName("fcol2");
|
||||
entity.Property(e => e.Fcol3).HasColumnName("fcol3");
|
||||
entity.Property(e => e.LastPlayTenpoId).HasColumnName("last_play_tenpo_id").IsRequired(false);
|
||||
entity.Property(e => e.LastPlayTime).HasColumnName("last_play_time")
|
||||
.HasConversion<DateTimeToTicksConverter>();
|
||||
entity.Property(e => e.ScoreBi1).HasColumnName("score_bi1");
|
||||
entity.Property(e => e.ScoreI1).HasColumnName("score_i1");
|
||||
entity.Property(e => e.ScoreUi1).HasColumnName("score_ui1");
|
||||
entity.Property(e => e.ScoreUi2).HasColumnName("score_ui2");
|
||||
entity.Property(e => e.ScoreUi3).HasColumnName("score_ui3");
|
||||
entity.Property(e => e.ScoreUi4).HasColumnName("score_ui4");
|
||||
entity.Property(e => e.ScoreUi5).HasColumnName("score_ui5");
|
||||
entity.Property(e => e.ScoreUi6).HasColumnName("score_ui6");
|
||||
});
|
||||
|
||||
modelBuilder.Entity<CardMain>(entity =>
|
||||
{
|
||||
entity.HasKey(e => e.CardId);
|
||||
|
||||
entity.ToTable("card_main");
|
||||
|
||||
entity.Property(e => e.CardId)
|
||||
.ValueGeneratedNever()
|
||||
.HasColumnName("card_id");
|
||||
entity.Property(e => e.AchieveStatus).HasColumnName("achieve_status");
|
||||
entity.Property(e => e.Created).HasColumnName("created").IsRequired(false);
|
||||
entity.Property(e => e.Fcol1).HasColumnName("fcol1");
|
||||
entity.Property(e => e.Fcol2).HasColumnName("fcol2");
|
||||
entity.Property(e => e.Fcol3).HasColumnName("fcol3");
|
||||
entity.Property(e => e.Modified).HasColumnName("modified").IsRequired(false);
|
||||
entity.Property(e => e.PlayerName).HasColumnName("player_name");
|
||||
entity.Property(e => e.ScoreI1).HasColumnName("score_i1");
|
||||
});
|
||||
|
||||
modelBuilder.Entity<CardPlayCount>(entity =>
|
||||
{
|
||||
entity.HasKey(e => e.CardId);
|
||||
|
||||
entity.ToTable("CardPlayCount");
|
||||
|
||||
entity.Property(e => e.CardId)
|
||||
.ValueGeneratedNever()
|
||||
.HasColumnName("card_id");
|
||||
entity.Property(e => e.LastPlayedTime).HasColumnName("last_played_time")
|
||||
.HasConversion<DateTimeToTicksConverter>();
|
||||
entity.Property(e => e.PlayCount).HasColumnName("play_count");
|
||||
});
|
||||
|
||||
OnModelCreatingPartial(modelBuilder);
|
||||
}
|
||||
|
||||
partial void OnModelCreatingPartial(ModelBuilder modelBuilder);
|
||||
}
|
82
Infrastructure/Persistence/MusicDbContext.cs
Normal file
82
Infrastructure/Persistence/MusicDbContext.cs
Normal file
@ -0,0 +1,82 @@
|
||||
using Application.Interfaces;
|
||||
using Domain.Entities;
|
||||
using Infrastructure.Common;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace Infrastructure.Persistence;
|
||||
|
||||
public partial class MusicDbContext : DbContext, IMusicDbContext
|
||||
{
|
||||
public MusicDbContext()
|
||||
{
|
||||
}
|
||||
|
||||
public MusicDbContext(DbContextOptions<MusicDbContext> options)
|
||||
: base(options)
|
||||
{
|
||||
}
|
||||
|
||||
public virtual DbSet<MusicAou> MusicAous { get; set; } = null!;
|
||||
|
||||
public virtual DbSet<MusicExtra> MusicExtras { get; set; } = null!;
|
||||
|
||||
public virtual DbSet<MusicUnlock> MusicUnlocks { get; set; } = null!;
|
||||
|
||||
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
|
||||
{
|
||||
if (optionsBuilder.IsConfigured)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var defaultDb = Path.Combine(PathHelper.DatabasePath, "music471omni.db3");
|
||||
optionsBuilder.UseSqlite($"Data Source={defaultDb}");
|
||||
}
|
||||
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
||||
{
|
||||
modelBuilder.Entity<MusicAou>(entity =>
|
||||
{
|
||||
entity.HasKey(e => e.MusicId);
|
||||
|
||||
entity.ToTable("music_aou");
|
||||
|
||||
entity.Property(e => e.MusicId)
|
||||
.ValueGeneratedNever()
|
||||
.HasColumnName("music_id");
|
||||
entity.Property(e => e.UseFlag).HasColumnName("use_flag");
|
||||
});
|
||||
|
||||
modelBuilder.Entity<MusicExtra>(entity =>
|
||||
{
|
||||
entity.HasKey(e => e.MusicId);
|
||||
|
||||
entity.ToTable("music_extra");
|
||||
|
||||
entity.Property(e => e.MusicId)
|
||||
.ValueGeneratedNever()
|
||||
.HasColumnName("music_id");
|
||||
entity.Property(e => e.UseFlag).HasColumnName("use_flag");
|
||||
});
|
||||
|
||||
modelBuilder.Entity<MusicUnlock>(entity =>
|
||||
{
|
||||
entity.HasKey(e => e.MusicId);
|
||||
|
||||
entity.ToTable("music_unlock");
|
||||
|
||||
entity.Property(e => e.MusicId)
|
||||
.ValueGeneratedNever()
|
||||
.HasColumnName("music_id");
|
||||
entity.Property(e => e.Artist).HasColumnName("artist");
|
||||
entity.Property(e => e.Title).HasColumnName("title");
|
||||
entity.Property(e => e.ReleaseDate).HasColumnName("release_date");
|
||||
entity.Property(e => e.EndDate).HasColumnName("end_date");
|
||||
entity.Property(e => e.NewFlag).HasColumnName("new_flag");
|
||||
entity.Property(e => e.UseFlag).HasColumnName("use_flag");
|
||||
});
|
||||
|
||||
OnModelCreatingPartial(modelBuilder);
|
||||
}
|
||||
|
||||
partial void OnModelCreatingPartial(ModelBuilder modelBuilder);
|
||||
}
|
160
Infrastructure/Services/EventManagerService.cs
Normal file
160
Infrastructure/Services/EventManagerService.cs
Normal file
@ -0,0 +1,160 @@
|
||||
using System.Security.Cryptography;
|
||||
using Application.Interfaces;
|
||||
using Domain.Config;
|
||||
using Domain.Models;
|
||||
using Infrastructure.Exceptions;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
|
||||
namespace Infrastructure.Services;
|
||||
|
||||
public class EventManagerService : IEventManagerService
|
||||
{
|
||||
#region Constants
|
||||
|
||||
private const string WWWROOT = "wwwroot";
|
||||
|
||||
private const string EVENT_FOLDER = "events";
|
||||
|
||||
private static readonly DateTimeOffset NOT_BEFORE = new(new DateTime(2013, 1, 1));
|
||||
|
||||
private static readonly DateTimeOffset NOT_AFTER = NOT_BEFORE + new TimeSpan(360 * 20, 0, 0, 0);
|
||||
|
||||
private static readonly string NOT_BEFORE_STRING = NOT_BEFORE.ToUnixTimeSeconds().ToString();
|
||||
|
||||
private static readonly string NOT_AFTER_STRING = NOT_AFTER.ToUnixTimeSeconds().ToString();
|
||||
|
||||
#endregion
|
||||
|
||||
private readonly ILogger<EventManagerService> logger;
|
||||
|
||||
private readonly EventConfig config;
|
||||
|
||||
private readonly List<Event> events;
|
||||
|
||||
private readonly bool useEvents;
|
||||
|
||||
public EventManagerService(IOptions<EventConfig> config, ILogger<EventManagerService> logger)
|
||||
{
|
||||
this.logger = logger;
|
||||
this.config = config.Value;
|
||||
events = new List<Event>();
|
||||
useEvents = this.config.UseEvents;
|
||||
}
|
||||
|
||||
public bool UseEvents()
|
||||
{
|
||||
return useEvents;
|
||||
}
|
||||
|
||||
public IEnumerable<Event> GetEvents()
|
||||
{
|
||||
return events;
|
||||
}
|
||||
|
||||
public void InitializeEvents()
|
||||
{
|
||||
foreach (var eventData in config.EventFiles)
|
||||
{
|
||||
var filePath = Path.Combine(WWWROOT, EVENT_FOLDER, eventData.FileName);
|
||||
if (!File.Exists(filePath))
|
||||
{
|
||||
logger.LogError("Event file {File} not found at path {Path}!", eventData.FileName,
|
||||
filePath);
|
||||
throw new EventFileNotFoundException();
|
||||
}
|
||||
|
||||
var md5 = ComputeFileMd5(filePath);
|
||||
var @event = new Event
|
||||
{
|
||||
Name = eventData.FileName,
|
||||
Md5 = md5,
|
||||
NotBefore = NOT_BEFORE_STRING,
|
||||
NotAfter = NOT_AFTER_STRING
|
||||
};
|
||||
|
||||
var eventType = DetermineFileType(eventData.FileName);
|
||||
@event.Index = eventType switch
|
||||
{
|
||||
EventFileType.Event => 0,
|
||||
EventFileType.EventRegPic => 1,
|
||||
EventFileType.EventSgRegPic => 2,
|
||||
EventFileType.NewsBigPic => eventData.Index,
|
||||
EventFileType.NewsSmallPic => 1,
|
||||
EventFileType.Telop => 0,
|
||||
EventFileType.EventCmp => 8,
|
||||
_ => throw new ArgumentOutOfRangeException()
|
||||
};
|
||||
events.Add(@event);
|
||||
}
|
||||
|
||||
if (events.Exists(event1 => event1.Name.StartsWith("news_big_") && event1.Index == 0))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
logger.LogWarning("No big news image with index 0! Changing a random one...");
|
||||
events.First(event1 => event1.Name.StartsWith("news_big_")).Index = 0;
|
||||
}
|
||||
|
||||
private EventFileType DetermineFileType(string fileName)
|
||||
{
|
||||
if (fileName.EndsWith(".evt"))
|
||||
{
|
||||
return EventFileType.Event;
|
||||
}
|
||||
|
||||
if (fileName.EndsWith(".cmp"))
|
||||
{
|
||||
return EventFileType.EventCmp;
|
||||
}
|
||||
|
||||
if (fileName.EndsWith(".txt"))
|
||||
{
|
||||
return EventFileType.Telop;
|
||||
}
|
||||
|
||||
if (fileName.Contains("_reg"))
|
||||
{
|
||||
return EventFileType.EventRegPic;
|
||||
}
|
||||
|
||||
if (fileName.Contains("_sgreg"))
|
||||
{
|
||||
return EventFileType.EventSgRegPic;
|
||||
}
|
||||
|
||||
if (fileName.StartsWith("news_big_"))
|
||||
{
|
||||
return EventFileType.NewsBigPic;
|
||||
}
|
||||
|
||||
if (fileName.StartsWith("news_small_"))
|
||||
{
|
||||
return EventFileType.NewsSmallPic;
|
||||
}
|
||||
|
||||
logger.LogError("Unknown event file type for file {File}", fileName);
|
||||
throw new EventFileTypeUnknownException();
|
||||
}
|
||||
|
||||
private static string ComputeFileMd5(string filePath)
|
||||
{
|
||||
using var file = File.OpenRead(filePath);
|
||||
var hash = MD5.HashData(file);
|
||||
var result = BitConverter.ToString(hash).Replace("-", "").ToLower();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private enum EventFileType
|
||||
{
|
||||
Event,
|
||||
EventRegPic,
|
||||
EventSgRegPic,
|
||||
NewsBigPic,
|
||||
NewsSmallPic,
|
||||
Telop,
|
||||
EventCmp
|
||||
}
|
||||
}
|
442
MainServer/.gitignore
vendored
Normal file
442
MainServer/.gitignore
vendored
Normal file
@ -0,0 +1,442 @@
|
||||
### JetBrains template
|
||||
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider
|
||||
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
|
||||
|
||||
# User-specific stuff
|
||||
.idea/**/workspace.xml
|
||||
.idea/**/tasks.xml
|
||||
.idea/**/usage.statistics.xml
|
||||
.idea/**/dictionaries
|
||||
.idea/**/shelf
|
||||
|
||||
# Generated files
|
||||
.idea/**/contentModel.xml
|
||||
|
||||
# Sensitive or high-churn files
|
||||
.idea/**/dataSources/
|
||||
.idea/**/dataSources.ids
|
||||
.idea/**/dataSources.local.xml
|
||||
.idea/**/sqlDataSources.xml
|
||||
.idea/**/dynamic.xml
|
||||
.idea/**/uiDesigner.xml
|
||||
.idea/**/dbnavigator.xml
|
||||
|
||||
# Gradle
|
||||
.idea/**/gradle.xml
|
||||
.idea/**/libraries
|
||||
|
||||
# Gradle and Maven with auto-import
|
||||
# When using Gradle or Maven with auto-import, you should exclude module files,
|
||||
# since they will be recreated, and may cause churn. Uncomment if using
|
||||
# auto-import.
|
||||
# .idea/artifacts
|
||||
# .idea/compiler.xml
|
||||
# .idea/jarRepositories.xml
|
||||
# .idea/modules.xml
|
||||
# .idea/*.iml
|
||||
# .idea/modules
|
||||
# *.iml
|
||||
# *.ipr
|
||||
|
||||
# CMake
|
||||
cmake-build-*/
|
||||
|
||||
# Mongo Explorer plugin
|
||||
.idea/**/mongoSettings.xml
|
||||
|
||||
# File-based project format
|
||||
*.iws
|
||||
|
||||
# IntelliJ
|
||||
out/
|
||||
|
||||
# mpeltonen/sbt-idea plugin
|
||||
.idea_modules/
|
||||
|
||||
# JIRA plugin
|
||||
atlassian-ide-plugin.xml
|
||||
|
||||
# Cursive Clojure plugin
|
||||
.idea/replstate.xml
|
||||
|
||||
# Crashlytics plugin (for Android Studio and IntelliJ)
|
||||
com_crashlytics_export_strings.xml
|
||||
crashlytics.properties
|
||||
crashlytics-build.properties
|
||||
fabric.properties
|
||||
|
||||
# Editor-based Rest Client
|
||||
.idea/httpRequests
|
||||
|
||||
# Android studio 3.1+ serialized cache file
|
||||
.idea/caches/build_file_checksums.ser
|
||||
|
||||
### VisualStudio template
|
||||
## Ignore Visual Studio temporary files, build results, and
|
||||
## files generated by popular Visual Studio add-ons.
|
||||
##
|
||||
## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
|
||||
|
||||
# User-specific files
|
||||
*.rsuser
|
||||
*.suo
|
||||
*.user
|
||||
*.userosscache
|
||||
*.sln.docstates
|
||||
|
||||
# User-specific files (MonoDevelop/Xamarin Studio)
|
||||
*.userprefs
|
||||
|
||||
# Mono auto generated files
|
||||
mono_crash.*
|
||||
|
||||
# Build results
|
||||
[Dd]ebug/
|
||||
[Dd]ebugPublic/
|
||||
[Rr]elease/
|
||||
[Rr]eleases/
|
||||
x64/
|
||||
x86/
|
||||
[Ww][Ii][Nn]32/
|
||||
[Aa][Rr][Mm]/
|
||||
[Aa][Rr][Mm]64/
|
||||
bld/
|
||||
[Bb]in/
|
||||
[Oo]bj/
|
||||
[Ll]og/
|
||||
[Ll]ogs/
|
||||
|
||||
# Visual Studio 2015/2017 cache/options directory
|
||||
.vs/
|
||||
# Uncomment if you have tasks that create the project's static files in wwwroot
|
||||
#wwwroot/
|
||||
|
||||
# Visual Studio 2017 auto generated files
|
||||
Generated\ Files/
|
||||
|
||||
# MSTest test Results
|
||||
[Tt]est[Rr]esult*/
|
||||
[Bb]uild[Ll]og.*
|
||||
|
||||
# NUnit
|
||||
*.VisualState.xml
|
||||
TestResult.xml
|
||||
nunit-*.xml
|
||||
|
||||
# Build Results of an ATL Project
|
||||
[Dd]ebugPS/
|
||||
[Rr]eleasePS/
|
||||
dlldata.c
|
||||
|
||||
# Benchmark Results
|
||||
BenchmarkDotNet.Artifacts/
|
||||
|
||||
# .NET Core
|
||||
project.lock.json
|
||||
project.fragment.lock.json
|
||||
artifacts/
|
||||
|
||||
# ASP.NET Scaffolding
|
||||
ScaffoldingReadMe.txt
|
||||
|
||||
# StyleCop
|
||||
StyleCopReport.xml
|
||||
|
||||
# Files built by Visual Studio
|
||||
*_i.c
|
||||
*_p.c
|
||||
*_h.h
|
||||
*.ilk
|
||||
*.meta
|
||||
*.obj
|
||||
*.iobj
|
||||
*.pch
|
||||
*.pdb
|
||||
*.ipdb
|
||||
*.pgc
|
||||
*.pgd
|
||||
*.rsp
|
||||
*.sbr
|
||||
*.tlb
|
||||
*.tli
|
||||
*.tlh
|
||||
*.tmp
|
||||
*.tmp_proj
|
||||
*_wpftmp.csproj
|
||||
*.log
|
||||
*.vspscc
|
||||
*.vssscc
|
||||
.builds
|
||||
*.pidb
|
||||
*.svclog
|
||||
*.scc
|
||||
|
||||
# Chutzpah Test files
|
||||
_Chutzpah*
|
||||
|
||||
# Visual C++ cache files
|
||||
ipch/
|
||||
*.aps
|
||||
*.ncb
|
||||
*.opendb
|
||||
*.opensdf
|
||||
*.sdf
|
||||
*.cachefile
|
||||
*.VC.db
|
||||
*.VC.VC.opendb
|
||||
|
||||
# Visual Studio profiler
|
||||
*.psess
|
||||
*.vsp
|
||||
*.vspx
|
||||
*.sap
|
||||
|
||||
# Visual Studio Trace Files
|
||||
*.e2e
|
||||
|
||||
# TFS 2012 Local Workspace
|
||||
$tf/
|
||||
|
||||
# Guidance Automation Toolkit
|
||||
*.gpState
|
||||
|
||||
# ReSharper is a .NET coding add-in
|
||||
_ReSharper*/
|
||||
*.[Rr]e[Ss]harper
|
||||
*.DotSettings.user
|
||||
|
||||
# TeamCity is a build add-in
|
||||
_TeamCity*
|
||||
|
||||
# DotCover is a Code Coverage Tool
|
||||
*.dotCover
|
||||
|
||||
# AxoCover is a Code Coverage Tool
|
||||
.axoCover/*
|
||||
!.axoCover/settings.json
|
||||
|
||||
# Coverlet is a free, cross platform Code Coverage Tool
|
||||
coverage*.json
|
||||
coverage*.xml
|
||||
coverage*.info
|
||||
|
||||
# Visual Studio code coverage results
|
||||
*.coverage
|
||||
*.coveragexml
|
||||
|
||||
# NCrunch
|
||||
_NCrunch_*
|
||||
.*crunch*.local.xml
|
||||
nCrunchTemp_*
|
||||
|
||||
# MightyMoose
|
||||
*.mm.*
|
||||
AutoTest.Net/
|
||||
|
||||
# Web workbench (sass)
|
||||
.sass-cache/
|
||||
|
||||
# Installshield output folder
|
||||
[Ee]xpress/
|
||||
|
||||
# DocProject is a documentation generator add-in
|
||||
DocProject/buildhelp/
|
||||
DocProject/Help/*.HxT
|
||||
DocProject/Help/*.HxC
|
||||
DocProject/Help/*.hhc
|
||||
DocProject/Help/*.hhk
|
||||
DocProject/Help/*.hhp
|
||||
DocProject/Help/Html2
|
||||
DocProject/Help/html
|
||||
|
||||
# Click-Once directory
|
||||
publish/
|
||||
|
||||
# Publish Web Output
|
||||
*.[Pp]ublish.xml
|
||||
*.azurePubxml
|
||||
# Note: Comment the next line if you want to checkin your web deploy settings,
|
||||
# but database connection strings (with potential passwords) will be unencrypted
|
||||
*.pubxml
|
||||
*.publishproj
|
||||
|
||||
# Microsoft Azure Web App publish settings. Comment the next line if you want to
|
||||
# checkin your Azure Web App publish settings, but sensitive information contained
|
||||
# in these scripts will be unencrypted
|
||||
PublishScripts/
|
||||
|
||||
# NuGet Packages
|
||||
*.nupkg
|
||||
# NuGet Symbol Packages
|
||||
*.snupkg
|
||||
# The packages folder can be ignored because of Package Restore
|
||||
**/[Pp]ackages/*
|
||||
# except build/, which is used as an MSBuild target.
|
||||
!**/[Pp]ackages/build/
|
||||
# Uncomment if necessary however generally it will be regenerated when needed
|
||||
#!**/[Pp]ackages/repositories.config
|
||||
# NuGet v3's project.json files produces more ignorable files
|
||||
*.nuget.props
|
||||
*.nuget.targets
|
||||
|
||||
# Microsoft Azure Build Output
|
||||
csx/
|
||||
*.build.csdef
|
||||
|
||||
# Microsoft Azure Emulator
|
||||
ecf/
|
||||
rcf/
|
||||
|
||||
# Windows Store app package directories and files
|
||||
AppPackages/
|
||||
BundleArtifacts/
|
||||
Package.StoreAssociation.xml
|
||||
_pkginfo.txt
|
||||
*.appx
|
||||
*.appxbundle
|
||||
*.appxupload
|
||||
|
||||
# Visual Studio cache files
|
||||
# files ending in .cache can be ignored
|
||||
*.[Cc]ache
|
||||
# but keep track of directories ending in .cache
|
||||
!?*.[Cc]ache/
|
||||
|
||||
# Others
|
||||
ClientBin/
|
||||
~$*
|
||||
*~
|
||||
*.dbmdl
|
||||
*.dbproj.schemaview
|
||||
*.jfm
|
||||
*.pfx
|
||||
*.publishsettings
|
||||
orleans.codegen.cs
|
||||
|
||||
# Including strong name files can present a security risk
|
||||
# (https://github.com/github/gitignore/pull/2483#issue-259490424)
|
||||
#*.snk
|
||||
|
||||
# Since there are multiple workflows, uncomment next line to ignore bower_components
|
||||
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
|
||||
#bower_components/
|
||||
|
||||
# RIA/Silverlight projects
|
||||
Generated_Code/
|
||||
|
||||
# Backup & report files from converting an old project file
|
||||
# to a newer Visual Studio version. Backup files are not needed,
|
||||
# because we have git ;-)
|
||||
_UpgradeReport_Files/
|
||||
Backup*/
|
||||
UpgradeLog*.XML
|
||||
UpgradeLog*.htm
|
||||
ServiceFabricBackup/
|
||||
*.rptproj.bak
|
||||
|
||||
# SQL Server files
|
||||
*.mdf
|
||||
*.ldf
|
||||
*.ndf
|
||||
|
||||
# Business Intelligence projects
|
||||
*.rdl.data
|
||||
*.bim.layout
|
||||
*.bim_*.settings
|
||||
*.rptproj.rsuser
|
||||
*- [Bb]ackup.rdl
|
||||
*- [Bb]ackup ([0-9]).rdl
|
||||
*- [Bb]ackup ([0-9][0-9]).rdl
|
||||
|
||||
# Microsoft Fakes
|
||||
FakesAssemblies/
|
||||
|
||||
# GhostDoc plugin setting file
|
||||
*.GhostDoc.xml
|
||||
|
||||
# Node.js Tools for Visual Studio
|
||||
.ntvs_analysis.dat
|
||||
node_modules/
|
||||
|
||||
# Visual Studio 6 build log
|
||||
*.plg
|
||||
|
||||
# Visual Studio 6 workspace options file
|
||||
*.opt
|
||||
|
||||
# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
|
||||
*.vbw
|
||||
|
||||
# Visual Studio LightSwitch build output
|
||||
**/*.HTMLClient/GeneratedArtifacts
|
||||
**/*.DesktopClient/GeneratedArtifacts
|
||||
**/*.DesktopClient/ModelManifest.xml
|
||||
**/*.Server/GeneratedArtifacts
|
||||
**/*.Server/ModelManifest.xml
|
||||
_Pvt_Extensions
|
||||
|
||||
# Paket dependency manager
|
||||
.paket/paket.exe
|
||||
paket-files/
|
||||
|
||||
# FAKE - F# Make
|
||||
.fake/
|
||||
|
||||
# CodeRush personal settings
|
||||
.cr/personal
|
||||
|
||||
# Python Tools for Visual Studio (PTVS)
|
||||
__pycache__/
|
||||
*.pyc
|
||||
|
||||
# Cake - Uncomment if you are using it
|
||||
# tools/**
|
||||
# !tools/packages.config
|
||||
|
||||
# Tabs Studio
|
||||
*.tss
|
||||
|
||||
# Telerik's JustMock configuration file
|
||||
*.jmconfig
|
||||
|
||||
# BizTalk build output
|
||||
*.btp.cs
|
||||
*.btm.cs
|
||||
*.odx.cs
|
||||
*.xsd.cs
|
||||
|
||||
# OpenCover UI analysis results
|
||||
OpenCover/
|
||||
|
||||
# Azure Stream Analytics local run output
|
||||
ASALocalRun/
|
||||
|
||||
# MSBuild Binary and Structured Log
|
||||
*.binlog
|
||||
|
||||
# NVidia Nsight GPU debugger configuration file
|
||||
*.nvuser
|
||||
|
||||
# MFractors (Xamarin productivity tool) working folder
|
||||
.mfractor/
|
||||
|
||||
# Local History for Visual Studio
|
||||
.localhistory/
|
||||
|
||||
# BeatPulse healthcheck temp database
|
||||
healthchecksdb
|
||||
|
||||
# Backup folder for Package Reference Convert tool in Visual Studio 2017
|
||||
MigrationBackup/
|
||||
|
||||
# Ionide (cross platform F# VS Code tools) working folder
|
||||
.ionide/
|
||||
|
||||
# Fody - auto-generated XML schema
|
||||
FodyWeavers.xsd
|
||||
|
||||
# Ignore card db since we should start from scratch
|
||||
/db/card.db3
|
||||
Certificates
|
||||
wwwroot/events/
|
||||
Database/*
|
4
MainServer/Configurations/database.json
Normal file
4
MainServer/Configurations/database.json
Normal file
@ -0,0 +1,4 @@
|
||||
{
|
||||
"CardDbName": "card.db3",
|
||||
"MusicDbName": "music471omni.db3"
|
||||
}
|
39
MainServer/Configurations/events.json
Normal file
39
MainServer/Configurations/events.json
Normal file
@ -0,0 +1,39 @@
|
||||
{
|
||||
"Events": {
|
||||
"UseEvents": true,
|
||||
"EventFiles": [
|
||||
{
|
||||
"FileName": "event_103_20201125.evt",
|
||||
"Index": 0
|
||||
},
|
||||
{
|
||||
"FileName": "event_20201125_reg.jpg",
|
||||
"Index": 1
|
||||
},
|
||||
{
|
||||
"FileName": "event_20201125_sgreg.png",
|
||||
"Index": 2
|
||||
},
|
||||
{
|
||||
"FileName": "news_big_20201125_0.jpg",
|
||||
"Index": 0
|
||||
},
|
||||
{
|
||||
"FileName": "news_big_20201125_2.jpg",
|
||||
"Index": 2
|
||||
},
|
||||
{
|
||||
"FileName": "news_small_20201125_1.jpg",
|
||||
"Index": 1
|
||||
},
|
||||
{
|
||||
"FileName": "telop_20201125.txt",
|
||||
"Index": 0
|
||||
},
|
||||
{
|
||||
"FileName": "event_unlock_20201125.cmp",
|
||||
"Index": 8
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
15
MainServer/Configurations/game.json
Normal file
15
MainServer/Configurations/game.json
Normal file
@ -0,0 +1,15 @@
|
||||
{
|
||||
"AvatarCount": 356,
|
||||
"NavigatorCount": 118,
|
||||
"ItemCount": 21,
|
||||
"SkinCount": 21,
|
||||
"SeCount": 26,
|
||||
"TitleCount": 5530,
|
||||
"UnlockableSongIds":
|
||||
[
|
||||
11, 13, 149, 273, 291, 320, 321, 371, 378, 384, 464, 471, 474, 475, 492,
|
||||
494, 498, 520, 548, 551, 558, 561, 565, 570, 577, 583, 612, 615, 622,
|
||||
632, 659, 666, 668, 670, 672, 676, 680, 682, 685, 686, 697, 700, 701,
|
||||
711, 720, 749, 875, 876, 877
|
||||
]
|
||||
}
|
27
MainServer/Configurations/logging.json
Normal file
27
MainServer/Configurations/logging.json
Normal file
@ -0,0 +1,27 @@
|
||||
{
|
||||
"Serilog": {
|
||||
"Using": [ "Serilog.Sinks.Console", "Serilog.Sinks.File" ],
|
||||
"MinimumLevel": {
|
||||
"Default": "Information",
|
||||
"Override": {
|
||||
"Microsoft": "Warning",
|
||||
"Microsoft.AspNetCore": "Warning",
|
||||
"Microsoft.Hosting.Lifetime": "Information"
|
||||
}
|
||||
},
|
||||
"Filter": [
|
||||
{
|
||||
"Name": "ByExcluding",
|
||||
"Args": {
|
||||
"expression": "@mt = 'An unhandled exception has occurred while executing the request.'"
|
||||
}
|
||||
}
|
||||
],
|
||||
"WriteTo": [
|
||||
{
|
||||
"Name": "File",
|
||||
"Args": { "path": "./Logs/log-.txt", "rollingInterval": "Day" }
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
6
MainServer/Configurations/matching.json
Normal file
6
MainServer/Configurations/matching.json
Normal file
@ -0,0 +1,6 @@
|
||||
{
|
||||
"Relay": {
|
||||
"RelayServer": "127.0.0.1",
|
||||
"RelayPort": 3333
|
||||
}
|
||||
}
|
3
MainServer/Configurations/server.json
Normal file
3
MainServer/Configurations/server.json
Normal file
@ -0,0 +1,3 @@
|
||||
{
|
||||
"ServerIp": "127.0.0.1"
|
||||
}
|
24
MainServer/Controllers/API/TestController.cs
Normal file
24
MainServer/Controllers/API/TestController.cs
Normal file
@ -0,0 +1,24 @@
|
||||
using Domain.Entities;
|
||||
using Infrastructure.Persistence;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace MainServer.Controllers.API
|
||||
{
|
||||
[Route("api/[controller]")]
|
||||
[ApiController]
|
||||
public class TestController : ControllerBase
|
||||
{
|
||||
private readonly MusicDbContext context;
|
||||
|
||||
public TestController(MusicDbContext context)
|
||||
{
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
public MusicUnlock GetOne()
|
||||
{
|
||||
return context.MusicUnlocks.First();
|
||||
}
|
||||
}
|
||||
}
|
15
MainServer/Controllers/BaseController.cs
Normal file
15
MainServer/Controllers/BaseController.cs
Normal file
@ -0,0 +1,15 @@
|
||||
using MediatR;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace MainServer.Controllers;
|
||||
|
||||
public abstract class BaseController<T> : ControllerBase where T : BaseController<T>
|
||||
{
|
||||
private ILogger<T>? logger;
|
||||
|
||||
private ISender? mediator;
|
||||
|
||||
protected ISender Mediator => (mediator ??= HttpContext.RequestServices.GetService<ISender>()) ?? throw new InvalidOperationException();
|
||||
|
||||
protected ILogger<T> Logger => (logger ??= HttpContext.RequestServices.GetService<ILogger<T>>()) ?? throw new InvalidOperationException();
|
||||
}
|
25
MainServer/Controllers/Game/AliveController.cs
Normal file
25
MainServer/Controllers/Game/AliveController.cs
Normal file
@ -0,0 +1,25 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace MainServer.Controllers.Game;
|
||||
|
||||
[ApiController]
|
||||
[Route("alive")]
|
||||
public class AliveController : ControllerBase
|
||||
{
|
||||
[HttpGet("i.php")]
|
||||
public IActionResult AliveCheck()
|
||||
{
|
||||
var remoteIpAddress = Request.HttpContext.Connection.RemoteIpAddress;
|
||||
var serverIpAddress = Request.HttpContext.Connection.LocalIpAddress;
|
||||
var response = $"REMOTE ADDRESS:{remoteIpAddress}\n" +
|
||||
"SERVER NAME:GCLocalServer\n" +
|
||||
$"SERVER ADDR:{serverIpAddress}";
|
||||
return Ok(response);
|
||||
}
|
||||
|
||||
[HttpGet("/{id}/Alive.txt")]
|
||||
public IActionResult GetAliveFile()
|
||||
{
|
||||
return Ok("");
|
||||
}
|
||||
}
|
22
MainServer/Controllers/Game/IncomController.cs
Normal file
22
MainServer/Controllers/Game/IncomController.cs
Normal file
@ -0,0 +1,22 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace MainServer.Controllers.Game;
|
||||
|
||||
[ApiController]
|
||||
[Route("service/incom")]
|
||||
public class IncomController : ControllerBase
|
||||
{
|
||||
private const string INCOM_RESPONSE = "1+1";
|
||||
|
||||
[HttpPost("incom.php")]
|
||||
public IActionResult Incom()
|
||||
{
|
||||
return Ok(INCOM_RESPONSE);
|
||||
}
|
||||
|
||||
[HttpPost("incomALL.php")]
|
||||
public IActionResult IncomAll()
|
||||
{
|
||||
return Ok(INCOM_RESPONSE);
|
||||
}
|
||||
}
|
14
MainServer/Controllers/Game/ResponeController.cs
Normal file
14
MainServer/Controllers/Game/ResponeController.cs
Normal file
@ -0,0 +1,14 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace MainServer.Controllers.Game;
|
||||
|
||||
[ApiController]
|
||||
[Route("service/respone")]
|
||||
public class ResponeController : ControllerBase
|
||||
{
|
||||
[HttpPost("respone.php")]
|
||||
public IActionResult Respone()
|
||||
{
|
||||
return Ok("1");
|
||||
}
|
||||
}
|
41
MainServer/Controllers/Game/ServerController.cs
Normal file
41
MainServer/Controllers/Game/ServerController.cs
Normal file
@ -0,0 +1,41 @@
|
||||
using Application.Game.Server;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace MainServer.Controllers.Game;
|
||||
|
||||
[ApiController]
|
||||
[Route("server")]
|
||||
public class ServerController : BaseController<ServerController>
|
||||
{
|
||||
[HttpGet("cursel.php")]
|
||||
public ActionResult<string> GetCursel()
|
||||
{
|
||||
return Ok("1\n");
|
||||
}
|
||||
|
||||
[HttpGet("gameinfo.php")]
|
||||
public ActionResult<string> GetGameInfo()
|
||||
{
|
||||
return Ok("0\n" +
|
||||
"3\n" +
|
||||
"301000,test1\n" +
|
||||
"302000,test2\n" +
|
||||
"303000,test3");
|
||||
}
|
||||
|
||||
[HttpGet("certify.php")]
|
||||
public async Task<ActionResult<string>> Certify(string? gid, string? mac,
|
||||
[FromQuery(Name = "r")]string? random, [FromQuery(Name = "md")]string? md5)
|
||||
{
|
||||
var host = Request.Host.Value;
|
||||
var command = new CertifyCommand(gid, mac, random, md5, host);
|
||||
return Ok(await Mediator.Send(command));
|
||||
}
|
||||
|
||||
[HttpGet("data.php")]
|
||||
public async Task<ActionResult<string>> GetData()
|
||||
{
|
||||
var query = new GetDataQuery(Request.Host.Value);
|
||||
return Ok(await Mediator.Send(query));
|
||||
}
|
||||
}
|
19
MainServer/Controllers/Game/ServiceOptionController.cs
Normal file
19
MainServer/Controllers/Game/ServiceOptionController.cs
Normal file
@ -0,0 +1,19 @@
|
||||
using Application.Game.Option;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace MainServer.Controllers.Game;
|
||||
|
||||
[ApiController]
|
||||
[Route("service/option")]
|
||||
public class ServiceOptionController : BaseController<ServiceOptionController>
|
||||
{
|
||||
[HttpGet("PlayInfo.php")]
|
||||
public async Task<ActionResult<string>> GetPlayCount([FromQuery(Name = "card_id")] long cardId)
|
||||
{
|
||||
var query = new PlayCountQuery(cardId);
|
||||
var count = await Mediator.Send(query);
|
||||
|
||||
return Ok("1\n" +
|
||||
$"{count}");
|
||||
}
|
||||
}
|
15
MainServer/Controllers/Game/UpdateController.cs
Normal file
15
MainServer/Controllers/Game/UpdateController.cs
Normal file
@ -0,0 +1,15 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace MainServer.Controllers.Game;
|
||||
|
||||
[ApiController]
|
||||
[Route("update/cgi")]
|
||||
public class UpdateController : ControllerBase
|
||||
{
|
||||
// TODO: Check update properly
|
||||
[HttpGet("check.php")]
|
||||
public IActionResult UpdateCheck()
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
}
|
16
MainServer/Controllers/Game/UploadController.cs
Normal file
16
MainServer/Controllers/Game/UploadController.cs
Normal file
@ -0,0 +1,16 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace MainServer.Controllers.Game;
|
||||
[ApiController]
|
||||
[Route("service/upload")]
|
||||
public class UploadController : ControllerBase
|
||||
{
|
||||
private const string UPLOAD_RESPONSE = "1\n" +
|
||||
"OK";
|
||||
|
||||
[HttpPost("upload.php")]
|
||||
public IActionResult Upload()
|
||||
{
|
||||
return Ok(UPLOAD_RESPONSE);
|
||||
}
|
||||
}
|
48
MainServer/MainServer.csproj
Normal file
48
MainServer/MainServer.csproj
Normal file
@ -0,0 +1,48 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<LangVersion>11</LangVersion>
|
||||
<Version>3.0.0-alpha</Version>
|
||||
<ApplicationManifest>app.manifest</ApplicationManifest>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="CertificateManager" Version="1.0.8" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="7.0.2" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="7.0.2">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="7.0.3" />
|
||||
<PackageReference Include="Serilog.AspNetCore" Version="6.1.1-dev-00293" />
|
||||
<PackageReference Include="Serilog.Expressions" Version="3.4.2-dev-00119" />
|
||||
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.5.0" />
|
||||
<PackageReference Include="System.Security.Cryptography.X509Certificates" Version="4.3.2" />
|
||||
<PackageReference Include="Throw" Version="1.3.1" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Content Remove="Configurations\database.json" />
|
||||
<None Include="Configurations\database.json" />
|
||||
<Content Remove="Configurations\events.json" />
|
||||
<None Include="Configurations\events.json" />
|
||||
<Content Remove="Configurations\game.json" />
|
||||
<None Include="Configurations\game.json" />
|
||||
<Content Remove="Configurations\logging.json" />
|
||||
<None Include="Configurations\logging.json" />
|
||||
<Content Remove="Configurations\matching.json" />
|
||||
<None Include="Configurations\matching.json" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Infrastructure\Infrastructure.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Folder Include="Controllers\API" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
103
MainServer/Program.cs
Normal file
103
MainServer/Program.cs
Normal file
@ -0,0 +1,103 @@
|
||||
using System.Reflection;
|
||||
using Application;
|
||||
using Application.Interfaces;
|
||||
using Domain.Config;
|
||||
using Infrastructure;
|
||||
using Infrastructure.Common;
|
||||
using Infrastructure.Persistence;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Serilog;
|
||||
using Serilog.Extensions.Logging;
|
||||
using Throw;
|
||||
|
||||
Log.Logger = new LoggerConfiguration()
|
||||
.WriteTo.Console()
|
||||
.CreateBootstrapLogger();
|
||||
|
||||
var version = Assembly.GetEntryAssembly()?.GetCustomAttribute<AssemblyInformationalVersionAttribute>()?
|
||||
.InformationalVersion;
|
||||
Log.Information("GCLocalServer version {Version}", version);
|
||||
|
||||
Log.Information("Server starting up...");
|
||||
|
||||
try
|
||||
{
|
||||
var builder = WebApplication.CreateBuilder(args);
|
||||
|
||||
// Add services to the container.
|
||||
const string configurationsDirectory = "Configurations";
|
||||
builder.Configuration
|
||||
.AddJsonFile($"{configurationsDirectory}/database.json", optional: false, reloadOnChange: false)
|
||||
.AddJsonFile($"{configurationsDirectory}/game.json", optional: false, reloadOnChange: false)
|
||||
.AddJsonFile($"{configurationsDirectory}/logging.json", optional: false, reloadOnChange: false)
|
||||
.AddJsonFile($"{configurationsDirectory}/events.json", optional: true, reloadOnChange: false)
|
||||
.AddJsonFile($"{configurationsDirectory}/matching.json", optional: true, reloadOnChange: false)
|
||||
.AddJsonFile($"{configurationsDirectory}/server.json", optional: true, reloadOnChange: false);
|
||||
|
||||
builder.Services.Configure<EventConfig>(
|
||||
builder.Configuration.GetSection(EventConfig.EVENT_SECTION));
|
||||
builder.Services.Configure<RelayConfig>(
|
||||
builder.Configuration.GetSection(RelayConfig.RELAY_SECTION));
|
||||
|
||||
var serverIp = builder.Configuration["ServerIp"] ?? "127.0.0.1";
|
||||
var certificateManager = new CertificateService(serverIp, new SerilogLoggerFactory(Log.Logger).CreateLogger(""));
|
||||
builder.WebHost.ConfigureKestrel(options =>
|
||||
options.ConfigureHttpsDefaults(adapterOptions =>
|
||||
adapterOptions.ServerCertificate = certificateManager.InitializeCertificate()
|
||||
));
|
||||
|
||||
builder.Host.UseSerilog((context, configuration) =>
|
||||
{
|
||||
configuration.WriteTo.Console().ReadFrom.Configuration(context.Configuration);
|
||||
});
|
||||
|
||||
builder.Services.AddControllers().AddXmlSerializerFormatters();
|
||||
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
|
||||
builder.Services.AddEndpointsApiExplorer();
|
||||
builder.Services.AddSwaggerGen();
|
||||
|
||||
builder.Services.AddApplication();
|
||||
builder.Services.AddInfrastructure(builder.Configuration);
|
||||
|
||||
var app = builder.Build();
|
||||
|
||||
using (var scope = app.Services.CreateScope())
|
||||
{
|
||||
var db = scope.ServiceProvider.GetRequiredService<CardDbContext>();
|
||||
db.Database.Migrate();
|
||||
}
|
||||
|
||||
app.UseSerilogRequestLogging(options =>
|
||||
{
|
||||
options.MessageTemplate = "HTTP {RequestMethod} {RequestPath} responded {StatusCode} in {Elapsed:0.0000} ms, " +
|
||||
"request host: {RequestHost}";
|
||||
options.EnrichDiagnosticContext = (diagnosticContext, httpContext) =>
|
||||
{
|
||||
diagnosticContext.Set("RequestHost", httpContext.Request.Host.Value);
|
||||
};
|
||||
});
|
||||
|
||||
var eventService = app.Services.GetService<IEventManagerService>();
|
||||
eventService.ThrowIfNull();
|
||||
eventService.InitializeEvents();
|
||||
|
||||
// Configure the HTTP request pipeline.
|
||||
if (app.Environment.IsDevelopment())
|
||||
{
|
||||
app.UseSwagger();
|
||||
app.UseSwaggerUI();
|
||||
}
|
||||
|
||||
app.MapControllers();
|
||||
|
||||
app.Run();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Fatal(ex, "Unhandled exception");
|
||||
}
|
||||
finally
|
||||
{
|
||||
Log.Information("Shut down complete");
|
||||
Log.CloseAndFlush();
|
||||
}
|
31
MainServer/Properties/launchSettings.json
Normal file
31
MainServer/Properties/launchSettings.json
Normal file
@ -0,0 +1,31 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/launchsettings.json",
|
||||
"iisSettings": {
|
||||
"windowsAuthentication": false,
|
||||
"anonymousAuthentication": true,
|
||||
"iisExpress": {
|
||||
"applicationUrl": "http://localhost:55391",
|
||||
"sslPort": 44317
|
||||
}
|
||||
},
|
||||
"profiles": {
|
||||
"MainServer": {
|
||||
"commandName": "Project",
|
||||
"dotnetRunMessages": true,
|
||||
"launchBrowser": true,
|
||||
"launchUrl": "swagger",
|
||||
"applicationUrl": "https://localhost:7114;http://localhost:5107",
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
}
|
||||
},
|
||||
"IIS Express": {
|
||||
"commandName": "IISExpress",
|
||||
"launchBrowser": true,
|
||||
"launchUrl": "swagger",
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
79
MainServer/app.manifest
Normal file
79
MainServer/app.manifest
Normal file
@ -0,0 +1,79 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
|
||||
<assemblyIdentity version="1.0.0.0" name="MyApplication.app"/>
|
||||
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">
|
||||
<security>
|
||||
<requestedPrivileges xmlns="urn:schemas-microsoft-com:asm.v3">
|
||||
<!-- UAC 清单选项
|
||||
如果想要更改 Windows 用户帐户控制级别,请使用
|
||||
以下节点之一替换 requestedExecutionLevel 节点。
|
||||
|
||||
<requestedExecutionLevel level="asInvoker" uiAccess="false" />
|
||||
<requestedExecutionLevel level="requireAdministrator" uiAccess="false" />
|
||||
<requestedExecutionLevel level="highestAvailable" uiAccess="false" />
|
||||
|
||||
指定 requestedExecutionLevel 元素将禁用文件和注册表虚拟化。
|
||||
如果你的应用程序需要此虚拟化来实现向后兼容性,则移除此
|
||||
元素。
|
||||
-->
|
||||
<requestedExecutionLevel level="requireAdministrator" uiAccess="false" />
|
||||
</requestedPrivileges>
|
||||
</security>
|
||||
</trustInfo>
|
||||
|
||||
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
|
||||
<application>
|
||||
<!-- 设计此应用程序与其一起工作且已针对此应用程序进行测试的
|
||||
Windows 版本的列表。取消评论适当的元素,
|
||||
Windows 将自动选择最兼容的环境。 -->
|
||||
|
||||
<!-- Windows Vista -->
|
||||
<!--<supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}" />-->
|
||||
|
||||
<!-- Windows 7 -->
|
||||
<!--<supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}" />-->
|
||||
|
||||
<!-- Windows 8 -->
|
||||
<!--<supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}" />-->
|
||||
|
||||
<!-- Windows 8.1 -->
|
||||
<!--<supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}" />-->
|
||||
|
||||
<!-- Windows 10 -->
|
||||
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}" />
|
||||
|
||||
</application>
|
||||
</compatibility>
|
||||
|
||||
<!-- 指示该应用程序可感知 DPI 且 Windows 在 DPI 较高时将不会对其进行
|
||||
自动缩放。Windows Presentation Foundation (WPF)应用程序自动感知 DPI,无需
|
||||
选择加入。选择加入此设置的 Windows 窗体应用程序(面向 .NET Framework 4.6)还应
|
||||
在其 app.config 中将 "EnableWindowsFormsHighDpiAutoResizing" 设置设置为 "true"。
|
||||
|
||||
将应用程序设为感知长路径。请参阅 https://docs.microsoft.com/windows/win32/fileio/maximum-file-path-limitation -->
|
||||
<!--
|
||||
<application xmlns="urn:schemas-microsoft-com:asm.v3">
|
||||
<windowsSettings>
|
||||
<dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true</dpiAware>
|
||||
<longPathAware xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">true</longPathAware>
|
||||
</windowsSettings>
|
||||
</application>
|
||||
-->
|
||||
|
||||
<!-- 启用 Windows 公共控件和对话框的主题(Windows XP 和更高版本) -->
|
||||
<!--
|
||||
<dependency>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity
|
||||
type="win32"
|
||||
name="Microsoft.Windows.Common-Controls"
|
||||
version="6.0.0.0"
|
||||
processorArchitecture="*"
|
||||
publicKeyToken="6595b64144ccf1df"
|
||||
language="*"
|
||||
/>
|
||||
</dependentAssembly>
|
||||
</dependency>
|
||||
-->
|
||||
|
||||
</assembly>
|
3
MainServer/appsettings.json
Normal file
3
MainServer/appsettings.json
Normal file
@ -0,0 +1,3 @@
|
||||
{
|
||||
"AllowedHosts": "*"
|
||||
}
|
Loading…
Reference in New Issue
Block a user