From f811a8ccd7090fed248337380942c3a99e92ec0a Mon Sep 17 00:00:00 2001 From: Tomas Bezouska Date: Sat, 5 Oct 2024 08:59:21 +0200 Subject: [PATCH 01/11] ui: show env only when set --- src/Netstr/Views/Home/Index.cshtml | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/Netstr/Views/Home/Index.cshtml b/src/Netstr/Views/Home/Index.cshtml index daa0497..ee92264 100644 --- a/src/Netstr/Views/Home/Index.cshtml +++ b/src/Netstr/Views/Home/Index.cshtml @@ -41,10 +41,13 @@ Software @Model.RelayInformation.Software - - Environment - @Model.Environment - + @if (!string.IsNullOrEmpty(Model.Environment)) + { + + Environment + @Model.Environment + + }
From 21096964b005fb16c4e18a9fbe23d245222ea2ad Mon Sep 17 00:00:00 2001 From: Tomas Bezouska Date: Sat, 5 Oct 2024 09:01:26 +0200 Subject: [PATCH 02/11] chores: use hex instead of npub --- src/Netstr/appsettings.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Netstr/appsettings.json b/src/Netstr/appsettings.json index 94d8a0e..2383bac 100644 --- a/src/Netstr/appsettings.json +++ b/src/Netstr/appsettings.json @@ -46,7 +46,7 @@ "RelayInformation": { "Name": "netstr.io", "Description": "A nostr relay", - "PublicKey": "npub1q84c9lheynjl33u6ha5ulfdd25yw0p90w2zqx6f23w6cjrn7w76s373p35", + "PublicKey": "01eb82fef924e5f8c79abf69cfa5ad5508e784af728403692a8bb5890e7e77b5", "Contact": "bezysoftware@outlook.com", "SupportedNips": [ 1, 2, 4, 9, 11, 13, 17, 40, 42, 45, 70 ], "Version": "v0.0.0" From 28bbb7b5e94890ebb8e675d7b6922d509e8edb47 Mon Sep 17 00:00:00 2001 From: Tomas Bezouska Date: Sat, 5 Oct 2024 09:04:28 +0200 Subject: [PATCH 03/11] chores: increase limits --- src/Netstr/appsettings.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Netstr/appsettings.json b/src/Netstr/appsettings.json index 2383bac..2e6c109 100644 --- a/src/Netstr/appsettings.json +++ b/src/Netstr/appsettings.json @@ -29,10 +29,10 @@ }, "Limits": { "MaxPayloadSize": 524288, - "MaxInitialLimit": 100, + "MaxInitialLimit": 1000, "MinPowDifficulty": 0, "MaxFilters": 20, - "MaxSubscriptions": 30, + "MaxSubscriptions": 50, "MaxSubscriptionIdLength": 128, "MaxSubscriptionLimit": 1000, "MaxEventTags": 1000, From 44020e3c2049377ffcecb745b6a5d184d3c76864 Mon Sep 17 00:00:00 2001 From: Tomas Bezouska Date: Sat, 5 Oct 2024 09:06:08 +0200 Subject: [PATCH 04/11] feat(logging): log processing errors as warnings --- src/Netstr/Messaging/MessageDispatcher.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Netstr/Messaging/MessageDispatcher.cs b/src/Netstr/Messaging/MessageDispatcher.cs index 22a8516..0f372ac 100644 --- a/src/Netstr/Messaging/MessageDispatcher.cs +++ b/src/Netstr/Messaging/MessageDispatcher.cs @@ -31,7 +31,7 @@ public async Task DispatchMessageAsync(IWebSocketAdapter sender, string message) } catch (MessageProcessingException ex) { - this.logger.LogError(ex, $"Failed to process message: {message}"); + this.logger.LogWarning(ex, $"Failed to process message: {message}"); await sender.SendAsync(ex.GetSenderReply()); } catch (Exception ex) From 9caff837cb74b83af7f110b3feb4be7b9cc89da1 Mon Sep 17 00:00:00 2001 From: Tomas Bezouska Date: Sat, 5 Oct 2024 15:06:24 +0200 Subject: [PATCH 05/11] fix(core): json escaping of some characters --- .../{Extensions => Json}/JsonExtensions.cs | 2 +- src/Netstr/Json/NostrJsonEncoder.cs | 29 +++++++++++++++++++ .../UnixTimestampJsonConverter.cs | 2 +- src/Netstr/Messaging/Events/EventParser.cs | 2 +- .../Events/Validators/EventHashValidator.cs | 5 ++-- .../FilterMessageHandlerBase.cs | 1 + .../UnsubscribeMessageHandler.cs | 2 +- src/Netstr/Messaging/Models/Event.cs | 2 +- .../Messaging/Models/SubscriptionFilter.cs | 2 +- src/Netstr/Netstr.csproj | 1 + test/Netstr.Tests/NIPs/01.feature | 16 +++++----- test/Netstr.Tests/NIPs/01.feature.cs | 8 ++--- test/Netstr.Tests/NIPs/Types.cs | 2 +- 13 files changed, 53 insertions(+), 21 deletions(-) rename src/Netstr/{Extensions => Json}/JsonExtensions.cs (97%) create mode 100644 src/Netstr/Json/NostrJsonEncoder.cs rename src/Netstr/{Converters => Json}/UnixTimestampJsonConverter.cs (96%) diff --git a/src/Netstr/Extensions/JsonExtensions.cs b/src/Netstr/Json/JsonExtensions.cs similarity index 97% rename from src/Netstr/Extensions/JsonExtensions.cs rename to src/Netstr/Json/JsonExtensions.cs index 65e8360..d1249c3 100644 --- a/src/Netstr/Extensions/JsonExtensions.cs +++ b/src/Netstr/Json/JsonExtensions.cs @@ -1,6 +1,6 @@ using System.Text.Json; -namespace Netstr.Extensions +namespace Netstr.Json { public static class JsonExtensions { diff --git a/src/Netstr/Json/NostrJsonEncoder.cs b/src/Netstr/Json/NostrJsonEncoder.cs new file mode 100644 index 0000000..0a09c00 --- /dev/null +++ b/src/Netstr/Json/NostrJsonEncoder.cs @@ -0,0 +1,29 @@ +using System.Text.Encodings.Web; + +namespace Netstr.Json +{ + /// + /// Json encoder for nostr events which follows NIP-01's character escaping rules. + /// + public class NostrJsonEncoder : JavaScriptEncoder + { + private static int[] EscapableCharacters = [0x0A, 0x22, 0x5C, 0x0D, 0x09, 0x08, 0x0C]; + + public override int MaxOutputCharactersPerInputCharacter => JavaScriptEncoder.Default.MaxOutputCharactersPerInputCharacter; + + public override unsafe int FindFirstCharacterToEncode(char* text, int textLength) + { + return JavaScriptEncoder.UnsafeRelaxedJsonEscaping.FindFirstCharacterToEncode(text, textLength); + } + + public override unsafe bool TryEncodeUnicodeScalar(int unicodeScalar, char* buffer, int bufferLength, out int numberOfCharactersWritten) + { + return JavaScriptEncoder.UnsafeRelaxedJsonEscaping.TryEncodeUnicodeScalar(unicodeScalar, buffer, bufferLength, out numberOfCharactersWritten); + } + + public override bool WillEncode(int unicodeScalar) + { + return EscapableCharacters.Contains(unicodeScalar); + } + } +} diff --git a/src/Netstr/Converters/UnixTimestampJsonConverter.cs b/src/Netstr/Json/UnixTimestampJsonConverter.cs similarity index 96% rename from src/Netstr/Converters/UnixTimestampJsonConverter.cs rename to src/Netstr/Json/UnixTimestampJsonConverter.cs index 6ed85b7..905976e 100644 --- a/src/Netstr/Converters/UnixTimestampJsonConverter.cs +++ b/src/Netstr/Json/UnixTimestampJsonConverter.cs @@ -1,7 +1,7 @@ using System.Text.Json.Serialization; using System.Text.Json; -namespace Netstr.Converters +namespace Netstr.Json { /// /// Converts Unix time to DateTimeOffset. diff --git a/src/Netstr/Messaging/Events/EventParser.cs b/src/Netstr/Messaging/Events/EventParser.cs index 9a22446..59a08a2 100644 --- a/src/Netstr/Messaging/Events/EventParser.cs +++ b/src/Netstr/Messaging/Events/EventParser.cs @@ -1,4 +1,4 @@ -using Netstr.Extensions; +using Netstr.Json; using Netstr.Messaging.Models; using System.Text.Json; diff --git a/src/Netstr/Messaging/Events/Validators/EventHashValidator.cs b/src/Netstr/Messaging/Events/Validators/EventHashValidator.cs index 24ec3ba..89160ad 100644 --- a/src/Netstr/Messaging/Events/Validators/EventHashValidator.cs +++ b/src/Netstr/Messaging/Events/Validators/EventHashValidator.cs @@ -1,4 +1,5 @@ -using Netstr.Messaging.Models; +using Netstr.Json; +using Netstr.Messaging.Models; using System.Security.Cryptography; using System.Text.Encodings.Web; using System.Text.Json; @@ -12,7 +13,7 @@ public class EventHashValidator : IEventValidator { private static JsonSerializerOptions serializerOptions = new JsonSerializerOptions { - Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping + Encoder = new NostrJsonEncoder() }; public string? Validate(Event e, ClientContext context) diff --git a/src/Netstr/Messaging/MessageHandlers/FilterMessageHandlerBase.cs b/src/Netstr/Messaging/MessageHandlers/FilterMessageHandlerBase.cs index 4e3ad64..7a8d15d 100644 --- a/src/Netstr/Messaging/MessageHandlers/FilterMessageHandlerBase.cs +++ b/src/Netstr/Messaging/MessageHandlers/FilterMessageHandlerBase.cs @@ -1,6 +1,7 @@ using Microsoft.Extensions.Options; using Netstr.Data; using Netstr.Extensions; +using Netstr.Json; using Netstr.Messaging.Models; using Netstr.Messaging.Subscriptions; using Netstr.Messaging.Subscriptions.Validators; diff --git a/src/Netstr/Messaging/MessageHandlers/UnsubscribeMessageHandler.cs b/src/Netstr/Messaging/MessageHandlers/UnsubscribeMessageHandler.cs index b8427a4..37368ea 100644 --- a/src/Netstr/Messaging/MessageHandlers/UnsubscribeMessageHandler.cs +++ b/src/Netstr/Messaging/MessageHandlers/UnsubscribeMessageHandler.cs @@ -1,4 +1,4 @@ -using Netstr.Extensions; +using Netstr.Json; using Netstr.Messaging.Models; using System.Text.Json; diff --git a/src/Netstr/Messaging/Models/Event.cs b/src/Netstr/Messaging/Models/Event.cs index 1e197fd..827ce97 100644 --- a/src/Netstr/Messaging/Models/Event.cs +++ b/src/Netstr/Messaging/Models/Event.cs @@ -1,5 +1,5 @@ using Microsoft.EntityFrameworkCore.Diagnostics; -using Netstr.Converters; +using Netstr.Json; using System.Numerics; using System.Text.Json.Serialization; diff --git a/src/Netstr/Messaging/Models/SubscriptionFilter.cs b/src/Netstr/Messaging/Models/SubscriptionFilter.cs index ff007f7..fed661e 100644 --- a/src/Netstr/Messaging/Models/SubscriptionFilter.cs +++ b/src/Netstr/Messaging/Models/SubscriptionFilter.cs @@ -1,4 +1,4 @@ -using Netstr.Converters; +using Netstr.Json; using System.Text.Json; using System.Text.Json.Serialization; diff --git a/src/Netstr/Netstr.csproj b/src/Netstr/Netstr.csproj index c25c05c..1a273a6 100644 --- a/src/Netstr/Netstr.csproj +++ b/src/Netstr/Netstr.csproj @@ -6,6 +6,7 @@ enable Linux ..\..\Dockerfile + true diff --git a/test/Netstr.Tests/NIPs/01.feature b/test/Netstr.Tests/NIPs/01.feature index 724e5d6..40b80e9 100644 --- a/test/Netstr.Tests/NIPs/01.feature +++ b/test/Netstr.Tests/NIPs/01.feature @@ -1,4 +1,4 @@ -Feature: NIP-01 +Feature: NIP-01 Defines the basic protocol that should be implemented by everybody. Background: @@ -20,21 +20,21 @@ Scenario: Invalid messages are discarded, valid ones accepted | Kinds | | 1 | And Bob publishes events - | Id | Content | Kind | CreatedAt | Signature | Tags | - | ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff | Hello 1 | 1 | 1722337838 | | | - | a6d166e834e78827af0770f31f15b13a772f281ad880f43ce12c24d4e3d0e346 | Hello 1 | 1 | 1722337838 | Invalid | | - | 9a6b4cefcd17f3bf7fb03c02da044c628836a118c47d5b92503c1d2bdb796296 | Hi ' \" \b \t \r \n | 1 | 1722337838 | | | - | 50ed63c449df67d89e9964a27a26abbf214ca155b03915067a5a0f75618802bb | Hello | 1 | 1722337838 | | [[]] | + | Id | Content | Kind | CreatedAt | Signature | Tags | + | ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff | Hello 1 | 1 | 1722337838 | | | + | a6d166e834e78827af0770f31f15b13a772f281ad880f43ce12c24d4e3d0e346 | Hello 1 | 1 | 1722337838 | Invalid | | + | bb5d2fe5b2c16c676d87ef446fa38581b9fa45e2e50ba89568664abf4e1d1396 | Hi ' \" \b \t \r \n 🎉 #nostr | 1 | 1722337838 | | | + | 50ed63c449df67d89e9964a27a26abbf214ca155b03915067a5a0f75618802bb | Hello | 1 | 1722337838 | | [[]] | Then Bob receives messages | Type | Id | Success | | OK | ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff | false | | OK | a6d166e834e78827af0770f31f15b13a772f281ad880f43ce12c24d4e3d0e346 | false | - | OK | 9a6b4cefcd17f3bf7fb03c02da044c628836a118c47d5b92503c1d2bdb796296 | true | + | OK | bb5d2fe5b2c16c676d87ef446fa38581b9fa45e2e50ba89568664abf4e1d1396 | true | | OK | 50ed63c449df67d89e9964a27a26abbf214ca155b03915067a5a0f75618802bb | false | And Alice receives a message | Type | Id | EventId | | EOSE | abcd | | - | EVENT | abcd | 9a6b4cefcd17f3bf7fb03c02da044c628836a118c47d5b92503c1d2bdb796296 | + | EVENT | abcd | bb5d2fe5b2c16c676d87ef446fa38581b9fa45e2e50ba89568664abf4e1d1396 | Scenario: Newly subscribed client receives matching events, EOSE and future events Bob publishes events which are stored by the relay before any subscription exists. diff --git a/test/Netstr.Tests/NIPs/01.feature.cs b/test/Netstr.Tests/NIPs/01.feature.cs index 292e76d..9a840ba 100644 --- a/test/Netstr.Tests/NIPs/01.feature.cs +++ b/test/Netstr.Tests/NIPs/01.feature.cs @@ -168,8 +168,8 @@ public void InvalidMessagesAreDiscardedValidOnesAccepted() "Invalid", ""}); table5.AddRow(new string[] { - "9a6b4cefcd17f3bf7fb03c02da044c628836a118c47d5b92503c1d2bdb796296", - "Hi \' \\\" \\b \\t \\r \n", + "bb5d2fe5b2c16c676d87ef446fa38581b9fa45e2e50ba89568664abf4e1d1396", + "Hi \' \\\" \\b \\t \\r \n 🎉 #nostr", "1", "1722337838", "", @@ -198,7 +198,7 @@ public void InvalidMessagesAreDiscardedValidOnesAccepted() "false"}); table6.AddRow(new string[] { "OK", - "9a6b4cefcd17f3bf7fb03c02da044c628836a118c47d5b92503c1d2bdb796296", + "bb5d2fe5b2c16c676d87ef446fa38581b9fa45e2e50ba89568664abf4e1d1396", "true"}); table6.AddRow(new string[] { "OK", @@ -218,7 +218,7 @@ public void InvalidMessagesAreDiscardedValidOnesAccepted() table7.AddRow(new string[] { "EVENT", "abcd", - "9a6b4cefcd17f3bf7fb03c02da044c628836a118c47d5b92503c1d2bdb796296"}); + "bb5d2fe5b2c16c676d87ef446fa38581b9fa45e2e50ba89568664abf4e1d1396"}); #line 34 testRunner.And("Alice receives a message", ((string)(null)), table7, "And "); #line hidden diff --git a/test/Netstr.Tests/NIPs/Types.cs b/test/Netstr.Tests/NIPs/Types.cs index d3d6191..afa1229 100644 --- a/test/Netstr.Tests/NIPs/Types.cs +++ b/test/Netstr.Tests/NIPs/Types.cs @@ -1,4 +1,4 @@ -using Netstr.Extensions; +using Netstr.Json; using Netstr.Messaging.Models; using System.Net.WebSockets; using System.Text.Json; From 1d171f1b38366a21f809f79ae535cc414f85b130 Mon Sep 17 00:00:00 2001 From: Tomas Bezouska Date: Sat, 5 Oct 2024 21:37:09 +0200 Subject: [PATCH 06/11] feat(deploy): merge deploy with build --- .github/workflows/build-deploy.yml | 12 +++++-- .github/workflows/build.yml | 53 ------------------------------ Netstr.sln | 1 - 3 files changed, 9 insertions(+), 57 deletions(-) delete mode 100644 .github/workflows/build.yml diff --git a/.github/workflows/build-deploy.yml b/.github/workflows/build-deploy.yml index c3f2298..193dbaa 100644 --- a/.github/workflows/build-deploy.yml +++ b/.github/workflows/build-deploy.yml @@ -2,7 +2,7 @@ name: Build & Deploy on: push: - branches: [ main ] + pull_request: jobs: build: @@ -36,8 +36,14 @@ jobs: - name: Get docker tag id: tag - run: echo "IMAGE_TAG=latest" >> $GITHUB_OUTPUT - + run: | + echo "${GITHUB_REF##*/}" + if [[ "${GITHUB_REF##*/}" == "main" ]]; then + echo "IMAGE_TAG=latest" >> $GITHUB_OUTPUT + else + echo "IMAGE_TAG=${GITHUB_REF##*/}" >> $GITHUB_OUTPUT + fi + - name: Build and push id: docker_build uses: docker/build-push-action@v6 diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml deleted file mode 100644 index 615637f..0000000 --- a/.github/workflows/build.yml +++ /dev/null @@ -1,53 +0,0 @@ -name: Build - -on: - push: - branches: - - '!main' - pull_request: - -jobs: - build: - runs-on: ubuntu-latest - permissions: - packages: write - outputs: - image-tag: ${{ steps.tag.outputs.IMAGE_TAG }} - steps: - - name: Check Out Repo - uses: actions/checkout@v4 - - - name: Login to Github Container Registry - uses: docker/login-action@v3 - with: - registry: ghcr.io - username: bezysoftware - password: ${{ secrets.GITHUB_TOKEN }} - - - name: Set up Docker Buildx - id: buildx - uses: docker/setup-buildx-action@v3 - - - name: Cache Docker layers - uses: actions/cache@v4 - with: - path: /tmp/.buildx-cache - key: ${{ runner.os }}-buildx-${{ github.sha }} - restore-keys: | - ${{ runner.os }}-buildx- - - - name: Get docker tag - id: tag - run: echo "IMAGE_TAG=${GITHUB_SHA}" >> $GITHUB_OUTPUT - - - name: Build and push - id: docker_build - uses: docker/build-push-action@v6 - with: - context: ./ - file: ./Dockerfile - builder: ${{ steps.buildx.outputs.name }} - push: true - tags: ghcr.io/bezysoftware/netstr:${{ steps.tag.outputs.IMAGE_TAG }} - cache-from: type=local,src=/tmp/.buildx-cache - cache-to: type=local,dest=/tmp/.buildx-cache \ No newline at end of file diff --git a/Netstr.sln b/Netstr.sln index 4d22922..20c0a78 100644 --- a/Netstr.sln +++ b/Netstr.sln @@ -23,7 +23,6 @@ EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{320F094E-4B63-40D7-8D8B-AB5B01F6FCB0}" ProjectSection(SolutionItems) = preProject .github\workflows\build-deploy.yml = .github\workflows\build-deploy.yml - .github\workflows\build.yml = .github\workflows\build.yml .github\workflows\manual.yml = .github\workflows\manual.yml .github\workflows\release.yml = .github\workflows\release.yml EndProjectSection From d11848e9c57d795ce05e56e61df12956faa65a56 Mon Sep 17 00:00:00 2001 From: Tomas Bezouska Date: Sat, 5 Oct 2024 21:39:56 +0200 Subject: [PATCH 07/11] feat(deploy): only build & deploy on main & PR --- .github/workflows/build-deploy.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/build-deploy.yml b/.github/workflows/build-deploy.yml index 193dbaa..d73b6fc 100644 --- a/.github/workflows/build-deploy.yml +++ b/.github/workflows/build-deploy.yml @@ -2,6 +2,7 @@ name: Build & Deploy on: push: + branches: [ main ] pull_request: jobs: From 0cc3dcb9e43b2a7e710698239eda5724ec7560d3 Mon Sep 17 00:00:00 2001 From: Tomas Bezouska Date: Sat, 5 Oct 2024 22:49:39 +0200 Subject: [PATCH 08/11] feat(deploy): use github.sha --- .github/workflows/build-deploy.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build-deploy.yml b/.github/workflows/build-deploy.yml index d73b6fc..fc52603 100644 --- a/.github/workflows/build-deploy.yml +++ b/.github/workflows/build-deploy.yml @@ -42,7 +42,7 @@ jobs: if [[ "${GITHUB_REF##*/}" == "main" ]]; then echo "IMAGE_TAG=latest" >> $GITHUB_OUTPUT else - echo "IMAGE_TAG=${GITHUB_REF##*/}" >> $GITHUB_OUTPUT + echo "IMAGE_TAG=${GITHUB_SHA}" >> $GITHUB_OUTPUT fi - name: Build and push @@ -87,4 +87,5 @@ jobs: NETSTR_IMAGE: "ghcr.io/bezysoftware/netstr:${{ env.IMAGE_TAG }}" NETSTR_ENVIRONMENT: dev NETSTR_ENVIRONMENT_LONG: Development - NETSTR_PORT: 8081 \ No newline at end of file + NETSTR_PORT: 8081 + RelayInformation__Version: ${{ github.sha }} \ No newline at end of file From 438ac21b761df82c19a08fafc03ca15809156ee0 Mon Sep 17 00:00:00 2001 From: Tomas Bezouska Date: Sat, 5 Oct 2024 23:02:54 +0200 Subject: [PATCH 09/11] feat(deploy): show correct version --- .github/workflows/build-deploy.yml | 2 +- .github/workflows/release.yml | 3 ++- compose.yaml | 1 + 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build-deploy.yml b/.github/workflows/build-deploy.yml index fc52603..519bd57 100644 --- a/.github/workflows/build-deploy.yml +++ b/.github/workflows/build-deploy.yml @@ -88,4 +88,4 @@ jobs: NETSTR_ENVIRONMENT: dev NETSTR_ENVIRONMENT_LONG: Development NETSTR_PORT: 8081 - RelayInformation__Version: ${{ github.sha }} \ No newline at end of file + NETSTR_VERSION: ${{ github.sha }} \ No newline at end of file diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index c028e43..5a19307 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -66,4 +66,5 @@ jobs: NETSTR_DB_PASSWORD: ${{ secrets.NETSTR_DB_PASSWORD }} NETSTR_IMAGE: "bezysoftware/netstr:${{ env.IMAGE_TAG }}" NETSTR_ENVIRONMENT: prod - NETSTR_PORT: 8080 \ No newline at end of file + NETSTR_PORT: 8080 + NETSTR_VERSION: ${{ env.IMAGE_TAG }} \ No newline at end of file diff --git a/compose.yaml b/compose.yaml index 9a2500b..ba5d818 100644 --- a/compose.yaml +++ b/compose.yaml @@ -8,6 +8,7 @@ services: - "${NETSTR_PORT:-8080}:8080" environment: ConnectionStrings__NetstrDatabase: Host=db:5432;Database=Netsrt;Username=Netstr;Password=${NETSTR_DB_PASSWORD:?Password must be set} + RelayInformation__Version: ${NETSTR_VERSION:-v0.0.0} ASPNETCORE_ENVIRONMENT: ${NETSTR_ENVIRONMENT_LONG} depends_on: - db From 1da728394a8b8b2c0671e59badb3c34975d2dd9a Mon Sep 17 00:00:00 2001 From: Tomas Bezouska Date: Sun, 6 Oct 2024 18:00:01 +0200 Subject: [PATCH 10/11] feat(ui): favicon --- src/Netstr/wwwroot/favicon.ico | Bin 0 -> 15406 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 src/Netstr/wwwroot/favicon.ico diff --git a/src/Netstr/wwwroot/favicon.ico b/src/Netstr/wwwroot/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..3800cb751b035d28c2be343a29b3348d1bf8230d GIT binary patch literal 15406 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 Date: Sun, 6 Oct 2024 18:43:31 +0200 Subject: [PATCH 11/11] feat(ui): larger favicon --- src/Netstr/wwwroot/favicon.ico | Bin 15406 -> 67646 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/src/Netstr/wwwroot/favicon.ico b/src/Netstr/wwwroot/favicon.ico index 3800cb751b035d28c2be343a29b3348d1bf8230d..c341ffa858d46a21f3d47ad4204ce20e695ede54 100644 GIT binary patch literal 67646 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