mirror of
https://github.com/JustArchiNET/ArchiSteamFarm.git
synced 2026-01-01 06:00:46 +00:00
Closes #3376
This commit is contained in:
@@ -16,8 +16,7 @@
|
||||
<PackageReference Include="Nito.AsyncEx.Coordination" />
|
||||
<PackageReference Include="NLog.Web.AspNetCore" />
|
||||
<PackageReference Include="SteamKit2" />
|
||||
<PackageReference Include="Swashbuckle.AspNetCore" />
|
||||
<PackageReference Include="Swashbuckle.AspNetCore.Annotations" />
|
||||
<PackageReference Include="Swashbuckle.AspNetCore.SwaggerUI" />
|
||||
<PackageReference Include="System.Composition" />
|
||||
<PackageReference Include="System.Linq.Async" />
|
||||
<PackageReference Include="System.Security.Cryptography.ProtectedData" />
|
||||
|
||||
@@ -36,7 +36,6 @@ using ArchiSteamFarm.Helpers.Json;
|
||||
using ArchiSteamFarm.IPC.Controllers.Api;
|
||||
using ArchiSteamFarm.IPC.Integration;
|
||||
using ArchiSteamFarm.IPC.OpenApi;
|
||||
using ArchiSteamFarm.IPC.Swashbuckle;
|
||||
using ArchiSteamFarm.Localization;
|
||||
using ArchiSteamFarm.NLog;
|
||||
using ArchiSteamFarm.NLog.Targets;
|
||||
@@ -54,7 +53,6 @@ using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.FileProviders;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Net.Http.Headers;
|
||||
using Microsoft.OpenApi.Models;
|
||||
using NLog.Web;
|
||||
using IPNetwork = Microsoft.AspNetCore.HttpOverrides.IPNetwork;
|
||||
|
||||
@@ -257,11 +255,7 @@ internal static class ArchiKestrel {
|
||||
app.MapControllers();
|
||||
|
||||
// Add support for OpenAPI, responsible for automatic API documentation generation, this should be on the end, once we're done with API
|
||||
if (Program.UseOpenApi) {
|
||||
app.MapOpenApi("/swagger/{documentName}/swagger.json");
|
||||
} else {
|
||||
app.UseSwagger();
|
||||
}
|
||||
app.MapOpenApi("/swagger/{documentName}/swagger.json");
|
||||
|
||||
// Add support for swagger UI, this should be after swagger, obviously
|
||||
app.UseSwaggerUI(
|
||||
@@ -338,71 +332,13 @@ internal static class ArchiKestrel {
|
||||
}
|
||||
|
||||
// Add support for OpenAPI, responsible for automatic API documentation generation
|
||||
if (Program.UseOpenApi) {
|
||||
services.AddOpenApi(
|
||||
SharedInfo.ASF, static options => {
|
||||
options.AddDocumentTransformer<DocumentTransformer>();
|
||||
options.AddOperationTransformer<OperationTransformer>();
|
||||
options.AddSchemaTransformer<SchemaTransformer>();
|
||||
}
|
||||
);
|
||||
} else {
|
||||
services.AddSwaggerGen(
|
||||
static options => {
|
||||
options.AddSecurityDefinition(
|
||||
nameof(GlobalConfig.IPCPassword), new OpenApiSecurityScheme {
|
||||
Description = $"{nameof(GlobalConfig.IPCPassword)} authentication using request headers. Check {SharedInfo.ProjectURL}/wiki/IPC#authentication for more info.",
|
||||
In = ParameterLocation.Header,
|
||||
Name = ApiAuthenticationMiddleware.HeadersField,
|
||||
Type = SecuritySchemeType.ApiKey
|
||||
}
|
||||
);
|
||||
|
||||
options.AddSecurityRequirement(
|
||||
new OpenApiSecurityRequirement {
|
||||
{
|
||||
new OpenApiSecurityScheme {
|
||||
Reference = new OpenApiReference {
|
||||
Id = nameof(GlobalConfig.IPCPassword),
|
||||
Type = ReferenceType.SecurityScheme
|
||||
}
|
||||
},
|
||||
|
||||
[]
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
// We require custom schema IDs due to conflicting type names, choosing the proper one is tricky as there is no good answer and any kind of convention has a potential to create conflict
|
||||
// FullName and Name both do, ToString() for unknown to me reason doesn't, and I don't have courage to call our WebUtilities.GetUnifiedName() better than what .NET ships with (because it isn't)
|
||||
// Let's use ToString() until we find a good enough reason to change it, also, the name must pass ^[a-zA-Z0-9.-_]+$ regex
|
||||
options.CustomSchemaIds(static type => type.ToString().Replace('+', '-'));
|
||||
|
||||
options.EnableAnnotations(true, true);
|
||||
|
||||
options.SchemaFilter<CustomAttributesSchemaFilter>();
|
||||
options.SchemaFilter<EnumSchemaFilter>();
|
||||
options.SchemaFilter<ReadOnlyFixesSchemaFilter>();
|
||||
|
||||
options.SwaggerDoc(
|
||||
SharedInfo.ASF, new OpenApiInfo {
|
||||
Contact = new OpenApiContact {
|
||||
Name = SharedInfo.GithubRepo,
|
||||
Url = new Uri(SharedInfo.ProjectURL)
|
||||
},
|
||||
|
||||
License = new OpenApiLicense {
|
||||
Name = SharedInfo.LicenseName,
|
||||
Url = new Uri(SharedInfo.LicenseURL)
|
||||
},
|
||||
|
||||
Title = $"{SharedInfo.AssemblyName} API",
|
||||
Version = SharedInfo.Version.ToString()
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
services.AddOpenApi(
|
||||
SharedInfo.ASF, static options => {
|
||||
options.AddDocumentTransformer<DocumentTransformer>();
|
||||
options.AddOperationTransformer<OperationTransformer>();
|
||||
options.AddSchemaTransformer<SchemaTransformer>();
|
||||
}
|
||||
);
|
||||
|
||||
// Add support for optional health-checks
|
||||
services.AddHealthChecks();
|
||||
|
||||
@@ -1,53 +0,0 @@
|
||||
// ----------------------------------------------------------------------------------------------
|
||||
// _ _ _ ____ _ _____
|
||||
// / \ _ __ ___ | |__ (_)/ ___| | |_ ___ __ _ _ __ ___ | ___|__ _ _ __ _ __ ___
|
||||
// / _ \ | '__|/ __|| '_ \ | |\___ \ | __|/ _ \ / _` || '_ ` _ \ | |_ / _` || '__|| '_ ` _ \
|
||||
// / ___ \ | | | (__ | | | || | ___) || |_| __/| (_| || | | | | || _|| (_| || | | | | | | |
|
||||
// /_/ \_\|_| \___||_| |_||_||____/ \__|\___| \__,_||_| |_| |_||_| \__,_||_| |_| |_| |_|
|
||||
// ----------------------------------------------------------------------------------------------
|
||||
// |
|
||||
// Copyright 2015-2025 Łukasz "JustArchi" Domeradzki
|
||||
// Contact: JustArchi@JustArchi.net
|
||||
// |
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
// |
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
// |
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
using System;
|
||||
using System.Reflection;
|
||||
using ArchiSteamFarm.IPC.Integration;
|
||||
using JetBrains.Annotations;
|
||||
using Microsoft.OpenApi.Models;
|
||||
using Swashbuckle.AspNetCore.SwaggerGen;
|
||||
|
||||
namespace ArchiSteamFarm.IPC.Swashbuckle;
|
||||
|
||||
[UsedImplicitly]
|
||||
internal sealed class CustomAttributesSchemaFilter : ISchemaFilter {
|
||||
public void Apply(OpenApiSchema schema, SchemaFilterContext context) {
|
||||
ArgumentNullException.ThrowIfNull(schema);
|
||||
ArgumentNullException.ThrowIfNull(context);
|
||||
|
||||
ICustomAttributeProvider attributesProvider;
|
||||
|
||||
if (context.MemberInfo != null) {
|
||||
attributesProvider = context.MemberInfo;
|
||||
} else if (context.ParameterInfo != null) {
|
||||
attributesProvider = context.ParameterInfo;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (CustomSwaggerAttribute customSwaggerAttribute in attributesProvider.GetCustomAttributes(typeof(CustomSwaggerAttribute), true)) {
|
||||
customSwaggerAttribute.Apply(schema);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,107 +0,0 @@
|
||||
// ----------------------------------------------------------------------------------------------
|
||||
// _ _ _ ____ _ _____
|
||||
// / \ _ __ ___ | |__ (_)/ ___| | |_ ___ __ _ _ __ ___ | ___|__ _ _ __ _ __ ___
|
||||
// / _ \ | '__|/ __|| '_ \ | |\___ \ | __|/ _ \ / _` || '_ ` _ \ | |_ / _` || '__|| '_ ` _ \
|
||||
// / ___ \ | | | (__ | | | || | ___) || |_| __/| (_| || | | | | || _|| (_| || | | | | | | |
|
||||
// /_/ \_\|_| \___||_| |_||_||____/ \__|\___| \__,_||_| |_| |_||_| \__,_||_| |_| |_| |_|
|
||||
// ----------------------------------------------------------------------------------------------
|
||||
// |
|
||||
// Copyright 2015-2025 Łukasz "JustArchi" Domeradzki
|
||||
// Contact: JustArchi@JustArchi.net
|
||||
// |
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
// |
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
// |
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using JetBrains.Annotations;
|
||||
using Microsoft.OpenApi.Any;
|
||||
using Microsoft.OpenApi.Extensions;
|
||||
using Microsoft.OpenApi.Models;
|
||||
using Swashbuckle.AspNetCore.SwaggerGen;
|
||||
|
||||
namespace ArchiSteamFarm.IPC.Swashbuckle;
|
||||
|
||||
[UsedImplicitly]
|
||||
internal sealed class EnumSchemaFilter : ISchemaFilter {
|
||||
public void Apply(OpenApiSchema schema, SchemaFilterContext context) {
|
||||
ArgumentNullException.ThrowIfNull(schema);
|
||||
ArgumentNullException.ThrowIfNull(context);
|
||||
|
||||
if (context.Type is not { IsEnum: true }) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (context.Type.IsDefined(typeof(FlagsAttribute), false)) {
|
||||
schema.Format = "flags";
|
||||
}
|
||||
|
||||
OpenApiObject definition = new();
|
||||
|
||||
foreach (object? enumValue in context.Type.GetEnumValues()) {
|
||||
if (enumValue == null) {
|
||||
throw new InvalidOperationException(nameof(enumValue));
|
||||
}
|
||||
|
||||
string? enumName = Enum.GetName(context.Type, enumValue);
|
||||
|
||||
if (string.IsNullOrEmpty(enumName)) {
|
||||
// Fallback
|
||||
enumName = enumValue.ToString();
|
||||
|
||||
if (string.IsNullOrEmpty(enumName)) {
|
||||
throw new InvalidOperationException(nameof(enumName));
|
||||
}
|
||||
}
|
||||
|
||||
if (definition.ContainsKey(enumName)) {
|
||||
// This is possible if we have multiple names for the same enum value, we'll ignore additional ones
|
||||
continue;
|
||||
}
|
||||
|
||||
IOpenApiPrimitive enumObject;
|
||||
|
||||
if (TryCast(enumValue, out int intValue)) {
|
||||
enumObject = new OpenApiInteger(intValue);
|
||||
} else if (TryCast(enumValue, out long longValue)) {
|
||||
enumObject = new OpenApiLong(longValue);
|
||||
} else if (TryCast(enumValue, out ulong ulongValue)) {
|
||||
// OpenApi spec doesn't support ulongs as of now
|
||||
enumObject = new OpenApiString(ulongValue.ToString(CultureInfo.InvariantCulture));
|
||||
} else {
|
||||
throw new InvalidOperationException(nameof(enumValue));
|
||||
}
|
||||
|
||||
definition.Add(enumName, enumObject);
|
||||
}
|
||||
|
||||
schema.AddExtension("x-definition", definition);
|
||||
}
|
||||
|
||||
private static bool TryCast<T>(object value, out T typedValue) where T : struct {
|
||||
ArgumentNullException.ThrowIfNull(value);
|
||||
|
||||
try {
|
||||
typedValue = (T) Convert.ChangeType(value, typeof(T), CultureInfo.InvariantCulture);
|
||||
|
||||
return true;
|
||||
} catch (InvalidCastException) {
|
||||
typedValue = default(T);
|
||||
|
||||
return false;
|
||||
} catch (OverflowException) {
|
||||
typedValue = default(T);
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
// ----------------------------------------------------------------------------------------------
|
||||
// _ _ _ ____ _ _____
|
||||
// / \ _ __ ___ | |__ (_)/ ___| | |_ ___ __ _ _ __ ___ | ___|__ _ _ __ _ __ ___
|
||||
// / _ \ | '__|/ __|| '_ \ | |\___ \ | __|/ _ \ / _` || '_ ` _ \ | |_ / _` || '__|| '_ ` _ \
|
||||
// / ___ \ | | | (__ | | | || | ___) || |_| __/| (_| || | | | | || _|| (_| || | | | | | | |
|
||||
// /_/ \_\|_| \___||_| |_||_||____/ \__|\___| \__,_||_| |_| |_||_| \__,_||_| |_| |_| |_|
|
||||
// ----------------------------------------------------------------------------------------------
|
||||
// |
|
||||
// Copyright 2015-2025 Łukasz "JustArchi" Domeradzki
|
||||
// Contact: JustArchi@JustArchi.net
|
||||
// |
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
// |
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
// |
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
using System;
|
||||
using System.Reflection;
|
||||
using JetBrains.Annotations;
|
||||
using Microsoft.OpenApi.Models;
|
||||
using Swashbuckle.AspNetCore.SwaggerGen;
|
||||
|
||||
namespace ArchiSteamFarm.IPC.Swashbuckle;
|
||||
|
||||
[UsedImplicitly]
|
||||
internal sealed class ReadOnlyFixesSchemaFilter : ISchemaFilter {
|
||||
public void Apply(OpenApiSchema schema, SchemaFilterContext context) {
|
||||
ArgumentNullException.ThrowIfNull(schema);
|
||||
ArgumentNullException.ThrowIfNull(context);
|
||||
|
||||
if (schema.ReadOnly && context.MemberInfo is PropertyInfo { CanWrite: true }) {
|
||||
schema.ReadOnly = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -59,7 +59,6 @@ internal static class Program {
|
||||
internal static bool Service { get; private set; }
|
||||
internal static bool ShutdownSequenceInitialized { get; private set; }
|
||||
internal static bool SteamParentalGeneration { get; private set; } = true;
|
||||
internal static bool UseOpenApi { get; private set; }
|
||||
|
||||
private static readonly Dictionary<PosixSignal, PosixSignalRegistration> RegisteredPosixSignals = new();
|
||||
private static readonly TaskCompletionSource<byte> ShutdownResetEvent = new();
|
||||
@@ -611,10 +610,6 @@ internal static class Program {
|
||||
case "--SYSTEM-REQUIRED" when noArgumentValueNext():
|
||||
SystemRequired = true;
|
||||
|
||||
break;
|
||||
case "--USE-OPENAPI" when noArgumentValueNext():
|
||||
UseOpenApi = true;
|
||||
|
||||
break;
|
||||
default:
|
||||
if (cryptKeyNext) {
|
||||
|
||||
@@ -17,8 +17,7 @@
|
||||
<PackageVersion Include="OpenTelemetry.Instrumentation.Http" Version="1.11.0" />
|
||||
<PackageVersion Include="OpenTelemetry.Instrumentation.Runtime" Version="1.11.0" />
|
||||
<PackageVersion Include="SteamKit2" Version="3.0.2" />
|
||||
<PackageVersion Include="Swashbuckle.AspNetCore" Version="7.2.0" />
|
||||
<PackageVersion Include="Swashbuckle.AspNetCore.Annotations" Version="7.2.0" />
|
||||
<PackageVersion Include="Swashbuckle.AspNetCore.SwaggerUI" Version="7.2.0" />
|
||||
<PackageVersion Include="System.Composition" Version="9.0.2" />
|
||||
<PackageVersion Include="System.Composition.AttributedModel" Version="9.0.2" />
|
||||
<PackageVersion Include="System.Linq.Async" Version="6.0.1" />
|
||||
|
||||
Reference in New Issue
Block a user