diff --git a/DorkNet.Server.Tests/VersionCheckControllerTests.cs b/DorkNet.Server.Tests/VersionCheckControllerTests.cs new file mode 100644 index 0000000..e63e2e0 --- /dev/null +++ b/DorkNet.Server.Tests/VersionCheckControllerTests.cs @@ -0,0 +1,56 @@ +using System.Text.Json; +using DorkNet.Server.Compat; +using DorkNet.Server.Controllers.Auth; +using DorkNet.Server.Versions.Late2020; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.Logging.Abstractions; + +namespace DorkNet.Server.Tests; + +public sealed class VersionCheckControllerTests +{ + [Fact] + public void December_2020_build_is_valid_with_minimal_service_config() + { + var controller = CreateController(); + + var result = Assert.IsType(controller.Check("20201210", "0")); + + Assert.Equal(0, ReadVersionStatus(result.Value)); + } + + [Fact] + public void Unknown_build_still_requires_update() + { + var controller = CreateController(); + + var result = Assert.IsType(controller.Check("unknown", "0")); + + Assert.Equal(2, ReadVersionStatus(result.Value)); + } + + private static VersionCheckController CreateController() + { + var config = new ConfigurationBuilder().Build(); + var registry = new VersionRegistry( + [new Late2020VersionPlugin()], + new HashSet(StringComparer.OrdinalIgnoreCase) + { + "december_2020_12_18", + }, + "december_2020_12_18"); + + return new VersionCheckController( + config, + registry, + NullLogger.Instance); + } + + private static int ReadVersionStatus(object? value) + { + var json = JsonSerializer.Serialize(value); + using var document = JsonDocument.Parse(json); + return document.RootElement.GetProperty("VersionStatus").GetInt32(); + } +} diff --git a/DorkNet.Server/Controllers/Auth/VersionCheckController.cs b/DorkNet.Server/Controllers/Auth/VersionCheckController.cs index 30f310a..d44225e 100644 --- a/DorkNet.Server/Controllers/Auth/VersionCheckController.cs +++ b/DorkNet.Server/Controllers/Auth/VersionCheckController.cs @@ -46,6 +46,12 @@ public class VersionCheckController( private const int VersionStatus_ValidForMenu = 1; private const int VersionStatus_UpdateRequired = 2; + private static readonly IReadOnlyDictionary BuiltInBuildIdToVersionKey = + new Dictionary(StringComparer.OrdinalIgnoreCase) + { + ["20201210"] = "december_2020_12_18", + }; + [HttpGet("/api/versioncheck/v4")] [HttpGet("/api/versioncheck/v3")] [HttpGet("/api/versioncheck/v2")] @@ -66,9 +72,17 @@ public class VersionCheckController( // } // Mapping ABSENT → unknown build → UpdateRequired. var mapSection = config.GetSection("DorkNet:BuildIdToVersionKey"); - string? versionKey = !string.IsNullOrWhiteSpace(buildId) - ? mapSection[buildId] - : null; + string? versionKey = null; + if (!string.IsNullOrWhiteSpace(buildId)) + { + var normalizedBuildId = buildId.Trim(); + versionKey = mapSection[normalizedBuildId]; + if (string.IsNullOrWhiteSpace(versionKey) + && BuiltInBuildIdToVersionKey.TryGetValue(normalizedBuildId, out var builtInVersionKey)) + { + versionKey = builtInVersionKey; + } + } bool allowed = versionKey is not null && registry.IsSupported(versionKey); var status = allowed ? VersionStatus_ValidForPlay : VersionStatus_UpdateRequired; diff --git a/DorkNet.Services.Web/DorkNet.Services.Web.csproj b/DorkNet.Services.Web/DorkNet.Services.Web.csproj index 6ddeec4..3714aa6 100644 --- a/DorkNet.Services.Web/DorkNet.Services.Web.csproj +++ b/DorkNet.Services.Web/DorkNet.Services.Web.csproj @@ -12,4 +12,12 @@ + + + wwwroot\%(RecursiveDir)%(Filename)%(Extension) + PreserveNewest + PreserveNewest + + + diff --git a/docker-compose.microservices.dokploy.yml b/docker-compose.microservices.dokploy.yml index 2033c11..39cd1af 100644 --- a/docker-compose.microservices.dokploy.yml +++ b/docker-compose.microservices.dokploy.yml @@ -8,6 +8,12 @@ x-dorknet-service-env: &dorknet-service-env S3__AccessKey: ${DORKNET_S3_ACCESS_KEY:?Set DORKNET_S3_ACCESS_KEY in Dokploy} S3__SecretKey: ${DORKNET_S3_SECRET_KEY:?Set DORKNET_S3_SECRET_KEY in Dokploy} S3__Region: ${DORKNET_S3_REGION:-garage} + Photon__AppId: ${Photon__AppId:?Set Photon__AppId in Dokploy} + Photon__VoiceAppId: ${Photon__VoiceAppId:-} + Photon__CloudRegion: ${Photon__CloudRegion:-eu} + DorkNet__DefaultClientVersion: ${DORKNET_DEFAULT_CLIENT_VERSION:-december_2020_12_18} + DorkNet__SupportedVersions__0: ${DORKNET_SUPPORTED_VERSION:-december_2020_12_18} + DorkNet__BuildIdToVersionKey__20201210: ${DORKNET_DECEMBER_BUILD_VERSION_KEY:-december_2020_12_18} x-dorknet-app-networks: &dorknet-app-networks networks: diff --git a/docker-compose.microservices.yml b/docker-compose.microservices.yml index 15449b1..1c2f6b4 100644 --- a/docker-compose.microservices.yml +++ b/docker-compose.microservices.yml @@ -8,6 +8,12 @@ x-dorknet-service-env: &dorknet-service-env S3__AccessKey: ${DORKNET_S3_ACCESS_KEY:-} S3__SecretKey: ${DORKNET_S3_SECRET_KEY:-} S3__Region: ${DORKNET_S3_REGION:-garage} + Photon__AppId: ${Photon__AppId:-} + Photon__VoiceAppId: ${Photon__VoiceAppId:-} + Photon__CloudRegion: ${Photon__CloudRegion:-eu} + DorkNet__DefaultClientVersion: ${DORKNET_DEFAULT_CLIENT_VERSION:-december_2020_12_18} + DorkNet__SupportedVersions__0: ${DORKNET_SUPPORTED_VERSION:-december_2020_12_18} + DorkNet__BuildIdToVersionKey__20201210: ${DORKNET_DECEMBER_BUILD_VERSION_KEY:-december_2020_12_18} services: gateway: diff --git a/docs/deploy.md b/docs/deploy.md index 99b0781..ee06641 100644 --- a/docs/deploy.md +++ b/docs/deploy.md @@ -53,6 +53,9 @@ S3__Region=garage Photon__AppId=your-photon-realtime-app-id Photon__VoiceAppId=your-photon-voice-app-id Photon__CloudRegion=eu +DORKNET_DEFAULT_CLIENT_VERSION=december_2020_12_18 +DORKNET_SUPPORTED_VERSION=december_2020_12_18 +DORKNET_DECEMBER_BUILD_VERSION_KEY=december_2020_12_18 ``` If Postgres and Redis are Dokploy-managed services in the same project, @@ -163,6 +166,9 @@ DORKNET_DOMAIN=yourdomain.com DORKNET_JWT_SECRET=replace-with-at-least-64-random-characters POSTGRES_PASSWORD=replace-with-a-random-db-password CLOUDFLARE_TUNNEL_TOKEN= +Photon__AppId=your-photon-realtime-app-id +Photon__VoiceAppId=your-photon-voice-app-id +Photon__CloudRegion=eu ``` By default the service containers connect to the bundled compose @@ -281,6 +287,24 @@ S3__TimeoutSeconds=300 For the microservices compose file, use the `DORKNET_S3_*` equivalents; Compose maps them into `S3__*` for the service containers. +The microservices compose files also map `Photon__AppId`, +`Photon__VoiceAppId`, and `Photon__CloudRegion` into every backend +service. Missing or wrong Photon values show up in the 2020 client as +NameServer/region connection failures after the boot version check. + +They also map the December 2020 build ID into the version gate. A quick +check for the 2020 client should return `{"VersionStatus":0}`: + +```bash +curl https://api.yourdomain.com/api/versioncheck/v4?v=20201210 +``` + +If `admin.` or `feed.` returns the NameServer JSON +instead of the static site, verify the `web` container contains +`/app/wwwroot/admin/index.html` and `/app/wwwroot/feed/index.html`. +Those hosts should resolve through the gateway to the `web` service, not +the `ns` service or monolith fallback. + --- ## First-time cutover from SQLite