diff --git a/.github/workflows/build-deploy.yml b/.github/workflows/build-deploy.yml index c3f2298..519bd57 100644 --- a/.github/workflows/build-deploy.yml +++ b/.github/workflows/build-deploy.yml @@ -3,6 +3,7 @@ name: Build & Deploy on: push: branches: [ main ] + pull_request: jobs: build: @@ -36,8 +37,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_SHA}" >> $GITHUB_OUTPUT + fi + - name: Build and push id: docker_build uses: docker/build-push-action@v6 @@ -80,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 + NETSTR_VERSION: ${{ github.sha }} \ No newline at end of file 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/.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/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 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 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/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) 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/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 + + }
diff --git a/src/Netstr/appsettings.json b/src/Netstr/appsettings.json index 94d8a0e..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, @@ -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" diff --git a/src/Netstr/wwwroot/favicon.ico b/src/Netstr/wwwroot/favicon.ico new file mode 100644 index 0000000..c341ffa Binary files /dev/null and b/src/Netstr/wwwroot/favicon.ico differ 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;