diff --git a/.github/workflows/docker-image.yml b/.github/workflows/docker-image.yml new file mode 100644 index 00000000..bbd94e7d --- /dev/null +++ b/.github/workflows/docker-image.yml @@ -0,0 +1,173 @@ +# Inspired by https://github.com/sredevopsorg/multi-arch-docker-github-workflow +name: Docker Image CI + +on: + push: + tags: + - "v[0-9]+.[0-9]+.[0-9]+" + workflow_dispatch: + inputs: + upstream_tag: + description: "Upstream tag of nextcloud/notify_push to build" + required: false + +jobs: + build: + runs-on: ${{ matrix.runner }} + + permissions: + contents: read + packages: write + + strategy: + fail-fast: false + matrix: + include: + - platform: linux/amd64 + runner: ubuntu-latest + - platform: linux/arm64 + runner: ubuntu-24.04-arm + + steps: + - name: Checkout code + uses: actions/checkout@v6 + with: + repository: ${{ inputs.upstream_tag && 'nextcloud/notify_push' || github.repository }} + ref: ${{ inputs.upstream_tag || github.ref_name }} + + - name: Log in to GitHub Packages + uses: docker/login-action@v4 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v4 + + - name: Lower case docker image name + id: image + uses: ASzc/change-string-case-action@v8 + with: + string: ${{ github.repository }} + + - name: Sanitize upstream tag + if: inputs.upstream_tag != '' + id: tag + run: echo "value=${INPUT_TAG#v}" >> $GITHUB_OUTPUT + env: + INPUT_TAG: ${{ inputs.upstream_tag }} + + - name: Extract metadata + id: meta + uses: docker/metadata-action@v6 + with: + images: ghcr.io/${{ github.repository }} + tags: | + type=ref,event=tag + type=raw,value=latest + type=raw,value=${{ steps.tag.outputs.value }},enable=${{ inputs.upstream_tag != '' }} + + - name: Build and push by digest + id: build + uses: docker/build-push-action@v7 + with: + context: . + platforms: ${{ matrix.platform }} + labels: ${{ steps.meta.outputs.labels }} + outputs: type=image,name=ghcr.io/${{ steps.image.outputs.lowercase }},push-by-digest=true,name-canonical=true,push=true + cache-from: type=gha,scope=${{ matrix.platform }} + cache-to: type=gha,mode=max,scope=${{ matrix.platform }} + + - name: Export digest + run: | + mkdir -p /tmp/digests + touch "/tmp/digests/${DIGEST#sha256:}" + env: + DIGEST: ${{ steps.build.outputs.digest }} + + - name: Upload digest + uses: actions/upload-artifact@v7 + with: + name: digests-${{ matrix.platform == 'linux/amd64' && 'amd64' || 'arm64' }} + path: /tmp/digests/* + if-no-files-found: error + retention-days: 1 + + merge: + runs-on: ubuntu-latest + needs: build + + permissions: + contents: read + packages: write + + steps: + - name: Download digests + uses: actions/download-artifact@v8 + with: + path: /tmp/digests + pattern: digests-* + merge-multiple: true + + - name: Log in to GitHub Packages + uses: docker/login-action@v4 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v4 + + - name: Lower case docker image name + id: image + uses: ASzc/change-string-case-action@v8 + with: + string: ${{ github.repository }} + + - name: Sanitize upstream tag + if: inputs.upstream_tag != '' + id: tag + run: echo "value=${INPUT_TAG#v}" >> $GITHUB_OUTPUT + env: + INPUT_TAG: ${{ inputs.upstream_tag }} + + - name: Extract metadata + id: meta + uses: docker/metadata-action@v6 + with: + images: ghcr.io/${{ github.repository }} + tags: | + type=ref,event=tag + type=raw,value=latest + type=raw,value=${{ steps.tag.outputs.value }},enable=${{ inputs.upstream_tag != '' }} + + - name: Get timestamp + id: timestamp + run: echo "timestamp=$(date -u +"%Y-%m-%dT%H:%M:%SZ")" >> $GITHUB_OUTPUT + + - name: Create and push manifest + id: manifest + working-directory: /tmp/digests + continue-on-error: true + run: | + docker buildx imagetools create \ + $(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \ + --annotation='index:org.opencontainers.image.description=${{ github.event.repository.description }}' \ + --annotation='index:org.opencontainers.image.created=${{ steps.timestamp.outputs.timestamp }}' \ + --annotation='index:org.opencontainers.image.url=${{ github.event.repository.url }}' \ + --annotation='index:org.opencontainers.image.source=${{ github.event.repository.url }}' \ + $(printf 'ghcr.io/${{ steps.image.outputs.lowercase }}@sha256:%s ' *) + + - name: Create and push manifest (without annotations) + if: steps.manifest.outcome == 'failure' + working-directory: /tmp/digests + run: | + docker buildx imagetools create \ + $(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \ + $(printf 'ghcr.io/${{ steps.image.outputs.lowercase }}@sha256:%s ' *) + + - name: Inspect manifest + run: | + docker buildx imagetools inspect 'ghcr.io/${{ steps.image.outputs.lowercase }}:${{ steps.meta.outputs.version }}' diff --git a/Cargo.lock b/Cargo.lock index dc4b756c..7c3c02a4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1629,9 +1629,9 @@ dependencies = [ [[package]] name = "nextcloud-config-parser" -version = "0.15.2" +version = "0.15.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a285761c91b18426c4cadf66ffb77f8881ea5cb417d94bad384bcf85a9965de" +checksum = "0ea4ec27a2e48f55995d7725a6ed8947630348e01803061a8b704ae3a96987d3" dependencies = [ "form_urlencoded", "indexmap", @@ -3876,7 +3876,7 @@ version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ - "windows-sys 0.61.2", + "windows-sys 0.48.0", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 26a56a78..da387087 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -29,7 +29,7 @@ rand = { version = "0.8.5", features = ["small_rng"] } ahash = "0.8.12" flexi_logger = { version = "0.31.8", features = ["colors"] } tokio-stream = { version = "0.1.17", features = ["net"] } -nextcloud-config-parser = "0.15.1" +nextcloud-config-parser = "0.15.2" url = "2.5.4" clap = { version = "4.5.43", features = ["derive"] } sd-notify = { version = "0.5.0", optional = true } diff --git a/appinfo/info.xml b/appinfo/info.xml index 0f63140e..3daa82b6 100644 --- a/appinfo/info.xml +++ b/appinfo/info.xml @@ -11,7 +11,7 @@ - 1.3.1 + 1.3.3 agpl Robin Appelman NotifyPush @@ -27,7 +27,7 @@ Once the app is installed, the push binary needs to be setup. You can either use https://github.com/nextcloud/notify_push/issues - + diff --git a/flake.nix b/flake.nix index fb776f23..b4775e5a 100644 --- a/flake.nix +++ b/flake.nix @@ -27,7 +27,7 @@ }; lib = pkgs.lib; - hostTarget = pkgs.hostPlatform.config; + hostTarget = pkgs.stdenv.hostPlatform.config; targets = [ "x86_64-unknown-linux-musl" "i686-unknown-linux-musl" diff --git a/lib/Listener.php b/lib/Listener.php index a5526ae7..97b13eec 100644 --- a/lib/Listener.php +++ b/lib/Listener.php @@ -22,6 +22,7 @@ use OCP\Notification\IDismissableNotifier; use OCP\Notification\INotification; use OCP\Notification\INotifier; +use OCP\Notification\UnknownNotificationException; use OCP\Share\Events\ShareCreatedEvent; use OCP\Share\IShare; @@ -102,7 +103,7 @@ public function getName(): string { } public function prepare(INotification $notification, string $languageCode): INotification { - throw new \InvalidArgumentException(); + throw new UnknownNotificationException(); } public function dismissNotification(INotification $notification): void { diff --git a/psalm.xml b/psalm.xml index 55758e1b..7a444190 100644 --- a/psalm.xml +++ b/psalm.xml @@ -8,7 +8,7 @@ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="https://getpsalm.org/schema/config" xsi:schemaLocation="https://getpsalm.org/schema/config https://getpsalm.org/schema/config" - phpVersion="8.0" + phpVersion="8.1" > diff --git a/src/config.rs b/src/config.rs index 1c0f2ec4..965b7dd1 100644 --- a/src/config.rs +++ b/src/config.rs @@ -20,7 +20,7 @@ use sqlx::any::AnyConnectOptions; use std::convert::{TryFrom, TryInto}; use std::env::var; use std::fmt::{Debug, Display, Formatter}; -use std::net::{IpAddr, Ipv4Addr, SocketAddr}; +use std::net::{IpAddr, Ipv6Addr, SocketAddr}; use std::path::{Path, PathBuf}; use std::str::FromStr; @@ -183,9 +183,7 @@ impl TryFrom for Config { let bind = match config.socket { Some(socket) => Bind::Unix(socket, socket_permissions), None => { - let ip = config - .bind - .unwrap_or_else(|| IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0))); + let ip = config.bind.unwrap_or(IpAddr::V6(Ipv6Addr::UNSPECIFIED)); let port = config.port.unwrap_or(7867); Bind::Tcp((ip, port).into()) } @@ -194,9 +192,7 @@ impl TryFrom for Config { let metrics_bind = match (config.metrics_socket, config.metrics_port) { (Some(socket), _) => Some(Bind::Unix(socket, socket_permissions)), (None, Some(port)) => { - let ip = config - .bind - .unwrap_or_else(|| IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0))); + let ip = config.bind.unwrap_or(IpAddr::V6(Ipv6Addr::UNSPECIFIED)); Some(Bind::Tcp((ip, port).into())) } _ => None, diff --git a/src/connection.rs b/src/connection.rs index e4cf55ae..2d458e94 100644 --- a/src/connection.rs +++ b/src/connection.rs @@ -226,6 +226,7 @@ pub async fn handle_user_socket( // hack while warp only has opaque error types match formatted.as_str() { "WebSocket protocol error: Connection reset without closing handshake" + | "Broken pipe (os error 32)" | "IO error: Connection reset by peer (os error 104)" => { log::debug!("websocket error: {e:#}") } @@ -262,11 +263,13 @@ async fn socket_auth( let username_msg = read_socket_auth_message(rx).await?; let username = username_msg .to_str() - .map_err(|_| AuthenticationError::InvalidMessage)?; + .map_err(|_| AuthenticationError::InvalidMessage)? + .trim(); let password_msg = read_socket_auth_message(rx).await?; let password = password_msg .to_str() - .map_err(|_| AuthenticationError::InvalidMessage)?; + .map_err(|_| AuthenticationError::InvalidMessage)? + .trim(); // cleanup all pre_auth tokens older than 15s let cutoff = Instant::now() - Duration::from_secs(15);