merge
/ build (push) Successful in 2m36s Details
/ build_x64 (push) Has been cancelled Details
/ build_arm64 (push) Failing after 2m52s Details
/ build_win64 (push) Has been cancelled Details

This commit is contained in:
core 2023-10-10 18:02:08 -04:00
commit 0193ffb828
Signed by: core
GPG Key ID: FDBF740DADDCEECF
52 changed files with 660 additions and 381 deletions

View File

@ -44,12 +44,42 @@ jobs:
profile: minimal
toolchain: stable
override: true
- name: Install aarch64 target
run: rustup target add aarch64-unknown-linux-gnu
- name: Install additional dependencies
run: apt update && apt-get install -y libclang-dev clang sshpass rsync gcc-arm-linux-gnueabi
- name: Compile release binary
uses: https://github.com/actions-rs/cargo@v1
with:
command: build
args: --release --bin tfclient
args: --release --bin tfclient --target aarch64-unknown-linux-gnu
- name: Upload binary
run: sshpass -p "${{ secrets.TRIFID_DLCDN_PASSWORD }}" rsync --mkpath -e 'ssh -p ${{ secrets.TRIFID_DLCDN_PORT }} -o StrictHostKeyChecking=no' target/release/tfclient ${{ secrets.TRIFID_DLCDN_USER }}@${{ secrets.TRIFID_DLCDN_IP }}:${{ secrets.TRIFID_DLCDN_PATH }}/tfclient/arm64/$GITHUB_SHA/tfclient
build_win64:
runs_on: docker
steps:
- name: Checkout code
uses: actions/checkout@v2
- name: Setup Go toolchain
uses: actions/setup-go@v4
with:
go-version: '1.20'
- name: Setup Rust toolchain
uses: https://github.com/actions-rs/toolchain@v1
with:
profile: minimal
toolchain: stable
override: true
- name: Install cross-compilation toolchain
run: rustup target add x86_64-pc-windows-gnu
- name: Install additional dependencies
run: apt update && apt-get install -y libclang-dev clang sshpass rsync mingw-w64 zip
- name: Compile release binary
uses: https://github.com/actions-rs/cargo@v1
with:
command: build
args: --release --bin tfclient --target x86_64-pc-windows-gnu
- name: Compile release bundle
run: ./build_windows.sh
- name: Upload binary
run: sshpass -p "${{ secrets.TRIFID_DLCDN_PASSWORD }}" rsync --mkpath -e 'ssh -p ${{ secrets.TRIFID_DLCDN_PORT }} -o StrictHostKeyChecking=no' target/x86_64-pc-windows-gnu/release/tfclient.zip ${{ secrets.TRIFID_DLCDN_USER }}@${{ secrets.TRIFID_DLCDN_IP }}:${{ secrets.TRIFID_DLCDN_PATH }}/tfclient/win64/$GITHUB_SHA/tfclient.zip

1
.gitignore vendored
View File

@ -8,3 +8,4 @@ packages/void_amd64_tfclient/work
packages/void_amd64_tfclient/*.xbps
*.pem
*.xbps
tfweb/build

View File

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="DataSourceManagerImpl" format="xml" multifile-model="true">
<data-source source="LOCAL" name="trifidapi@localhost" uuid="39c81b89-3fc4-493f-b203-7a00527cffe6">
<data-source source="LOCAL" name="trifid@localhost" uuid="39c81b89-3fc4-493f-b203-7a00527cffe6">
<driver-ref>postgresql</driver-ref>
<synchronize>true</synchronize>
<jdbc-driver>org.postgresql.Driver</jdbc-driver>

18
build_windows.sh Executable file
View File

@ -0,0 +1,18 @@
#!/bin/bash
set -e
cargo build --target x86_64-pc-windows-gnu --release --bin tfclient
rm -rf target/x86_64-pc-windows-gnu/release/tfclient_dist/
mkdir -p target/x86_64-pc-windows-gnu/release/tfclient_dist/
cp target/x86_64-pc-windows-gnu/release/tfclient.exe target/x86_64-pc-windows-gnu/release/tfclient_dist/tfclient.exe
echo "[*] Downloading WinTun"
mkdir -p target/x86_64-pc-windows-gnu/release/tfclient_dist/dist/windows/
wget https://www.wintun.net/builds/wintun-0.14.1.zip -O /tmp/wintun.zip
echo "[*] Unzipping WinTun"
unzip -d target/x86_64-pc-windows-gnu/release/tfclient_dist/dist/windows/ /tmp/wintun.zip
echo "[*] Copying nebula.dll"
cp target/x86_64-pc-windows-gnu/release/nebula.dll target/x86_64-pc-windows-gnu/release/tfclient_dist/nebula.dll
echo "[*] Building zip bundle"
cd target/x86_64-pc-windows-gnu/release
zip -r tfclient.zip tfclient_dist/*
cd ../../../
echo "[*] Windows production build success!"

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

View File

@ -0,0 +1,11 @@
---
sidebar_position: 3
---
# Troubleshooting
This page lists common issues with tfclient and how to resolve them. This page will be updated occasionally as needed.
## On Windows, traffic can leave but cannot enter
This is [a known issue with Nebula](https://github.com/slackhq/nebula/issues/621), and by extension tfclient. [A workaround is available - see here for details.](/docs/tfclient/windows_firewall)

View File

@ -0,0 +1,45 @@
---
sidebar_position: 2
---
# Windows Firewall Setup
:::info
This is only needed on Windows-based platforms. On macOS and Linux, the firewall works correctly out-of-the-box.
:::
On Windows-based platforms, Nebula (and by extension tfclient) cannot properly configure Windows Defender Firewall automatically, thus preventing any inbound traffic from reaching your machine over your network (outbound traffic will still work). To fix this, you need to add a firewall rule to Windows Defender Firewall.
It is recommended that you add a rule allowing **all inbound traffic from your network.** Nebula has a built-in firewall, thus it is preferred to allow the Nebula firewall to function as intended and leave Windows Defender Firewall essentially disabled for your VPN's subnet, allowing Nebula to function in it's place.
## Adding the inbound rule
1. Search for "Windows Defender Firewall with Advanced Security" and open it. This requires administrator privileges. You should see the window below.
![Windows Defender Firewall Window](img/firewall_main.png)
2. Select "Inbound Rules" on the left, and then click "New Rule" to open the New Inbound Rule Wizard. You should see the window below.
![New Inbound Rule Wizard](img/new_rule_1.png)
3. Select the Custom option, and click Next.
4. Click the Next button for the Program step.
5. Click the Next button for the Protocol and Port step.
6. Select the "These IP addresses" checkbox under "Which remote IP addresses does this rule apply to?" as shown below.
![These IP addresses radio](img/new_rule_2.png)
7. Select the Add button next to "These IP addresses" to open the window shown below.
![IP Address popup](img/new_rule_3.png)
8. In the box under "This IP address or subnet" enter the CIDR notation of your network i.e. `10.16.0.0/15`. Ask your network administrator for this value if you do not know it.
9. Click the "OK" button.
10. Click the "Next" button.
11. Under "What action should be taken when a connection matches the specified conditions?", select "Allow the connection", then click Next.
12. Click "Next" on the Profile page.
13. Enter a title for the rule. We recommend using something like `tfclient/nebula/YOUR_NETWORK_NAME/YOUR_NETWORK_CIDR`, such as `tfclient/nebula/corporate/10.16.0.0/15`.
14. If desired, enter additional descriptions in the second text box.
15. Select the "Finish" option.
The changes should take effect immediately, and traffic should begin flowing 5-10 seconds after applying the rule (give Nebula time to perform handshakes and establish tunnels). If it does not take effect immediately, rebooting the affected machine typically resolves any issues.

View File

@ -3,6 +3,22 @@ use std::path::PathBuf;
use bindgen::CargoCallbacks;
use std::path::Path;
fn get_cargo_target_dir() -> Result<std::path::PathBuf, Box<dyn std::error::Error>> {
let out_dir = std::path::PathBuf::from(std::env::var("OUT_DIR")?);
let profile = std::env::var("PROFILE")?;
let mut target_dir = None;
let mut sub_path = out_dir.as_path();
while let Some(parent) = sub_path.parent() {
if parent.ends_with(&profile) {
target_dir = Some(parent);
break;
}
sub_path = parent;
}
let target_dir = target_dir.ok_or("not found")?;
Ok(target_dir.to_path_buf())
}
fn main() {
// Find compiler:
@ -29,11 +45,11 @@ fn main() {
let out_dir = env::var("OUT_DIR").unwrap();
let out_path = PathBuf::from(out_dir);
let out_file = LIBRARY_PREFIX.to_owned() + "nebula" + LIBRARY_EXTENSION;
let out_file = lib_name();
let out = out_path.join(out_file);
let mut command = process::Command::new(compiler);
command.args(["build", "-buildmode", "c-archive", "-o", out.display().to_string().as_str(), "main.go"]);
command.args(["build", "-buildmode", link_type().as_str(), "-o", out.display().to_string().as_str(), "main.go"]);
command.env("CGO_ENABLED", "1");
command.env("CC", c_compiler.path());
command.env("GOARCH", goarch());
@ -49,8 +65,9 @@ fn main() {
}
println!("Go compile success");
copy_if_windows();
println!("cargo:rustc-link-lib=static=nebula");
print_link();
println!("cargo:rustc-link-search=native={}", env::var("OUT_DIR").unwrap());
//let out_path = PathBuf::from(env::var("OUT_DIR").unwrap());
@ -63,7 +80,7 @@ fn main() {
println!("Generating bindings");
let bindings = bindgen::Builder::default()
.header(out_path.join("libnebula.h").display().to_string())
.header(out_path.join(header_name()).display().to_string())
.parse_callbacks(Box::new(CargoCallbacks))
.generate()
.expect("Error generating CFFI bindings");
@ -74,15 +91,30 @@ fn main() {
.expect("Couldn't write bindings!");
}
#[cfg(target_family = "unix")]
const LIBRARY_EXTENSION: & str = ".a";
#[cfg(target_family = "unix")]
const LIBRARY_PREFIX: & str = "lib";
fn lib_name() -> String {
if env::var("CARGO_CFG_TARGET_FAMILY").unwrap() == "windows" {
"nebula.dll".to_string()
} else {
"libnebula.a".to_string()
}
}
fn header_name() -> String {
if env::var("CARGO_CFG_TARGET_FAMILY").unwrap() == "windows" {
"nebula.h".to_string()
} else {
"libnebula.h".to_string()
}
}
#[cfg(target_family = "windows")]
const LIBRARY_EXTENSION: &str = ".lib";
#[cfg(target_family = "windows")]
const LIBRARY_PREFIX: &str = "";
fn copy_if_windows() {
let target_dir = get_cargo_target_dir().unwrap();
let target_file = target_dir.join(lib_name());
let out_dir = env::var("OUT_DIR").unwrap();
let out_path = PathBuf::from(out_dir);
let out_file = lib_name();
let out = out_path.join(out_file);
std::fs::copy(out, target_file).unwrap();
}
fn goarch() -> String {
match env::var("CARGO_CFG_TARGET_ARCH").unwrap().as_str() {
@ -109,4 +141,20 @@ fn goos() -> String {
"netbsd" => "netbsd",
os => panic!("unsupported operating system {os}")
}.to_string()
}
fn print_link() {
if env::var("CARGO_CFG_TARGET_FAMILY").unwrap() == "windows" {
println!("cargo:rustc-link-lib=dylib=nebula");
} else {
println!("cargo:rustc-link-lib=static=nebula");
}
}
fn link_type() -> String {
if env::var("CARGO_CFG_TARGET_FAMILY").unwrap() == "windows" {
"c-shared".to_string()
} else {
"c-archive".to_string()
}
}

View File

@ -124,6 +124,8 @@ fn handle_client(
) -> Result<(), io::Error> {
info!("Handling connection from {}", stream.peer_addr()?);
stream.set_nonblocking(false)?;
let mut client = Client {
state: ClientState::WaitHello,
reader: BufReader::new(&stream),

View File

@ -12,6 +12,7 @@
},
"devDependencies": {
"@sveltejs/adapter-auto": "^2.0.0",
"@sveltejs/adapter-node": "^1.3.1",
"@sveltejs/kit": "^1.5.0",
"@typescript-eslint/eslint-plugin": "^5.45.0",
"@typescript-eslint/parser": "^5.45.0",

View File

@ -1,5 +1,4 @@
<script lang="ts">
import {t} from "svelte-i18n";
import Sidebar from "$components/Sidebar.svelte";
export let selected;
@ -14,4 +13,4 @@
<slot></slot>
</div>
</div>
</div>
</div>

View File

@ -1,5 +1,5 @@
<script lang="ts">
import {t} from "svelte-i18n";
import {t} from "$lib/i18n/translations";
export let selected;
</script>
@ -39,7 +39,7 @@
<hr>
<div class="nav-item">
<button class="nav-link py-2 px-4" on:click={() => {window.localStorage.setItem("mfa", "")}}>
<button class="nav-link py-2 px-4" on:click={() => {window.localStorage.setItem("mfa", ""); window.location.href = "/2fa"}}>
<i class="me-2 fas fa-right-from-bracket fa-fw"></i>
{$t("common.logout")}
</button>

17
tfweb/src/lib/Tooltips.js Normal file
View File

@ -0,0 +1,17 @@
import {browser} from "$app/environment";
export function updateTooltips() {
if (browser) {
setTimeout(() => {
const tooltipTriggerList = document.querySelectorAll('[data-bs-toggle="tooltip"]')
const tooltipList = [...tooltipTriggerList].map(tooltipTriggerEl => {
let tooltip = new document.B.Tooltip(tooltipTriggerEl, {trigger: 'hover'})
tooltipTriggerEl.addEventListener('click', () => {
tooltip.hide();
tooltip.dispose();
})
});
console.log(tooltipList);
});
}
}

View File

@ -1,32 +0,0 @@
{
"itworks": {
},
"login": {
},
"signup": {
},
"ml": {
},
"2fasetup": {
},
"2fa": {
},
"networkcreate": {
},
"hosts": {
},
"roles": {
},
"common": {
}
}

View File

@ -6,8 +6,8 @@
"verify": "Verify by entering the 6-digit code shown in your authenticator into the box below.",
"button": "Enable 2FA",
"error": {
"generic": "Unable to enable 2fa: {err}",
"generic": "Unable to enable 2fa: {{err}}",
"api": "Unable to contact the server. Try again later.",
"ERR_UNAUTHORIZED": "Incorrect 2FA code"
}
}
}

View File

@ -1,5 +1,5 @@
{
"title": "{title} | Trifid Web UI",
"title": "{{title}} | Trifid Web UI",
"page": {
"itworks": "It Works!",
"admin": "Admin Panel",
@ -16,4 +16,4 @@
},
"logout": "Log out",
"loading": "Dashboard is loading"
}
}

View File

@ -7,6 +7,7 @@
"edit": "Edit",
"enrollbtn": "Enroll",
"deletehost": "Delete",
"blockhost": "Block",
"config": "Configuration",
"add": "Add",
"create": {
@ -23,19 +24,28 @@
}
},
"enroll": {
"title": "Enrolling host {host}",
"title": "Enrolling host {{host}}",
"explainer": "Enrolling this host will allow it to communicate on your network. You'll need to have compatible client software installed first.",
"done": "Done, return me to the hosts page",
"cancel": "Nevermind",
"code": "Input {code} as your enrollment code into any trifid-compatible client software to enroll. It will expire in {expires} minutes."
"code": "Input {code} as your enrollment code into any trifid-compatible client software to enroll. It will expire in {{expires}} minutes."
},
"delete": {
"title": "Are you sure you want to delete the host {host}?",
"title": "Are you sure you want to delete the host {{host}}?",
"explainer": "This action cannot be undone. This host will still be able to connect to the network unless you block it first.",
"confirm": "I'm sure",
"cancel": "Nevermind",
"error": {
"ERR_DB_ERROR": "Removal failed"
}
},
"block": {
"title": "Are you sure you want to block the host {{host}}?",
"explainer": "The host will not be able to connect to the network once it is blocked. Re-enroll this host to unblock it.",
"confirm": "I'm sure",
"cancel": "Nevermind",
"error": {
"ERR_DB_ERROR": "Block failed"
}
}
}
}

View File

@ -1,7 +1,7 @@
{
"header": "It works!",
"body": "If you're seeing this page, tfweb is installed and (probably) correctly configured.",
"linkbody": "Perhaps you meant to visit the {link0} or {link1}?",
"linkbody": "Perhaps you meant to visit the {{link0}} or {{link1}}?",
"linkbody.link0": "admin panel",
"linkbody.link1": "create an account"
}
}

View File

@ -5,13 +5,13 @@
"button": "Log in",
"email": "Check your email",
"emailbody": "We sent you an email with a link to complete logging in.",
"emailbody2": "Didn't work? Check your junk inbox or click {link0} to try again.",
"emailbody2": "Didn't work? Check your junk inbox or click {{link0}} to try again.",
"emailbody2.link0": "here",
"error": {
"invalidEmail": "That email address isn't valid. Try again.",
"generic": "There was an error logging you in. Try again or contact support with the error code {err}",
"generic": "There was an error logging you in. Try again or contact support with the error code {{err}}",
"usermissing": "That user does not exist."
},
"need": "Don't have an account? {link0}",
"need": "Don't have an account? {{link0}}",
"need.link0": "Signup"
}
}

View File

@ -3,10 +3,10 @@
"explain": "This defines what IP addresses will be assigned to your devices. The range you enter below must fall within either the RFC 1918 Private Address Space or the RFC 6598 Shared Address Space. Enter your network via CIDR notation.",
"label": "Network range",
"button": "Create network",
"valid": "Valid - {numIps} addresses ({start} to {end})",
"valid": "Valid - {{numIps:number}} addresses ({{start}} to {{end}})",
"invalid": "Invalid",
"error": {
"generic": "Unable to create network: {err}",
"generic": "Unable to create network: {{err}}",
"api": "Unable to contact the server. Try again later."
}
}
}

View File

@ -1,6 +1,6 @@
{
"delete": {
"title": "Are you sure you want to delete the role {rule}?",
"title": "Are you sure you want to delete the role {{rule}}?",
"explainer": "This action cannot be undone. This role must be removed from all hosts, lighthouses, and relays prior to deleting it.",
"confirm": "I'm sure",
"cancel": "Nevermind",
@ -15,6 +15,8 @@
"rules": "Rule count",
"description": "Description",
"actions": "Actions",
"editrole": "Edit",
"deleterole": "Delete",
"add": {
"title": "Create Role",
"any": "Any",
@ -46,7 +48,7 @@
}
},
"edit": {
"title": "Editing role {rule}",
"title": "Editing role {{rule}}",
"any": "Any",
"name": "Role name",
"desc": "Role description",
@ -74,4 +76,4 @@
"cancel": "Cancel"
}
}
}
}

View File

@ -5,14 +5,14 @@
"button": "Create account",
"email": "Check your email",
"emailbody": "We sent you an email with a link to complete signing up.",
"emailbody2": "Didn't work? Check your junk inbox or click {link0} to try again.",
"emailbody2": "Didn't work? Check your junk inbox or click {{link0}} to try again.",
"emailbody2.link0": "here",
"error": {
"invalidEmail": "That email address isn't valid. Try again.",
"generic": "There was an error logging you in. Try again or contact support with the error code {err}",
"userexists": "That user already exists. Try {link0}?",
"generic": "There was an error logging you in. Try again or contact support with the error code {{err}}",
"userexists": "That user already exists. Try {{link0}}?",
"userexists.link0": "logging in"
},
"already": "Already have an account? {link0}",
"already": "Already have an account? {{link0}}",
"already.link0": "Login"
}
}

View File

@ -1,223 +0,0 @@
{
"itworks": {
"header": "Het werkt!",
"body": "Als u deze pagina ziet, is tfweb geïnstalleerd en (waarschijnlijk) correct geconfigureerd.",
"linkbody": "Misschien wilde je het {link0} of {link1}?",
"linkbody.link0": "beheerdersdashboard bezoeken",
"linkbody.link1": "een account aanmaken"
},
"login": {
"title": "Log in to your account",
"subtitle": "We'll send you an email with a \"magic link\".",
"label": "What is your email?",
"button": "Log in",
"email": "Check your email",
"emailbody": "We sent you an email with a link to complete logging in.",
"emailbody2": "Didn't work? Check your junk inbox or click {link0} to try again.",
"emailbody2.link0": "here",
"error": {
"invalidEmail": "That email address isn't valid. Try again.",
"generic": "There was an error logging you in. Try again or contact support with the error code {err}",
"usermissing": "That user does not exist."
},
"need": "Don't have an account? {link0}",
"need.link0": "Signup"
},
"signup": {
"title": "Create an account",
"subtitle": "We'll send you an email with a \"magic link\"",
"label": "What is your email?",
"button": "Create account",
"email": "Check your email",
"emailbody": "We sent you an email with a link to complete signing up.",
"emailbody2": "Didn't work? Check your junk inbox or click {link0} to try again.",
"emailbody2.link0": "here",
"error": {
"invalidEmail": "That email address isn't valid. Try again.",
"generic": "There was an error logging you in. Try again or contact support with the error code {err}",
"userexists": "That user already exists. Try {link0}?",
"userexists.link0": "logging in"
},
"already": "Already have an account? {link0}",
"already.link0": "Login"
},
"ml": {
"header": "Authenticated!",
"body": "Redirecting to admin page...",
"error": {
"notoken": "magic link token missing",
"badtoken": "token is invalid or has expired"
}
},
"2fasetup": {
"title": "Configure two-factor authentication",
"body": "Trifid requires all accounts to use TOTP two-factor authentication to help ensure the security of your network.",
"scan": "Scan this QR code with an authenticator app, like Authy or Google Authenticator.",
"code": "Or, enter the TOTP secret below into an authenticator app.",
"verify": "Verify by entering the 6-digit code shown in your authenticator into the box below.",
"button": "Enable 2FA",
"error": {
"generic": "Unable to enable 2fa: {err}",
"api": "Unable to contact the server. Try again later.",
"ERR_UNAUTHORIZED": "Incorrect 2FA code"
}
},
"2fa": {
"title": "Authenticate with TOTP",
"subtitle": "Enter the 6-digit code displayed in your authenticator app",
"label": "TOTP Code",
"button": "Verify",
"error": {
"ERR_UNAUTHORIZED": "Invalid 2FA code"
}
},
"networkcreate": {
"title": "Create your network",
"explain": "This defines what IP addresses will be assigned to your devices. The range you enter below must fall within either the RFC 1918 Private Address Space or the RFC 6598 Shared Address Space. Enter your network via CIDR notation.",
"label": "Network range",
"button": "Create network",
"valid": "Valid - {numIps} addresses ({start} to {end})",
"invalid": "Invalid",
"error": {
"generic": "Unable to create network: {err}",
"api": "Unable to contact the server. Try again later."
}
},
"hosts": {
"name": "Name",
"role": "Role",
"lastseen": "Last Seen",
"actions": "Actions",
"ipaddr": "IP Address",
"edit": "Edit",
"enrollbtn": "Enroll",
"deletehost": "Delete",
"config": "Configuration",
"add": "Add",
"create": {
"name": "Host name",
"btn": "Add host",
"cancel": "Cancel",
"role": "Role",
"ip": "IP Address",
"error": {
"needsname": "Host name is required.",
"needsrole": "Role is required.",
"invalidip": "Invalid IP address",
"ERR_DUPLICATE_VALUE": "IP address already in use"
}
},
"enroll": {
"title": "Enrolling host {host}",
"explainer": "Enrolling this host will allow it to communicate on your network. You'll need to have compatible client software installed first.",
"done": "Done, return me to the hosts page",
"cancel": "Nevermind",
"code": "Input {code} as your enrollment code into any trifid-compatible client software to enroll. It will expire in {expires} minutes."
},
"delete": {
"title": "Are you sure you want to delete the host {host}?",
"explainer": "This action cannot be undone. This host will still be able to connect to the network unless you block it first.",
"confirm": "I'm sure",
"cancel": "Nevermind",
"error": {
"ERR_DB_ERROR": "Removal failed"
}
}
},
"roles": {
"delete": {
"title": "Are you sure you want to delete the role {rule}?",
"explainer": "This action cannot be undone. This role must be removed from all hosts, lighthouses, and relays prior to deleting it.",
"confirm": "I'm sure",
"cancel": "Nevermind",
"error": {
"ERR_DB_ERROR": "Removal failed (role was probably still attached to a host)"
}
},
"create": "Add",
"explain": "Roles control how hosts, lighthouses, and relays communicate through firewall rules.",
"noroles": "You don't have any roles. You'll need to add at least one before you can add any hosts.",
"name": "Name",
"rules": "Rule count",
"description": "Description",
"actions": "Actions",
"add": {
"title": "Create Role",
"any": "Any",
"name": "Role name",
"desc": "Role description",
"button": "Create role",
"cancel": "Cancel",
"rules": "Inbound firewall rules",
"rulesexplainer": "Inbound traffic is denied by default. Add rules to allow traffic from hosts belonging to specific roles.",
"rulescols": {
"description": "Description",
"protocol": "Protocol",
"portrange": "Port range",
"allowedrole": "Allowed role",
"actions": "Actions"
},
"rulesadd": "Add rule",
"editrule": {
"protocol": "Protocol",
"range": "Port or port range",
"role": "Allowed role",
"desc": "Description",
"add": "Add rule",
"edit": "Save edit",
"cancel": "Cancel"
},
"error": {
"needsname": "Role Name is required"
}
},
"edit": {
"title": "Editing role {rule}",
"any": "Any",
"name": "Role name",
"desc": "Role description",
"button": "Save",
"cancel": "Cancel",
"rules": "Inbound firewall rules",
"rulesexplainer": "Inbound traffic is denied by default. Add rules to allow traffic from hosts belonging to specific roles.",
"rulescols": {
"description": "Description",
"protocol": "Protocol",
"portrange": "Port range",
"allowedrole": "Allowed role",
"actions": "Actions"
},
"ruleremove": "Remove rule",
"ruleedit": "Edit rule",
"rulesadd": "Add rule",
"editrule": {
"protocol": "Protocol",
"range": "Port or port range",
"role": "Allowed role",
"desc": "Description",
"add": "Add rule",
"edit": "Save edit",
"cancel": "Cancel"
}
}
},
"common": {
"title": "{title} | Trifid Web UI",
"page": {
"itworks": "It Works!",
"admin": "Admin Panel",
"login": "Login",
"ml": "Verify Magic Link",
"2fasetup": "Configure TOTP",
"2fa": "2-Factor Authentication",
"networkcreate": "Create Network",
"hosts": "Hosts",
"roles": "Roles",
"lighthouses": "Lighthouses",
"relays": "Relays",
"addhost": "Add Host"
},
"logout": "Log out",
"loading": "Dashboard is loading"
}
}

View File

@ -1,27 +1,61 @@
import i18n from 'sveltekit-i18n';
import type {Config} from 'sveltekit-i18n';
function buildLoader(locale: string, key: string, file: string) {
return {
locale: locale,
key: key,
loader: async () => ( await import (file)).default
}
}
import type {Config} from 'sveltekit-i18n'
const config: Config = ({
loaders: [
buildLoader('en', '2fa', './locales/en/2fa.json'),
buildLoader('en', '2fasetup', './locales/en/2fasetup.json'),
buildLoader('en', 'common', './locales/en/common.json'),
buildLoader('en', 'hosts', './locales/en/hosts.json'),
buildLoader('en', 'itworks', './locales/en/itworks.json'),
buildLoader('en', 'login', './locales/en/login.json'),
buildLoader('en', 'ml', './locales/en/ml.json'),
buildLoader('en', 'networkcreate', './locales/en/networkcreate.json'),
buildLoader('en', 'roles', './locales/en/roles.json'),
buildLoader('en', 'signup', './locales/en/signup.json'),
// ENGLISH //
{
locale: 'en',
key: '2fa',
loader: async () => (await import('./locales/en/2fa.json')).default
},
{
locale: 'en',
key: '2fasetup',
loader: async () => (await import('./locales/en/2fasetup.json')).default
},
{
locale: 'en',
key: 'common',
loader: async () => (await import('./locales/en/common.json')).default
},
{
locale: 'en',
key: 'hosts',
loader: async () => (await import('./locales/en/hosts.json')).default
},
{
locale: 'en',
key: 'itworks',
loader: async () => (await import('./locales/en/itworks.json')).default
},
{
locale: 'en',
key: 'login',
loader: async () => (await import('./locales/en/login.json')).default
},
{
locale: 'en',
key: 'ml',
loader: async () => (await import('./locales/en/ml.json')).default
},
{
locale: 'en',
key: 'networkcreate',
loader: async () => (await import('./locales/en/networkcreate.json')).default
},
{
locale: 'en',
key: 'roles',
loader: async () => (await import('./locales/en/roles.json')).default
},
{
locale: 'en',
key: 'signup',
loader: async () => (await import('./locales/en/signup.json')).default
},
// END ENGLISH //
]
})
export const { t, locale, locales, loading, loadTranslations } = new i18n(config);
export const { t, locale, locales, loading, loadTranslations } = new i18n(config);

View File

@ -6,12 +6,13 @@
import {onMount} from "svelte";
onMount(async () => {
const popper = await import("@popperjs/core");
const bootstrap = await import("bootstrap/dist/js/bootstrap.js");
document.onload(() => {
const tooltipTriggerList = document.querySelectorAll('[data-bs-toggle="tooltip"]')
const tooltipList = [...tooltipTriggerList].map(tooltipTriggerEl => new bootstrap.Tooltip(tooltipTriggerEl))
});
document.B = bootstrap;
document.P = popper;
})
</script>

View File

@ -1,9 +1,21 @@
import type { LayoutLoad } from './$types'
import {loadTranslations} from "$lib/i18n/translations";
import {browser} from "$app/environment";
export const load: LayoutLoad = async ({url}) => {
const { pathname } = url;
const initLocale = 'en';
let locale = 'en';
// determine locale
if (browser) {
if (window.localStorage.getItem("locale") != null) {
locale = window.localStorage.getItem("locale");
}
window.localStorage.setItem("locale", locale);
}
const initLocale = locale;
await loadTranslations(initLocale, pathname);
return {};
}

View File

@ -14,12 +14,12 @@
</script>
<svelte:head>
<title>{$t("common.title", {values: {title: $t("common.page.admin")}})}</title>
<title>{$t("common.title", {title: $t("common.page.admin")})}</title>
</svelte:head>
<LoadingWrapper isLoading={currentlyLoading} isError={isError} error={error}>
<h1>{$t('itworks.header')}</h1>
<p>{$t('itworks.body')}</p>
<!-- eslint-disable-next-line svelte/no-at-html-tags -->
<p>{@html $t('itworks.linkbody', {values:{link0:'<a href="/admin">'+$t('itworks.linkbody.link0')+'</a>',link1:'<a href="/signup">'+$t('itworks.linkbody.link1')+'</a>'}})}</p>
<p>{@html $t('itworks.linkbody', {link0:'<a href="/admin">'+$t('itworks.linkbody.link0')+'</a>',link1:'<a href="/signup">'+$t('itworks.linkbody.link1')+'</a>'})}</p>
</LoadingWrapper>

View File

@ -67,7 +67,7 @@
</script>
<svelte:head>
<title>{$t("common.title", {values: {title: $t("common.page.2fa")}})}</title>
<title>{$t("common.title", {title: $t("common.page.2fa")})}</title>
</svelte:head>
<LoadingWrapper isLoading={currentlyLoading} isError={isError} error={error}>

View File

@ -102,7 +102,7 @@
</script>
<svelte:head>
<title>{$t("common.title", {values: {title: $t("common.page.2fasetup")}})}</title>
<title>{$t("common.title", {title: $t("common.page.2fasetup")})}</title>
</svelte:head>
<LoadingWrapper isLoading={currentlyLoading} isError={isError} error={error}>

View File

@ -49,7 +49,7 @@
return;
} else {
isError = true;
error = $t("networkcreate.error.generic", {values:{err:resp_json.errors[0].code}});
error = $t("networkcreate.error.generic", {err:resp_json.errors[0].code});
loading = false;
return;
}
@ -68,7 +68,7 @@
</script>
<svelte:head>
<title>{$t("common.title", {values: {title: $t("common.page.admin")}})}</title>
<title>{$t("common.title", {title: $t("common.page.admin")})}</title>
</svelte:head>
<LoadingWrapper isLoading={currentlyLoading} isError={isError} error={error}>

View File

@ -10,6 +10,7 @@
import type {Host} from "$lib/api/models/Host.ts";
import type {Role} from "$lib/api/models/Role.ts";
import AdminLayout from "$components/AdminLayout.svelte";
import {updateTooltips} from "$lib/Tooltips";
let loading = true;
let isError = false;
@ -56,7 +57,7 @@
return;
} else {
isError = true;
error = $t("networkcreate.error.generic", {values:{err:resp_json.errors[0].code}});
error = $t("networkcreate.error.generic", {err:resp_json.errors[0].code});
loading = false;
return;
}
@ -83,6 +84,7 @@
roles = (await rolesApi.rolesList()).data!;
loading = false;
updateTooltips();
});
function getRoleName(byId: string): string {
@ -96,7 +98,7 @@
</script>
<svelte:head>
<title>{$t("common.title", {values: {title: $t("common.page.hosts")}})}</title>
<title>{$t("common.title", {title: $t("common.page.hosts")})}</title>
</svelte:head>
<LoadingWrapper isLoading={currentlyLoading} isError={isError} error={error}>
@ -118,16 +120,22 @@
{#each hosts as host}
{#if !(host.isLighthouse || host.isRelay)}
<tr>
<td><a href="/hosts/{host.id}/edit">{host.name}</a></td>
<td>
<a href="/hosts/{host.id}/edit">{host.name}</a>
{#if host.isBlocked}
<u class="text-danger" data-bs-toggle="tooltip" data-bs-title="Re-enroll to unblock"><i class="fas fa-fw fa-ban"></i> Blocked</u>
{/if}
</td>
<td>{host.metadata?.lastSeenAt}</td>
<td>{host.ipAddress}</td>
<td><a href="/roles/{host.roleID}/edit">{getRoleName(host.roleID)}</a></td>
<td>
<div class="btn-group">
<a href="/hosts/{host.id}/enroll" title="{$t('hosts.enrollbtn')}" class="btn btn-success"><i class="fas fa-arrows-rotate fa-fw"></i></a>
<a href="/hosts/{host.id}/edit" title="{$t('hosts.edit')}" class="btn btn-primary"><i class="fas fa-pencil fa-fw"></i></a>
<a href="/hosts/{host.id}/edit/config" title="{$t('hosts.config')}" class="btn btn-info"><i class="fas fa-gear fa-fw"></i></a>
<a href="/hosts/{host.id}/delete" title="{$t('hosts.deletehost')}" class="btn btn-danger"><i class="fas fa-trash fa-fw"></i></a>
<a href="/hosts/{host.id}/enroll" title="{$t('hosts.enrollbtn')}" data-bs-toggle="tooltip" class="btn btn-success"><i class="fas fa-arrows-rotate fa-fw"></i></a>
<a href="/hosts/{host.id}/edit" title="{$t('hosts.edit')}" data-bs-toggle="tooltip" class="btn btn-primary"><i class="fas fa-pencil fa-fw"></i></a>
<a href="/hosts/{host.id}/edit/config" title="{$t('hosts.config')}" data-bs-toggle="tooltip" class="btn btn-info"><i class="fas fa-gear fa-fw"></i></a>
<a href="/hosts/{host.id}/delete" title="{$t('hosts.deletehost')}" data-bs-toggle="tooltip" class="btn btn-danger"><i class="fas fa-trash fa-fw"></i></a>
<a href="/hosts/{host.id}/block" title="{$t('hosts.blockhost')}" data-bs-toggle="tooltip" class="btn btn-secondary"><i class="fas fa-ban fa-fw"></i></a>
</div>
</td>
</tr>

View File

@ -0,0 +1,136 @@
<script lang="ts">
import {loading as tLoading, t} from "$lib/i18n/translations";
import LoadingWrapper from "$components/LoadingWrapper.svelte";
import {onMount} from "svelte";
import {APIResult, isAuthedMFA, isAuthedSession} from "$lib/auth.ts";
import {Logger, logSetup} from "$lib/logger";
import type {APIError} from "$lib/auth.ts";
import {PUBLIC_BASE_URL} from "$env/static/public";
import {Configuration, NetworksApi, HostsApi, FirewallRuleProtocolEnum, ResponseError} from "$lib/api";
import type {FirewallRule} from "$lib/api";
import AdminBar from "$components/AdminLayout.svelte";
import {page} from "$app/stores";
import AdminLayout from "$components/AdminLayout.svelte";
import {load} from "../../../+layout";
import type {HostGet200Response} from "$lib/api";
let loading = true;
let fullPageLoading = true;
let isError = false;
let error = '';
$: currentlyLoading = $tLoading || fullPageLoading;
logSetup();
let logger = new Logger("hosts/block/+page.svelte");
let hosts;
let host: HostGet200Response = {
data: {
name: 'Loading'
}
};
let formErr = '';
let hasFormErr = false;
onMount(async () => {
let session_load_info = await isAuthedSession();
if (session_load_info[0] == APIResult.Failed) {
let err = session_load_info[1] as APIError;
logger.error(`session load failed: ${err.code} ${err.message}`);
window.location.href = '/login';
return;
}
let mfa_load_info = await isAuthedMFA();
if (mfa_load_info[0] == APIResult.Failed) {
let err = mfa_load_info[1] as APIError;
logger.error(`mfa load failed: ${err.code} ${err.message}`);
window.location.href = '/2fa';
return;
}
// pull networks
const configuration = new Configuration({
basePath: PUBLIC_BASE_URL,
accessToken: window.localStorage.getItem("session") + " " + window.localStorage.getItem("mfa")
});
const networksApi = new NetworksApi(configuration);
let networks;
try {
networks = await networksApi.networksList();
} catch (e) {
let resp_json = await e.response.json();
if (resp_json.errors[0].code == "ERR_NO_ORG") {
window.location.href = "/networkcreate";
return;
} else {
isError = true;
error = $t("networkcreate.error.generic", {err:resp_json.errors[0].code});
loading = false;
return;
}
}
console.log(networks);
if (networks.data?.length == 0) {
window.location.href = '/networkcreate';
return;
}
const hostsApi = new HostsApi(configuration);
hosts = await hostsApi.hostsList();
// pull our role
host = await hostsApi.hostGet({
hostID: $page.params.host_id
});
fullPageLoading = false;
loading = false;
});
async function blockRole() {
loading = true;
const configuration = new Configuration({
basePath: PUBLIC_BASE_URL,
accessToken: window.localStorage.getItem("session") + " " + window.localStorage.getItem("mfa")
});
const hostsApi = new HostsApi(configuration);
try {
await hostsApi.hostBlock({hostID: $page.params.host_id});
} catch (e) {
let body = await (<ResponseError>e).response.json();
console.log(body);
formErr = $t("hosts.block.error." + body.errors[0].code);
hasFormErr = true;
loading = false;
return;
}
window.location.href = "/hosts";
}
</script>
<svelte:head>
<title>{$t("common.title", {title: $t("common.page.hosts")})}</title>
</svelte:head>
<LoadingWrapper isLoading={currentlyLoading} isError={isError} error={error}>
<AdminLayout selected="hosts">
<h3>{$t("hosts.block.title", {host: host.data.name})}</h3>
<p>{$t("hosts.block.explainer")}</p>
{#if loading}
<button disabled class="btn btn-danger"><i class="fas fa-gear fa-spin"></i></button>
{:else}
<button on:click={blockRole} class="btn btn-danger">{$t("hosts.block.confirm")}</button>
{/if}
<button on:click={() => {window.location.href = "/hosts"}} class="btn btn-outline-info">{$t("hosts.block.cancel")}</button>
{#if hasFormErr}
<p class="text-danger">{formErr}</p>
{/if}
</AdminLayout>
</LoadingWrapper>

View File

@ -67,7 +67,7 @@
return;
} else {
isError = true;
error = $t("networkcreate.error.generic", {values:{err:resp_json.errors[0].code}});
error = $t("networkcreate.error.generic", {err:resp_json.errors[0].code});
loading = false;
return;
}
@ -116,12 +116,12 @@
</script>
<svelte:head>
<title>{$t("common.title", {values: {title: $t("common.page.hosts")}})}</title>
<title>{$t("common.title", {title: $t("common.page.hosts")})}</title>
</svelte:head>
<LoadingWrapper isLoading={currentlyLoading} isError={isError} error={error}>
<AdminLayout selected="hosts">
<h3>{$t("hosts.delete.title", {values: {host: host.data.name}})}</h3>
<h3>{$t("hosts.delete.title", {host: host.data.name})}</h3>
<p>{$t("hosts.delete.explainer")}</p>
{#if loading}
<button disabled class="btn btn-danger"><i class="fas fa-gear fa-spin"></i></button>

View File

@ -68,7 +68,7 @@
return;
} else {
isError = true;
error = $t("networkcreate.error.generic", {values:{err:resp_json.errors[0].code}});
error = $t("networkcreate.error.generic", {err:resp_json.errors[0].code});
loading = false;
return;
}
@ -100,17 +100,17 @@
</script>
<svelte:head>
<title>{$t("common.title", {values: {title: $t("common.page.hosts")}})}</title>
<title>{$t("common.title", {title: $t("common.page.hosts")})}</title>
</svelte:head>
<LoadingWrapper isLoading={currentlyLoading} isError={isError} error={error}>
<AdminLayout selected="hosts">
<h3>{$t("hosts.enroll.title", {values: {host: host.data.name}})}</h3>
<h3>{$t("hosts.enroll.title", {host: host.data.name})}</h3>
<p>{$t("hosts.enroll.explainer")}</p>
{#if loading}
<button disabled class="btn btn-success"><i class="fas fa-gear fa-spin"></i></button>
{:else}
<p>{$t("hosts.enroll.code", {values:{code: code.code, expires: code.lifetimeSeconds / 60}})}</p>
<p>{$t("hosts.enroll.code", {code: code.code, expires: code.lifetimeSeconds / 60})}</p>
<button on:click={window.location.href = "/hosts"} class="btn btn-success">{$t("hosts.enroll.done")}</button>
{/if}

View File

@ -59,7 +59,7 @@
return;
} else {
isError = true;
error = $t("networkcreate.error.generic", {values:{err:resp_json.errors[0].code}});
error = $t("networkcreate.error.generic", {err:resp_json.errors[0].code});
loading = false;
return;
}
@ -164,7 +164,7 @@
</script>
<svelte:head>
<title>{$t("common.title", {values: {title: $t("common.page.hosts")}})}</title>
<title>{$t("common.title", {title: $t("common.page.hosts")})}</title>
</svelte:head>
<LoadingWrapper isLoading={currentlyLoading} isError={isError} error={error}>

View File

@ -51,7 +51,7 @@
return;
} else {
isError = true;
error = $t("networkcreate.error.generic", {values:{err:resp_json.errors[0].code}});
error = $t("networkcreate.error.generic", {err:resp_json.errors[0].code});
loading = false;
return;
}
@ -68,7 +68,7 @@
</script>
<svelte:head>
<title>{$t("common.title", {values: {title: $t("common.page.lighthouses")}})}</title>
<title>{$t("common.title", {title: $t("common.page.lighthouses")})}</title>
</svelte:head>
<LoadingWrapper isLoading={currentlyLoading} isError={isError} error={error}>

View File

@ -55,7 +55,7 @@
if (err.code == "ERR_USER_DOES_NOT_EXIST") {
errForm = $t('login.error.usermissing');
} else {
errForm = $t('login.error.generic', {values: {err: (auth_result[1] as APIError).code}});
errForm = $t('login.error.generic', {err: (auth_result[1] as APIError).code});
}
loading = false;
@ -68,7 +68,7 @@
</script>
<svelte:head>
<title>{$t("common.title", {values: {title: $t("common.page.login")}})}</title>
<title>{$t("common.title", {title: $t("common.page.login")})}</title>
</svelte:head>
<LoadingWrapper isLoading={currentlyLoading} isError={isError} error={error}>
@ -80,7 +80,7 @@
<p class="card-text">{$t('login.emailbody')}</p>
<!-- eslint-disable-next-line svelte/no-at-html-tags -->
<p class="card-text">{@html $t('login.emailbody2', {values:{link0:'<a href="/admin?definitely_not_taking_advantage_of_the_redirect">'+$t('login.emailbody2.link0')+'</a>'}})}</p>
<p class="card-text">{@html $t('login.emailbody2', {link0:'<a href="/admin?definitely_not_taking_advantage_of_the_redirect">'+$t('login.emailbody2.link0')+'</a>'})}</p>
{:else}
<h4 class="card-title">{$t('login.title')}</h4>
<h6 class="card-subtitle">{$t('login.subtitle')}</h6>
@ -99,7 +99,7 @@
{/if}
</form>
<!-- eslint-disable-next-line svelte/no-at-html-tags -->
<p class="block mt-2 mb-0">{@html $t('login.need', {values:{link0:'<a href="/signup">'+$t('login.need.link0')+'</a>'}})}</p>
<p class="block mt-2 mb-0">{@html $t('login.need', {link0:'<a href="/signup">'+$t('login.need.link0')+'</a>'})}</p>
{/if}
</div>
</div>

View File

@ -45,7 +45,7 @@
</script>
<svelte:head>
<title>{$t("common.title", {values: {title: $t("common.page.login")}})}</title>
<title>{$t("common.title", {title: $t("common.page.login")})}</title>
</svelte:head>
<LoadingWrapper isLoading={currentlyLoading} isError={isError} error={error}>

View File

@ -1,5 +1,5 @@
<script lang="ts">
import {isLoading, number, t} from "svelte-i18n";
import {loading as tLoading, t} from "$lib/i18n/translations";
import LoadingWrapper from "$components/LoadingWrapper.svelte";
import {onMount} from "svelte";
import {APIResult, authTotp, isAuthedMFA, isAuthedSession} from "$lib/auth.ts";
@ -47,7 +47,7 @@
let resp_json = await e.response.json();
if (resp_json.errors[0].code != "ERR_NO_ORG") {
isError = true;
error = $t("networkcreate.error.generic", {values:{err:resp_json.errors[0].code}});
error = $t("networkcreate.error.generic", {err:resp_json.errors[0].code});
loading = false;
return;
}
@ -63,7 +63,7 @@
let cidr = "100.100.0.0/22";
let valid = true;
let sub = $t("networkcreate.valid", {values:{numIps:$number(1024), start: "100.64.0.0", end: "100.127.255.255"}})
let sub = $t("networkcreate.valid", {numIps:1024, start: "100.64.0.0", end: "100.127.255.255"})
const regex = new RegExp(/^([0-9]{1,3}\.){3}[0-9]{1,3}(\/([0-9]|[1-2][0-9]|3[0-2]))?$/im);
@ -138,7 +138,7 @@
let first = [first_num >>> 24 & 0xFF, first_num >>> 16 & 0xFF, first_num >>> 8 & 0xFF, first_num & 0xFF].join(".");
let last = [last_num >>> 24 & 0xFF, last_num >>> 16 & 0xFF, last_num >>> 8 & 0xFF, last_num & 0xFF].join(".");
sub = $t("networkcreate.valid", {values:{numIps:$number(addressCount), start: first, end: last}});
sub = $t("networkcreate.valid", {numIps:addressCount, start: first, end: last});
valid = true;
}
@ -161,7 +161,7 @@
return;
}
error = $t('networkcreate.error.generic', {values:{err:etext}});
error = $t('networkcreate.error.generic', {err:etext});
loading = false;
return;
}
@ -173,7 +173,7 @@
</script>
<svelte:head>
<title>{$t("common.title", {values: {title: $t("common.page.networkcreate")}})}</title>
<title>{$t("common.title", {title: $t("common.page.networkcreate")})}</title>
</svelte:head>
<LoadingWrapper isLoading={currentlyLoading} isError={isError} error={error}>

View File

@ -51,7 +51,7 @@
return;
} else {
isError = true;
error = $t("networkcreate.error.generic", {values:{err:resp_json.errors[0].code}});
error = $t("networkcreate.error.generic", {err:resp_json.errors[0].code});
loading = false;
return;
}
@ -68,7 +68,7 @@
</script>
<svelte:head>
<title>{$t("common.title", {values: {title: $t("common.page.relays")}})}</title>
<title>{$t("common.title", {title: $t("common.page.relays")})}</title>
</svelte:head>
<LoadingWrapper isLoading={currentlyLoading} isError={isError} error={error}>

View File

@ -8,6 +8,7 @@
import {PUBLIC_BASE_URL} from "$env/static/public";
import {Configuration, NetworksApi, RolesApi} from "$lib/api";
import AdminLayout from "$components/AdminLayout.svelte";
import {updateTooltips} from "$lib/Tooltips";
let loading = true;
let isError = false;
@ -53,7 +54,7 @@
return;
} else {
isError = true;
error = $t("networkcreate.error.generic", {values:{err:resp_json.errors[0].code}});
error = $t("networkcreate.error.generic", {err:resp_json.errors[0].code});
loading = false;
return;
}
@ -71,6 +72,7 @@
console.log(roles);
loading = false;
updateTooltips();
})
async function roleAdd() {
@ -80,7 +82,7 @@
</script>
<svelte:head>
<title>{$t("common.title", {values: {title: $t("common.page.roles")}})}</title>
<title>{$t("common.title", {title: $t("common.page.roles")})}</title>
</svelte:head>
<LoadingWrapper isLoading={currentlyLoading} isError={isError} error={error}>
@ -115,8 +117,8 @@
<td>{role.description}</td>
<td>
<div class="btn-group">
<a href="/roles/{role.id}/edit" class="btn btn-primary"><i class="fas fa-pencil fa-fw"></i></a>
<a href="/roles/{role.id}/delete" class="btn btn-danger"><i class="fas fa-trash fa-fw"></i></a>
<a href="/roles/{role.id}/edit" title="{$t('roles.editrole')}" data-bs-toggle="tooltip" class="btn btn-primary"><i class="fas fa-pencil fa-fw"></i></a>
<a href="/roles/{role.id}/delete" title="{$t('roles.deleterole')}" data-bs-toggle="tooltip" class="btn btn-danger"><i class="fas fa-trash fa-fw"></i></a>
</div>
</td>

View File

@ -68,7 +68,7 @@
return;
} else {
isError = true;
error = $t("networkcreate.error.generic", {values:{err:resp_json.errors[0].code}});
error = $t("networkcreate.error.generic", {err:resp_json.errors[0].code});
loading = false;
return;
}
@ -117,12 +117,12 @@
</script>
<svelte:head>
<title>{$t("common.title", {values: {title: $t("common.page.roles")}})}</title>
<title>{$t("common.title", {title: $t("common.page.roles")})}</title>
</svelte:head>
<LoadingWrapper isLoading={currentlyLoading} isError={isError} error={error}>
<AdminLayout selected="roles">
<h3>{$t("roles.delete.title", {values: {rule: role.data.name}})}</h3>
<h3>{$t("roles.delete.title", {rule: role.data.name})}</h3>
<p>{$t("roles.delete.explainer")}</p>
{#if loading}
<button disabled class="btn btn-danger"><i class="fas fa-gear fa-spin"></i></button>

View File

@ -276,7 +276,7 @@
return;
} else {
isError = true;
error = $t("networkcreate.error.generic", {values:{err:resp_json.errors[0].code}});
error = $t("networkcreate.error.generic", {err:resp_json.errors[0].code});
loading = false;
return;
}
@ -346,12 +346,12 @@
</script>
<svelte:head>
<title>{$t("common.title", {values: {title: $t("common.page.roles")}})}</title>
<title>{$t("common.title", {title: $t("common.page.roles")})}</title>
</svelte:head>
<LoadingWrapper isLoading={currentlyLoading} isError={isError} error={error}>
<AdminLayout selected="roles">
<h3>{$t("roles.edit.title", {values: {rule: roleName}})}</h3>
<h3>{$t("roles.edit.title", {rule: roleName})}</h3>
<form>
<label class="mt-2 form-label" for="roleDesc">{$t("roles.edit.desc")}</label>
<input class="form-control" bind:value={roleDescription} type="text" id="roleDesc" />

View File

@ -58,7 +58,7 @@
return;
} else {
isError = true;
error = $t("networkcreate.error.generic", {values:{err:resp_json.errors[0].code}});
error = $t("networkcreate.error.generic", {err:resp_json.errors[0].code});
loading = false;
return;
}
@ -306,7 +306,7 @@
</script>
<svelte:head>
<title>{$t("common.title", {values: {title: $t("common.page.roles")}})}</title>
<title>{$t("common.title", {title: $t("common.page.roles")})}</title>
</svelte:head>
<LoadingWrapper isLoading={currentlyLoading} isError={isError} error={error}>

View File

@ -53,9 +53,9 @@
let err = auth_result[1] as APIError;
if (err.code == "ERR_USER_EXISTS") {
errForm = $t('signup.error.userexists', {values:{link0:'<a href="/login">'+$t('signup.error.userexists.link0')+'</a>'}});
errForm = $t('signup.error.userexists', {link0:'<a href="/login">'+$t('signup.error.userexists.link0')+'</a>'});
} else {
errForm = $t('signup.error.generic', {values: {err: (auth_result[1] as APIError).code}});
errForm = $t('signup.error.generic', {err: (auth_result[1] as APIError).code});
}
loading = false;
@ -68,7 +68,7 @@
</script>
<svelte:head>
<title>{$t("common.title", {values: {title: $t("common.page.signup")}})}</title>
<title>{$t("common.title", {title: $t("common.page.signup")})}</title>
</svelte:head>
<LoadingWrapper isLoading={currentlyLoading} isError={isError} error={error}>
@ -80,7 +80,7 @@
<p class="card-text">{$t('signup.emailbody')}</p>
<!-- eslint-disable-next-line svelte/no-at-html-tags -->
<p class="card-text">{@html $t('signup.emailbody2', {values:{link0:'<a href="/admin?definitely_not_taking_advantage_of_the_redirect">'+$t('signup.emailbody2.link0')+'</a>'}})}</p>
<p class="card-text">{@html $t('signup.emailbody2', {link0:'<a href="/admin?definitely_not_taking_advantage_of_the_redirect">'+$t('signup.emailbody2.link0')+'</a>'})}</p>
{:else}
<h4 class="card-title">{$t('signup.title')}</h4>
<h6 class="card-subtitle">{$t('signup.subtitle')}</h6>
@ -100,7 +100,7 @@
{/if}
</form>
<!-- eslint-disable-next-line svelte/no-at-html-tags -->
<p class="block mt-2 mb-0">{@html $t('signup.already', {values:{link0:'<a href="/login">'+$t('signup.already.link0')+'</a>'}})}</p>
<p class="block mt-2 mb-0">{@html $t('signup.already', {link0:'<a href="/login">'+$t('signup.already.link0')+'</a>'})}</p>
{/if}
</div>

View File

@ -1,4 +1,4 @@
import adapter from '@sveltejs/adapter-auto';
import adapter from '@sveltejs/adapter-node';
import { vitePreprocess } from '@sveltejs/kit/vite';
/** @type {import('@sveltejs/kit').Config} */

View File

@ -222,6 +222,46 @@
resolved "https://registry.yarnpkg.com/@popperjs/core/-/core-2.11.8.tgz#6b79032e760a0899cd4204710beede972a3a185f"
integrity sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==
"@rollup/plugin-commonjs@^25.0.0":
version "25.0.5"
resolved "https://registry.yarnpkg.com/@rollup/plugin-commonjs/-/plugin-commonjs-25.0.5.tgz#0bac8f985a5de151b4b09338847f8c7f20a28a29"
integrity sha512-xY8r/A9oisSeSuLCTfhssyDjo9Vp/eDiRLXkg1MXCcEEgEjPmLU+ZyDB20OOD0NlyDa/8SGbK5uIggF5XTx77w==
dependencies:
"@rollup/pluginutils" "^5.0.1"
commondir "^1.0.1"
estree-walker "^2.0.2"
glob "^8.0.3"
is-reference "1.2.1"
magic-string "^0.27.0"
"@rollup/plugin-json@^6.0.0":
version "6.0.1"
resolved "https://registry.yarnpkg.com/@rollup/plugin-json/-/plugin-json-6.0.1.tgz#7e2efcf5ed549963f1444e010611d22f463931c0"
integrity sha512-RgVfl5hWMkxN1h/uZj8FVESvPuBJ/uf6ly6GTj0GONnkfoBN5KC0MSz+PN2OLDgYXMhtG0mWpTrkiOjoxAIevw==
dependencies:
"@rollup/pluginutils" "^5.0.1"
"@rollup/plugin-node-resolve@^15.0.1":
version "15.2.3"
resolved "https://registry.yarnpkg.com/@rollup/plugin-node-resolve/-/plugin-node-resolve-15.2.3.tgz#e5e0b059bd85ca57489492f295ce88c2d4b0daf9"
integrity sha512-j/lym8nf5E21LwBT4Df1VD6hRO2L2iwUeUmP7litikRsVp1H6NWx20NEp0Y7su+7XGc476GnXXc4kFeZNGmaSQ==
dependencies:
"@rollup/pluginutils" "^5.0.1"
"@types/resolve" "1.20.2"
deepmerge "^4.2.2"
is-builtin-module "^3.2.1"
is-module "^1.0.0"
resolve "^1.22.1"
"@rollup/pluginutils@^5.0.1":
version "5.0.5"
resolved "https://registry.yarnpkg.com/@rollup/pluginutils/-/pluginutils-5.0.5.tgz#bbb4c175e19ebfeeb8c132c2eea0ecb89941a66c"
integrity sha512-6aEYR910NyP73oHiJglti74iRyOwgFU4x3meH/H8OJx6Ry0j6cOVZ5X/wTvub7G7Ao6qaHBEaNsV3GLJkSsF+Q==
dependencies:
"@types/estree" "^1.0.0"
estree-walker "^2.0.2"
picomatch "^2.3.1"
"@sveltejs/adapter-auto@^2.0.0":
version "2.1.0"
resolved "https://registry.yarnpkg.com/@sveltejs/adapter-auto/-/adapter-auto-2.1.0.tgz#cb36fd7081e3c4b1c9a9192b1a23c8c82dce8a1b"
@ -229,6 +269,16 @@
dependencies:
import-meta-resolve "^3.0.0"
"@sveltejs/adapter-node@^1.3.1":
version "1.3.1"
resolved "https://registry.yarnpkg.com/@sveltejs/adapter-node/-/adapter-node-1.3.1.tgz#b3d8dee1ae09d008b2add093e84cf085f3333bbc"
integrity sha512-A0VgRQDCDPzdLNoiAbcOxGw4zT1Mc+n1LwT1OmO350R7WxrEqdMUChPPOd1iMfIDWlP4ie6E2d/WQf5es2d4Zw==
dependencies:
"@rollup/plugin-commonjs" "^25.0.0"
"@rollup/plugin-json" "^6.0.0"
"@rollup/plugin-node-resolve" "^15.0.1"
rollup "^3.7.0"
"@sveltejs/kit@^1.5.0":
version "1.20.0"
resolved "https://registry.yarnpkg.com/@sveltejs/kit/-/kit-1.20.0.tgz#5deed969badda2cd9d4c68c580362c492425be75"
@ -283,6 +333,11 @@
resolved "https://registry.yarnpkg.com/@types/cookie/-/cookie-0.5.1.tgz#b29aa1f91a59f35e29ff8f7cb24faf1a3a750554"
integrity sha512-COUnqfB2+ckwXXSFInsFdOAWQzCCx+a5hq2ruyj+Vjund94RJQd4LG2u9hnvJrTgunKAaax7ancBYlDrNYxA0g==
"@types/estree@*", "@types/estree@^1.0.0":
version "1.0.2"
resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.2.tgz#ff02bc3dc8317cd668dfec247b750ba1f1d62453"
integrity sha512-VeiPZ9MMwXjO32/Xu7+OwflfmeoRwkE/qzndw42gGtgJwZopBnzy2gD//NN1+go1mADzkDcqf/KnFRSjTJ8xJA==
"@types/json-schema@^7.0.9":
version "7.0.12"
resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.12.tgz#d70faba7039d5fca54c83c7dbab41051d2b6f6cb"
@ -305,6 +360,11 @@
dependencies:
"@types/node" "*"
"@types/resolve@1.20.2":
version "1.20.2"
resolved "https://registry.yarnpkg.com/@types/resolve/-/resolve-1.20.2.tgz#97d26e00cd4a0423b4af620abecf3e6f442b7975"
integrity sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==
"@types/semver@^7.3.12":
version "7.5.0"
resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.5.0.tgz#591c1ce3a702c45ee15f47a42ade72c2fd78978a"
@ -472,6 +532,13 @@ brace-expansion@^1.1.7:
balanced-match "^1.0.0"
concat-map "0.0.1"
brace-expansion@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-2.0.1.tgz#1edc459e0f0c548486ecf9fc99f2221364b9a0ae"
integrity sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==
dependencies:
balanced-match "^1.0.0"
braces@^3.0.2, braces@~3.0.2:
version "3.0.2"
resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107"
@ -484,6 +551,11 @@ buffer-crc32@^0.2.5:
resolved "https://registry.yarnpkg.com/buffer-crc32/-/buffer-crc32-0.2.13.tgz#0d333e3f00eac50aa1454abd30ef8c2a5d9a7242"
integrity sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==
builtin-modules@^3.3.0:
version "3.3.0"
resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-3.3.0.tgz#cae62812b89801e9656336e46223e030386be7b6"
integrity sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==
busboy@^1.6.0:
version "1.6.0"
resolved "https://registry.yarnpkg.com/busboy/-/busboy-1.6.0.tgz#966ea36a9502e43cdb9146962523b92f531f6893"
@ -545,6 +617,11 @@ color-name@~1.1.4:
resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2"
integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==
commondir@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b"
integrity sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==
concat-map@0.0.1:
version "0.0.1"
resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
@ -581,7 +658,7 @@ deep-is@^0.1.3:
resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.4.tgz#a6f2dce612fadd2ef1f519b73551f17e85199831"
integrity sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==
deepmerge@^4.3.1:
deepmerge@^4.2.2, deepmerge@^4.3.1:
version "4.3.1"
resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.3.1.tgz#44b5f2147cd3b00d4b56137685966f26fd25dd4a"
integrity sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==
@ -782,6 +859,11 @@ estraverse@^5.1.0, estraverse@^5.2.0:
resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.3.0.tgz#2eea5290702f26ab8fe5370370ff86c965d21123"
integrity sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==
estree-walker@^2.0.2:
version "2.0.2"
resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-2.0.2.tgz#52f010178c2a4c117a7757cfe942adb7d2da4cac"
integrity sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==
esutils@^2.0.2, esutils@^2.0.3:
version "2.0.3"
resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64"
@ -904,6 +986,17 @@ glob@^7.1.3:
once "^1.3.0"
path-is-absolute "^1.0.0"
glob@^8.0.3:
version "8.1.0"
resolved "https://registry.yarnpkg.com/glob/-/glob-8.1.0.tgz#d388f656593ef708ee3e34640fdfb99a9fd1c33e"
integrity sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==
dependencies:
fs.realpath "^1.0.0"
inflight "^1.0.4"
inherits "2"
minimatch "^5.0.1"
once "^1.3.0"
globals@^13.19.0:
version "13.20.0"
resolved "https://registry.yarnpkg.com/globals/-/globals-13.20.0.tgz#ea276a1e508ffd4f1612888f9d1bad1e2717bf82"
@ -953,6 +1046,11 @@ has-flag@^4.0.0:
resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b"
integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==
has@^1.0.3:
version "1.0.4"
resolved "https://registry.yarnpkg.com/has/-/has-1.0.4.tgz#2eb2860e000011dae4f1406a86fe80e530fb2ec6"
integrity sha512-qdSAmqLF6209RFj4VVItywPMbm3vWylknmB3nvNiUIs72xAimcM8nVYxYr7ncvZq5qzk9MKIZR8ijqD/1QuYjQ==
ignore@^5.2.0:
version "5.2.4"
resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.2.4.tgz#a291c0c6178ff1b960befe47fcdec301674a6324"
@ -996,6 +1094,20 @@ is-binary-path@~2.1.0:
dependencies:
binary-extensions "^2.0.0"
is-builtin-module@^3.2.1:
version "3.2.1"
resolved "https://registry.yarnpkg.com/is-builtin-module/-/is-builtin-module-3.2.1.tgz#f03271717d8654cfcaf07ab0463faa3571581169"
integrity sha512-BSLE3HnV2syZ0FK0iMA/yUGplUeMmNz4AW5fnTunbCIqZi4vG3WjJT9FHMy5D69xmAYBHXQhJdALdpwVxV501A==
dependencies:
builtin-modules "^3.3.0"
is-core-module@^2.13.0:
version "2.13.0"
resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.13.0.tgz#bb52aa6e2cbd49a30c2ba68c42bf3435ba6072db"
integrity sha512-Z7dk6Qo8pOCp3l4tsX2C5ZVas4V+UxwQodwZhLopL91TX8UyyHEXafPcyoeeWuLrwzHcr3igO78wNLwHJHsMCQ==
dependencies:
has "^1.0.3"
is-extglob@^2.1.1:
version "2.1.1"
resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2"
@ -1013,6 +1125,11 @@ is-glob@^4.0.0, is-glob@^4.0.1, is-glob@^4.0.3, is-glob@~4.0.1:
dependencies:
is-extglob "^2.1.1"
is-module@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/is-module/-/is-module-1.0.0.tgz#3258fb69f78c14d5b815d664336b4cffb6441591"
integrity sha512-51ypPSPCoTEIN9dy5Oy+h4pShgJmPCygKfyRCISBI+JoWT/2oJvK8QPxmwv7b/p239jXrm9M1mlQbyKJ5A152g==
is-number@^7.0.0:
version "7.0.0"
resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b"
@ -1023,6 +1140,13 @@ is-path-inside@^3.0.3:
resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-3.0.3.tgz#d231362e53a07ff2b0e0ea7fed049161ffd16283"
integrity sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==
is-reference@1.2.1:
version "1.2.1"
resolved "https://registry.yarnpkg.com/is-reference/-/is-reference-1.2.1.tgz#8b2dac0b371f4bc994fdeaba9eb542d03002d0b7"
integrity sha512-U82MsXXiFIrjCK4otLT+o2NA2Cd2g5MLoOVXUZjIOhLurrRxpEXzI8O0KZHr3IjLvlAH1kTPYSuqer5T9ZVBKQ==
dependencies:
"@types/estree" "*"
isexe@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10"
@ -1138,6 +1262,13 @@ minimatch@^3.0.5, minimatch@^3.1.1, minimatch@^3.1.2:
dependencies:
brace-expansion "^1.1.7"
minimatch@^5.0.1:
version "5.1.6"
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-5.1.6.tgz#1cfcb8cf5522ea69952cd2af95ae09477f122a96"
integrity sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==
dependencies:
brace-expansion "^2.0.1"
minimist@^1.2.0, minimist@^1.2.6:
version "1.2.8"
resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c"
@ -1259,6 +1390,11 @@ path-key@^3.1.0:
resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375"
integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==
path-parse@^1.0.7:
version "1.0.7"
resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735"
integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==
path-type@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b"
@ -1348,6 +1484,15 @@ resolve-from@^4.0.0:
resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6"
integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==
resolve@^1.22.1:
version "1.22.6"
resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.6.tgz#dd209739eca3aef739c626fea1b4f3c506195362"
integrity sha512-njhxM7mV12JfufShqGy3Rz8j11RPdLy4xi15UurGJeoHLfJpVXKdh3ueuOqbYUcDZnffr6X739JBo5LzyahEsw==
dependencies:
is-core-module "^2.13.0"
path-parse "^1.0.7"
supports-preserve-symlinks-flag "^1.0.0"
reusify@^1.0.4:
version "1.0.4"
resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76"
@ -1374,6 +1519,13 @@ rollup@^3.21.0:
optionalDependencies:
fsevents "~2.3.2"
rollup@^3.7.0:
version "3.29.4"
resolved "https://registry.yarnpkg.com/rollup/-/rollup-3.29.4.tgz#4d70c0f9834146df8705bfb69a9a19c9e1109981"
integrity sha512-oWzmBZwvYrU0iJHtDmhsm662rC15FRXmcjCk1xD771dFDx5jJ02ufAQQTn0etB2emNk4J9EZg/yWKpsn9BWGRw==
optionalDependencies:
fsevents "~2.3.2"
run-parallel@^1.1.9:
version "1.2.0"
resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.2.0.tgz#66d1368da7bdf921eb9d95bd1a9229e7f21a43ee"
@ -1496,6 +1648,11 @@ supports-color@^7.1.0:
dependencies:
has-flag "^4.0.0"
supports-preserve-symlinks-flag@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09"
integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==
svelte-check@^3.0.1:
version "3.4.3"
resolved "https://registry.yarnpkg.com/svelte-check/-/svelte-check-3.4.3.tgz#591c66568d227b22e6dab21de1dfc250ce2109d2"

View File

@ -1,6 +1,6 @@
[package]
name = "trifid-api"
version = "0.2.2"
version = "0.2.3"
edition = "2021"
description = "Pure-rust Defined Networking compatible management server"
license = "GPL-3.0-or-later"

View File

@ -232,7 +232,7 @@ pub async fn dnclient(
}
};
let mut config_is_different = current_cfg == cfg;
let mut config_is_different = current_cfg != cfg;
if config_is_different {
// check if it is a certificate issue