as is
This commit is contained in:
@@ -0,0 +1,49 @@
|
||||
using System.ComponentModel;
|
||||
using System.Text.Json.Nodes;
|
||||
using Microsoft.AspNetCore.OpenApi;
|
||||
using Microsoft.OpenApi;
|
||||
|
||||
namespace StopShopping.FileApi.Extensions;
|
||||
|
||||
/// <summary>
|
||||
/// 处理enum类型openapi显示
|
||||
/// </summary>
|
||||
public class EnumOpenApiSchemaTransformer : IOpenApiSchemaTransformer
|
||||
{
|
||||
public Task TransformAsync(OpenApiSchema schema, OpenApiSchemaTransformerContext context, CancellationToken cancellationToken)
|
||||
{
|
||||
if (context.JsonTypeInfo.Type.IsEnum)
|
||||
{
|
||||
schema.Type = JsonSchemaType.Integer;
|
||||
|
||||
var enumValues = Enum.GetValues(context.JsonTypeInfo.Type)
|
||||
.Cast<object>()
|
||||
.Select(v => JsonNode.Parse(Convert.ToInt32(v).ToString())!)
|
||||
.ToList();
|
||||
|
||||
schema.Enum = enumValues;
|
||||
|
||||
var enumNames = Enum.GetNames(context.JsonTypeInfo.Type);
|
||||
schema.Extensions ??= new Dictionary<string, IOpenApiExtension>();
|
||||
var namesExtension = new JsonNodeExtension(new JsonArray(
|
||||
enumNames
|
||||
.Select(n => (JsonNode)n)
|
||||
.ToArray()));
|
||||
schema.Extensions.Add("x-enumNames", namesExtension);
|
||||
|
||||
var descMap = new JsonObject();
|
||||
foreach (var name in enumNames)
|
||||
{
|
||||
if (context.JsonTypeInfo.Type.GetField(name)
|
||||
?.GetCustomAttributes(typeof(DescriptionAttribute), false)
|
||||
.FirstOrDefault() is DescriptionAttribute attr)
|
||||
{
|
||||
descMap[name] = attr.Description;
|
||||
}
|
||||
}
|
||||
schema.Extensions.Add("x-enumDescriptions", new JsonNodeExtension(descMap));
|
||||
}
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
13
StopShopping.FileApi/Extensions/MiddlewareExtensions.cs
Normal file
13
StopShopping.FileApi/Extensions/MiddlewareExtensions.cs
Normal file
@@ -0,0 +1,13 @@
|
||||
using StopShopping.FileApi.Middlewares;
|
||||
|
||||
namespace StopShopping.FileApi.Extensions;
|
||||
|
||||
public static class MiddlewareExtensions
|
||||
{
|
||||
public static IApplicationBuilder UseInternalOnlyAccess(this IApplicationBuilder applicationBuilder)
|
||||
{
|
||||
applicationBuilder.UseMiddleware<InternalAccessOnlyMiddleware>();
|
||||
|
||||
return applicationBuilder;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
namespace StopShopping.FileApi.Extensions;
|
||||
|
||||
public static class RouteGroupBuilderExtensions
|
||||
{
|
||||
public static RouteGroupBuilder WithInternalOnly(this RouteGroupBuilder routes)
|
||||
{
|
||||
routes.WithMetadata(new InternalOnlyMetadata());
|
||||
|
||||
return routes;
|
||||
}
|
||||
}
|
||||
|
||||
public class InternalOnlyMetadata { }
|
||||
@@ -0,0 +1,58 @@
|
||||
using System.Net;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using StopShopping.FileApi.Extensions;
|
||||
|
||||
namespace StopShopping.FileApi.Middlewares;
|
||||
|
||||
public class InternalAccessOnlyMiddleware
|
||||
{
|
||||
public InternalAccessOnlyMiddleware(
|
||||
RequestDelegate next,
|
||||
IProblemDetailsService problemDetailsService,
|
||||
ILogger<InternalAccessOnlyMiddleware> logger)
|
||||
{
|
||||
_next = next;
|
||||
_problemService = problemDetailsService;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
private readonly RequestDelegate _next;
|
||||
private readonly IProblemDetailsService _problemService;
|
||||
private readonly ILogger<InternalAccessOnlyMiddleware> _logger;
|
||||
|
||||
public async Task InvokeAsync(HttpContext httpContext)
|
||||
{
|
||||
var endpoint = httpContext.GetEndpoint();
|
||||
if (null != endpoint)
|
||||
{
|
||||
var internalOnlyMetadata = endpoint.Metadata.GetMetadata<InternalOnlyMetadata>();
|
||||
if (null != internalOnlyMetadata)
|
||||
{
|
||||
if (null == httpContext.Connection.RemoteIpAddress
|
||||
|| !IPAddress.IsLoopback(httpContext.Connection.RemoteIpAddress))
|
||||
{
|
||||
var problemDetails = new ProblemDetails
|
||||
{
|
||||
Detail = $"remote ip: {httpContext.Connection.RemoteIpAddress}",
|
||||
Instance = httpContext.Request.Path,
|
||||
Status = StatusCodes.Status403Forbidden,
|
||||
Title = "access denied, local access only."
|
||||
};
|
||||
|
||||
httpContext.Response.StatusCode = StatusCodes.Status403Forbidden;
|
||||
httpContext.Response.ContentType = "application/problem+json";
|
||||
|
||||
await _problemService.WriteAsync(new ProblemDetailsContext
|
||||
{
|
||||
HttpContext = httpContext,
|
||||
ProblemDetails = problemDetails,
|
||||
});
|
||||
|
||||
_logger.LogInformation("denied access: {Ip}", httpContext.Connection.RemoteIpAddress);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
await _next(httpContext);
|
||||
}
|
||||
}
|
||||
69
StopShopping.FileApi/Program.cs
Normal file
69
StopShopping.FileApi/Program.cs
Normal file
@@ -0,0 +1,69 @@
|
||||
using Microsoft.AspNetCore.HostFiltering;
|
||||
using Microsoft.AspNetCore.HttpOverrides;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Scalar.AspNetCore;
|
||||
using Serilog;
|
||||
using StopShopping.FileApi;
|
||||
using StopShopping.FileApi.Extensions;
|
||||
using StopShopping.FileApi.Services;
|
||||
|
||||
// 将启动日志写入控制台,用于捕获启动时异常,启动后WriteTo被后续配置替代
|
||||
Log.Logger = new LoggerConfiguration()
|
||||
.WriteTo.Console()
|
||||
.CreateBootstrapLogger();
|
||||
|
||||
try
|
||||
{
|
||||
var builder = WebApplication.CreateBuilder(args);
|
||||
|
||||
builder.Services.AddSerilog((services, seriLogger) =>
|
||||
{
|
||||
seriLogger.ReadFrom.Configuration(builder.Configuration)
|
||||
.ReadFrom.Services(services);
|
||||
});
|
||||
|
||||
builder.Services.AddOpenApi(options =>
|
||||
{
|
||||
options.AddSchemaTransformer<EnumOpenApiSchemaTransformer>();
|
||||
});
|
||||
builder.Services.AddProblemDetails();
|
||||
builder.Services.AddServices(builder.Configuration.GetSection("AppOptions"));
|
||||
/********************************************************************/
|
||||
var app = builder.Build();
|
||||
|
||||
if (app.Environment.IsDevelopment())
|
||||
{
|
||||
app.MapOpenApi();
|
||||
app.MapScalarApiReference();
|
||||
}
|
||||
|
||||
if (!app.Environment.IsDevelopment())
|
||||
{
|
||||
app.UseHostFiltering();
|
||||
|
||||
var forwardedHeadersOptions = new ForwardedHeadersOptions
|
||||
{
|
||||
ForwardedHeaders = ForwardedHeaders.All,
|
||||
};
|
||||
var hostFilteringOptions = app.Services.GetRequiredService<IOptions<HostFilteringOptions>>();
|
||||
if (null != hostFilteringOptions)
|
||||
forwardedHeadersOptions.AllowedHosts = hostFilteringOptions.Value.AllowedHosts;
|
||||
app.UseForwardedHeaders(forwardedHeadersOptions);
|
||||
}
|
||||
|
||||
app.UseStaticFiles();
|
||||
|
||||
Routes.MapRoutes(app);
|
||||
|
||||
app.UseInternalOnlyAccess();
|
||||
|
||||
app.Run();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Fatal(ex, "启动异常!");
|
||||
}
|
||||
finally
|
||||
{
|
||||
Log.CloseAndFlush();
|
||||
}
|
||||
23
StopShopping.FileApi/Properties/launchSettings.json
Normal file
23
StopShopping.FileApi/Properties/launchSettings.json
Normal file
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/launchsettings.json",
|
||||
"profiles": {
|
||||
"http": {
|
||||
"commandName": "Project",
|
||||
"dotnetRunMessages": true,
|
||||
"launchBrowser": false,
|
||||
"applicationUrl": "http://localhost:5072",
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
}
|
||||
},
|
||||
"https": {
|
||||
"commandName": "Project",
|
||||
"dotnetRunMessages": true,
|
||||
"launchBrowser": false,
|
||||
"applicationUrl": "https://localhost:7178;http://localhost:5072",
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
31
StopShopping.FileApi/Routes.cs
Normal file
31
StopShopping.FileApi/Routes.cs
Normal file
@@ -0,0 +1,31 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using StopShopping.FileApi.Extensions;
|
||||
using StopShopping.FileApi.Services;
|
||||
|
||||
namespace StopShopping.FileApi;
|
||||
|
||||
public static class Routes
|
||||
{
|
||||
public static void MapRoutes(WebApplication app)
|
||||
{
|
||||
app.MapGroup("")
|
||||
.MapRoutes()
|
||||
.WithInternalOnly();
|
||||
}
|
||||
|
||||
public static RouteGroupBuilder MapRoutes(this RouteGroupBuilder routes)
|
||||
{
|
||||
routes.MapPost("/upload", UploadAsync)
|
||||
.DisableAntiforgery()
|
||||
.WithDescription("上传文件,对外接口自己实现anti-forgery(重要)");
|
||||
|
||||
return routes;
|
||||
}
|
||||
|
||||
private static async Task<ApiResponse<FileUploadResp>> UploadAsync(
|
||||
[FromForm] UploadParams payload,
|
||||
IFileService fileService)
|
||||
{
|
||||
return await fileService.UploadFileAsync(payload);
|
||||
}
|
||||
}
|
||||
64
StopShopping.FileApi/Services/ApiResponse.cs
Normal file
64
StopShopping.FileApi/Services/ApiResponse.cs
Normal file
@@ -0,0 +1,64 @@
|
||||
namespace StopShopping.FileApi.Services;
|
||||
|
||||
/// <summary>
|
||||
/// 强类型返回值
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
public class ApiResponse<T>
|
||||
{
|
||||
public ApiResponse()
|
||||
{ }
|
||||
|
||||
public ApiResponse(T data)
|
||||
{
|
||||
Data = data;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 是否成功
|
||||
/// </summary>
|
||||
/// <value></value>
|
||||
public bool IsSucced { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// 错误消息
|
||||
/// </summary>
|
||||
/// <value></value>
|
||||
public string? Message { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 关联数据
|
||||
/// </summary>
|
||||
/// <value></value>
|
||||
public T? Data { get; set; }
|
||||
|
||||
public ApiResponse<T> Failed(string message)
|
||||
{
|
||||
IsSucced = false;
|
||||
Message = message;
|
||||
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 强类型返回值,只返回成功与否和消息
|
||||
/// </summary>
|
||||
public class ApiResponse : ApiResponse<object?>
|
||||
{
|
||||
public ApiResponse(bool isSucced = true, string message = "")
|
||||
{
|
||||
IsSucced = isSucced;
|
||||
Message = message;
|
||||
}
|
||||
|
||||
public static ApiResponse Succed(string message = "")
|
||||
{
|
||||
return new ApiResponse(message: message);
|
||||
}
|
||||
|
||||
public static new ApiResponse Failed(string message)
|
||||
{
|
||||
return new ApiResponse(false, message);
|
||||
}
|
||||
}
|
||||
6
StopShopping.FileApi/Services/AppOptions.cs
Normal file
6
StopShopping.FileApi/Services/AppOptions.cs
Normal file
@@ -0,0 +1,6 @@
|
||||
namespace StopShopping.FileApi.Services;
|
||||
|
||||
public class AppOptions
|
||||
{
|
||||
public string Domain { get; set; } = string.Empty;
|
||||
}
|
||||
34
StopShopping.FileApi/Services/Extensions.cs
Normal file
34
StopShopping.FileApi/Services/Extensions.cs
Normal file
@@ -0,0 +1,34 @@
|
||||
|
||||
using FileSignatures;
|
||||
using FileSignatures.Formats;
|
||||
using StopShopping.FileApi.Services.Implementions;
|
||||
|
||||
namespace StopShopping.FileApi.Services;
|
||||
|
||||
public static class Extensions
|
||||
{
|
||||
public static IServiceCollection AddServices(this IServiceCollection services, IConfiguration appOptions)
|
||||
{
|
||||
services.Configure<AppOptions>(appOptions);
|
||||
services.AddValidation();
|
||||
|
||||
var imageFormats = FileFormatLocator.GetFormats().OfType<Image>();
|
||||
var imageInspector = new FileFormatInspector(imageFormats);
|
||||
services.AddSingleton<IFileFormatInspector>(imageInspector);
|
||||
|
||||
services.AddScoped<IFileService, FileService>();
|
||||
|
||||
return services;
|
||||
}
|
||||
|
||||
public static string GetTargetDirectory(this UploadScences uploadScences)
|
||||
{
|
||||
return uploadScences switch
|
||||
{
|
||||
UploadScences.Avatar => "avatar",
|
||||
UploadScences.Product => "product",
|
||||
UploadScences.Category => "category",
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(uploadScences))
|
||||
};
|
||||
}
|
||||
}
|
||||
18
StopShopping.FileApi/Services/FileUploadResp.cs
Normal file
18
StopShopping.FileApi/Services/FileUploadResp.cs
Normal file
@@ -0,0 +1,18 @@
|
||||
namespace StopShopping.FileApi.Services;
|
||||
|
||||
/// <summary>
|
||||
/// 文件上传结果
|
||||
/// </summary>
|
||||
public class FileUploadResp
|
||||
{
|
||||
/// <summary>
|
||||
/// 新名,上传后重命名
|
||||
/// </summary>
|
||||
/// <value></value>
|
||||
public string NewName { get; set; } = string.Empty;
|
||||
/// <summary>
|
||||
/// Url
|
||||
/// </summary>
|
||||
/// <value></value>
|
||||
public string Url { get; set; } = string.Empty;
|
||||
}
|
||||
14
StopShopping.FileApi/Services/IFileService.cs
Normal file
14
StopShopping.FileApi/Services/IFileService.cs
Normal file
@@ -0,0 +1,14 @@
|
||||
namespace StopShopping.FileApi.Services;
|
||||
|
||||
/// <summary>
|
||||
/// 文件服务
|
||||
/// </summary>
|
||||
public interface IFileService
|
||||
{
|
||||
/// <summary>
|
||||
/// 上传文件
|
||||
/// </summary>
|
||||
/// <param name="payload"></param>
|
||||
/// <returns></returns>
|
||||
Task<ApiResponse<FileUploadResp>> UploadFileAsync(UploadParams payload);
|
||||
}
|
||||
59
StopShopping.FileApi/Services/Implementions/FileService.cs
Normal file
59
StopShopping.FileApi/Services/Implementions/FileService.cs
Normal file
@@ -0,0 +1,59 @@
|
||||
using Microsoft.Extensions.Options;
|
||||
|
||||
namespace StopShopping.FileApi.Services.Implementions;
|
||||
|
||||
public class FileService : IFileService
|
||||
{
|
||||
public FileService(IOptions<AppOptions> appOptions,
|
||||
IWebHostEnvironment webHostEnvironment)
|
||||
{
|
||||
_appOptions = appOptions.Value;
|
||||
_env = webHostEnvironment;
|
||||
}
|
||||
|
||||
private readonly IWebHostEnvironment _env;
|
||||
private readonly AppOptions _appOptions;
|
||||
|
||||
public async Task<ApiResponse<FileUploadResp>> UploadFileAsync(UploadParams payload)
|
||||
{
|
||||
var newName = Guid.NewGuid().ToString("N").ToLower();
|
||||
var extension = Path.GetExtension(payload.File!.FileName);
|
||||
var newFullName = $"{newName}{extension}";
|
||||
var relativeToRootPath = GetRelativeToRootPath(payload.Scences, newFullName);
|
||||
var targetPath = Path.Combine(_env.WebRootPath, GetRelativeToRootPath(payload.Scences));
|
||||
|
||||
if (!Directory.Exists(targetPath))
|
||||
{
|
||||
Directory.CreateDirectory(targetPath);
|
||||
}
|
||||
|
||||
using var file = new FileStream(
|
||||
Path.Combine(_env.WebRootPath, relativeToRootPath),
|
||||
FileMode.CreateNew,
|
||||
FileAccess.Write);
|
||||
|
||||
await payload.File.CopyToAsync(file);
|
||||
|
||||
FileUploadResp result = new()
|
||||
{
|
||||
NewName = newFullName,
|
||||
Url = GetFileUrl(payload.Scences, newFullName),
|
||||
};
|
||||
|
||||
return new ApiResponse<FileUploadResp>(result);
|
||||
}
|
||||
|
||||
private string GetFileUrl(UploadScences scences, string fileName)
|
||||
{
|
||||
return $"{_appOptions.Domain}/{GetRelativeToRootPath(scences, fileName)
|
||||
.Replace(Path.DirectorySeparatorChar, '/')}";
|
||||
}
|
||||
|
||||
private string GetRelativeToRootPath(UploadScences scences, string fileName = "")
|
||||
{
|
||||
return Path.Combine(
|
||||
"images",
|
||||
scences.GetTargetDirectory(),
|
||||
fileName);
|
||||
}
|
||||
}
|
||||
22
StopShopping.FileApi/Services/NameUrlParams.cs
Normal file
22
StopShopping.FileApi/Services/NameUrlParams.cs
Normal file
@@ -0,0 +1,22 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace StopShopping.FileApi.Services;
|
||||
|
||||
/// <summary>
|
||||
/// url查询参数
|
||||
/// </summary>
|
||||
/// <value></value>
|
||||
public record NameUrlParams
|
||||
{
|
||||
/// <summary>
|
||||
/// 场景
|
||||
/// </summary>
|
||||
/// <value></value>
|
||||
public UploadScences Scences { get; set; }
|
||||
/// <summary>
|
||||
/// 文件名
|
||||
/// </summary>
|
||||
/// <value></value>
|
||||
[Required]
|
||||
public string[] Names { get; set; } = [];
|
||||
}
|
||||
14
StopShopping.FileApi/Services/NameUrlResp.cs
Normal file
14
StopShopping.FileApi/Services/NameUrlResp.cs
Normal file
@@ -0,0 +1,14 @@
|
||||
namespace StopShopping.FileApi.Services;
|
||||
|
||||
/// <summary>
|
||||
/// url查询响应
|
||||
/// </summary>
|
||||
/// <value></value>
|
||||
public class NameUrlResp
|
||||
{
|
||||
/// <summary>
|
||||
/// 文件名:文件链接
|
||||
/// </summary>
|
||||
/// <value></value>
|
||||
public KeyValuePair<string, string>[] NameUrls { get; set; } = [];
|
||||
}
|
||||
23
StopShopping.FileApi/Services/UploadParams.cs
Normal file
23
StopShopping.FileApi/Services/UploadParams.cs
Normal file
@@ -0,0 +1,23 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace StopShopping.FileApi.Services;
|
||||
|
||||
/// <summary>
|
||||
/// 上传
|
||||
/// </summary>
|
||||
/// <value></value>
|
||||
public record UploadParams
|
||||
{
|
||||
/// <summary>
|
||||
/// 场景
|
||||
/// </summary>
|
||||
/// <value></value>
|
||||
public UploadScences Scences { get; set; }
|
||||
/// <summary>
|
||||
/// 文件
|
||||
/// </summary>
|
||||
/// <value></value>
|
||||
[Required]
|
||||
[ImageFileValidation(2 * 1024 * 1024)]
|
||||
public IFormFile? File { get; set; }
|
||||
}
|
||||
20
StopShopping.FileApi/Services/UploadScences.cs
Normal file
20
StopShopping.FileApi/Services/UploadScences.cs
Normal file
@@ -0,0 +1,20 @@
|
||||
namespace StopShopping.FileApi.Services;
|
||||
|
||||
/// <summary>
|
||||
/// 文件上传场景
|
||||
/// </summary>
|
||||
public enum UploadScences
|
||||
{
|
||||
/// <summary>
|
||||
/// 头像
|
||||
/// </summary>
|
||||
Avatar,
|
||||
/// <summary>
|
||||
/// 商品
|
||||
/// </summary>
|
||||
Product,
|
||||
/// <summary>
|
||||
/// 商品分类
|
||||
/// </summary>
|
||||
Category,
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
using FileSignatures;
|
||||
|
||||
namespace System.ComponentModel.DataAnnotations;
|
||||
|
||||
public class ImageFileValidationAttribute : ValidationAttribute
|
||||
{
|
||||
public ImageFileValidationAttribute(long length)
|
||||
{
|
||||
Length = length;
|
||||
}
|
||||
|
||||
public long Length { get; set; }
|
||||
|
||||
protected override ValidationResult? IsValid(object? value, ValidationContext validationContext)
|
||||
{
|
||||
var fileInspector = validationContext.GetRequiredService<IFileFormatInspector>();
|
||||
if (null == fileInspector)
|
||||
{
|
||||
return new ValidationResult("未配置文件验证器");
|
||||
}
|
||||
if (value is IFormFile file)
|
||||
{
|
||||
var result = Validate(fileInspector, file);
|
||||
if (null != result)
|
||||
return result;
|
||||
}
|
||||
else if (value is IFormFileCollection files)
|
||||
{
|
||||
foreach (var f in files)
|
||||
{
|
||||
var result = Validate(fileInspector, f);
|
||||
if (null != result)
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
return ValidationResult.Success;
|
||||
}
|
||||
|
||||
private ValidationResult? Validate(IFileFormatInspector fileInspector, IFormFile file)
|
||||
{
|
||||
if (file.Length > Length)
|
||||
return new ValidationResult("文件太大");
|
||||
var format = fileInspector.DetermineFileFormat(file.OpenReadStream());
|
||||
if (null == format)
|
||||
return new ValidationResult("文件格式不支持");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
25
StopShopping.FileApi/StopShopping.FileApi.csproj
Normal file
25
StopShopping.FileApi/StopShopping.FileApi.csproj
Normal file
@@ -0,0 +1,25 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net10.0</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="10.0.1" />
|
||||
<PackageReference Include="Scalar.AspNetCore" Version="2.13.5" />
|
||||
<PackageReference Include="Serilog.AspNetCore" Version="10.0.0" />
|
||||
<PackageReference Include="FileSignatures" Version="7.0.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Content Update="wwwroot\**\*">
|
||||
<CopyToPublishDirectory>Never</CopyToPublishDirectory>
|
||||
</Content>
|
||||
<Content Update="appsettings.Template.json">
|
||||
<CopyToPublishDirectory>Never</CopyToPublishDirectory>
|
||||
</Content>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
26
StopShopping.FileApi/appsettings.Template.json
Normal file
26
StopShopping.FileApi/appsettings.Template.json
Normal file
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"AppOptions": {
|
||||
"Domain": "DOMAIN"
|
||||
},
|
||||
"Serilog": {
|
||||
"Using": ["Serilog.Sinks.File"],
|
||||
"MinimumLevel": {
|
||||
"Default": "Information",
|
||||
"Override": {
|
||||
"Microsoft.AspNetCore": "Warning",
|
||||
"Microsoft.EntityFrameworkCore": "Warning"
|
||||
}
|
||||
},
|
||||
"WriteTo": [
|
||||
{
|
||||
"Name": "File",
|
||||
"Args": {
|
||||
"path": "./logs/log-.txt",
|
||||
"rollingInterval": "Day",
|
||||
"outputTemplate": "{Timestamp:HH:mm:ss.fff} [{Level:u3}] {SourceContext} - {Message:lj}{NewLine}{Exception}"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"AllowedHosts": "*"
|
||||
}
|
||||
Reference in New Issue
Block a user