Compare commits

...

28 Commits

Author SHA1 Message Date
4b6391f57e Bump version to 4.10.1 2024-11-04 15:20:07 +00:00
ed11c88307 Bump version to 4.10.1-test.1 2024-11-04 12:33:08 +00:00
0ed78a1d0a Fix incorrect systemd service-name after #540.
Currently, cargo-deb does not use the variant name option as systemd
service-name, causing the postgres variant with name 'chirpstack' to
install a 'chirpstack-postgres' systemd service. See also
https://github.com/kornelski/cargo-deb/issues/150.

This also fixes the RPM systemd service-name for the sqlite variant.
2024-11-04 12:19:19 +00:00
fdf168bd09 Bump version to 4.10.0 2024-11-04 09:09:44 +00:00
dddb471878 Update dependencies. 2024-11-01 13:45:59 +00:00
f265a815a6 Bump version to 4.10.0-test.7 2024-10-30 14:20:21 +00:00
9bf78eebfd Attempt #3 to fix matrix.database Docker image suffix. 2024-10-30 11:58:35 +00:00
3d470c6a14 Update dependencies. 2024-10-29 16:21:24 +00:00
2b5b54a7b7 Bump version to 4.10.0-test.6 2024-10-29 16:04:44 +00:00
905db6e9b7 Attempt #2 to fix matrix.database Docker image suffix. 2024-10-29 15:58:54 +00:00
1245386f38 Bump version to 4.10.0-test.5 2024-10-29 12:27:29 +00:00
49e7c1ccb9 Try fix matrix.database == 'postgres'. 2024-10-29 12:16:15 +00:00
eafda74526 Bump version to 4.10.0-test.4 2024-10-29 11:06:48 +00:00
3378a81a37 Fix GitHub workflow. 2024-10-29 11:00:16 +00:00
04e676cad6 Bump version to 4.10.0-test.3 2024-10-29 10:52:37 +00:00
e3b83066b7 Build separate Docker image for SQLite. 2024-10-29 10:48:28 +00:00
dcf6ea14bd Bump version to 4.10.0-test.2 2024-10-29 09:30:29 +00:00
b1766e506d Remove debug println. 2024-10-23 13:45:02 +01:00
87c918bc35 api: Add C support (#549) 2024-10-23 13:24:07 +01:00
d5929e0523 lrwn-filters: Expose is_match method on prefix types. 2024-10-17 15:41:20 +01:00
d69f18edad Build/archive package for both Postgres and SQLite (#540) 2024-10-17 15:39:49 +01:00
6621a23652 ui: Update page-title according to page. (#539) 2024-10-15 10:48:33 +01:00
20ec2877f2 Fix fmt after clippy fixes. 2024-10-14 15:30:15 +01:00
10cbdb1e00 Fix clippy feedback. 2024-10-14 15:12:33 +01:00
d24b8241c5 api: Rename compile > compile_protos. 2024-10-14 15:12:33 +01:00
eecdeeb092 Bump rollup from 4.18.1 to 4.22.4 in /ui (#526)
Bumps [rollup](https://github.com/rollup/rollup) from 4.18.1 to 4.22.4.
- [Release notes](https://github.com/rollup/rollup/releases)
- [Changelog](https://github.com/rollup/rollup/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rollup/rollup/compare/v4.18.1...v4.22.4)

---
updated-dependencies:
- dependency-name: rollup
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-10-14 14:38:14 +01:00
20e9e087ed Update dependencies. 2024-10-14 14:35:11 +01:00
c9bdea87f1 Fix validate_mic clause. 2024-10-04 08:41:00 +01:00
77 changed files with 826 additions and 551 deletions

View File

@ -54,6 +54,13 @@ jobs:
needs: tests
runs-on: ubuntu-latest
if: startsWith(github.ref, 'refs/tags/v')
strategy:
matrix:
database:
- postgres
- sqlite
env:
DATABASE: ${{ matrix.database }}
steps:
-
name: Checkout
@ -101,7 +108,7 @@ jobs:
uses: docker/metadata-action@v3
with:
images: |
chirpstack/${{ github.event.repository.name }}
chirpstack/${{ github.event.repository.name }}${{ matrix.database != 'postgres' && format('-{0}', matrix.database) || '' }}
tags: |
type=semver,pattern={{version}}
type=semver,pattern={{major}}

767
Cargo.lock generated

File diff suppressed because it is too large Load Diff

16
api/Dockerfile-c vendored Normal file
View File

@ -0,0 +1,16 @@
FROM alpine:latest
ENV PROJECT_PATH=/chirpstack/api
RUN apk add --no-cache make git bash protobuf protobuf-dev pkgconfig autoconf automake libtool gcc g++
# Install protobuf implementation for C
RUN git clone --depth=1 https://github.com/protobuf-c/protobuf-c.git /protobuf-c
RUN cd /protobuf-c && ./autogen.sh && ./configure && make && make install
# Proto dependencies stuff
RUN git clone --depth=1 https://github.com/protocolbuffers/protobuf.git /protobuf
RUN git clone --depth=1 https://github.com/googleapis/googleapis.git /googleapis
RUN mkdir -p PROJECT_PATH
WORKDIR $PROJECT_PATH

9
api/Makefile vendored
View File

@ -1,6 +1,6 @@
.PHONY: rust grpc-web go js python md java kotlin csharp php
.PHONY: rust grpc-web go js python md java kotlin csharp php c
all: rust grpc-web go js python md java kotlin csharp php
all: rust grpc-web go js python md java kotlin csharp php c
rust:
cd rust && make
@ -30,4 +30,7 @@ csharp:
docker compose run --rm chirpstack-csharp
php:
docker compose run --rm chirpstack-api-php
docker compose run --rm chirpstack-api-php
c:
docker compose run --rm chirpstack-api-c

2
api/c/.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
/src/
/proto/

34
api/c/Makefile vendored Normal file
View File

@ -0,0 +1,34 @@
.PHONY: all pre-build
PROTOC := protoc-c
PROTOC_ARGS := -I=proto -I=proto/chirpstack-api --c_out=./src
GOOGLE_PROTOBUF_REQUIREMENTS =
GOOGLE_PROTOBUF_REQUIREMENTS += timestamp.proto
GOOGLE_PROTOBUF_REQUIREMENTS += duration.proto
GOOGLE_PROTOBUF_REQUIREMENTS += struct.proto
GOOGLE_PROTOBUF_REQUIREMENTS += empty.proto
GOOGLE_PROTOBUF_REQUIREMENTS += descriptor.proto
GOOGLE_PROTOBUF_OUTPUT = $(patsubst %.proto, src/google/protobuf/%.pb-c.h, $(GOOGLE_PROTOBUF_REQUIREMENTS))
all: pre-build $(GOOGLE_PROTOBUF_OUTPUT) build
pre-build:
rm -rf proto src
mkdir -p proto/chirpstack-api
mkdir -p proto/google/protobuf
cp -r ../proto/* proto/chirpstack-api/
mv proto/chirpstack-api/google/api proto/google/api
mkdir -p src
sed -i \
-r 's/(\s+bytes std)(in|out|err)(\s+=.*)/\1_\2\3/' \
proto/chirpstack-api/gw/gw.proto
src/google/protobuf/%.pb-c.h: /protobuf/src/google/protobuf/%.proto
cp $< proto/google/protobuf/
build:
find proto \
-name '*.proto' \
-type f \
-exec $(PROTOC) $(PROTOC_ARGS) {} \;

View File

@ -49,3 +49,10 @@ services:
command: bash -c "cd php && make all"
volumes:
- ./:/chirpstack/api
chirpstack-api-c:
build:
context: .
dockerfile: Dockerfile-c
command: bash -c "cd c && make all"
volumes:
- ./:/chirpstack/api

View File

@ -1,6 +1,6 @@
{
"name": "@chirpstack/chirpstack-api-grpc-web",
"version": "4.10.0-test.1",
"version": "4.10.1",
"description": "Chirpstack gRPC-web API",
"license": "MIT",
"devDependencies": {

View File

@ -8,7 +8,7 @@ plugins {
}
group = "io.chirpstack"
version = "4.10.0-test.1"
version = "4.10.1"
repositories {
mavenCentral()

2
api/js/package.json vendored
View File

@ -1,6 +1,6 @@
{
"name": "@chirpstack/chirpstack-api",
"version": "4.10.0-test.1",
"version": "4.10.1",
"description": "Chirpstack JS and TS API",
"license": "MIT",
"devDependencies": {

View File

@ -9,7 +9,7 @@ plugins {
}
group = "io.chirpstack"
version = "4.10.0-test.1"
version = "4.10.1"
repositories {
mavenCentral()

View File

@ -3,7 +3,7 @@
"description": "Chirpstack PHP API",
"license": "MIT",
"type": "library",
"version": "4.10.0-test.1",
"version": "4.10.1",
"require": {
"php": ">=7.0.0",
"grpc/grpc": "^v1.57.0",

View File

@ -18,7 +18,7 @@ CLASSIFIERS = [
setup(
name='chirpstack-api',
version = "4.10.0-test.1",
version = "4.10.1",
url='https://github.com/brocaar/chirpstack-api',
author='Orne Brocaar',
author_email='info@brocaar.com',

4
api/rust/Cargo.toml vendored
View File

@ -1,7 +1,7 @@
[package]
name = "chirpstack_api"
description = "ChirpStack Protobuf / gRPC API definitions."
version = "4.10.0-test.1"
version = "4.10.1"
authors = ["Orne Brocaar <info@brocaar.com>"]
license = "MIT"
homepage = "https://www.chirpstack.io"
@ -24,7 +24,7 @@
"codegen",
"prost",
], default-features = false, optional = true }
tokio = { version = "1.40", features = ["macros"], optional = true }
tokio = { version = "1.41", features = ["macros"], optional = true }
pbjson = { version = "0.7", optional = true }
pbjson-types = { version = "0.7", optional = true }
serde = { version = "1.0", optional = true }

12
api/rust/build.rs vendored
View File

@ -28,7 +28,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
.file_descriptor_set_path(out_dir.join("common").join("proto_descriptor.bin"))
.compile_well_known_types(true)
.extern_path(".google.protobuf", well_known_types_path)
.compile(
.compile_protos(
&[cs_dir.join("common").join("common.proto").to_str().unwrap()],
&[
proto_dir.join("chirpstack").to_str().unwrap(),
@ -53,7 +53,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
.compile_well_known_types(true)
.extern_path(".google.protobuf", well_known_types_path)
.extern_path(".common", "crate::common")
.compile(
.compile_protos(
&[cs_dir.join("gw").join("gw.proto").to_str().unwrap()],
&[
proto_dir.join("chirpstack").to_str().unwrap(),
@ -87,7 +87,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
builder = builder.message_attribute("internal.DeviceSession", "#[derive(diesel::expression::AsExpression, diesel::deserialize::FromSqlRow)] #[diesel(sql_type = diesel::sql_types::Binary)]");
}
builder.compile(
builder.compile_protos(
&[cs_dir
.join("internal")
.join("internal.proto")
@ -119,7 +119,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
.extern_path(".google.protobuf", well_known_types_path)
.extern_path(".common", "crate::common")
.extern_path(".gw", "crate::gw")
.compile(
.compile_protos(
&[cs_dir
.join("integration")
.join("integration.proto")
@ -153,7 +153,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
.extern_path(".google.protobuf", well_known_types_path)
.extern_path(".common", "crate::common")
.extern_path(".gw", "crate::gw")
.compile(
.compile_protos(
&[
cs_dir.join("stream").join("meta.proto").to_str().unwrap(),
cs_dir.join("stream").join("frame.proto").to_str().unwrap(),
@ -192,7 +192,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
.file_descriptor_set_path(out_dir.join("api").join("proto_descriptor.bin"))
.extern_path(".common", "crate::common")
.extern_path(".gw", "crate::gw")
.compile(
.compile_protos(
&[
cs_dir.join("api").join("internal.proto").to_str().unwrap(),
cs_dir.join("api").join("user.proto").to_str().unwrap(),

View File

@ -1,6 +1,6 @@
[package]
name = "backend"
version = "4.10.0-test.1"
version = "4.10.1"
authors = ["Orne Brocaar <info@brocaar.com>"]
edition = "2018"
publish = false
@ -19,7 +19,7 @@
"rustls-tls",
], default-features = false }
chrono = { version = "0.4", features = ["serde"] }
tokio = { version = "1.40", features = ["macros"] }
tokio = { version = "1.41", features = ["macros"] }
chirpstack_api = { path = "../api/rust", default-features = false, features = [
"json",
] }

View File

@ -3,14 +3,14 @@
description = "Library for building external ChirpStack integrations"
homepage = "https://www.chirpstack.io/"
license = "MIT"
version = "4.10.0-test.1"
version = "4.10.1"
authors = ["Orne Brocaar <info@brocaar.com>"]
edition = "2021"
repository = "https://github.com/chirpstack/chirpstack"
[dependencies]
chirpstack_api = { path = "../api/rust", version = "4.10.0-test.1" }
redis = { version = "0.26", features = [
chirpstack_api = { path = "../api/rust", version = "4.10.1" }
redis = { version = "0.27", features = [
"cluster-async",
"tokio-rustls-comp",
] }
@ -23,7 +23,7 @@
], default-features = true }
async-trait = "0.1"
serde = { version = "1.0", features = ["derive"] }
tokio = { version = "1.40", features = ["macros", "rt-multi-thread"] }
tokio = { version = "1.41", features = ["macros", "rt-multi-thread"] }
lazy_static = "1.5"
serde_json = "1.0"
toml = "0.8"

View File

@ -3,7 +3,7 @@
description = "ChirpStack is an open-source LoRaWAN(TM) Network Server"
repository = "https://github.com/chirpstack/chirpstack"
homepage = "https://www.chirpstack.io/"
version = "4.10.0-test.1"
version = "4.10.1"
authors = ["Orne Brocaar <info@brocaar.com>"]
edition = "2021"
publish = false
@ -20,7 +20,7 @@
serde_urlencoded = "0.7"
humantime-serde = "1.1"
toml = "0.8"
handlebars = "6.1"
handlebars = "6.2"
# Database
email_address = "0.2"
@ -35,10 +35,10 @@
"async-connection-wrapper",
] }
tokio-postgres = { version = "0.7", optional = true }
tokio-postgres-rustls = { version = "0.12", optional = true }
tokio-postgres-rustls = { version = "0.13", optional = true }
bigdecimal = "0.4"
redis = { version = "0.26", features = ["tls-rustls", "tokio-rustls-comp"] }
deadpool-redis = { version = "0.16", features = ["cluster", "serde"] }
redis = { version = "0.27", features = ["tls-rustls", "tokio-rustls-comp"] }
deadpool-redis = { version = "0.18", features = ["cluster", "serde"] }
# Logging
tracing = "0.1"
@ -83,7 +83,7 @@
tonic = "0.12"
tonic-web = "0.12"
tonic-reflection = "0.12"
tokio = { version = "1.40", features = ["macros", "rt-multi-thread"] }
tokio = { version = "1.41", features = ["macros", "rt-multi-thread"] }
tokio-stream = "0.1"
prost-types = "0.13"
prost = "0.13"
@ -99,7 +99,7 @@
http-body = "1.0"
rust-embed = "8.5"
mime_guess = "2.0"
tower-http = { version = "0.5", features = ["trace", "auth"] }
tower-http = { version = "0.6", features = ["trace", "auth"] }
# Error handling
thiserror = "1.0"
@ -116,7 +116,7 @@
"ring",
] }
rustls-native-certs = "0.8"
rustls-pemfile = "2.1"
rustls-pemfile = "2.2"
pem = "3.0"
x509-parser = "0.16"
rsa = "0.9"
@ -142,14 +142,14 @@
# Misc
lazy_static = "1.5"
uuid = { version = "1.10", features = ["v4", "serde"] }
uuid = { version = "1.11", features = ["v4", "serde"] }
chrono = "0.4"
async-trait = "0.1"
aes = "0.8"
rand = "0.8"
base64 = "0.22"
async-recursion = "1.1"
regex = "1.10"
regex = "1.11"
petgraph = "0.6"
prometheus-client = "0.22"
pin-project = "1.1"
@ -160,7 +160,7 @@
# Development and testing
[dev-dependencies]
httpmock = "0.7.0"
bytes = "1.7"
bytes = "1.8"
dotenv = "0.15"
[features]
@ -235,9 +235,15 @@
"/etc/chirpstack/region_us915_7.toml",
]
suggests = "postgresql, mosquitto, redis"
conflicts = "chirpstack-sqlite"
maintainer-scripts = "debian/"
systemd-units = { enable = true }
[package.metadata.deb.variants.sqlite]
name = "chirpstack-sqlite"
suggests = "mosquitto, redis"
conflicts = "chirpstack"
[package.metadata.generate-rpm]
auto-req = "no"
@ -259,3 +265,17 @@ chmod 640 /etc/chirpstack/*.toml
{ source = "configuration/*", dest = "/etc/chirpstack" },
{ source = "rpm/chirpstack.service", dest = "/lib/systemd/system/chirpstack.service" },
]
[package.metadata.generate-rpm.conflicts]
chirpstack-sqlite = "*"
[package.metadata.generate-rpm.variants.sqlite]
name = "chirpstack-sqlite"
assets = [
{ source = "target/release/chirpstack", dest = "/usr/bin/chirpstack", mode = "755" },
{ source = "configuration/*", dest = "/etc/chirpstack" },
{ source = "rpm/chirpstack.service", dest = "/lib/systemd/system/chirpstack-sqlite.service" },
]
[package.metadata.generate-rpm.variants.sqlite.conflicts]
chirpstack = "*"

View File

@ -2,6 +2,11 @@
PKG_VERSION := $(shell cargo metadata --no-deps --format-version 1 | jq -r '.packages[0].version')
DATABASE ?= postgres
ifeq ($(DATABASE),postgres)
VARIANT_FLAGS ?=
else
VARIANT_FLAGS ?= --variant="$(DATABASE)"
endif
debug-amd64:
cross build --target x86_64-unknown-linux-musl --no-default-features --features="$(DATABASE)"
@ -17,13 +22,13 @@ dist:
cross build --target x86_64-unknown-linux-musl --release --no-default-features --features="$(DATABASE)"
cross build --target armv7-unknown-linux-musleabihf --release --no-default-features --features="$(DATABASE)"
cargo deb --target x86_64-unknown-linux-musl --no-build --no-strip
cargo deb --target armv7-unknown-linux-musleabihf --no-build --no-strip
cargo deb --target aarch64-unknown-linux-musl --no-build --no-strip
cargo deb --target x86_64-unknown-linux-musl --no-build --no-strip $(VARIANT_FLAGS)
cargo deb --target armv7-unknown-linux-musleabihf --no-build --no-strip $(VARIANT_FLAGS)
cargo deb --target aarch64-unknown-linux-musl --no-build --no-strip $(VARIANT_FLAGS)
cargo generate-rpm --target x86_64-unknown-linux-musl --target-dir ../target
cargo generate-rpm --target armv7-unknown-linux-musleabihf --target-dir ../target
cargo generate-rpm --target aarch64-unknown-linux-musl --target-dir ../target
cargo generate-rpm --target x86_64-unknown-linux-musl --target-dir ../target $(VARIANT_FLAGS)
cargo generate-rpm --target armv7-unknown-linux-musleabihf --target-dir ../target $(VARIANT_FLAGS)
cargo generate-rpm --target aarch64-unknown-linux-musl --target-dir ../target $(VARIANT_FLAGS)
mkdir -p ../dist
@ -35,9 +40,9 @@ dist:
cp ../target/armv7-unknown-linux-musleabihf/generate-rpm/*.rpm ../dist
cp ../target/aarch64-unknown-linux-musl/generate-rpm/*.rpm ../dist
tar -czvf ../dist/chirpstack_$(PKG_VERSION)_amd64.tar.gz -C ../target/x86_64-unknown-linux-musl/release chirpstack
tar -czvf ../dist/chirpstack_$(PKG_VERSION)_armv7hf.tar.gz -C ../target/armv7-unknown-linux-musleabihf/release chirpstack
tar -czvf ../dist/chirpstack_$(PKG_VERSION)_arm64.tar.gz -C ../target/aarch64-unknown-linux-musl/release chirpstack
tar -czvf ../dist/chirpstack_$(PKG_VERSION)_$(DATABASE)_amd64.tar.gz -C ../target/x86_64-unknown-linux-musl/release chirpstack
tar -czvf ../dist/chirpstack_$(PKG_VERSION)_$(DATABASE)_armv7hf.tar.gz -C ../target/armv7-unknown-linux-musleabihf/release chirpstack
tar -czvf ../dist/chirpstack_$(PKG_VERSION)_$(DATABASE)_arm64.tar.gz -C ../target/aarch64-unknown-linux-musl/release chirpstack
test:
cargo fmt --check

View File

@ -328,6 +328,7 @@ async fn _handle_pr_start_req_data(
let kek_label = roaming::get_passive_roaming_kek_label(sender_id)?;
let ds = d.get_device_session()?;
// Only in case validate_mic=true and LoRaWAN=1.0.x.
let nwk_s_key = if validate_mic && ds.mac_version().to_string().starts_with("1.0") {
Some(keywrap::wrap(
&kek_label,
@ -337,13 +338,14 @@ async fn _handle_pr_start_req_data(
None
};
let f_nwk_s_int_key = if validate_mic && ds.mac_version().to_string().starts_with("1.0") {
None
} else {
// Only in case validate_mic=true and LoRaWAN=1.1.x.
let f_nwk_s_int_key = if validate_mic && ds.mac_version().to_string().starts_with("1.1") {
Some(keywrap::wrap(
&kek_label,
AES128Key::from_slice(&ds.f_nwk_s_int_key)?,
)?)
} else {
None
};
// In case of stateless, the payload is directly handled

View File

@ -287,11 +287,7 @@ impl InternalService for Internal {
let tenant_id = if req_key.tenant_id.is_empty() {
None
} else {
Some(
Uuid::from_str(&req_key.tenant_id)
.map_err(|e| e.status())?
.into(),
)
Some(Uuid::from_str(&req_key.tenant_id).map_err(|e| e.status())?)
};
if req_key.is_admin && tenant_id.is_some() {

View File

@ -258,8 +258,8 @@ impl TenantService for Tenant {
.await?;
let _ = tenant::add_user(tenant::TenantUser {
user_id,
tenant_id: tenant_id.into(),
user_id: user_id.into(),
is_admin: req_user.is_admin,
is_device_admin: req_user.is_device_admin,
is_gateway_admin: req_user.is_gateway_admin,

View File

@ -65,7 +65,7 @@ impl UserService for User {
tenant::add_user(tenant::TenantUser {
tenant_id: tenant_id.into(),
user_id: u.id.into(),
user_id: u.id,
is_admin: tu.is_admin,
is_device_admin: tu.is_device_admin,
is_gateway_admin: tu.is_gateway_admin,

View File

@ -3,6 +3,7 @@ use handlebars::Handlebars;
use super::super::config;
pub fn run() {
#[allow(clippy::useless_vec)]
let template = vec![
r#"
# Logging configuration

View File

@ -111,18 +111,16 @@ pub async fn run(dir: &Path) -> Result<()> {
let vendors_dir = dir.join("vendors");
let vendors = fs::read_dir(vendors_dir)?;
for vendor in vendors {
if let Ok(vendor) = vendor {
if vendor.file_name() == "example-vendor" {
continue;
}
for vendor in vendors.flatten() {
if vendor.file_name() == "example-vendor" {
continue;
}
let span = span!(Level::INFO, "", vendor = ?vendor.file_name());
let span = span!(Level::INFO, "", vendor = ?vendor.file_name());
let vendor_dir = vendor.path();
if vendor_dir.is_dir() {
handle_vendor(&vendor_dir).instrument(span).await?;
}
let vendor_dir = vendor.path();
if vendor_dir.is_dir() {
handle_vendor(&vendor_dir).instrument(span).await?;
}
}

View File

@ -466,10 +466,10 @@ impl Data {
// * should not be pending
// * should not be expired
// * in case encrypted, should have a valid FCntDown
if qi.data.len() <= max_payload_size
&& !qi.is_pending
&& !(qi.expires_at.is_some() && qi.expires_at.unwrap() < Utc::now())
&& !(qi.is_encrypted
if !(qi.data.len() > max_payload_size
|| qi.is_pending
|| qi.expires_at.is_some() && qi.expires_at.unwrap() < Utc::now()
|| qi.is_encrypted
&& (qi.f_cnt_down.unwrap_or_default() as u32) < ds.get_a_f_cnt_down())
{
trace!(id = %qi.id, more_in_queue = more_in_queue, "Found device queue-item for downlink");

View File

@ -6,8 +6,8 @@ pub enum Error {
Abort,
#[error(transparent)]
AnyhowError(#[from] anyhow::Error),
Anyhow(#[from] anyhow::Error),
#[error(transparent)]
StorageError(#[from] crate::storage::error::Error),
Storage(#[from] crate::storage::error::Error),
}

View File

@ -1,6 +1,5 @@
use std::collections::HashMap;
use std::fmt;
use std::ops::{Deref, DerefMut};
use std::str::FromStr;
use anyhow::{Context, Result};
@ -146,15 +145,13 @@ impl Device {
pub fn get_device_session(&self) -> Result<&internal::DeviceSession, Error> {
self.device_session
.as_ref()
.map(|ds| ds.deref())
.as_deref()
.ok_or_else(|| Error::NotFound(self.dev_eui.to_string()))
}
pub fn get_device_session_mut(&mut self) -> Result<&mut internal::DeviceSession, Error> {
self.device_session
.as_mut()
.map(|ds| ds.deref_mut())
.as_deref_mut()
.ok_or_else(|| Error::NotFound(self.dev_eui.to_string()))
}

View File

@ -54,7 +54,7 @@ impl deserialize::FromSql<Numeric, Pg> for BigDecimal {
#[cfg(feature = "postgres")]
impl serialize::ToSql<Numeric, Pg> for BigDecimal {
fn to_sql<'b>(&self, out: &mut serialize::Output<'b, '_, Pg>) -> serialize::Result {
fn to_sql(&self, out: &mut serialize::Output<'_, '_, Pg>) -> serialize::Result {
<bigdecimal::BigDecimal as serialize::ToSql<Numeric, Pg>>::to_sql(
&self.0,
&mut out.reborrow(),

View File

@ -17,16 +17,11 @@ type DevNoncesPgType = Array<Nullable<Int4>>;
#[serde(transparent)]
#[cfg_attr(feature = "postgres", diesel(sql_type = DevNoncesPgType))]
#[cfg_attr(feature = "sqlite", diesel(sql_type = Text))]
#[derive(Default)]
pub struct DevNonces(DevNoncesInner);
pub type DevNoncesInner = Vec<Option<i32>>;
impl std::default::Default for DevNonces {
fn default() -> Self {
Self(Vec::new())
}
}
impl std::convert::AsRef<DevNoncesInner> for DevNonces {
fn as_ref(&self) -> &DevNoncesInner {
&self.0
@ -62,7 +57,7 @@ impl deserialize::FromSql<DevNoncesPgType, Pg> for DevNonces {
#[cfg(feature = "postgres")]
impl serialize::ToSql<DevNoncesPgType, Pg> for DevNonces {
fn to_sql<'b>(&self, out: &mut serialize::Output<'b, '_, Pg>) -> serialize::Result {
fn to_sql(&self, out: &mut serialize::Output<'_, '_, Pg>) -> serialize::Result {
<DevNoncesInner as serialize::ToSql<DevNoncesPgType, Pg>>::to_sql(
&self.0,
&mut out.reborrow(),

View File

@ -34,9 +34,9 @@ impl std::convert::From<&internal::DeviceSession> for DeviceSession {
}
}
impl std::convert::Into<internal::DeviceSession> for DeviceSession {
fn into(self) -> internal::DeviceSession {
self.0
impl std::convert::From<DeviceSession> for internal::DeviceSession {
fn from(val: DeviceSession) -> Self {
val.0
}
}

View File

@ -21,13 +21,13 @@ impl std::convert::From<uuid::Uuid> for Uuid {
impl std::convert::From<&uuid::Uuid> for Uuid {
fn from(u: &uuid::Uuid) -> Self {
Self::from(u.clone())
Self::from(*u)
}
}
impl std::convert::Into<uuid::Uuid> for Uuid {
fn into(self) -> uuid::Uuid {
self.0
impl std::convert::From<Uuid> for uuid::Uuid {
fn from(val: Uuid) -> Self {
val.0
}
}
@ -60,7 +60,7 @@ impl deserialize::FromSql<diesel::sql_types::Uuid, Pg> for Uuid {
#[cfg(feature = "postgres")]
impl serialize::ToSql<diesel::sql_types::Uuid, Pg> for Uuid {
fn to_sql<'b>(&self, out: &mut serialize::Output<'b, '_, Pg>) -> serialize::Result {
fn to_sql(&self, out: &mut serialize::Output<'_, '_, Pg>) -> serialize::Result {
<uuid::Uuid as serialize::ToSql<diesel::sql_types::Uuid, Pg>>::to_sql(
&self.0,
&mut out.reborrow(),

View File

@ -390,7 +390,7 @@ pub async fn get_counts_by_state(tenant_id: &Option<Uuid>) -> Result<GatewayCoun
gateway
where
$1 is null or tenant_id = $1
"#).bind::<diesel::sql_types::Nullable<fields::sql_types::Uuid>, _>(tenant_id.map(|u| fields::Uuid::from(u))).get_result(&mut get_async_db_conn().await?).await?;
"#).bind::<diesel::sql_types::Nullable<fields::sql_types::Uuid>, _>(tenant_id.map(fields::Uuid::from)).get_result(&mut get_async_db_conn().await?).await?;
Ok(counts)
}

View File

@ -465,7 +465,7 @@ pub async fn enqueue(
for gateway_id in gateway_ids {
let qi = MulticastGroupQueueItem {
scheduler_run_after: scheduler_run_after_ts,
multicast_group_id: mg.id.into(),
multicast_group_id: mg.id,
gateway_id: *gateway_id,
f_cnt: mg.f_cnt,
f_port: qi.f_port,
@ -473,7 +473,7 @@ pub async fn enqueue(
emit_at_time_since_gps_epoch: Some(
emit_at_time_since_gps_epoch.num_milliseconds(),
),
expires_at: qi.expires_at.clone(),
expires_at: qi.expires_at,
..Default::default()
};
@ -540,13 +540,13 @@ pub async fn enqueue(
for gateway_id in gateway_ids {
let qi = MulticastGroupQueueItem {
scheduler_run_after: scheduler_run_after_ts,
multicast_group_id: mg.id.into(),
multicast_group_id: mg.id,
gateway_id: *gateway_id,
f_cnt: mg.f_cnt,
f_port: qi.f_port,
data: qi.data.clone(),
emit_at_time_since_gps_epoch,
expires_at: qi.expires_at.clone(),
expires_at: qi.expires_at,
..Default::default()
};

View File

@ -3,7 +3,7 @@
description = "Library for filtering LoRaWAN payloads on DevAddr and JoinEUIs prefixes"
homepage = "https://www.chirpstack.io/"
license = "MIT"
version = "4.10.0-test.1"
version = "4.10.1"
authors = ["Orne Brocaar <info@brocaar.com>"]
edition = "2021"
repository = "https://github.com/chirpstack/chirpstack"

View File

@ -32,7 +32,7 @@ pub fn matches(phy_payload: &[u8], config: &Filters) -> bool {
let mhdr = phy_payload[0];
let m_type = mhdr >> 5;
let dev_addr: Option<u32> = match m_type {
let dev_addr: Option<[u8; 4]> = match m_type {
// DataUp
0x02 | 0x04 => {
// MHDR + DevAddr
@ -40,7 +40,7 @@ pub fn matches(phy_payload: &[u8], config: &Filters) -> bool {
if phy_payload.len() >= 5 {
let mut dev_addr: [u8; 4] = [0; 4];
dev_addr.clone_from_slice(&phy_payload[1..5]);
Some(u32::from_le_bytes(dev_addr))
Some(dev_addr)
} else {
None
}
@ -48,7 +48,7 @@ pub fn matches(phy_payload: &[u8], config: &Filters) -> bool {
_ => None,
};
let join_eui: Option<u64> = match m_type {
let join_eui: Option<[u8; 8]> = match m_type {
// JoinRequest
0x00 => {
// MHDR + JoinEUI + DevEUI
@ -56,7 +56,7 @@ pub fn matches(phy_payload: &[u8], config: &Filters) -> bool {
if phy_payload.len() >= 17 {
let mut join_eui: [u8; 8] = [0; 8];
join_eui.clone_from_slice(&phy_payload[1..9]);
Some(u64::from_le_bytes(join_eui))
Some(join_eui)
} else {
None
}
@ -76,8 +76,7 @@ pub fn matches(phy_payload: &[u8], config: &Filters) -> bool {
}
for p in &config.dev_addr_prefixes {
let prefix = u32::from_be_bytes(p.prefix());
if dev_addr >> (32 - p.size()) == prefix >> (32 - p.size()) {
if p.is_match(dev_addr) {
return true;
}
}
@ -89,8 +88,7 @@ pub fn matches(phy_payload: &[u8], config: &Filters) -> bool {
}
for p in &config.join_eui_prefixes {
let prefix = u64::from_be_bytes(p.prefix());
if join_eui >> (64 - p.size()) == prefix >> (64 - p.size()) {
if p.is_match(join_eui) {
return true;
}
}
@ -108,6 +106,12 @@ impl DevAddrPrefix {
DevAddrPrefix(prefix, size)
}
pub fn is_match(&self, dev_addr_le: [u8; 4]) -> bool {
let dev_addr = u32::from_le_bytes(dev_addr_le);
let prefix = u32::from_be_bytes(self.prefix());
dev_addr >> (32 - self.size()) == prefix >> (32 - self.size())
}
fn prefix(&self) -> [u8; 4] {
self.0
}
@ -204,6 +208,14 @@ impl EuiPrefix {
EuiPrefix(prefix, size)
}
pub fn is_match(&self, eui_le: [u8; 8]) -> bool {
let eui = u64::from_le_bytes(eui_le);
let prefix = u64::from_be_bytes(self.prefix());
println!("EUI: {}", eui >> (64 - self.size()));
println!("PREFIX: {}", prefix >> (64 - self.size()));
eui >> (64 - self.size()) == prefix >> (64 - self.size())
}
fn prefix(&self) -> [u8; 8] {
self.0
}

View File

@ -3,7 +3,7 @@
description = "Library for encoding / decoding LoRaWAN frames."
homepage = "https://www.chirpstack.io"
license = "MIT"
version = "4.10.0-test.1"
version = "4.10.1"
authors = ["Orne Brocaar <info@brocaar.com>"]
edition = "2018"
repository = "https://github.com/chirpstack/chirpstack"

View File

@ -1,6 +1,6 @@
{
"name": "chirpstack-ui",
"version": "4.10.0-test.1",
"version": "4.10.1",
"private": true,
"type": "module",
"scripts": {

View File

@ -10,8 +10,10 @@ import { ApiKey, CreateApiKeyRequest } from "@chirpstack/chirpstack-api-grpc-web
import ApiKeyForm from "./ApiKeyForm";
import ApiKeyToken from "./ApiKeyToken";
import InternalStore from "../../stores/InternalStore";
import { useTitle } from "../helpers";
function CreateAdminApiKey() {
useTitle("Network Server", "API keys", "Add");
const [createApiKeyResponse, setCreateApiKeyResponse] = useState<CreateApiKeyResponse | undefined>(undefined);
const onFinish = (obj: ApiKey) => {
@ -34,7 +36,7 @@ function CreateAdminApiKey() {
breadcrumbRender={() => (
<Breadcrumb>
<Breadcrumb.Item>
<span>Network-server</span>
<span>Network Server</span>
</Breadcrumb.Item>
<Breadcrumb.Item>
<span>

View File

@ -11,6 +11,7 @@ import type { Tenant } from "@chirpstack/chirpstack-api-grpc-web/api/tenant_pb";
import ApiKeyForm from "./ApiKeyForm";
import ApiKeyToken from "./ApiKeyToken";
import InternalStore from "../../stores/InternalStore";
import { useTitle } from "../helpers";
interface IProps {
tenant: Tenant;
@ -18,6 +19,7 @@ interface IProps {
function CreateTenantApiKey(props: IProps) {
const [createApiKeyResponse, setCreateApiKeyResponse] = useState<CreateApiKeyResponse | undefined>(undefined);
useTitle("Tenants", props.tenant.getName(), "API Keys", "Add");
const onFinish = (obj: ApiKey) => {
obj.setTenantId(props.tenant.getId());

View File

@ -13,8 +13,10 @@ import type { GetPageCallbackFunc } from "../../components/DataTable";
import DataTable from "../../components/DataTable";
import InternalStore from "../../stores/InternalStore";
import DeleteConfirm from "../../components/DeleteConfirm";
import { useTitle } from "../helpers";
function ListAdminApiKeys() {
useTitle("Network Server", "API keys");
const [refreshKey, setRefreshKey] = useState<number>(1);
const columns: ColumnsType<ApiKey.AsObject> = [

View File

@ -15,6 +15,7 @@ import DataTable from "../../components/DataTable";
import InternalStore from "../../stores/InternalStore";
import DeleteConfirm from "../../components/DeleteConfirm";
import Admin from "../../components/Admin";
import { useTitle } from "../helpers";
interface IProps {
tenant: Tenant;
@ -22,6 +23,7 @@ interface IProps {
function ListTenantApiKeys(props: IProps) {
const [refreshKey, setRefreshKey] = useState<number>(1);
useTitle("Tenants", props.tenant.getName(), "API Keys");
const columns: ColumnsType<ApiKey.AsObject> = [
{

View File

@ -38,6 +38,7 @@ import EditThingsBoardIntegration from "./integrations/EditThingsBoardIntegratio
import GenerateMqttCertificate from "./integrations/GenerateMqttCertificate";
import CreateIftttIntegration from "./integrations/CreateIftttIntegration";
import EditIftttIntegration from "./integrations/EditIftttIntegration";
import { useTitle } from "../helpers";
interface IProps {
tenant: Tenant;
@ -48,6 +49,7 @@ interface IProps {
function ApplicationLayout(props: IProps) {
const navigate = useNavigate();
const location = useLocation();
useTitle("Tenants", props.tenant.getName(), "Applications", props.application.getName());
const deleteApplication = () => {
const req = new DeleteApplicationRequest();

View File

@ -9,6 +9,7 @@ import { Application, CreateApplicationRequest } from "@chirpstack/chirpstack-ap
import ApplicationForm from "./ApplicationForm";
import ApplicationStore from "../../stores/ApplicationStore";
import { useTitle } from "../helpers";
interface IProps {
tenant: Tenant;
@ -16,6 +17,7 @@ interface IProps {
function CreateApplication(props: IProps) {
const navigate = useNavigate();
useTitle("Tenants", props.tenant.getName(), "Applications", "Add");
const onFinish = (obj: Application) => {
obj.setTenantId(props.tenant.getId());

View File

@ -15,12 +15,14 @@ import type { GetPageCallbackFunc } from "../../components/DataTable";
import DataTable from "../../components/DataTable";
import ApplicationStore from "../../stores/ApplicationStore";
import Admin from "../../components/Admin";
import { useTitle } from "../helpers";
interface IProps {
tenant: Tenant;
}
function ListApplications(props: IProps) {
useTitle("Tenants", props.tenant.getName(), "Applications");
const columns: ColumnsType<ApplicationListItem.AsObject> = [
{
title: "Name",

View File

@ -26,6 +26,7 @@ import InternalStore from "../../stores/InternalStore";
import GatewayStore from "../../stores/GatewayStore";
import type { MarkerColor } from "../../components/Map";
import Map, { Marker } from "../../components/Map";
import { useTitle } from "../helpers";
function GatewaysMap() {
const [items, setItems] = useState<GatewayListItem[]>([]);
@ -224,6 +225,7 @@ function DevicesDataRates({ summary }: { summary?: GetDevicesSummaryResponse })
}
function Dashboard() {
useTitle("Network Server", "Dashboard");
const [gatewaysSummary, setGatewaysSummary] = useState<GetGatewaysSummaryResponse | undefined>(undefined);
const [devicesSummary, setDevicesSummary] = useState<GetDevicesSummaryResponse | undefined>(undefined);

View File

@ -11,8 +11,10 @@ import {
import DeviceProfileTemplateForm from "./DeviceProfileTemplateForm";
import DeviceProfileTemplateStore from "../../stores/DeviceProfileTemplateStore";
import { useTitle } from "../helpers";
function CreateDeviceProfileTemplate() {
useTitle("Network Server", "Device-profile templates", "Add");
const navigate = useNavigate();
const onFinish = (obj: DeviceProfileTemplate) => {

View File

@ -18,11 +18,13 @@ import {
import DeviceProfileTemplateForm from "./DeviceProfileTemplateForm";
import DeviceProfileTemplateStore from "../../stores/DeviceProfileTemplateStore";
import DeleteConfirm from "../../components/DeleteConfirm";
import { useTitle } from "../helpers";
function EditDeviceProfileTemplate() {
const navigate = useNavigate();
const [deviceProfileTemplate, setDeviceProfileTemplate] = useState<DeviceProfileTemplate | undefined>(undefined);
const { deviceProfileTemplateId } = useParams();
useTitle("Network Server", "Device-profile templates", deviceProfileTemplate?.getName());
useEffect(() => {
const id = deviceProfileTemplateId!;

View File

@ -15,8 +15,10 @@ import { getEnumName } from "../helpers";
import type { GetPageCallbackFunc } from "../../components/DataTable";
import DataTable from "../../components/DataTable";
import DeviceProfileTemplateStore from "../../stores/DeviceProfileTemplateStore";
import { useTitle } from "../helpers";
function ListDeviceProfileTemplates() {
useTitle("Network Server", "Device-profile templates");
const columns: ColumnsType<DeviceProfileTemplateListItem.AsObject> = [
{
title: "Vendor",

View File

@ -11,6 +11,7 @@ import type { Tenant } from "@chirpstack/chirpstack-api-grpc-web/api/tenant_pb";
import DeviceProfileForm from "./DeviceProfileForm";
import DeviceProfileStore from "../../stores/DeviceProfileStore";
import { useTitle } from "../helpers";
interface IProps {
tenant: Tenant;
@ -18,6 +19,7 @@ interface IProps {
function CreateDeviceProfile(props: IProps) {
const navigate = useNavigate();
useTitle("Tenants", props.tenant.getName(), "Device profiles", "Add");
const onFinish = (obj: DeviceProfile) => {
obj.setTenantId(props.tenant.getId());

View File

@ -20,6 +20,7 @@ import DeviceProfileStore from "../../stores/DeviceProfileStore";
import SessionStore from "../../stores/SessionStore";
import DeleteConfirm from "../../components/DeleteConfirm";
import Admin from "../../components/Admin";
import { useTitle } from "../helpers";
interface IProps {
tenant: Tenant;
@ -29,6 +30,7 @@ function EditDeviceProfile(props: IProps) {
const navigate = useNavigate();
const [deviceProfile, setDeviceProfile] = useState<DeviceProfile | undefined>(undefined);
const { deviceProfileId } = useParams();
useTitle("Tenants", props.tenant.getName(), "Device profiles", deviceProfile?.getName());
useEffect(() => {
const id = deviceProfileId!;

View File

@ -17,12 +17,14 @@ import type { GetPageCallbackFunc } from "../../components/DataTable";
import DataTable from "../../components/DataTable";
import DeviceProfileStore from "../../stores/DeviceProfileStore";
import Admin from "../../components/Admin";
import { useTitle } from "../helpers";
interface IProps {
tenant: Tenant;
}
function ListDeviceProfiles(props: IProps) {
useTitle("Tenants", props.tenant.getName(), "Device profiles");
const columns: ColumnsType<DeviceProfileListItem.AsObject> = [
{
title: "Name",

View File

@ -12,6 +12,7 @@ import { GetDeviceProfileRequest } from "@chirpstack/chirpstack-api-grpc-web/api
import DeviceForm from "./DeviceForm";
import DeviceStore from "../../stores/DeviceStore";
import DeviceProfileStore from "../../stores/DeviceProfileStore";
import { useTitle } from "../helpers";
interface IProps {
tenant: Tenant;
@ -20,6 +21,7 @@ interface IProps {
function CreateDevice(props: IProps) {
const navigate = useNavigate();
useTitle("Tenants", props.tenant.getName(), "Applications", props.application.getName(), "Add device");
const onFinish = (obj: Device) => {
obj.setApplicationId(props.application.getId());

View File

@ -26,6 +26,7 @@ import DeviceFrames from "./DeviceFrames";
import DeviceEvents from "./DeviceEvents";
import DeviceQueue from "./DeviceQueue";
import DeviceActivation from "./DeviceActivation";
import { useTitle } from "../helpers";
interface IProps {
tenant: Tenant;
@ -40,6 +41,14 @@ function DeviceLayout(props: IProps) {
const [device, setDevice] = useState<Device | undefined>(undefined);
const [deviceProfile, setDeviceProfile] = useState<DeviceProfile | undefined>(undefined);
const [lastSeenAt, setLastSeenAt] = useState<Date | undefined>(undefined);
useTitle(
"Tenants",
props.tenant.getName(),
"Applications",
props.application.getName(),
"Devices",
device?.getName(),
);
useEffect(() => {
const loadDevice = () => {

View File

@ -8,6 +8,7 @@ import type { Tenant } from "@chirpstack/chirpstack-api-grpc-web/api/tenant_pb";
import GatewayForm from "./GatewayForm";
import GatewayStore from "../../stores/GatewayStore";
import { useTitle } from "../helpers";
interface IProps {
tenant: Tenant;
@ -15,6 +16,7 @@ interface IProps {
function CreateGateway(props: IProps) {
const navigate = useNavigate();
useTitle("Tenants", props.tenant.getName(), "Gateways", "Add");
const onFinish = (obj: Gateway) => {
obj.setTenantId(props.tenant.getId());

View File

@ -18,6 +18,7 @@ import GatewayFrames from "./GatewayFrames";
import GatewayCertificate from "./GatewayCertificate";
import Admin from "../../components/Admin";
import SessionStore from "../../stores/SessionStore";
import { useTitle } from "../helpers";
interface IProps {
tenant: Tenant;
@ -29,6 +30,7 @@ function GatewayLayout(props: IProps) {
const location = useLocation();
const [gateway, setGateway] = useState<Gateway | undefined>(undefined);
const [lastSeenAt, setLastSeenAt] = useState<Date | undefined>(undefined);
useTitle("Tenants", props.tenant.getName(), "Gateways", gateway?.getName());
useEffect(() => {
const req = new GetGatewayRequest();

View File

@ -23,6 +23,7 @@ import GatewayStore from "../../stores/GatewayStore";
import ApplicationStore from "../../stores/ApplicationStore";
import MulticastGroupStore from "../../stores/MulticastGroupStore";
import Admin from "../../components/Admin";
import { useTitle } from "../helpers";
interface IProps {
tenant: Tenant;
@ -40,6 +41,7 @@ function ListGateways(props: IProps) {
const [multicastGroups, setMulticastGroups] = useState<MulticastGroup[]>([]);
const [mgModalVisible, setMgModalVisible] = useState<boolean>(false);
const [mgSelected, setMgSelected] = useState<string>("");
useTitle("Tenants", props.tenant.getName(), "Gateways");
const columns: ColumnsType<GatewayListItem.AsObject> = [
{

View File

@ -15,12 +15,14 @@ import type { Tenant } from "@chirpstack/chirpstack-api-grpc-web/api/tenant_pb";
import type { GetPageCallbackFunc } from "../../../components/DataTable";
import DataTable from "../../../components/DataTable";
import GatewayStore from "../../../stores/GatewayStore";
import { useTitle } from "../../helpers";
interface IProps {
tenant: Tenant;
}
function ListRelayGateways(props: IProps) {
useTitle("Tenants", props.tenant.getName(), "Gateway Mesh", "Relay Gateways");
const columns: ColumnsType<RelayGatewayListItem.AsObject> = [
{
title: "",

View File

@ -15,6 +15,7 @@ import GatewayStore from "../../../stores/GatewayStore";
import DeleteConfirm from "../../../components/DeleteConfirm";
import EditRelayGateway from "./EditRelayGateway";
import { useTitle } from "../../helpers";
interface IProps {
tenant: Tenant;
@ -24,6 +25,7 @@ function RelayGatewayLayout(props: IProps) {
const { relayId } = useParams();
const navigate = useNavigate();
const [relayGateway, setRelayGateway] = useState<RelayGateway | undefined>(undefined);
useTitle("Tenants", props.tenant.getName(), "Gateway Mesh", "Relay Gateways", relayGateway?.getName());
useEffect(() => {
const req = new GetRelayGatewayRequest();

View File

@ -1,5 +1,6 @@
import { notification } from "antd";
import { MacVersion, RegParamsRevision } from "@chirpstack/chirpstack-api-grpc-web/common/common_pb";
import { useRef, useEffect } from "react";
export function formatMacVersion(m: MacVersion) {
switch (m) {
@ -61,3 +62,28 @@ export function onFinishFailed() {
duration: 3,
});
}
/**
* Sets the Document Title in Reverse Order
* @example
* ```
* useTitle("Tenants", "Tenant", "Edit"); // Edit | Tenant | Tenants | ChirpStack LoRaWAN® Network-Server
* ```
*/
export function useTitle(...v: unknown[]) {
const documentDefined = typeof document !== "undefined";
useEffect(() => {
if (!documentDefined) return;
const title = ["ChirpStack LoRaWAN® Network-Server", ...v].reverse().join(" | ");
if (document.title !== title) {
document.title = title;
}
return () => {
document.title = "ChirpStack LoRaWAN® Network-Server";
};
}, [documentDefined, v]);
}

View File

@ -13,6 +13,7 @@ import {
import MulticastGroupForm from "./MulticastGroupForm";
import MulticastGroupStore from "../../stores/MulticastGroupStore";
import { useTitle } from "../helpers";
interface IProps {
tenant: Tenant;
@ -21,6 +22,7 @@ interface IProps {
function CreateMulticastGroup(props: IProps) {
const navigate = useNavigate();
useTitle("Tenants", props.tenant.getName(), "Applications", props.application.getName(), "Add multicast-group");
const onFinish = (obj: MulticastGroup) => {
obj.setApplicationId(props.application.getId());

View File

@ -22,6 +22,7 @@ import ListMulticastGroupGateways from "./ListMulticastGroupGateways";
import EditMulticastGroup from "./EditMulticastGroup";
import Admin from "../../components/Admin";
import MulticastGroupQueue from "./MulticastGroupQueue";
import { useTitle } from "../helpers";
interface IProps {
tenant: Tenant;
@ -33,6 +34,14 @@ function MulticastGroupLayout(props: IProps) {
const navigate = useNavigate();
const location = useLocation();
const [multicastGroup, setMulticastGroup] = useState<MulticastGroup | undefined>(undefined);
useTitle(
"Tenants",
props.tenant.getName(),
"Applications",
props.application.getName(),
"Multicast-groups",
multicastGroup?.getName(),
);
useEffect(() => {
const req = new GetMulticastGroupRequest();

View File

@ -9,8 +9,10 @@ import type { ListRegionsResponse, RegionListItem } from "@chirpstack/chirpstack
import { getEnumName } from "../helpers";
import InternalStore from "../../stores/InternalStore";
import { useTitle } from "../helpers";
function ListRegions() {
useTitle("Network Server", "Regions");
const [regions, setRegions] = useState<ListRegionsResponse | undefined>(undefined);
useEffect(() => {

View File

@ -12,10 +12,12 @@ import { GetRegionRequest } from "@chirpstack/chirpstack-api-grpc-web/api/intern
import { getEnumName } from "../helpers";
import InternalStore from "../../stores/InternalStore";
import { useTitle } from "../helpers";
function RegionDetails() {
const [region, setRegion] = useState<GetRegionResponse | undefined>(undefined);
const { id } = useParams();
useTitle("Network Server", "Regions", region?.getDescription());
useEffect(() => {
const req = new GetRegionRequest();

View File

@ -11,6 +11,7 @@ import { GetDeviceRequest } from "@chirpstack/chirpstack-api-grpc-web/api/device
import DeviceStore from "../../stores/DeviceStore";
import ListRelayDevices from "./ListRelayDevices";
import { useTitle } from "../helpers";
interface IProps {
tenant: Tenant;
@ -20,6 +21,14 @@ interface IProps {
function RelayLayout(props: IProps) {
const [relayDevice, setRelayDevice] = useState<Device | undefined>(undefined);
const { relayDevEui } = useParams();
useTitle(
"Tenants",
props.tenant.getName(),
"Applications",
props.application.getName(),
"Relays",
relayDevice?.getName(),
);
useEffect(() => {
const req = new GetDeviceRequest();

View File

@ -8,8 +8,10 @@ import { Tenant, CreateTenantRequest } from "@chirpstack/chirpstack-api-grpc-web
import TenantForm from "./TenantForm";
import TenantStore from "../../stores/TenantStore";
import { useTitle } from "../helpers";
function CreateTenant() {
useTitle("Network Server", "Tenants", "Add");
const navigate = useNavigate();
const onFinish = (obj: Tenant) => {
@ -29,7 +31,7 @@ function CreateTenant() {
breadcrumbRender={() => (
<Breadcrumb>
<Breadcrumb.Item>
<span>Network-server</span>
<span>Network Server</span>
</Breadcrumb.Item>
<Breadcrumb.Item>
<span>

View File

@ -8,9 +8,11 @@ import { TenantUser, AddTenantUserRequest } from "@chirpstack/chirpstack-api-grp
import TenantUserForm from "./TenantUserForm";
import TenantStore from "../../stores/TenantStore";
import { useTitle } from "../helpers";
function CreateTenantUser({ tenant }: { tenant: Tenant }) {
const navigate = useNavigate();
useTitle("Tenants", tenant.getName(), "Tenant users", "Add");
const onFinish = (obj: TenantUser) => {
obj.setTenantId(tenant.getId());

View File

@ -16,11 +16,13 @@ import TenantStore from "../../stores/TenantStore";
import SessionStore from "../../stores/SessionStore";
import DeleteConfirm from "../../components/DeleteConfirm";
import Admin from "../../components/Admin";
import { useTitle } from "../helpers";
function EditTenantUser({ tenant }: { tenant: Tenant }) {
const [tenantUser, setTenantUser] = useState<TenantUser | undefined>(undefined);
const { userId } = useParams();
const navigate = useNavigate();
useTitle("Tenants", tenant.getName(), "Tenant users", tenantUser?.getEmail());
useEffect(() => {
const req = new GetTenantUserRequest();

View File

@ -12,12 +12,14 @@ import type { GetPageCallbackFunc } from "../../components/DataTable";
import DataTable from "../../components/DataTable";
import TenantStore from "../../stores/TenantStore";
import Admin from "../../components/Admin";
import { useTitle } from "../helpers";
interface IProps {
tenant: Tenant;
}
function ListTenatUsers(props: IProps) {
function ListTenantUsers(props: IProps) {
useTitle("Tenants", props.tenant.getName(), "Tenant users");
const columns: ColumnsType<TenantUserListItem.AsObject> = [
{
title: "Email",
@ -107,4 +109,4 @@ function ListTenatUsers(props: IProps) {
);
}
export default ListTenatUsers;
export default ListTenantUsers;

View File

@ -10,8 +10,10 @@ import { ListTenantsRequest } from "@chirpstack/chirpstack-api-grpc-web/api/tena
import type { GetPageCallbackFunc } from "../../components/DataTable";
import DataTable from "../../components/DataTable";
import TenantStore from "../../stores/TenantStore";
import { useTitle } from "../helpers";
function ListTenants() {
useTitle("Network Server", "Tenants");
const columns: ColumnsType<TenantListItem.AsObject> = [
{
title: "Name",

View File

@ -11,8 +11,10 @@ import DeleteConfirm from "../../components/DeleteConfirm";
import Admin from "../../components/Admin";
import EditTenant from "./EditTenant";
import TenantDashboard from "./TenantDashboard";
import { useTitle } from "../helpers";
function TenantLayout({ tenant }: { tenant: Tenant }) {
useTitle("Tenants", tenant.getName());
const navigate = useNavigate();
const location = useLocation();

View File

@ -8,11 +8,13 @@ import type { User, GetUserResponse } from "@chirpstack/chirpstack-api-grpc-web/
import { GetUserRequest, UpdateUserPasswordRequest } from "@chirpstack/chirpstack-api-grpc-web/api/user_pb";
import UserStore from "../../stores/UserStore";
import PasswordForm from "./PasswordForm";
import { useTitle } from "../helpers";
function ChangeUserPassword() {
const navigate = useNavigate();
const { userId } = useParams();
const [user, setUser] = useState<User | undefined>(undefined);
useTitle("Users", user?.getEmail(), "Change password");
useEffect(() => {
const req = new GetUserRequest();

View File

@ -8,8 +8,10 @@ import { User, CreateUserRequest } from "@chirpstack/chirpstack-api-grpc-web/api
import UserForm from "./UserForm";
import UserStore from "../../stores/UserStore";
import { useTitle } from "../helpers";
function CreateUser() {
useTitle("Network Server", "Users", "Add");
const navigate = useNavigate();
const onFinish = (obj: User, password: string) => {
@ -30,7 +32,7 @@ function CreateUser() {
breadcrumbRender={() => (
<Breadcrumb>
<Breadcrumb.Item>
<span>Network-server</span>
<span>Network Server</span>
</Breadcrumb.Item>
<Breadcrumb.Item>
<span>

View File

@ -10,11 +10,13 @@ import { GetUserRequest, UpdateUserRequest, DeleteUserRequest } from "@chirpstac
import UserForm from "./UserForm";
import UserStore from "../../stores/UserStore";
import DeleteConfirm from "../../components/DeleteConfirm";
import { useTitle } from "../helpers";
function EditUser() {
const navigate = useNavigate();
const { userId } = useParams();
const [user, setUser] = useState<User | undefined>(undefined);
useTitle("Network Server", "Users", user?.getEmail());
useEffect(() => {
const req = new GetUserRequest();
@ -57,7 +59,7 @@ function EditUser() {
breadcrumbRender={() => (
<Breadcrumb>
<Breadcrumb.Item>
<span>Network-server</span>
<span>Network Server</span>
</Breadcrumb.Item>
<Breadcrumb.Item>
<span>

View File

@ -11,8 +11,10 @@ import type { GetPageCallbackFunc } from "../../components/DataTable";
import DataTable from "../../components/DataTable";
import UserStore from "../../stores/UserStore";
import { useTitle } from "../helpers";
function ListUsers() {
useTitle("Network Server", "Users");
const columns: ColumnsType<UserListItem.AsObject> = [
{
title: "Email",

View File

@ -9,6 +9,7 @@ import { OpenIdConnectLoginRequest, OAuth2LoginRequest } from "@chirpstack/chirp
import SessionStore from "../../stores/SessionStore";
import InternalStore from "../../stores/InternalStore";
import { useTitle } from "../helpers";
const layout = {
labelCol: {
@ -122,6 +123,7 @@ function LoginForm() {
}
function Login() {
useTitle("Login");
const location = useLocation();
const navigate = useNavigate();

View File

@ -108,7 +108,7 @@
regenerator-runtime "^0.14.0"
"@chirpstack/chirpstack-api-grpc-web@file:../api/grpc-web":
version "4.9.0"
version "4.10.0-test.1"
dependencies:
"@types/google-protobuf" "^3.15.12"
google-protobuf "^3.21.2"
@ -444,85 +444,85 @@
resolved "https://registry.yarnpkg.com/@remix-run/router/-/router-1.17.1.tgz#bf93997beb81863fde042ebd05013a2618471362"
integrity sha512-mCOMec4BKd6BRGBZeSnGiIgwsbLGp3yhVqAD8H+PxiRNEHgDpZb8J1TnrSDlg97t0ySKMQJTHCWBCmBpSmkF6Q==
"@rollup/rollup-android-arm-eabi@4.18.1":
version "4.18.1"
resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.18.1.tgz#f0da481244b7d9ea15296b35f7fe39cd81157396"
integrity sha512-lncuC4aHicncmbORnx+dUaAgzee9cm/PbIqgWz1PpXuwc+sa1Ct83tnqUDy/GFKleLiN7ZIeytM6KJ4cAn1SxA==
"@rollup/rollup-android-arm-eabi@4.22.4":
version "4.22.4"
resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.22.4.tgz#8b613b9725e8f9479d142970b106b6ae878610d5"
integrity sha512-Fxamp4aEZnfPOcGA8KSNEohV8hX7zVHOemC8jVBoBUHu5zpJK/Eu3uJwt6BMgy9fkvzxDaurgj96F/NiLukF2w==
"@rollup/rollup-android-arm64@4.18.1":
version "4.18.1"
resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.18.1.tgz#82ab3c575f4235fb647abea5e08eec6cf325964e"
integrity sha512-F/tkdw0WSs4ojqz5Ovrw5r9odqzFjb5LIgHdHZG65dFI1lWTWRVy32KDJLKRISHgJvqUeUhdIvy43fX41znyDg==
"@rollup/rollup-android-arm64@4.22.4":
version "4.22.4"
resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.22.4.tgz#654ca1049189132ff602bfcf8df14c18da1f15fb"
integrity sha512-VXoK5UMrgECLYaMuGuVTOx5kcuap1Jm8g/M83RnCHBKOqvPPmROFJGQaZhGccnsFtfXQ3XYa4/jMCJvZnbJBdA==
"@rollup/rollup-darwin-arm64@4.18.1":
version "4.18.1"
resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.18.1.tgz#6a530452e68a9152809ce58de1f89597632a085b"
integrity sha512-vk+ma8iC1ebje/ahpxpnrfVQJibTMyHdWpOGZ3JpQ7Mgn/3QNHmPq7YwjZbIE7km73dH5M1e6MRRsnEBW7v5CQ==
"@rollup/rollup-darwin-arm64@4.22.4":
version "4.22.4"
resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.22.4.tgz#6d241d099d1518ef0c2205d96b3fa52e0fe1954b"
integrity sha512-xMM9ORBqu81jyMKCDP+SZDhnX2QEVQzTcC6G18KlTQEzWK8r/oNZtKuZaCcHhnsa6fEeOBionoyl5JsAbE/36Q==
"@rollup/rollup-darwin-x64@4.18.1":
version "4.18.1"
resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.18.1.tgz#47727479f5ca292cf434d7e75af2725b724ecbc7"
integrity sha512-IgpzXKauRe1Tafcej9STjSSuG0Ghu/xGYH+qG6JwsAUxXrnkvNHcq/NL6nz1+jzvWAnQkuAJ4uIwGB48K9OCGA==
"@rollup/rollup-darwin-x64@4.22.4":
version "4.22.4"
resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.22.4.tgz#42bd19d292a57ee11734c980c4650de26b457791"
integrity sha512-aJJyYKQwbHuhTUrjWjxEvGnNNBCnmpHDvrb8JFDbeSH3m2XdHcxDd3jthAzvmoI8w/kSjd2y0udT+4okADsZIw==
"@rollup/rollup-linux-arm-gnueabihf@4.18.1":
version "4.18.1"
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.18.1.tgz#46193c498aa7902a8db89ac00128060320e84fef"
integrity sha512-P9bSiAUnSSM7EmyRK+e5wgpqai86QOSv8BwvkGjLwYuOpaeomiZWifEos517CwbG+aZl1T4clSE1YqqH2JRs+g==
"@rollup/rollup-linux-arm-gnueabihf@4.22.4":
version "4.22.4"
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.22.4.tgz#f23555ee3d8fe941c5c5fd458cd22b65eb1c2232"
integrity sha512-j63YtCIRAzbO+gC2L9dWXRh5BFetsv0j0va0Wi9epXDgU/XUi5dJKo4USTttVyK7fGw2nPWK0PbAvyliz50SCQ==
"@rollup/rollup-linux-arm-musleabihf@4.18.1":
version "4.18.1"
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.18.1.tgz#22d831fe239643c1d05c98906420325cee439d85"
integrity sha512-5RnjpACoxtS+aWOI1dURKno11d7krfpGDEn19jI8BuWmSBbUC4ytIADfROM1FZrFhQPSoP+KEa3NlEScznBTyQ==
"@rollup/rollup-linux-arm-musleabihf@4.22.4":
version "4.22.4"
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.22.4.tgz#f3bbd1ae2420f5539d40ac1fde2b38da67779baa"
integrity sha512-dJnWUgwWBX1YBRsuKKMOlXCzh2Wu1mlHzv20TpqEsfdZLb3WoJW2kIEsGwLkroYf24IrPAvOT/ZQ2OYMV6vlrg==
"@rollup/rollup-linux-arm64-gnu@4.18.1":
version "4.18.1"
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.18.1.tgz#19abd33695ec9d588b4a858d122631433084e4a3"
integrity sha512-8mwmGD668m8WaGbthrEYZ9CBmPug2QPGWxhJxh/vCgBjro5o96gL04WLlg5BA233OCWLqERy4YUzX3bJGXaJgQ==
"@rollup/rollup-linux-arm64-gnu@4.22.4":
version "4.22.4"
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.22.4.tgz#7abe900120113e08a1f90afb84c7c28774054d15"
integrity sha512-AdPRoNi3NKVLolCN/Sp4F4N1d98c4SBnHMKoLuiG6RXgoZ4sllseuGioszumnPGmPM2O7qaAX/IJdeDU8f26Aw==
"@rollup/rollup-linux-arm64-musl@4.18.1":
version "4.18.1"
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.18.1.tgz#d60af8c0b9be424424ff96a0ba19fce65d26f6ab"
integrity sha512-dJX9u4r4bqInMGOAQoGYdwDP8lQiisWb9et+T84l2WXk41yEej8v2iGKodmdKimT8cTAYt0jFb+UEBxnPkbXEQ==
"@rollup/rollup-linux-arm64-musl@4.22.4":
version "4.22.4"
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.22.4.tgz#9e655285c8175cd44f57d6a1e8e5dedfbba1d820"
integrity sha512-Gl0AxBtDg8uoAn5CCqQDMqAx22Wx22pjDOjBdmG0VIWX3qUBHzYmOKh8KXHL4UpogfJ14G4wk16EQogF+v8hmA==
"@rollup/rollup-linux-powerpc64le-gnu@4.18.1":
version "4.18.1"
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.18.1.tgz#b1194e5ed6d138fdde0842d126fccde74a90f457"
integrity sha512-V72cXdTl4EI0x6FNmho4D502sy7ed+LuVW6Ym8aI6DRQ9hQZdp5sj0a2usYOlqvFBNKQnLQGwmYnujo2HvjCxQ==
"@rollup/rollup-linux-powerpc64le-gnu@4.22.4":
version "4.22.4"
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.22.4.tgz#9a79ae6c9e9d8fe83d49e2712ecf4302db5bef5e"
integrity sha512-3aVCK9xfWW1oGQpTsYJJPF6bfpWfhbRnhdlyhak2ZiyFLDaayz0EP5j9V1RVLAAxlmWKTDfS9wyRyY3hvhPoOg==
"@rollup/rollup-linux-riscv64-gnu@4.18.1":
version "4.18.1"
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.18.1.tgz#f5a635c017b9bff8b856b0221fbd5c0e3373b7ec"
integrity sha512-f+pJih7sxoKmbjghrM2RkWo2WHUW8UbfxIQiWo5yeCaCM0TveMEuAzKJte4QskBp1TIinpnRcxkquY+4WuY/tg==
"@rollup/rollup-linux-riscv64-gnu@4.22.4":
version "4.22.4"
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.22.4.tgz#67ac70eca4ace8e2942fabca95164e8874ab8128"
integrity sha512-ePYIir6VYnhgv2C5Xe9u+ico4t8sZWXschR6fMgoPUK31yQu7hTEJb7bCqivHECwIClJfKgE7zYsh1qTP3WHUA==
"@rollup/rollup-linux-s390x-gnu@4.18.1":
version "4.18.1"
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.18.1.tgz#f1043d9f4026bf6995863cb3f8dd4732606e4baa"
integrity sha512-qb1hMMT3Fr/Qz1OKovCuUM11MUNLUuHeBC2DPPAWUYYUAOFWaxInaTwTQmc7Fl5La7DShTEpmYwgdt2hG+4TEg==
"@rollup/rollup-linux-s390x-gnu@4.22.4":
version "4.22.4"
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.22.4.tgz#9f883a7440f51a22ed7f99e1d070bd84ea5005fc"
integrity sha512-GqFJ9wLlbB9daxhVlrTe61vJtEY99/xB3C8e4ULVsVfflcpmR6c8UZXjtkMA6FhNONhj2eA5Tk9uAVw5orEs4Q==
"@rollup/rollup-linux-x64-gnu@4.18.1":
version "4.18.1"
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.18.1.tgz#1e781730be445119f06c9df5f185e193bc82c610"
integrity sha512-7O5u/p6oKUFYjRbZkL2FLbwsyoJAjyeXHCU3O4ndvzg2OFO2GinFPSJFGbiwFDaCFc+k7gs9CF243PwdPQFh5g==
"@rollup/rollup-linux-x64-gnu@4.22.4":
version "4.22.4"
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.22.4.tgz#70116ae6c577fe367f58559e2cffb5641a1dd9d0"
integrity sha512-87v0ol2sH9GE3cLQLNEy0K/R0pz1nvg76o8M5nhMR0+Q+BBGLnb35P0fVz4CQxHYXaAOhE8HhlkaZfsdUOlHwg==
"@rollup/rollup-linux-x64-musl@4.18.1":
version "4.18.1"
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.18.1.tgz#08f12e1965d6f27d6898ff932592121cca6abc4b"
integrity sha512-pDLkYITdYrH/9Cv/Vlj8HppDuLMDUBmgsM0+N+xLtFd18aXgM9Nyqupb/Uw+HeidhfYg2lD6CXvz6CjoVOaKjQ==
"@rollup/rollup-linux-x64-musl@4.22.4":
version "4.22.4"
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.22.4.tgz#f473f88219feb07b0b98b53a7923be716d1d182f"
integrity sha512-UV6FZMUgePDZrFjrNGIWzDo/vABebuXBhJEqrHxrGiU6HikPy0Z3LfdtciIttEUQfuDdCn8fqh7wiFJjCNwO+g==
"@rollup/rollup-win32-arm64-msvc@4.18.1":
version "4.18.1"
resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.18.1.tgz#4a5dcbbe7af7d41cac92b09798e7c1831da1f599"
integrity sha512-W2ZNI323O/8pJdBGil1oCauuCzmVd9lDmWBBqxYZcOqWD6aWqJtVBQ1dFrF4dYpZPks6F+xCZHfzG5hYlSHZ6g==
"@rollup/rollup-win32-arm64-msvc@4.22.4":
version "4.22.4"
resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.22.4.tgz#4349482d17f5d1c58604d1c8900540d676f420e0"
integrity sha512-BjI+NVVEGAXjGWYHz/vv0pBqfGoUH0IGZ0cICTn7kB9PyjrATSkX+8WkguNjWoj2qSr1im/+tTGRaY+4/PdcQw==
"@rollup/rollup-win32-ia32-msvc@4.18.1":
version "4.18.1"
resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.18.1.tgz#075b0713de627843a73b4cf0e087c56b53e9d780"
integrity sha512-ELfEX1/+eGZYMaCIbK4jqLxO1gyTSOIlZr6pbC4SRYFaSIDVKOnZNMdoZ+ON0mrFDp4+H5MhwNC1H/AhE3zQLg==
"@rollup/rollup-win32-ia32-msvc@4.22.4":
version "4.22.4"
resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.22.4.tgz#a6fc39a15db618040ec3c2a24c1e26cb5f4d7422"
integrity sha512-SiWG/1TuUdPvYmzmYnmd3IEifzR61Tragkbx9D3+R8mzQqDBz8v+BvZNDlkiTtI9T15KYZhP0ehn3Dld4n9J5g==
"@rollup/rollup-win32-x64-msvc@4.18.1":
version "4.18.1"
resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.18.1.tgz#0cb240c147c0dfd0e3eaff4cc060a772d39e155c"
integrity sha512-yjk2MAkQmoaPYCSu35RLJ62+dz358nE83VfTePJRp8CG7aMg25mEJYpXFiD+NcevhX8LxD5OP5tktPXnXN7GDw==
"@rollup/rollup-win32-x64-msvc@4.22.4":
version "4.22.4"
resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.22.4.tgz#3dd5d53e900df2a40841882c02e56f866c04d202"
integrity sha512-j8pPKp53/lq9lMXN57S8cFz0MynJk8OWNuUnXct/9KCpKU7DgU3bYMJhwWmcqC0UU29p8Lr0/7KEVcaM6bf47Q==
"@swc/core-darwin-arm64@1.6.13":
version "1.6.13"
@ -2732,28 +2732,28 @@ rimraf@^3.0.2:
glob "^7.1.3"
rollup@^4.13.0:
version "4.18.1"
resolved "https://registry.yarnpkg.com/rollup/-/rollup-4.18.1.tgz#18a606df5e76ca53b8a69f2d8eab256d69dda851"
integrity sha512-Elx2UT8lzxxOXMpy5HWQGZqkrQOtrVDDa/bm9l10+U4rQnVzbL/LgZ4NOM1MPIDyHk69W4InuYDF5dzRh4Kw1A==
version "4.22.4"
resolved "https://registry.yarnpkg.com/rollup/-/rollup-4.22.4.tgz#4135a6446671cd2a2453e1ad42a45d5973ec3a0f"
integrity sha512-vD8HJ5raRcWOyymsR6Z3o6+RzfEPCnVLMFJ6vRslO1jt4LO6dUo5Qnpg7y4RkZFM2DMe3WUirkI5c16onjrc6A==
dependencies:
"@types/estree" "1.0.5"
optionalDependencies:
"@rollup/rollup-android-arm-eabi" "4.18.1"
"@rollup/rollup-android-arm64" "4.18.1"
"@rollup/rollup-darwin-arm64" "4.18.1"
"@rollup/rollup-darwin-x64" "4.18.1"
"@rollup/rollup-linux-arm-gnueabihf" "4.18.1"
"@rollup/rollup-linux-arm-musleabihf" "4.18.1"
"@rollup/rollup-linux-arm64-gnu" "4.18.1"
"@rollup/rollup-linux-arm64-musl" "4.18.1"
"@rollup/rollup-linux-powerpc64le-gnu" "4.18.1"
"@rollup/rollup-linux-riscv64-gnu" "4.18.1"
"@rollup/rollup-linux-s390x-gnu" "4.18.1"
"@rollup/rollup-linux-x64-gnu" "4.18.1"
"@rollup/rollup-linux-x64-musl" "4.18.1"
"@rollup/rollup-win32-arm64-msvc" "4.18.1"
"@rollup/rollup-win32-ia32-msvc" "4.18.1"
"@rollup/rollup-win32-x64-msvc" "4.18.1"
"@rollup/rollup-android-arm-eabi" "4.22.4"
"@rollup/rollup-android-arm64" "4.22.4"
"@rollup/rollup-darwin-arm64" "4.22.4"
"@rollup/rollup-darwin-x64" "4.22.4"
"@rollup/rollup-linux-arm-gnueabihf" "4.22.4"
"@rollup/rollup-linux-arm-musleabihf" "4.22.4"
"@rollup/rollup-linux-arm64-gnu" "4.22.4"
"@rollup/rollup-linux-arm64-musl" "4.22.4"
"@rollup/rollup-linux-powerpc64le-gnu" "4.22.4"
"@rollup/rollup-linux-riscv64-gnu" "4.22.4"
"@rollup/rollup-linux-s390x-gnu" "4.22.4"
"@rollup/rollup-linux-x64-gnu" "4.22.4"
"@rollup/rollup-linux-x64-musl" "4.22.4"
"@rollup/rollup-win32-arm64-msvc" "4.22.4"
"@rollup/rollup-win32-ia32-msvc" "4.22.4"
"@rollup/rollup-win32-x64-msvc" "4.22.4"
fsevents "~2.3.2"
run-parallel@^1.1.9: